More move effects
This commit is contained in:
parent
9b0ac36597
commit
c0bc905c46
|
@ -4,6 +4,7 @@ using System.Collections.Immutable;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using JetBrains.Annotations;
|
||||
using PkmnLib.Dataloader.Models;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
@ -25,13 +26,15 @@ public static class ItemDataLoader
|
|||
library.Add(i);
|
||||
return library;
|
||||
}
|
||||
|
||||
public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
|
||||
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags,
|
||||
ISecondaryEffect? effect, ISecondaryEffect? battleTriggerEffect, byte flingPower);
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
public static Func<SerializedItem, StringKey, ItemCategory, BattleItemCategory, int,
|
||||
IEnumerable<StringKey>, ISecondaryEffect?, ISecondaryEffect?,
|
||||
// ReSharper disable once FieldCanBeMadeReadOnly.Global
|
||||
IItem> ItemConstructor = (_, name, type, battleType, price, flags, effect, battleTriggerEffect) =>
|
||||
new ItemImpl(name, type, battleType, price, flags, effect, battleTriggerEffect);
|
||||
[PublicAPI]
|
||||
public static ItemFactoryDelegate ItemConstructor { get; set; } = (_, name, type, battleType, price, flags, effect,
|
||||
battleTriggerEffect, flingPower) =>
|
||||
new ItemImpl(name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower);
|
||||
|
||||
private static IItem DeserializeItem(SerializedItem serialized)
|
||||
{
|
||||
|
@ -42,6 +45,7 @@ public static class ItemDataLoader
|
|||
var battleTriggerEffect = serialized.BattleEffect?.ParseEffect();
|
||||
|
||||
return ItemConstructor(serialized, serialized.Name, itemType, battleType, serialized.Price,
|
||||
serialized.Flags.Select(x => (StringKey)x).ToImmutableHashSet(), effect, battleTriggerEffect);
|
||||
serialized.Flags.Select(x => (StringKey)x).ToImmutableHashSet(), effect, battleTriggerEffect,
|
||||
serialized.FlingPower);
|
||||
}
|
||||
}
|
|
@ -14,4 +14,10 @@
|
|||
<ProjectReference Include="..\PkmnLib.Static\PkmnLib.Static.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations">
|
||||
<HintPath>..\..\..\..\.nuget\packages\jetbrains.annotations\2024.2.0\lib\netstandard2.0\JetBrains.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -240,6 +240,20 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||
/// </summary>
|
||||
[MustUseReturnValue]
|
||||
IItem? RemoveHeldItem();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the held item from the Pokemon for the duration of the battle. Returns the previously held item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for moves that remove a held item, but do not consume it. In this case, the item needs to be
|
||||
/// restored after the battle.
|
||||
/// </remarks>
|
||||
IItem? RemoveHeldItemForBattle();
|
||||
|
||||
/// <summary>
|
||||
/// Restores the held item of a Pokémon if it was temporarily removed.
|
||||
/// </summary>
|
||||
void RestoreStolenHeldItem();
|
||||
|
||||
/// <summary>
|
||||
/// Makes the Pokemon uses its held item. Returns whether the item was consumed.
|
||||
|
@ -716,6 +730,21 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
HeldItem = null;
|
||||
return previous;
|
||||
}
|
||||
|
||||
private IItem? _stolenHeldItem;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IItem? RemoveHeldItemForBattle()
|
||||
{
|
||||
return _stolenHeldItem = RemoveHeldItem();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RestoreStolenHeldItem()
|
||||
{
|
||||
_ = SetHeldItem(_stolenHeldItem);
|
||||
_stolenHeldItem = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ConsumeHeldItem()
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
|||
/// <summary>
|
||||
/// Helper attribute to register scripts through reflection.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[MeansImplicitUse]
|
||||
public class ScriptAttribute : Attribute
|
||||
{
|
||||
|
|
|
@ -554,4 +554,8 @@ public abstract class Script : IDeepCloneable
|
|||
public virtual void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -108,6 +108,8 @@ public interface IItem : INamedValue
|
|||
|
||||
ISecondaryEffect? Effect { get; }
|
||||
ISecondaryEffect? BattleEffect { get; }
|
||||
|
||||
byte FlingPower { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the item has a specific flag.
|
||||
|
@ -122,7 +124,7 @@ public class ItemImpl : IItem
|
|||
{
|
||||
/// <inheritdoc cref="ItemImpl"/>
|
||||
public ItemImpl(StringKey name, ItemCategory category, BattleItemCategory battleCategory, int price,
|
||||
IEnumerable<StringKey> flags, ISecondaryEffect? effect, ISecondaryEffect? battleTriggerEffect)
|
||||
IEnumerable<StringKey> flags, ISecondaryEffect? effect, ISecondaryEffect? battleTriggerEffect, byte flingPower)
|
||||
{
|
||||
Name = name;
|
||||
Category = category;
|
||||
|
@ -130,6 +132,7 @@ public class ItemImpl : IItem
|
|||
Price = price;
|
||||
Effect = effect;
|
||||
BattleEffect = battleTriggerEffect;
|
||||
FlingPower = flingPower;
|
||||
Flags = [..flags];
|
||||
}
|
||||
|
||||
|
@ -154,6 +157,10 @@ public class ItemImpl : IItem
|
|||
/// <inheritdoc />
|
||||
public ISecondaryEffect? BattleEffect { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte FlingPower { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(string key)
|
||||
{
|
||||
|
|
|
@ -27,7 +27,7 @@ public interface IReadOnlyTypeLibrary
|
|||
/// Gets the effectiveness for a single attacking type against an amount of defending types.
|
||||
/// This is equivalent to running get_single_effectiveness on each defending type, and multiplying the results with each other.
|
||||
/// </summary>
|
||||
float GetEffectiveness(TypeIdentifier attacking, IReadOnlyList<TypeIdentifier> defending);
|
||||
float GetEffectiveness(TypeIdentifier attacking, IEnumerable<TypeIdentifier> defending);
|
||||
|
||||
IEnumerable<(TypeIdentifier type, float effectiveness)> GetAllEffectivenessFromAttacking(TypeIdentifier attacking);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ public class TypeLibrary : IReadOnlyTypeLibrary
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public float GetEffectiveness(TypeIdentifier attacking, IReadOnlyList<TypeIdentifier> defending) =>
|
||||
public float GetEffectiveness(TypeIdentifier attacking, IEnumerable<TypeIdentifier> defending) =>
|
||||
defending.Aggregate<TypeIdentifier, float>(1,
|
||||
(current, type) => current * GetSingleEffectiveness(attacking, type));
|
||||
|
||||
|
|
|
@ -3614,7 +3614,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
// Done up to here
|
||||
{
|
||||
"name": "fire_spin",
|
||||
"type": "fire",
|
||||
|
@ -3627,7 +3626,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "fire_spin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "first_impression",
|
||||
|
@ -3642,7 +3644,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "first_impression"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fissure",
|
||||
|
@ -3657,7 +3662,10 @@
|
|||
"protect",
|
||||
"mirror",
|
||||
"nonskybattle"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "one_hit_ko"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flail",
|
||||
|
@ -3672,7 +3680,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flail"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flame_burst",
|
||||
|
@ -3686,7 +3697,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flame_burst"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flame_charge",
|
||||
|
@ -3701,7 +3715,13 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_speed",
|
||||
"parameters": {
|
||||
"amount": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flame_wheel",
|
||||
|
@ -3717,7 +3737,14 @@
|
|||
"protect",
|
||||
"mirror",
|
||||
"defrost"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "set_status",
|
||||
"chance": 10,
|
||||
"parameters": {
|
||||
"status": "burned"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flamethrower",
|
||||
|
@ -3731,7 +3758,14 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "set_status",
|
||||
"chance": 10,
|
||||
"parameters": {
|
||||
"status": "burned"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flare_blitz",
|
||||
|
@ -3747,7 +3781,10 @@
|
|||
"protect",
|
||||
"mirror",
|
||||
"defrost"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flare_blitz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flash",
|
||||
|
@ -3762,7 +3799,13 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_accuracy",
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flash_cannon",
|
||||
|
@ -3776,7 +3819,14 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_special_defense",
|
||||
"chance": 10,
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flatter",
|
||||
|
@ -3791,7 +3841,10 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flatter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fleur_cannon",
|
||||
|
@ -3805,7 +3858,13 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_special_attack",
|
||||
"parameters": {
|
||||
"amount": -2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fling",
|
||||
|
@ -3819,7 +3878,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "fling"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "floral_healing",
|
||||
|
@ -3834,7 +3896,10 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"heal"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "floral_healing"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flower_shield",
|
||||
|
@ -3847,7 +3912,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"distance"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flower_shield"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fly",
|
||||
|
@ -3865,7 +3933,10 @@
|
|||
"mirror",
|
||||
"gravity",
|
||||
"distance"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "fly"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "flying_press",
|
||||
|
@ -3883,7 +3954,10 @@
|
|||
"gravity",
|
||||
"distance",
|
||||
"nonskybattle"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "flying_press"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "focus_blast",
|
||||
|
@ -3898,7 +3972,14 @@
|
|||
"protect",
|
||||
"mirror",
|
||||
"ballistics"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_special_defense",
|
||||
"chance": 10,
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "focus_energy",
|
||||
|
@ -3911,7 +3992,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "focus_energy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "focus_punch",
|
||||
|
@ -3926,7 +4010,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"punch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "focus_punch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "follow_me",
|
||||
|
@ -3937,7 +4024,10 @@
|
|||
"priority": 2,
|
||||
"target": "Self",
|
||||
"category": "status",
|
||||
"flags": []
|
||||
"flags": [],
|
||||
"effect": {
|
||||
"name": "follow_me"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "force_palm",
|
||||
|
@ -3952,7 +4042,14 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "set_status",
|
||||
"chance": 30,
|
||||
"parameters": {
|
||||
"status": "paralyzed"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "foresight",
|
||||
|
@ -3968,7 +4065,10 @@
|
|||
"reflectable",
|
||||
"mirror",
|
||||
"ignore-substitute"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "foresight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "forests_curse",
|
||||
|
@ -3983,7 +4083,10 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "forests_curse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "foul_play",
|
||||
|
@ -3998,7 +4101,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "foul_play"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "freeze_dry",
|
||||
|
@ -4012,7 +4118,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "freeze_dry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "freeze_shock",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
var accuracyModifier = 1.0f;
|
||||
executingMove.RunScriptHook(x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier));
|
||||
var modifiedAccuracy = (int)(moveAccuracy * accuracyModifier);
|
||||
executingMove.RunScriptHook(x => x.ChangeAccuracy(executingMove, target, hitIndex, ref modifiedAccuracy));
|
||||
var targetEvasion = target.StatBoost.Evasion;
|
||||
var ignoreEvasion = false;
|
||||
executingMove.RunScriptHook(x => x.BypassEvasionStatBoosts(executingMove, target, hitIndex, ref ignoreEvasion));
|
||||
|
|
|
@ -7,7 +7,7 @@ public class Bestow : Script
|
|||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var user = move.User;
|
||||
var userHeldItem = user.HeldItem;
|
||||
var userHeldItem = user.RemoveHeldItemForBattle();
|
||||
var targetHeldItem = target.HeldItem;
|
||||
|
||||
if (userHeldItem == null || targetHeldItem != null)
|
||||
|
|
|
@ -20,6 +20,12 @@ public class Bounce : Script
|
|||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnBeforeMove(IExecutingMove move)
|
||||
{
|
||||
move.User.Volatile.Remove(ScriptUtils.ResolveName<ChargeBounceEffect>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
|
@ -31,6 +37,5 @@ public class Bounce : Script
|
|||
{
|
||||
target.SetStatus("paralyzed");
|
||||
}
|
||||
move.User.Volatile.Remove(ScriptUtils.ResolveName<ChargeBounceEffect>());
|
||||
}
|
||||
}
|
|
@ -76,4 +76,12 @@ public class ChangeTargetSpeed : ChangeTargetStats
|
|||
public ChangeTargetSpeed() : base(Statistic.Speed)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_target_accuracy")]
|
||||
public class ChangeTargetAccuracy : ChangeTargetStats
|
||||
{
|
||||
public ChangeTargetAccuracy() : base(Statistic.Accuracy)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -10,6 +10,6 @@ public class Covet : Script
|
|||
return;
|
||||
if (move.User.HeldItem != null)
|
||||
return;
|
||||
_ = move.User.SetHeldItem(target.RemoveHeldItem());
|
||||
_ = move.User.SetHeldItem(target.RemoveHeldItemForBattle());
|
||||
}
|
||||
}
|
|
@ -19,11 +19,10 @@ public class Dig : Script
|
|||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
public override void OnBeforeMove(IExecutingMove move)
|
||||
{
|
||||
move.User.Volatile.Remove(ScriptUtils.ResolveName<DigEffect>());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "fire_spin")]
|
||||
public class FireSpin : MultiHitMove
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.StackOrAdd("fire_spin", () => new FireSpinEffect(target));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "first_impression")]
|
||||
public class FirstImpression : 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flail")]
|
||||
public class Flail : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
|
||||
{
|
||||
var remainingHealth = move.User.CurrentHealth / move.User.BoostedStats.Hp;
|
||||
var fraction = remainingHealth * 48;
|
||||
basePower = fraction switch
|
||||
{
|
||||
< 2 => 200,
|
||||
< 5 => 150,
|
||||
< 10 => 100,
|
||||
< 17 => 80,
|
||||
< 33 => 40,
|
||||
_ => 20
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flame_burst")]
|
||||
public class FlameBurst : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var adjacentFoes = GetAdjacentFoes(move.User).WhereNotNull();
|
||||
EventBatchId batchId = new();
|
||||
foreach (var adjacentFoe in adjacentFoes)
|
||||
{
|
||||
adjacentFoe.Damage(adjacentFoe.BoostedStats.Hp / 16, DamageSource.Misc, batchId);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<IPokemon?> GetAdjacentFoes(IPokemon pokemon)
|
||||
{
|
||||
var battleData = pokemon.BattleData;
|
||||
if (battleData == null)
|
||||
yield break;
|
||||
if (battleData.Battle.PositionsPerSide == 1)
|
||||
yield break;
|
||||
|
||||
var position = battleData.Position;
|
||||
var side = battleData.Battle.Sides[battleData.SideIndex];
|
||||
|
||||
if (position == 0)
|
||||
{
|
||||
yield return side.Pokemon[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return side.Pokemon[position - 1];
|
||||
if (position < side.Pokemon.Count - 1)
|
||||
yield return side.Pokemon[position + 1];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flare_blitz")]
|
||||
public class FlareBlitz : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
|
||||
{
|
||||
target.SetStatus("burned");
|
||||
}
|
||||
|
||||
|
||||
var hitData = move.GetHitData(target, hit);
|
||||
var recoilDamage = (uint)(hitData.Damage * (1 / 3));
|
||||
move.User.Damage(recoilDamage, DamageSource.Misc);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flatter")]
|
||||
public class Flatter : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.ChangeStatBoost(Statistic.SpecialAttack, 1, false);
|
||||
target.Volatile.StackOrAdd("confusion", () => new Confusion());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "fling")]
|
||||
public class Fling : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
|
||||
{
|
||||
var item = move.User.HeldItem;
|
||||
if (item == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.Category is ItemCategory.FormChanger or ItemCategory.Pokeball or ItemCategory.Mail
|
||||
or ItemCategory.KeyItem or ItemCategory.TmHm)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
basePower = item.FlingPower;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) =>
|
||||
move.User.RemoveHeldItemForBattle();
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Terrain;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "floral_healing")]
|
||||
public class FloralHealing : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (target.IsFainted)
|
||||
return;
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
var modifier = 1f / 2;
|
||||
if (battleData.Battle.TerrainName == ScriptUtils.ResolveName<GrassyTerrain>())
|
||||
modifier = 2f / 3;
|
||||
|
||||
var healing = target.BoostedStats.Hp * modifier;
|
||||
target.Heal((uint)healing);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flower_shield")]
|
||||
public class FlowerShield : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
if (!battleData.Battle.Library.StaticLibrary.Types.TryGetTypeIdentifier("grass", out var grassType))
|
||||
return;
|
||||
|
||||
var batchId = new EventBatchId();
|
||||
var sides = battleData.Battle.Sides;
|
||||
foreach (var side in sides)
|
||||
{
|
||||
foreach (var pokemon in side.Pokemon)
|
||||
{
|
||||
if (pokemon == null || pokemon.IsFainted)
|
||||
continue;
|
||||
if (!pokemon.Types.Contains(grassType))
|
||||
continue;
|
||||
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, batchId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "fly")]
|
||||
public class Fly : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void PreventMove(IExecutingMove move, ref bool prevent)
|
||||
{
|
||||
if (move.User.Volatile.Contains<ChargeFlyEffect>())
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new ChargeFlyEffect(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("fly_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", move.User }
|
||||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnBeforeMove(IExecutingMove move)
|
||||
{
|
||||
move.User.Volatile.Remove(ScriptUtils.ResolveName<ChargeFlyEffect>());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flying_press")]
|
||||
public class FlyingPress : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeEffectiveness(IExecutingMove move, IPokemon target, byte hit, ref float effectiveness)
|
||||
{
|
||||
var battleData = move.User.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
// If flying type is not found, return
|
||||
if (!typeLibrary.TryGetTypeIdentifier("flying", out var flyingType))
|
||||
return;
|
||||
var flyingEffectiveness = typeLibrary.GetEffectiveness(flyingType, target.Types);
|
||||
effectiveness *= flyingEffectiveness;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "focus_energy")]
|
||||
public class FocusEnergy : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new Pokemon.IncreasedCriticalStage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "focus_punch")]
|
||||
public class FocusPunch : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnBeforeTurnStart(ITurnChoice choice)
|
||||
{
|
||||
choice.User.Volatile.Add(new FocusPunchEffect());
|
||||
choice.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("focus_punch_charge",
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "pokemon", choice.User }
|
||||
}));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PreventMove(IExecutingMove move, ref bool prevent)
|
||||
{
|
||||
var focusPunchEffect = move.User.Volatile.Get<FocusPunchEffect>();
|
||||
if (focusPunchEffect == null || focusPunchEffect.WasHit)
|
||||
{
|
||||
prevent = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "follow_me")]
|
||||
public class FollowMe : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
|
||||
{
|
||||
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];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "foresight")]
|
||||
public class Foresight : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
target.Volatile.Add(new ForesightEffect(typeLibrary));
|
||||
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "forests_curse")]
|
||||
public class ForestsCurse : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
if (!typeLibrary.TryGetTypeIdentifier("grass", out var grassType))
|
||||
return;
|
||||
target.AddType(grassType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "foul_play")]
|
||||
public class FoulPlay : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, ref uint value)
|
||||
{
|
||||
value = move.UseMove.Category == MoveCategory.Physical
|
||||
? target.BoostedStats.Attack
|
||||
: target.BoostedStats.SpecialAttack;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "freeze_dry")]
|
||||
public class FreezeDry : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeEffectiveness(IExecutingMove move, IPokemon target, byte hit, ref float effectiveness)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
if (!typeLibrary.TryGetTypeIdentifier("water", out var waterType))
|
||||
return;
|
||||
|
||||
if (target.Types.Contains(waterType))
|
||||
{
|
||||
var effectivenessWithoutWater = target.Types
|
||||
.Where(x => x != waterType)
|
||||
.Select(x => typeLibrary.GetEffectiveness(x, target.Types))
|
||||
.Aggregate(1f, (a, b) => a * b);
|
||||
effectiveness = effectivenessWithoutWater * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
|
||||
{
|
||||
target.SetStatus("frozen");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "one_hit_ko")]
|
||||
public class OneHitKo : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy)
|
||||
{
|
||||
var levelDifference = executingMove.User.Level - target.Level;
|
||||
if (levelDifference < 0)
|
||||
{
|
||||
executingMove.GetHitData(target, hitIndex).Fail();
|
||||
return;
|
||||
}
|
||||
modifiedAccuracy = 30 + levelDifference;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
damage = target.BoostedStats.Hp.MultiplyOrMax(10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "charge_fly")]
|
||||
public class ChargeFlyEffect : Script
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
public ChargeFlyEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <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, "fly", opposingSideIndex, position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
||||
{
|
||||
if (!executingMove.UseMove.HasFlag("hit_flying"))
|
||||
block = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
if (!move.UseMove.HasFlag("effective_against_fly"))
|
||||
damage *= 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "fire_spin")]
|
||||
public class FireSpinEffect : Script
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
public FireSpinEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndTurn(IBattle battle)
|
||||
{
|
||||
_owner.Damage(_owner.BoostedStats.Hp / 8, DamageSource.Misc);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PreventSelfRunAway(IFleeChoice choice, ref bool prevent) => prevent = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "focus_punch")]
|
||||
public class FocusPunchEffect : Script
|
||||
{
|
||||
public bool WasHit { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
WasHit = true;
|
||||
target.BattleData?.Battle.EventHook.Invoke(new DialogEvent("focus_punch_lost_focus",
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "pokemon", target }
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "foresight")]
|
||||
public class ForesightEffect : Script
|
||||
{
|
||||
private readonly IReadOnlyTypeLibrary _typeLibrary;
|
||||
private readonly TypeIdentifier _normalType;
|
||||
private readonly TypeIdentifier _fightingType;
|
||||
private readonly TypeIdentifier _ghostType;
|
||||
|
||||
public ForesightEffect(IReadOnlyTypeLibrary typeLibrary)
|
||||
{
|
||||
_typeLibrary = typeLibrary;
|
||||
typeLibrary.TryGetTypeIdentifier("normal", out _normalType);
|
||||
typeLibrary.TryGetTypeIdentifier("fighting", out _fightingType);
|
||||
typeLibrary.TryGetTypeIdentifier("ghost", out _ghostType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted, ref bool prevent)
|
||||
{
|
||||
if (stat == Statistic.Evasion)
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ChangeEffectiveness(IExecutingMove move, IPokemon target, byte hit, ref float effectiveness)
|
||||
{
|
||||
|
||||
var hitData = move.GetHitData(target, hit);
|
||||
if (hitData.Type == _normalType && target.Types.Contains(_fightingType))
|
||||
effectiveness = _typeLibrary.GetEffectiveness(_normalType, target.Types.Where(x => x != _ghostType));
|
||||
else if (hitData.Type == _fightingType && target.Types.Contains(_ghostType))
|
||||
effectiveness = _typeLibrary.GetEffectiveness(_fightingType, target.Types.Where(x => x != _ghostType));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "increased_critical_stage")]
|
||||
public class IncreasedCriticalStage : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
|
||||
{
|
||||
// Extreme edge case, should never happen
|
||||
if (stage == byte.MaxValue)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
stage += 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Terrain;
|
||||
|
||||
[Script(ScriptCategory.Terrain, "grassy_terrain")]
|
||||
public class GrassyTerrain : Script
|
||||
{
|
||||
// TODO: Implement Electric Terrain
|
||||
}
|
Loading…
Reference in New Issue