More moves implemented

This commit is contained in:
Deukhoofd 2025-02-03 11:40:26 +01:00
parent 0c5ca487d7
commit 51dfc4d07e
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
40 changed files with 639 additions and 65 deletions

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
@ -62,7 +63,9 @@ public static class AbilityDataLoader
StringKey? effectName = effect == null ? null! : new StringKey(effect); StringKey? effectName = effect == null ? null! : new StringKey(effect);
var ability = new AbilityImpl(name, effectName, parameters); var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet();
var ability = new AbilityImpl(name, effectName, parameters, flags);
return ability; return ability;
} }
} }

View File

@ -7,4 +7,5 @@ public class SerializedAbility
{ {
public string? Effect { get; set; } public string? Effect { get; set; }
public Dictionary<string, JsonNode> Parameters { get; set; } = new(); public Dictionary<string, JsonNode> Parameters { get; set; } = new();
public string[] Flags { get; set; } = [];
} }

View File

@ -110,6 +110,8 @@ public interface IBattle : IScriptSource, IDeepCloneable
/// </summary> /// </summary>
void SetWeather(StringKey? weatherName); void SetWeather(StringKey? weatherName);
public IScriptSet Volatile { get; }
/// <summary> /// <summary>
/// Gets the current weather of the battle. If no weather is present, this returns null. /// Gets the current weather of the battle. If no weather is present, this returns null.
/// </summary> /// </summary>
@ -359,7 +361,7 @@ public class BattleImpl : ScriptSource, IBattle
// TODO: Trigger weather change script hooks // TODO: Trigger weather change script hooks
} }
private IScriptSet Volatile { get; } = new ScriptSet(); public IScriptSet Volatile { get; } = new ScriptSet();
/// <inheritdoc /> /// <inheritdoc />
public StringKey? WeatherName => _weatherScript.Script?.Name; public StringKey? WeatherName => _weatherScript.Script?.Name;

View File

@ -102,5 +102,5 @@ public class BattleChoiceQueue : IDeepCloneable
internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices; internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices;
public ITurnChoice? Where(Func<ITurnChoice, bool> predicate) => _choices.WhereNotNull().FirstOrDefault(predicate); public ITurnChoice? FirstOrDefault(Func<ITurnChoice, bool> predicate) => _choices.WhereNotNull().FirstOrDefault(predicate);
} }

View File

@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.Models;
public interface IBattleRandom : IRandom, IDeepCloneable public interface IBattleRandom : IRandom, IDeepCloneable
{ {
/// <summary> /// <summary>
/// Gets whether or not a move triggers its secondary effect. This takes its chance, and /// Gets whether or not a move triggers its secondary effect. This takes its chance as percent, and
/// rolls whether it triggers. As a side effect this run scripts to allow modifying this random /// rolls whether it triggers. As a side effect this run scripts to allow modifying this random
/// chance. /// chance.
/// </summary> /// </summary>

View File

@ -374,6 +374,8 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// </summary> /// </summary>
void SetTypes(IReadOnlyList<TypeIdentifier> types); void SetTypes(IReadOnlyList<TypeIdentifier> types);
void ChangeAbility(IAbility ability);
/// <summary> /// <summary>
/// Converts the data structure to a serializable format. /// Converts the data structure to a serializable format.
/// </summary> /// </summary>
@ -409,7 +411,7 @@ public interface IPokemonBattleData : IDeepCloneable
/// <summary> /// <summary>
/// Whether the Pokémon is on the battlefield. /// Whether the Pokémon is on the battlefield.
/// </summary> /// </summary>
bool IsOnBattlefield { get; set; } bool IsOnBattlefield { get; internal set; }
/// <summary> /// <summary>
/// Adds an opponent to the list of seen opponents. /// Adds an opponent to the list of seen opponents.
@ -425,6 +427,8 @@ public interface IPokemonBattleData : IDeepCloneable
/// Marks an item as consumed. /// Marks an item as consumed.
/// </summary> /// </summary>
void MarkItemAsConsumed(IItem itemName); void MarkItemAsConsumed(IItem itemName);
uint SwitchInTurn { get; internal set; }
} }
/// <inheritdoc cref="IPokemon"/> /// <inheritdoc cref="IPokemon"/>
@ -720,6 +724,15 @@ public class PokemonImpl : ScriptSource, IPokemon
return false; return false;
if (!Library.ScriptResolver.TryResolveBattleItemScript(HeldItem, out _)) if (!Library.ScriptResolver.TryResolveBattleItemScript(HeldItem, out _))
return false; return false;
if (BattleData != null)
{
var prevented = false;
this.RunScriptHook(script => script.PreventHeldItemConsume(this, HeldItem, ref prevented));
if (prevented)
return false;
}
// TODO: actually consume the item // TODO: actually consume the item
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -885,6 +898,13 @@ public class PokemonImpl : ScriptSource, IPokemon
// If the Pokémon is already fainted, we don't need to do anything. // If the Pokémon is already fainted, we don't need to do anything.
if (IsFainted) if (IsFainted)
return; return;
if (BattleData is not null)
{
var dmg = damage;
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
damage = dmg;
}
// If the damage is more than the current health, we cap it at the current health, to prevent // If the damage is more than the current health, we cap it at the current health, to prevent
// underflow. // underflow.
if (damage >= CurrentHealth) if (damage >= CurrentHealth)
@ -1013,10 +1033,11 @@ public class PokemonImpl : ScriptSource, IPokemon
{ {
BattleData.Battle = battle; BattleData.Battle = battle;
BattleData.SideIndex = sideIndex; BattleData.SideIndex = sideIndex;
BattleData.SwitchInTurn = battle.CurrentTurnNumber;
} }
else else
{ {
BattleData = new PokemonBattleDataImpl(battle, sideIndex); BattleData = new PokemonBattleDataImpl(battle, sideIndex, battle.CurrentTurnNumber);
} }
} }
@ -1068,6 +1089,12 @@ public class PokemonImpl : ScriptSource, IPokemon
_types = types.ToList(); _types = types.ToList();
} }
/// <inheritdoc />
public void ChangeAbility(IAbility ability)
{
OverrideAbility = ability;
}
/// <inheritdoc /> /// <inheritdoc />
public SerializedPokemon Serialize() => new(this); public SerializedPokemon Serialize() => new(this);
@ -1112,10 +1139,11 @@ public class PokemonImpl : ScriptSource, IPokemon
public class PokemonBattleDataImpl : IPokemonBattleData public class PokemonBattleDataImpl : IPokemonBattleData
{ {
/// <inheritdoc cref="PokemonBattleDataImpl"/> /// <inheritdoc cref="PokemonBattleDataImpl"/>
public PokemonBattleDataImpl(IBattle battle, byte sideIndex) public PokemonBattleDataImpl(IBattle battle, byte sideIndex, uint switchInTurn)
{ {
Battle = battle; Battle = battle;
SideIndex = sideIndex; SideIndex = sideIndex;
SwitchInTurn = switchInTurn;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -1151,4 +1179,7 @@ public class PokemonBattleDataImpl : IPokemonBattleData
{ {
_consumedItems.Add(itemName); _consumedItems.Add(itemName);
} }
/// <inheritdoc />
public uint SwitchInTurn { get; set; }
} }

View File

@ -317,14 +317,14 @@ public abstract class Script : IDeepCloneable
/// <summary> /// <summary>
/// This function allows a script to modify the outgoing damage done by a move. /// This function allows a script to modify the outgoing damage done by a move.
/// </summary> /// </summary>
public virtual void ChangeDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public virtual void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
} }
/// <summary> /// <summary>
/// This function allows a script to modify the incoming damage done by a move. /// This function allows a script to modify the incoming damage done by a move.
/// </summary> /// </summary>
public virtual void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public virtual void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
} }
@ -546,4 +546,12 @@ public abstract class Script : IDeepCloneable
public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters) public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
{ {
} }
public virtual void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented)
{
}
public virtual void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage)
{
}
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Immutable;
using PkmnLib.Static.Utils; using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Species; namespace PkmnLib.Static.Species;
@ -17,17 +18,20 @@ public interface IAbility : INamedValue
/// The parameters for the script effect of the ability. /// The parameters for the script effect of the ability.
/// </summary> /// </summary>
IReadOnlyDictionary<StringKey, object?> Parameters { get; } IReadOnlyDictionary<StringKey, object?> Parameters { get; }
bool HasFlag(StringKey key);
} }
/// <inheritdoc /> /// <inheritdoc />
public class AbilityImpl : IAbility public class AbilityImpl : IAbility
{ {
/// <inheritdoc cref="AbilityImpl" /> /// <inheritdoc cref="AbilityImpl" />
public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary<StringKey, object?> parameters) public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary<StringKey, object?> parameters, ImmutableHashSet<StringKey> flags)
{ {
Name = name; Name = name;
Effect = effect; Effect = effect;
Parameters = parameters; Parameters = parameters;
Flags = flags;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -38,6 +42,14 @@ public class AbilityImpl : IAbility
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyDictionary<StringKey, object?> Parameters { get; } public IReadOnlyDictionary<StringKey, object?> Parameters { get; }
public ImmutableHashSet<StringKey> Flags;
/// <inheritdoc />
public bool HasFlag(StringKey key)
{
return Flags.Contains(key);
}
} }
/// <summary> /// <summary>

View File

@ -43,7 +43,8 @@
"effect": "PreventCritical" "effect": "PreventCritical"
}, },
"battle_bond": { "battle_bond": {
"effect": "BattleBond" "effect": "BattleBond",
"flags": ["cant_be_changed"]
}, },
"beast_boost": { "beast_boost": {
"effect": "BeastBoost" "effect": "BeastBoost"
@ -81,7 +82,9 @@
"color_change": { "color_change": {
"effect": "ColorChange" "effect": "ColorChange"
}, },
"comatose": {}, "comatose": {
"flags": ["cant_be_changed"]
},
"competitive": {}, "competitive": {},
"compound_eyes": {}, "compound_eyes": {},
"contrary": {}, "contrary": {},
@ -96,7 +99,9 @@
"defiant": {}, "defiant": {},
"delta_stream": {}, "delta_stream": {},
"desolate_land": {}, "desolate_land": {},
"disguise": {}, "disguise": {
"flags": ["cant_be_changed", "cant_be_copied"]
},
"download": {}, "download": {},
"drizzle": {}, "drizzle": {},
"drought": {}, "drought": {},
@ -110,10 +115,14 @@
"flame_body": {}, "flame_body": {},
"flare_boost": {}, "flare_boost": {},
"flash_fire": {}, "flash_fire": {},
"flower_gift": {}, "flower_gift": {
"flags": ["cant_be_copied"]
},
"flower_veil": {}, "flower_veil": {},
"fluffy": {}, "fluffy": {},
"forecast": {}, "forecast": {
"flags": ["cant_be_copied"]
},
"forewarn": {}, "forewarn": {},
"friend_guard": {}, "friend_guard": {},
"frisk": {}, "frisk": {},
@ -137,9 +146,13 @@
"hyper_cutter": {}, "hyper_cutter": {},
"ice_body": {}, "ice_body": {},
"illuminate": {}, "illuminate": {},
"illusion": {}, "illusion": {
"flags": ["cant_be_copied"]
},
"immunity": {}, "immunity": {},
"imposter": {}, "imposter": {
"flags": ["cant_be_copied"]
},
"infiltrator": {}, "infiltrator": {},
"innards_out": {}, "innards_out": {},
"inner_focus": {}, "inner_focus": {},
@ -173,7 +186,9 @@
"motor_drive": {}, "motor_drive": {},
"moxie": {}, "moxie": {},
"multiscale": {}, "multiscale": {},
"multitype": {}, "multitype": {
"flags": ["cant_be_changed"]
},
"mummy": {}, "mummy": {},
"natural_cure": {}, "natural_cure": {},
"no_guard": {}, "no_guard": {},
@ -190,8 +205,12 @@
"poison_heal": {}, "poison_heal": {},
"poison_point": {}, "poison_point": {},
"poison_touch": {}, "poison_touch": {},
"power_construct": {}, "power_construct": {
"power_of_alchemy": {}, "flags": ["cant_be_changed", "cant_be_copied"]
},
"power_of_alchemy": {
"flags": ["cant_be_copied"]
},
"prankster": {}, "prankster": {},
"pressure": {}, "pressure": {},
"primordial_sea": {}, "primordial_sea": {},
@ -203,12 +222,16 @@
"quick_feet": {}, "quick_feet": {},
"rain_dish": {}, "rain_dish": {},
"rattled": {}, "rattled": {},
"receiver": {}, "receiver": {
"flags": ["cant_be_copied"]
},
"reckless": {}, "reckless": {},
"refrigerate": {}, "refrigerate": {},
"regenerator": {}, "regenerator": {},
"rivalry": {}, "rivalry": {},
"rks_system": {}, "rks_system": {
"flags": ["cant_be_changed"]
},
"rock_head": {}, "rock_head": {},
"rough_skin": {}, "rough_skin": {},
"run_away": {}, "run_away": {},
@ -217,7 +240,9 @@
"sand_stream": {}, "sand_stream": {},
"sand_veil": {}, "sand_veil": {},
"sap_sipper": {}, "sap_sipper": {},
"schooling": {}, "schooling": {
"flags": ["cant_be_changed"]
},
"scrappy": {}, "scrappy": {},
"serene_grace": {}, "serene_grace": {},
"shadow_shield": {}, "shadow_shield": {},
@ -226,7 +251,9 @@
"sheer_force": {}, "sheer_force": {},
"shell_armor": {}, "shell_armor": {},
"shield_dust": {}, "shield_dust": {},
"shields_down": {}, "shields_down": {
"flags": ["cant_be_changed"]
},
"simple": {}, "simple": {},
"skill_link": {}, "skill_link": {},
"slow_start": {}, "slow_start": {},
@ -242,7 +269,9 @@
"stakeout": {}, "stakeout": {},
"stall": {}, "stall": {},
"stamina": {}, "stamina": {},
"stance_change": {}, "stance_change": {
"flags": ["cant_be_changed"]
},
"static": {}, "static": {},
"steadfast": {}, "steadfast": {},
"steelworker": {}, "steelworker": {},
@ -269,9 +298,13 @@
"torrent": {}, "torrent": {},
"tough_claws": {}, "tough_claws": {},
"toxic_boost": {}, "toxic_boost": {},
"trace": {}, "trace": {
"flags": ["cant_be_copied"]
},
"triage": {}, "triage": {},
"truant": {}, "truant": {
"flags": ["cant_be_changed"]
},
"turboblaze": {}, "turboblaze": {},
"unaware": {}, "unaware": {},
"unburden": {}, "unburden": {},
@ -288,5 +321,7 @@
"wimp_out": {}, "wimp_out": {},
"wonder_guard": {}, "wonder_guard": {},
"wonder_skin": {}, "wonder_skin": {},
"zen_mode": {} "zen_mode": {
"flags": ["cant_be_copied"]
}
} }

View File

@ -3097,7 +3097,10 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "embargo"
}
}, },
{ {
"name": "ember", "name": "ember",
@ -3111,7 +3114,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 10,
"parameters": {
"status": "burned"
}
}
}, },
{ {
"name": "encore", "name": "encore",
@ -3129,7 +3139,10 @@
"ignore-substitute", "ignore-substitute",
"mental", "mental",
"limit_move_choice" "limit_move_choice"
] ],
"effect": {
"name": "encore"
}
}, },
{ {
"name": "endeavor", "name": "endeavor",
@ -3144,7 +3157,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "endeavor"
}
}, },
{ {
"name": "endure", "name": "endure",
@ -3155,7 +3171,10 @@
"priority": 4, "priority": 4,
"target": "Self", "target": "Self",
"category": "status", "category": "status",
"flags": [] "flags": [],
"effect": {
"name": "endure"
}
}, },
{ {
"name": "energy_ball", "name": "energy_ball",
@ -3170,7 +3189,14 @@
"protect", "protect",
"mirror", "mirror",
"ballistics" "ballistics"
] ],
"effect": {
"name": "change_target_special_defense",
"chance": 10,
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "entrainment", "name": "entrainment",
@ -3185,7 +3211,10 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "entrainment"
}
}, },
{ {
"name": "eruption", "name": "eruption",
@ -3199,7 +3228,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "eruption"
}
}, },
{ {
"name": "explosion", "name": "explosion",
@ -3213,7 +3245,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "explosion"
}
}, },
{ {
"name": "extrasensory", "name": "extrasensory",
@ -3227,7 +3262,11 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "flinch",
"chance": 10
}
}, },
{ {
"name": "extreme_evoboost", "name": "extreme_evoboost",
@ -3238,7 +3277,13 @@
"priority": 0, "priority": 0,
"target": "Self", "target": "Self",
"category": "status", "category": "status",
"flags": [] "flags": [],
"effect": {
"name": "change_all_target_stats",
"parameters": {
"amount": 2
}
}
}, },
{ {
"name": "extreme_speed", "name": "extreme_speed",
@ -3268,7 +3313,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "facade"
}
}, },
{ {
"name": "fairy_lock", "name": "fairy_lock",
@ -3282,7 +3330,10 @@
"flags": [ "flags": [
"mirror", "mirror",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "fairy_lock"
}
}, },
{ {
"name": "fairy_wind", "name": "fairy_wind",
@ -3311,7 +3362,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "fake_out"
}
}, },
{ {
"name": "fake_tears", "name": "fake_tears",
@ -3326,7 +3380,13 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_special_defense",
"parameters": {
"amount": -2
}
}
}, },
{ {
"name": "false_swipe", "name": "false_swipe",
@ -3341,7 +3401,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "false_swipe"
}
}, },
{ {
"name": "feather_dance", "name": "feather_dance",
@ -3357,7 +3420,13 @@
"reflectable", "reflectable",
"mirror", "mirror",
"dance" "dance"
] ],
"effect": {
"name": "change_target_attack",
"parameters": {
"amount": -2
}
}
}, },
{ {
"name": "feint", "name": "feint",
@ -3400,7 +3469,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "fell_stinger"
}
}, },
{ {
"name": "fiery_dance", "name": "fiery_dance",
@ -3415,7 +3487,14 @@
"protect", "protect",
"mirror", "mirror",
"dance" "dance"
] ],
"effect": {
"name": "change_user_special_attack",
"chance": 50,
"parameters": {
"amount": 1
}
}
}, },
{ {
"name": "final_gambit", "name": "final_gambit",
@ -3428,7 +3507,10 @@
"category": "special", "category": "special",
"flags": [ "flags": [
"protect" "protect"
] ],
"effect": {
"name": "final_gambit"
}
}, },
{ {
"name": "fire_blast", "name": "fire_blast",
@ -3442,7 +3524,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 10,
"parameters": {
"status": "burned"
}
}
}, },
{ {
"name": "fire_fang", "name": "fire_fang",
@ -3458,7 +3547,10 @@
"protect", "protect",
"mirror", "mirror",
"bite" "bite"
] ],
"effect": {
"name": "fire_fang"
}
}, },
{ {
"name": "fire_lash", "name": "fire_lash",
@ -3473,7 +3565,13 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_defense",
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "fire_pledge", "name": "fire_pledge",
@ -3488,7 +3586,10 @@
"protect", "protect",
"mirror", "mirror",
"nonskybattle" "nonskybattle"
] ],
"effect": {
"name": "fire_pledge"
}
}, },
{ {
"name": "fire_punch", "name": "fire_punch",
@ -3504,7 +3605,14 @@
"protect", "protect",
"mirror", "mirror",
"punch" "punch"
] ],
"effect": {
"name": "set_status",
"chance": 10,
"parameters": {
"status": "burned"
}
}
}, },
{ {
"name": "fire_spin", "name": "fire_spin",

View File

@ -63,9 +63,9 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
_ => (uint)floatDamage, _ => (uint)floatDamage,
}; };
executingMove.RunScriptHook(script => executingMove.RunScriptHook(script =>
script.ChangeDamage(executingMove, target, hitNumber, ref damage)); script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage));
target.RunScriptHook(script => target.RunScriptHook(script =>
script.ChangeIncomingDamage(executingMove, target, hitNumber, ref damage)); script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage));
return damage; return damage;
} }

View File

@ -0,0 +1,27 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Battle;
[Script(ScriptCategory.Battle, "fairy_lock")]
public class FairyLockEffect : Script
{
private int _turns = 1;
/// <inheritdoc />
public override void PreventSelfRunAway(IFleeChoice choice, ref bool prevent)
{
prevent = true;
}
/// <inheritdoc />
public override void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent)
{
prevent = true;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (_turns <= 0)
RemoveSelf();
_turns--;
}
}

View File

@ -6,7 +6,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class BanefulBunker : ProtectionScript public class BanefulBunker : ProtectionScript
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override ProtectionEffectScript GetEffectScript() protected override Script GetEffectScript()
{ {
return new BanefulBunkerEffect(); return new BanefulBunkerEffect();
} }

View File

@ -27,7 +27,7 @@ public class Counter : Script
} }
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
var counterHelper = move.User.Volatile.Get<CounterHelperEffect>(); var counterHelper = move.User.Volatile.Get<CounterHelperEffect>();
if (counterHelper == null || counterHelper.LastHitBy == null || counterHelper.LastHitBy != target) if (counterHelper == null || counterHelper.LastHitBy == null || counterHelper.LastHitBy != target)

View File

@ -4,7 +4,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class DragonRage : Script public class DragonRage : Script
{ {
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
damage = 40; damage = 40;
} }

View File

@ -12,7 +12,7 @@ public class Electrify : Script
if (choiceQueue == null) if (choiceQueue == null)
return; return;
if (choiceQueue.Where(x => x is IMoveChoice moveChoice && moveChoice.User == target) is not IMoveChoice choice) if (choiceQueue.FirstOrDefault(x => x is IMoveChoice moveChoice && moveChoice.User == target) is not IMoveChoice choice)
{ {
move.GetHitData(target, hit).Fail(); move.GetHitData(target, hit).Fail();
return; return;

View File

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

View File

@ -0,0 +1,31 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "encore")]
public class Encore : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battle = target.BattleData?.Battle;
if (battle == null)
return;
var currentTurn = battle.ChoiceQueue!.LastRanChoice;
var lastMove = battle.PreviousTurnChoices
.SelectMany(x => x)
.OfType<IMoveChoice>()
.TakeWhile(x => x != currentTurn)
.LastOrDefault(x => x.User == target);
if (lastMove == null)
{
move.GetHitData(target, hit).Fail();
return;
}
var effect = new EncoreEffect(target, lastMove.ChosenMove.MoveData.Name, 3);
target.Volatile.Add(effect);
}
}

View File

@ -0,0 +1,18 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "endeavor")]
public class Endeavor : Script
{
/// <inheritdoc />
public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
var user = move.User;
var userHealth = user.CurrentHealth;
var targetHealth = target.CurrentHealth;
if (userHealth >= targetHealth)
{
return;
}
damage = targetHealth - userHealth;
}
}

View File

@ -0,0 +1,10 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "endure")]
public class Endure : ProtectionScript
{
/// <inheritdoc />
protected override Script GetEffectScript() => new EndureEffect();
}

View File

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

View File

@ -0,0 +1,13 @@
using System;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "eruption")]
public class Eruption : Script
{
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
{
basePower = Math.Max((byte)(150 * move.User.CurrentHealth / move.User.BoostedStats.Hp), (byte)1);
}
}

View File

@ -0,0 +1,11 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "explosion")]
public class Explosion : Script
{
/// <inheritdoc />
public override void OnAfterHits(IExecutingMove move, IPokemon target)
{
move.User.Damage(move.User.CurrentHealth * 10, DamageSource.Misc);
}
}

View File

@ -0,0 +1,17 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "facade")]
public class Facade : Script
{
/// <inheritdoc />
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
{
var status = move.User.StatusScript.Script?.Name;
if (status == "paralyzed" || status == "burned" || status == "poisoned")
{
basePower.MultiplyOrMax(2);
}
}
}

View File

@ -0,0 +1,14 @@
using PkmnLib.Plugin.Gen7.Scripts.Battle;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "fairy_lock")]
public class FairyLock : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battle = target.BattleData?.Battle;
battle?.Volatile.Add(new FairyLockEffect());
}
}

View File

@ -0,0 +1,23 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "fake_out")]
public class FakeOut : Script
{
/// <inheritdoc />
public override void StopBeforeMove(IExecutingMove move, ref bool stop)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
if (battleData.SwitchInTurn != battleData.Battle.CurrentTurnNumber)
stop = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.Volatile.Add(new FlinchEffect());
}
}

View File

@ -0,0 +1,14 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "false_swipe")]
public class FalseSwipe : Script
{
/// <inheritdoc />
public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
if (target.CurrentHealth - damage < 1)
{
damage = target.CurrentHealth - 1;
}
}
}

View File

@ -0,0 +1,16 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "fell_stinger")]
public class FellStinger : Script
{
/// <inheritdoc />
public override void OnAfterHits(IExecutingMove move, IPokemon target)
{
if (target.IsFainted)
{
move.User.ChangeStatBoost(Statistic.Attack, 2, true);
}
}
}

View File

@ -0,0 +1,17 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "final_gambit")]
public class FinalGambit : Script
{
/// <inheritdoc />
public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
damage = move.User.CurrentHealth;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.Damage(move.User.CurrentHealth * 10, DamageSource.Misc);
}
}

View File

@ -0,0 +1,29 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class FireFang : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = target.BattleData;
if (battleData == null)
return;
var random = battleData.Battle.Random;
if (random.EffectChance(10, move, target, hit))
{
target.SetStatus("burned");
}
// 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)
{
if (random.EffectChance(10, move, target, hit))
{
target.Volatile.Add(new FlinchEffect());
}
}
}
}

View File

@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "fire_pledge")]
public class FirePledge : Script
{
// TODO: pledge moves
}

View File

@ -6,7 +6,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "protect")] [Script(ScriptCategory.Move, "protect")]
public class ProtectionScript : Script public class ProtectionScript : Script
{ {
protected virtual ProtectionEffectScript GetEffectScript() => new(); protected virtual Script GetEffectScript() => new ProtectionEffectScript();
/// <inheritdoc /> /// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)

View File

@ -27,7 +27,7 @@ public class ChargeBounceEffect : Script
} }
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
if (!move.UseMove.HasFlag("effective_against_fly")) if (!move.UseMove.HasFlag("effective_against_fly"))
damage *= 2; damage *= 2;

View File

@ -26,7 +26,7 @@ public class DigEffect : Script
} }
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
if (!move.UseMove.HasFlag("effective_against_underground")) if (!move.UseMove.HasFlag("effective_against_underground"))
damage *= 2; damage *= 2;

View File

@ -26,7 +26,7 @@ public class DiveEffect : Script
} }
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
if (!move.UseMove.HasFlag("effective_against_underwater")) if (!move.UseMove.HasFlag("effective_against_underwater"))
damage *= 2; damage *= 2;

View File

@ -0,0 +1,31 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "embargo")]
public class EmbargoEffect : Script
{
private int _turns = 5;
/// <inheritdoc />
public override void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented)
{
prevented = true;
}
/// <inheritdoc />
public override void Stack()
{
_turns = 5;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
_turns--;
if (_turns == 0)
{
RemoveSelf();
}
}
}

View File

@ -0,0 +1,42 @@
using PkmnLib.Plugin.Gen7.Scripts.Utils;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "encore")]
public class EncoreEffect : Script
{
private readonly IPokemon _owner;
private readonly StringKey _move;
private int _turns;
public EncoreEffect(IPokemon owner, StringKey move, int turns)
{
_owner = owner;
_move = move;
_turns = turns;
}
/// <inheritdoc />
public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
{
var opposingSideIndex = (byte)(_owner.BattleData?.SideIndex == 0 ? 1 : 0);
choice = TurnChoiceHelper.CreateMoveChoice(_owner, _move, opposingSideIndex, position);
if (choice is IMoveChoice { ChosenMove.CurrentPp: <= 0 } moveChoice)
{
choice =
moveChoice.User.BattleData?.Battle.Library.MiscLibrary.ReplacementChoice(_owner, opposingSideIndex,
position);
}
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
_turns--;
if (_turns <= 0)
{
RemoveSelf();
}
}
}

View File

@ -0,0 +1,15 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "endure")]
public class EndureEffect : Script
{
/// <inheritdoc />
public override void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage)
{
if (damage > pokemon.CurrentHealth)
damage = pokemon.CurrentHealth - 1;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle) => RemoveSelf();
}

View File

@ -43,7 +43,7 @@ public class AuroraVeilEffect : Script
} }
/// <inheritdoc /> /// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{ {
var hitData = move.GetHitData(target, hit); var hitData = move.GetHitData(target, hit);
if (hitData.IsCritical) if (hitData.IsCritical)

View File

@ -1,11 +1,12 @@
using System; using System;
using System.Linq; using System.Linq;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Utils; namespace PkmnLib.Plugin.Gen7.Scripts.Utils;
public static class TurnChoiceHelper public static class TurnChoiceHelper
{ {
public static IMoveChoice CreateMoveChoice(IPokemon owner, string moveName, byte targetSide, byte targetPosition) public static IMoveChoice CreateMoveChoice(IPokemon owner, StringKey moveName, byte targetSide, byte targetPosition)
{ {
var move = owner.Moves.FirstOrDefault(x => x?.MoveData.Name == moveName); var move = owner.Moves.FirstOrDefault(x => x?.MoveData.Name == moveName);
if (move == null) if (move == null)