diff --git a/PkmnLib.Dataloader/AbilityDataLoader.cs b/PkmnLib.Dataloader/AbilityDataLoader.cs index 7fecc39..103c0b2 100644 --- a/PkmnLib.Dataloader/AbilityDataLoader.cs +++ b/PkmnLib.Dataloader/AbilityDataLoader.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text.Json; @@ -61,8 +62,10 @@ public static class AbilityDataLoader var parameters = serialized.Parameters.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter()); StringKey? effectName = effect == null ? null! : new StringKey(effect); + + var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet(); - var ability = new AbilityImpl(name, effectName, parameters); + var ability = new AbilityImpl(name, effectName, parameters, flags); return ability; } } \ No newline at end of file diff --git a/PkmnLib.Dataloader/Models/SerializedAbility.cs b/PkmnLib.Dataloader/Models/SerializedAbility.cs index 61e13be..400dcc1 100644 --- a/PkmnLib.Dataloader/Models/SerializedAbility.cs +++ b/PkmnLib.Dataloader/Models/SerializedAbility.cs @@ -7,4 +7,5 @@ public class SerializedAbility { public string? Effect { get; set; } public Dictionary Parameters { get; set; } = new(); + public string[] Flags { get; set; } = []; } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs index 792c8f9..d118e02 100644 --- a/PkmnLib.Dynamic/Models/Battle.cs +++ b/PkmnLib.Dynamic/Models/Battle.cs @@ -110,6 +110,8 @@ public interface IBattle : IScriptSource, IDeepCloneable /// void SetWeather(StringKey? weatherName); + public IScriptSet Volatile { get; } + /// /// Gets the current weather of the battle. If no weather is present, this returns null. /// @@ -359,7 +361,7 @@ public class BattleImpl : ScriptSource, IBattle // TODO: Trigger weather change script hooks } - private IScriptSet Volatile { get; } = new ScriptSet(); + public IScriptSet Volatile { get; } = new ScriptSet(); /// public StringKey? WeatherName => _weatherScript.Script?.Name; diff --git a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs index fddae4d..c49333e 100644 --- a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs +++ b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs @@ -102,5 +102,5 @@ public class BattleChoiceQueue : IDeepCloneable internal IReadOnlyList GetChoices() => _choices; - public ITurnChoice? Where(Func predicate) => _choices.WhereNotNull().FirstOrDefault(predicate); + public ITurnChoice? FirstOrDefault(Func predicate) => _choices.WhereNotNull().FirstOrDefault(predicate); } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/BattleRandom.cs b/PkmnLib.Dynamic/Models/BattleRandom.cs index 85c80d9..ff7cca7 100644 --- a/PkmnLib.Dynamic/Models/BattleRandom.cs +++ b/PkmnLib.Dynamic/Models/BattleRandom.cs @@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.Models; public interface IBattleRandom : IRandom, IDeepCloneable { /// - /// 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 /// chance. /// diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs index ce9983b..4f54841 100644 --- a/PkmnLib.Dynamic/Models/Pokemon.cs +++ b/PkmnLib.Dynamic/Models/Pokemon.cs @@ -373,6 +373,8 @@ public interface IPokemon : IScriptSource, IDeepCloneable /// Replace the types of the Pokémon with the provided types. /// void SetTypes(IReadOnlyList types); + + void ChangeAbility(IAbility ability); /// /// Converts the data structure to a serializable format. @@ -409,7 +411,7 @@ public interface IPokemonBattleData : IDeepCloneable /// /// Whether the Pokémon is on the battlefield. /// - bool IsOnBattlefield { get; set; } + bool IsOnBattlefield { get; internal set; } /// /// Adds an opponent to the list of seen opponents. @@ -425,6 +427,8 @@ public interface IPokemonBattleData : IDeepCloneable /// Marks an item as consumed. /// void MarkItemAsConsumed(IItem itemName); + + uint SwitchInTurn { get; internal set; } } /// @@ -720,6 +724,15 @@ public class PokemonImpl : ScriptSource, IPokemon return false; if (!Library.ScriptResolver.TryResolveBattleItemScript(HeldItem, out _)) 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 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 (IsFainted) 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 // underflow. if (damage >= CurrentHealth) @@ -1013,10 +1033,11 @@ public class PokemonImpl : ScriptSource, IPokemon { BattleData.Battle = battle; BattleData.SideIndex = sideIndex; + BattleData.SwitchInTurn = battle.CurrentTurnNumber; } 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(); } + /// + public void ChangeAbility(IAbility ability) + { + OverrideAbility = ability; + } + /// public SerializedPokemon Serialize() => new(this); @@ -1112,10 +1139,11 @@ public class PokemonImpl : ScriptSource, IPokemon public class PokemonBattleDataImpl : IPokemonBattleData { /// - public PokemonBattleDataImpl(IBattle battle, byte sideIndex) + public PokemonBattleDataImpl(IBattle battle, byte sideIndex, uint switchInTurn) { Battle = battle; SideIndex = sideIndex; + SwitchInTurn = switchInTurn; } /// @@ -1151,4 +1179,7 @@ public class PokemonBattleDataImpl : IPokemonBattleData { _consumedItems.Add(itemName); } + + /// + public uint SwitchInTurn { get; set; } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index ba54df7..a53ba68 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -317,14 +317,14 @@ public abstract class Script : IDeepCloneable /// /// This function allows a script to modify the outgoing damage done by a move. /// - 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) { } /// /// This function allows a script to modify the incoming damage done by a move. /// - 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? parameters) { } + + public virtual void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented) + { + } + + public virtual void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage) + { + } } \ No newline at end of file diff --git a/PkmnLib.Static/Species/Ability.cs b/PkmnLib.Static/Species/Ability.cs index 4f01a12..bd7a5a5 100644 --- a/PkmnLib.Static/Species/Ability.cs +++ b/PkmnLib.Static/Species/Ability.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using PkmnLib.Static.Utils; namespace PkmnLib.Static.Species; @@ -17,17 +18,20 @@ public interface IAbility : INamedValue /// The parameters for the script effect of the ability. /// IReadOnlyDictionary Parameters { get; } + + bool HasFlag(StringKey key); } /// public class AbilityImpl : IAbility { /// - public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary parameters) + public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary parameters, ImmutableHashSet flags) { Name = name; Effect = effect; Parameters = parameters; + Flags = flags; } /// @@ -38,6 +42,14 @@ public class AbilityImpl : IAbility /// public IReadOnlyDictionary Parameters { get; } + + public ImmutableHashSet Flags; + + /// + public bool HasFlag(StringKey key) + { + return Flags.Contains(key); + } } /// diff --git a/PkmnLib.Tests/Data/Abilities.json b/PkmnLib.Tests/Data/Abilities.json index db40e7e..4904743 100755 --- a/PkmnLib.Tests/Data/Abilities.json +++ b/PkmnLib.Tests/Data/Abilities.json @@ -43,7 +43,8 @@ "effect": "PreventCritical" }, "battle_bond": { - "effect": "BattleBond" + "effect": "BattleBond", + "flags": ["cant_be_changed"] }, "beast_boost": { "effect": "BeastBoost" @@ -81,7 +82,9 @@ "color_change": { "effect": "ColorChange" }, - "comatose": {}, + "comatose": { + "flags": ["cant_be_changed"] + }, "competitive": {}, "compound_eyes": {}, "contrary": {}, @@ -96,7 +99,9 @@ "defiant": {}, "delta_stream": {}, "desolate_land": {}, - "disguise": {}, + "disguise": { + "flags": ["cant_be_changed", "cant_be_copied"] + }, "download": {}, "drizzle": {}, "drought": {}, @@ -110,10 +115,14 @@ "flame_body": {}, "flare_boost": {}, "flash_fire": {}, - "flower_gift": {}, + "flower_gift": { + "flags": ["cant_be_copied"] + }, "flower_veil": {}, "fluffy": {}, - "forecast": {}, + "forecast": { + "flags": ["cant_be_copied"] + }, "forewarn": {}, "friend_guard": {}, "frisk": {}, @@ -137,9 +146,13 @@ "hyper_cutter": {}, "ice_body": {}, "illuminate": {}, - "illusion": {}, + "illusion": { + "flags": ["cant_be_copied"] + }, "immunity": {}, - "imposter": {}, + "imposter": { + "flags": ["cant_be_copied"] + }, "infiltrator": {}, "innards_out": {}, "inner_focus": {}, @@ -173,7 +186,9 @@ "motor_drive": {}, "moxie": {}, "multiscale": {}, - "multitype": {}, + "multitype": { + "flags": ["cant_be_changed"] + }, "mummy": {}, "natural_cure": {}, "no_guard": {}, @@ -190,8 +205,12 @@ "poison_heal": {}, "poison_point": {}, "poison_touch": {}, - "power_construct": {}, - "power_of_alchemy": {}, + "power_construct": { + "flags": ["cant_be_changed", "cant_be_copied"] + }, + "power_of_alchemy": { + "flags": ["cant_be_copied"] + }, "prankster": {}, "pressure": {}, "primordial_sea": {}, @@ -203,12 +222,16 @@ "quick_feet": {}, "rain_dish": {}, "rattled": {}, - "receiver": {}, + "receiver": { + "flags": ["cant_be_copied"] + }, "reckless": {}, "refrigerate": {}, "regenerator": {}, "rivalry": {}, - "rks_system": {}, + "rks_system": { + "flags": ["cant_be_changed"] + }, "rock_head": {}, "rough_skin": {}, "run_away": {}, @@ -217,7 +240,9 @@ "sand_stream": {}, "sand_veil": {}, "sap_sipper": {}, - "schooling": {}, + "schooling": { + "flags": ["cant_be_changed"] + }, "scrappy": {}, "serene_grace": {}, "shadow_shield": {}, @@ -226,7 +251,9 @@ "sheer_force": {}, "shell_armor": {}, "shield_dust": {}, - "shields_down": {}, + "shields_down": { + "flags": ["cant_be_changed"] + }, "simple": {}, "skill_link": {}, "slow_start": {}, @@ -242,7 +269,9 @@ "stakeout": {}, "stall": {}, "stamina": {}, - "stance_change": {}, + "stance_change": { + "flags": ["cant_be_changed"] + }, "static": {}, "steadfast": {}, "steelworker": {}, @@ -269,9 +298,13 @@ "torrent": {}, "tough_claws": {}, "toxic_boost": {}, - "trace": {}, + "trace": { + "flags": ["cant_be_copied"] + }, "triage": {}, - "truant": {}, + "truant": { + "flags": ["cant_be_changed"] + }, "turboblaze": {}, "unaware": {}, "unburden": {}, @@ -288,5 +321,7 @@ "wimp_out": {}, "wonder_guard": {}, "wonder_skin": {}, - "zen_mode": {} + "zen_mode": { + "flags": ["cant_be_copied"] + } } \ No newline at end of file diff --git a/PkmnLib.Tests/Data/Moves.json b/PkmnLib.Tests/Data/Moves.json index 30ca5a1..5bd7f4b 100755 --- a/PkmnLib.Tests/Data/Moves.json +++ b/PkmnLib.Tests/Data/Moves.json @@ -3097,7 +3097,10 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "embargo" + } }, { "name": "ember", @@ -3111,7 +3114,14 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 10, + "parameters": { + "status": "burned" + } + } }, { "name": "encore", @@ -3129,7 +3139,10 @@ "ignore-substitute", "mental", "limit_move_choice" - ] + ], + "effect": { + "name": "encore" + } }, { "name": "endeavor", @@ -3144,7 +3157,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "endeavor" + } }, { "name": "endure", @@ -3155,7 +3171,10 @@ "priority": 4, "target": "Self", "category": "status", - "flags": [] + "flags": [], + "effect": { + "name": "endure" + } }, { "name": "energy_ball", @@ -3170,7 +3189,14 @@ "protect", "mirror", "ballistics" - ] + ], + "effect": { + "name": "change_target_special_defense", + "chance": 10, + "parameters": { + "amount": -1 + } + } }, { "name": "entrainment", @@ -3185,7 +3211,10 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "entrainment" + } }, { "name": "eruption", @@ -3199,7 +3228,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "eruption" + } }, { "name": "explosion", @@ -3213,7 +3245,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "explosion" + } }, { "name": "extrasensory", @@ -3227,7 +3262,11 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "flinch", + "chance": 10 + } }, { "name": "extreme_evoboost", @@ -3238,7 +3277,13 @@ "priority": 0, "target": "Self", "category": "status", - "flags": [] + "flags": [], + "effect": { + "name": "change_all_target_stats", + "parameters": { + "amount": 2 + } + } }, { "name": "extreme_speed", @@ -3268,7 +3313,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "facade" + } }, { "name": "fairy_lock", @@ -3282,7 +3330,10 @@ "flags": [ "mirror", "ignore-substitute" - ] + ], + "effect": { + "name": "fairy_lock" + } }, { "name": "fairy_wind", @@ -3311,7 +3362,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "fake_out" + } }, { "name": "fake_tears", @@ -3326,7 +3380,13 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "change_target_special_defense", + "parameters": { + "amount": -2 + } + } }, { "name": "false_swipe", @@ -3341,7 +3401,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "false_swipe" + } }, { "name": "feather_dance", @@ -3357,7 +3420,13 @@ "reflectable", "mirror", "dance" - ] + ], + "effect": { + "name": "change_target_attack", + "parameters": { + "amount": -2 + } + } }, { "name": "feint", @@ -3400,7 +3469,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "fell_stinger" + } }, { "name": "fiery_dance", @@ -3415,7 +3487,14 @@ "protect", "mirror", "dance" - ] + ], + "effect": { + "name": "change_user_special_attack", + "chance": 50, + "parameters": { + "amount": 1 + } + } }, { "name": "final_gambit", @@ -3428,7 +3507,10 @@ "category": "special", "flags": [ "protect" - ] + ], + "effect": { + "name": "final_gambit" + } }, { "name": "fire_blast", @@ -3442,7 +3524,14 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 10, + "parameters": { + "status": "burned" + } + } }, { "name": "fire_fang", @@ -3458,7 +3547,10 @@ "protect", "mirror", "bite" - ] + ], + "effect": { + "name": "fire_fang" + } }, { "name": "fire_lash", @@ -3473,7 +3565,13 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "change_target_defense", + "parameters": { + "amount": -1 + } + } }, { "name": "fire_pledge", @@ -3488,7 +3586,10 @@ "protect", "mirror", "nonskybattle" - ] + ], + "effect": { + "name": "fire_pledge" + } }, { "name": "fire_punch", @@ -3504,7 +3605,14 @@ "protect", "mirror", "punch" - ] + ], + "effect": { + "name": "set_status", + "chance": 10, + "parameters": { + "status": "burned" + } + } }, { "name": "fire_spin", diff --git a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs index cc791ba..5f9b9c9 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs @@ -63,9 +63,9 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator _ => (uint)floatDamage, }; executingMove.RunScriptHook(script => - script.ChangeDamage(executingMove, target, hitNumber, ref damage)); + script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage)); target.RunScriptHook(script => - script.ChangeIncomingDamage(executingMove, target, hitNumber, ref damage)); + script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage)); return damage; } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FairyLockEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FairyLockEffect.cs new file mode 100644 index 0000000..f7f1f2c --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FairyLockEffect.cs @@ -0,0 +1,27 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Battle; + +[Script(ScriptCategory.Battle, "fairy_lock")] +public class FairyLockEffect : Script +{ + private int _turns = 1; + + /// + public override void PreventSelfRunAway(IFleeChoice choice, ref bool prevent) + { + prevent = true; + } + + /// + public override void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent) + { + prevent = true; + } + + /// + public override void OnEndTurn(IBattle battle) + { + if (_turns <= 0) + RemoveSelf(); + _turns--; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BanefulBunker.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BanefulBunker.cs index 2f87b57..b3cce84 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BanefulBunker.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BanefulBunker.cs @@ -6,7 +6,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves; public class BanefulBunker : ProtectionScript { /// - protected override ProtectionEffectScript GetEffectScript() + protected override Script GetEffectScript() { return new BanefulBunkerEffect(); } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Counter.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Counter.cs index d94627f..ff1fc41 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Counter.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Counter.cs @@ -27,7 +27,7 @@ public class Counter : Script } /// - 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(); if (counterHelper == null || counterHelper.LastHitBy == null || counterHelper.LastHitBy != target) diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs index 8d5d9d2..bcf045e 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs @@ -4,7 +4,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves; public class DragonRage : Script { /// - 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; } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Electrify.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Electrify.cs index 0b9ff76..a197784 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Electrify.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Electrify.cs @@ -12,7 +12,7 @@ public class Electrify : Script if (choiceQueue == null) 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(); return; diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Embargo.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Embargo.cs new file mode 100644 index 0000000..5c71e04 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Embargo.cs @@ -0,0 +1,13 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "embargo")] +public class Embargo : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + target.Volatile.Add(new EmbargoEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Encore.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Encore.cs new file mode 100644 index 0000000..88331ec --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Encore.cs @@ -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 +{ + /// + 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() + .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); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endeavor.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endeavor.cs new file mode 100644 index 0000000..b80e90f --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endeavor.cs @@ -0,0 +1,18 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "endeavor")] +public class Endeavor : Script +{ + /// + 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; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endure.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endure.cs new file mode 100644 index 0000000..d18b2bd --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Endure.cs @@ -0,0 +1,10 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "endure")] +public class Endure : ProtectionScript +{ + /// + protected override Script GetEffectScript() => new EndureEffect(); +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Entrainment.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Entrainment.cs new file mode 100644 index 0000000..16333b4 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Entrainment.cs @@ -0,0 +1,25 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "entrainment")] +public class Entrainment : Script +{ + /// + 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); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Eruption.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Eruption.cs new file mode 100644 index 0000000..2111666 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Eruption.cs @@ -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); + } + +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Explosion.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Explosion.cs new file mode 100644 index 0000000..d41d44a --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Explosion.cs @@ -0,0 +1,11 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "explosion")] +public class Explosion : Script +{ + /// + public override void OnAfterHits(IExecutingMove move, IPokemon target) + { + move.User.Damage(move.User.CurrentHealth * 10, DamageSource.Misc); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Facade.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Facade.cs new file mode 100644 index 0000000..33aeed1 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Facade.cs @@ -0,0 +1,17 @@ +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "facade")] +public class Facade : Script +{ + /// + 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); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FairyLock.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FairyLock.cs new file mode 100644 index 0000000..9fa5a41 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FairyLock.cs @@ -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 +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var battle = target.BattleData?.Battle; + battle?.Volatile.Add(new FairyLockEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FakeOut.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FakeOut.cs new file mode 100644 index 0000000..a5098fb --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FakeOut.cs @@ -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 +{ + /// + 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; + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + target.Volatile.Add(new FlinchEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FalseSwipe.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FalseSwipe.cs new file mode 100644 index 0000000..885e90c --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FalseSwipe.cs @@ -0,0 +1,14 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "false_swipe")] +public class FalseSwipe : Script +{ + /// + public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + if (target.CurrentHealth - damage < 1) + { + damage = target.CurrentHealth - 1; + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FellStinger.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FellStinger.cs new file mode 100644 index 0000000..85ebc89 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FellStinger.cs @@ -0,0 +1,16 @@ +using PkmnLib.Static; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "fell_stinger")] +public class FellStinger : Script +{ + /// + public override void OnAfterHits(IExecutingMove move, IPokemon target) + { + if (target.IsFainted) + { + move.User.ChangeStatBoost(Statistic.Attack, 2, true); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FinalGambit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FinalGambit.cs new file mode 100644 index 0000000..5c18156 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FinalGambit.cs @@ -0,0 +1,17 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "final_gambit")] +public class FinalGambit : Script +{ + /// + public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + damage = move.User.CurrentHealth; + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Damage(move.User.CurrentHealth * 10, DamageSource.Misc); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FireFang.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FireFang.cs new file mode 100644 index 0000000..7df9942 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FireFang.cs @@ -0,0 +1,29 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +public class FireFang : Script +{ + /// + 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()); + } + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FirePledge.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FirePledge.cs new file mode 100644 index 0000000..ee1c23b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FirePledge.cs @@ -0,0 +1,7 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "fire_pledge")] +public class FirePledge : Script +{ + // TODO: pledge moves +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ProtectionScript.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ProtectionScript.cs index 014fc27..6edaac1 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ProtectionScript.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ProtectionScript.cs @@ -6,7 +6,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves; [Script(ScriptCategory.Move, "protect")] public class ProtectionScript : Script { - protected virtual ProtectionEffectScript GetEffectScript() => new(); + protected virtual Script GetEffectScript() => new ProtectionEffectScript(); /// public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeBounceEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeBounceEffect.cs index 52a4faf..1758556 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeBounceEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeBounceEffect.cs @@ -27,7 +27,7 @@ public class ChargeBounceEffect : Script } /// - 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")) damage *= 2; diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DigEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DigEffect.cs index 3721551..9e2a5c1 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DigEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DigEffect.cs @@ -26,7 +26,7 @@ public class DigEffect : Script } /// - 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")) damage *= 2; diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DiveEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DiveEffect.cs index 596375e..96ea543 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DiveEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DiveEffect.cs @@ -26,7 +26,7 @@ public class DiveEffect : Script } /// - 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")) damage *= 2; diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EmbargoEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EmbargoEffect.cs new file mode 100644 index 0000000..6d83f82 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EmbargoEffect.cs @@ -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; + + /// + public override void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented) + { + prevented = true; + } + + /// + public override void Stack() + { + _turns = 5; + } + + /// + public override void OnEndTurn(IBattle battle) + { + _turns--; + if (_turns == 0) + { + RemoveSelf(); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EncoreEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EncoreEffect.cs new file mode 100644 index 0000000..f871fc5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EncoreEffect.cs @@ -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; + } + + /// + 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); + } + } + + /// + public override void OnEndTurn(IBattle battle) + { + _turns--; + if (_turns <= 0) + { + RemoveSelf(); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EndureEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EndureEffect.cs new file mode 100644 index 0000000..44a61e4 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/EndureEffect.cs @@ -0,0 +1,15 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "endure")] +public class EndureEffect : Script +{ + /// + public override void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage) + { + if (damage > pokemon.CurrentHealth) + damage = pokemon.CurrentHealth - 1; + } + + /// + public override void OnEndTurn(IBattle battle) => RemoveSelf(); +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AuroraVeilEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AuroraVeilEffect.cs index f0ec0a0..2a66452 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AuroraVeilEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AuroraVeilEffect.cs @@ -43,7 +43,7 @@ public class AuroraVeilEffect : Script } /// - 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); if (hitData.IsCritical) diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/TurnChoiceHelper.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/TurnChoiceHelper.cs index 7bcd641..32aec0c 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/TurnChoiceHelper.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/TurnChoiceHelper.cs @@ -1,11 +1,12 @@ using System; using System.Linq; +using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Scripts.Utils; 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); if (move == null)