Implements several more moves
This commit is contained in:
parent
549b92048a
commit
3a75493912
|
@ -252,12 +252,18 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||
/// <param name="stat">The stat to be changed</param>
|
||||
/// <param name="change">The amount to change the stat by</param>
|
||||
/// <param name="selfInflicted">Whether the change was self-inflicted. This can be relevant in scripts.</param>
|
||||
bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted);
|
||||
/// <param name="batchId">The event batch ID this change is a part of. This is relevant for visual handling</param>
|
||||
bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, EventBatchId batchId = default);
|
||||
|
||||
/// <summary>
|
||||
/// Suppresses the ability of the Pokémon.
|
||||
/// </summary>
|
||||
public void SuppressAbility();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently active ability.
|
||||
/// </summary>
|
||||
IAbility ActiveAbility { get; }
|
||||
IAbility? ActiveAbility { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
|
||||
|
@ -363,6 +369,11 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||
/// </summary>
|
||||
bool AddType(TypeIdentifier type);
|
||||
|
||||
/// <summary>
|
||||
/// Replace the types of the Pokémon with the provided types.
|
||||
/// </summary>
|
||||
void SetTypes(IReadOnlyList<TypeIdentifier> types);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the data structure to a serializable format.
|
||||
/// </summary>
|
||||
|
@ -714,7 +725,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted)
|
||||
public bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, EventBatchId batchId = default)
|
||||
{
|
||||
var prevented = false;
|
||||
this.RunScriptHook(script => script.PreventStatBoostChange(this, stat, change, selfInflicted, ref prevented));
|
||||
|
@ -736,18 +747,34 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
if (BattleData != null)
|
||||
{
|
||||
var newBoost = StatBoost.GetStatistic(stat);
|
||||
BattleData.Battle.EventHook.Invoke(new StatBoostEvent(this, stat, oldBoost, newBoost));
|
||||
BattleData.Battle.EventHook.Invoke(new StatBoostEvent(this, stat, oldBoost, newBoost)
|
||||
{
|
||||
BatchId = batchId,
|
||||
});
|
||||
}
|
||||
|
||||
RecalculateBoostedStats();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the ability of the Pokémon is suppressed.
|
||||
/// </summary>
|
||||
public bool AbilitySuppressed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAbility ActiveAbility
|
||||
public void SuppressAbility()
|
||||
{
|
||||
OverrideAbility = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAbility? ActiveAbility
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AbilitySuppressed)
|
||||
return null;
|
||||
if (OverrideAbility != null)
|
||||
return OverrideAbility;
|
||||
var ability = Form.GetAbility(AbilityIndex);
|
||||
|
@ -1004,6 +1031,9 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
Volatile.Clear();
|
||||
WeightInKg = Form.Weight;
|
||||
HeightInMeters = Form.Height;
|
||||
Types = Form.Types;
|
||||
OverrideAbility = null;
|
||||
AbilitySuppressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1032,6 +1062,12 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTypes(IReadOnlyList<TypeIdentifier> types)
|
||||
{
|
||||
_types = types.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SerializedPokemon Serialize() => new(this);
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ public interface IReadOnlyTypeLibrary
|
|||
/// 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);
|
||||
|
||||
IEnumerable<(TypeIdentifier type, float effectiveness)> GetAllEffectivenessFromAttacking(TypeIdentifier attacking);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -70,6 +72,17 @@ public class TypeLibrary : IReadOnlyTypeLibrary
|
|||
defending.Aggregate<TypeIdentifier, float>(1,
|
||||
(current, type) => current * GetSingleEffectiveness(attacking, type));
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<(TypeIdentifier, float)> GetAllEffectivenessFromAttacking(TypeIdentifier attacking)
|
||||
{
|
||||
if (attacking.Value < 1 || attacking.Value > _effectiveness.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(attacking));
|
||||
for (var i = 0; i < _effectiveness.Count; i++)
|
||||
{
|
||||
yield return (new TypeIdentifier((byte)(i + 1)), _effectiveness[attacking.Value - 1][i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new type in the library.
|
||||
/// </summary>
|
||||
|
|
|
@ -29,4 +29,16 @@ public enum Statistic : byte
|
|||
/// Speed determines the order that a Pokémon can act in battle.
|
||||
/// </summary>
|
||||
Speed,
|
||||
|
||||
/// <summary>
|
||||
/// Evasion determines the likelihood that a Pokémon will dodge an attack.
|
||||
/// This is not part of base stats, but is a temporary stat boost.
|
||||
/// </summary>
|
||||
Evasion,
|
||||
|
||||
/// <summary>
|
||||
/// Accuracy determines the likelihood that a Pokémon will hit an attack.
|
||||
/// This is not part of base stats, but is a temporary stat boost.
|
||||
/// </summary>
|
||||
Accuracy,
|
||||
}
|
|
@ -168,12 +168,24 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
|||
Speed = Add(Speed, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Invalid statistic.");
|
||||
SetUnknownStat(stat, Add(GetUnknownStat(stat), value));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual T GetUnknownStat(Statistic stat)
|
||||
{
|
||||
throw new ArgumentException($"Invalid statistic {stat}");
|
||||
}
|
||||
|
||||
protected virtual void SetUnknownStat(Statistic stat, T value)
|
||||
{
|
||||
throw new ArgumentException($"Invalid statistic {stat}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decreases a statistic in the set by a value.
|
||||
/// </summary>
|
||||
|
@ -200,14 +212,15 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
|||
Speed = Subtract(Speed, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Invalid statistic.");
|
||||
SetUnknownStat(stat, Subtract(GetUnknownStat(stat), value));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
public virtual IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
yield return Hp;
|
||||
yield return Attack;
|
||||
|
@ -332,6 +345,47 @@ public record StatBoostStatisticSet : ClampedStatisticSet<sbyte>
|
|||
sbyte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override sbyte GetUnknownStat(Statistic stat)
|
||||
{
|
||||
return stat switch
|
||||
{
|
||||
Statistic.Evasion => Evasion,
|
||||
Statistic.Accuracy => Accuracy,
|
||||
_ => throw new ArgumentException($"Invalid statistic {stat}"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc />
|
||||
protected override void SetUnknownStat(Statistic stat, sbyte value)
|
||||
{
|
||||
switch (stat)
|
||||
{
|
||||
case Statistic.Evasion:
|
||||
Evasion = value;
|
||||
break;
|
||||
case Statistic.Accuracy:
|
||||
Accuracy = value;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Invalid statistic {stat}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerator<sbyte> GetEnumerator()
|
||||
{
|
||||
yield return Hp;
|
||||
yield return Attack;
|
||||
yield return Defense;
|
||||
yield return SpecialAttack;
|
||||
yield return SpecialDefense;
|
||||
yield return Speed;
|
||||
yield return Evasion;
|
||||
yield return Accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1424,7 +1424,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "calm_mind"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "camouflage",
|
||||
|
@ -1437,7 +1440,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "camouflage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "captivate",
|
||||
|
@ -1452,7 +1458,10 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "captivate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "catastropika",
|
||||
|
@ -1476,7 +1485,10 @@
|
|||
"priority": 0,
|
||||
"target": "Self",
|
||||
"category": "status",
|
||||
"flags": []
|
||||
"flags": [],
|
||||
"effect": {
|
||||
"name": "celebrate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "charge",
|
||||
|
@ -1489,7 +1501,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "charge"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "charge_beam",
|
||||
|
@ -1503,7 +1518,14 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_special_attack",
|
||||
"chance": 70,
|
||||
"parameters": {
|
||||
"amount": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "charm",
|
||||
|
@ -1518,7 +1540,13 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_attack",
|
||||
"parameters": {
|
||||
"amount": -2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "chatter",
|
||||
|
@ -1535,7 +1563,10 @@
|
|||
"sound",
|
||||
"distance",
|
||||
"ignore-substitute"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "confuse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "chip_away",
|
||||
|
@ -1550,7 +1581,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "chip_away"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "circle_throw",
|
||||
|
@ -1565,7 +1599,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "circle_throw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clamp",
|
||||
|
@ -1580,7 +1617,10 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "bind"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clanging_scales",
|
||||
|
@ -1596,7 +1636,13 @@
|
|||
"mirror",
|
||||
"sound",
|
||||
"ignore-substitute"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_defense",
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clear_smog",
|
||||
|
@ -1610,7 +1656,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "reset_target_stats"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "close_combat",
|
||||
|
@ -1625,7 +1674,13 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_defense",
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "coil",
|
||||
|
@ -1638,7 +1693,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "coil"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "comet_punch",
|
||||
|
@ -1654,7 +1712,10 @@
|
|||
"protect",
|
||||
"mirror",
|
||||
"punch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "2_5_hit_move"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "confide",
|
||||
|
@ -1670,7 +1731,13 @@
|
|||
"mirror",
|
||||
"sound",
|
||||
"ignore-substitute"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_special_attack",
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "confuse_ray",
|
||||
|
@ -1685,7 +1752,10 @@
|
|||
"protect",
|
||||
"reflectable",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "confuse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "confusion",
|
||||
|
@ -1699,7 +1769,11 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "confuse",
|
||||
"chance": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "constrict",
|
||||
|
@ -1714,7 +1788,14 @@
|
|||
"contact",
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_speed",
|
||||
"chance": 10,
|
||||
"parameters": {
|
||||
"amount": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "continental_crush__physical",
|
||||
|
@ -1749,7 +1830,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "conversion"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conversion_2",
|
||||
|
@ -1762,7 +1846,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"ignore-substitute"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "conversion_2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "copycat",
|
||||
|
@ -1773,7 +1860,10 @@
|
|||
"priority": 0,
|
||||
"target": "Self",
|
||||
"category": "status",
|
||||
"flags": []
|
||||
"flags": [],
|
||||
"effect": {
|
||||
"name": "copycat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "core_enforcer",
|
||||
|
@ -1787,7 +1877,10 @@
|
|||
"flags": [
|
||||
"protect",
|
||||
"mirror"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "core_enforcer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "corkscrew_crash__physical",
|
||||
|
@ -1822,7 +1915,10 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "cosmic_power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cotton_guard",
|
||||
|
@ -1835,7 +1931,13 @@
|
|||
"category": "status",
|
||||
"flags": [
|
||||
"snatch"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_defense",
|
||||
"parameters": {
|
||||
"amount": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cotton_spore",
|
||||
|
@ -1851,7 +1953,13 @@
|
|||
"reflectable",
|
||||
"mirror",
|
||||
"powder"
|
||||
]
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_target_speed",
|
||||
"parameters": {
|
||||
"amount": -2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "counter",
|
||||
|
|
|
@ -8,7 +8,8 @@ public class BulkUp : Script
|
|||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
move.User.ChangeStatBoost(Statistic.Attack, 1, true);
|
||||
move.User.ChangeStatBoost(Statistic.Defense, 1, true);
|
||||
EventBatchId eventBatchId = new();
|
||||
move.User.ChangeStatBoost(Statistic.Attack, 1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.Defense, 1, true, eventBatchId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "calm_mind")]
|
||||
public class CalmMind : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
EventBatchId eventBatchId = new();
|
||||
move.User.ChangeStatBoost(Statistic.SpecialAttack, 1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true, eventBatchId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "camouflage")]
|
||||
public class Camouflage : Script
|
||||
{
|
||||
// FIXME: Implement this. How to get the terrain in a sane manner?
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Species;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "captivate")]
|
||||
public class Captivate : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var user = move.User;
|
||||
if (target.Gender == Gender.Genderless || target.Gender == user.Gender)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
if (target.ActiveAbility?.Name == "oblivious")
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "celebrate")]
|
||||
public class Celebrate : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
|
||||
{
|
||||
// This move is mostly useless, and it's not worth the effort to implement it.
|
||||
// Prevent it from being selected.
|
||||
prevent = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
public abstract class ChangeUserStats : Script
|
||||
{
|
||||
private readonly Statistic _stat;
|
||||
private sbyte _amount;
|
||||
|
||||
protected ChangeUserStats(Statistic stat)
|
||||
{
|
||||
_stat = stat;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameters));
|
||||
}
|
||||
|
||||
if (!parameters.TryGetValue("amount", out var amount) || amount == null)
|
||||
{
|
||||
throw new ArgumentException("Parameter 'amount' is required.");
|
||||
}
|
||||
|
||||
_amount = (sbyte)amount;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
move.User.ChangeStatBoost(_stat, _amount, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_user_attack")]
|
||||
public class ChangeUserAttack : ChangeUserStats
|
||||
{
|
||||
public ChangeUserAttack() : base(Statistic.Attack)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_user_defense")]
|
||||
public class ChangeUserDefense : ChangeUserStats
|
||||
{
|
||||
public ChangeUserDefense() : base(Statistic.Defense)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_user_special_attack")]
|
||||
public class ChangeUserSpecialAttack : ChangeUserStats
|
||||
{
|
||||
public ChangeUserSpecialAttack() : base(Statistic.SpecialAttack)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_user_special_defense")]
|
||||
public class ChangeUserSpecialDefense : ChangeUserStats
|
||||
{
|
||||
public ChangeUserSpecialDefense() : base(Statistic.SpecialDefense)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Move, "change_user_speed")]
|
||||
public class ChangeUserSpeed : ChangeUserStats
|
||||
{
|
||||
public ChangeUserSpeed() : base(Statistic.Speed)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "charge")]
|
||||
public class Charge : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true);
|
||||
move.User.Volatile.Add(new ChargeEffect());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "chip_away")]
|
||||
public class ChipAway : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass)
|
||||
{
|
||||
bypass = true;
|
||||
}
|
||||
|
||||
// TODO: bypass evasion stat.
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "circle_throw")]
|
||||
public class CircleThrow : Script
|
||||
{
|
||||
// TODO: Implement this. How to handle forced switch?
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "close_combat")]
|
||||
public class CloseCombat : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
EventBatchId eventBatchId = new();
|
||||
move.User.ChangeStatBoost(Statistic.Defense, -1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, eventBatchId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "coil")]
|
||||
public class Coil : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
EventBatchId eventBatchId = new();
|
||||
move.User.ChangeStatBoost(Statistic.Attack, 1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.Defense, 1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.Accuracy, 1, true, eventBatchId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "confuse")]
|
||||
public class Confuse : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new Confusion());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "conversion")]
|
||||
public class Conversion : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var moveType = move.User.Moves.WhereNotNull().FirstOrDefault()?.MoveData.MoveType;
|
||||
if (moveType == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
move.User.SetTypes([moveType.Value]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "conversion_2")]
|
||||
public class Conversion2 : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var previousTurnChoices = target.BattleData?.Battle.PreviousTurnChoices;
|
||||
var nextExecutingChoice = target.BattleData?.Battle.ChoiceQueue?.Peek();
|
||||
var lastMoveByTarget = previousTurnChoices?
|
||||
// The previous turn choices include the choices of the current turn, so we need to have special handling for
|
||||
// the current turn
|
||||
.Select((x, index) =>
|
||||
{
|
||||
// All choices before the current turn are valid
|
||||
if (index < previousTurnChoices.Count - 1)
|
||||
return x;
|
||||
// If there is no next choice, we're at the end of the list, so we can just return the whole list
|
||||
if (nextExecutingChoice == null)
|
||||
return x;
|
||||
// Otherwise we determine where the next choice is and return everything before that
|
||||
var indexOfNext = x.IndexOf(nextExecutingChoice);
|
||||
if (indexOfNext == -1)
|
||||
return x;
|
||||
return x.Take(indexOfNext);
|
||||
})
|
||||
.SelectMany(x => x)
|
||||
// We only want the last move choice by the target
|
||||
.OfType<IMoveChoice>().FirstOrDefault(x => x.User == target);
|
||||
if (lastMoveByTarget == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var typeLibrary = move.User.BattleData!.Battle.Library.StaticLibrary.Types;
|
||||
// Get all types against which the last move would be not very effective
|
||||
var type = typeLibrary.GetAllEffectivenessFromAttacking(lastMoveByTarget.ChosenMove.MoveData.MoveType)
|
||||
.Where(x => x.effectiveness < 1)
|
||||
// Shuffle them randomly, but deterministically
|
||||
.OrderBy(_ => move.User.BattleData.Battle.Random.GetInt())
|
||||
.ThenBy(x => x.type.Value)
|
||||
// And grab the first one
|
||||
.Select(x => x.type)
|
||||
.FirstOrDefault();
|
||||
if (type == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
move.User.SetTypes([type]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "copycat")]
|
||||
public class Copycat : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeMove(IMoveChoice choice, ref StringKey moveName)
|
||||
{
|
||||
var lastMove = choice.User.BattleData?.Battle.PreviousTurnChoices
|
||||
.SelectMany(x => x)
|
||||
.OfType<IMoveChoice>()
|
||||
.LastOrDefault();
|
||||
if (lastMove == null || !lastMove.ChosenMove.MoveData.CanCopyMove())
|
||||
{
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
moveName = lastMove.ChosenMove.MoveData.Name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System.Linq;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "core_enforcer")]
|
||||
public class CoreEnforcer : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
var turnChoices = battleData.Battle.PreviousTurnChoices.Last();
|
||||
var currentChoiceIndex = turnChoices.IndexOf(move.MoveChoice);
|
||||
if (currentChoiceIndex == -1 ||
|
||||
!turnChoices.Take(currentChoiceIndex).Any(choice => choice is IMoveChoice or IItemChoice))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
target.SuppressAbility();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "cosmic_power")]
|
||||
public class CosmicPower : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
EventBatchId eventBatchId = new();
|
||||
move.User.ChangeStatBoost(Statistic.Defense, 1, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true, eventBatchId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "reset_target_stats")]
|
||||
public class ResetTargetStats : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
EventBatchId eventBatchId = new();
|
||||
foreach (Statistic stat in Enum.GetValues(typeof(Statistic)))
|
||||
{
|
||||
target.ChangeStatBoost(stat, (sbyte)-target.StatBoost.GetStatistic(stat), true, eventBatchId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "charge_effect")]
|
||||
public class ChargeEffect : Script
|
||||
{
|
||||
private bool _turnOfUse = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
|
||||
{
|
||||
var library = target.BattleData?.Battle.Library;
|
||||
if (library == null)
|
||||
return;
|
||||
if (!library.StaticLibrary.Types.TryGetTypeIdentifier("electric", out var electricType))
|
||||
return;
|
||||
if (move.UseMove.MoveType == electricType)
|
||||
modifier *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndTurn(IBattle battle)
|
||||
{
|
||||
if (_turnOfUse)
|
||||
{
|
||||
_turnOfUse = false;
|
||||
return;
|
||||
}
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "confusion")]
|
||||
public class Confusion : Script
|
||||
{
|
||||
// TODO: Implement confusion
|
||||
}
|
|
@ -36,7 +36,7 @@ public class Hail : Script
|
|||
continue;
|
||||
if (pokemon.Types.Contains(iceType))
|
||||
continue;
|
||||
if (_hailIgnoreAbilities.Contains(pokemon.ActiveAbility.Name))
|
||||
if (pokemon.ActiveAbility != null && _hailIgnoreAbilities.Contains(pokemon.ActiveAbility.Name))
|
||||
continue;
|
||||
|
||||
var maxHealth = pokemon.BoostedStats.Hp;
|
||||
|
|
Loading…
Reference in New Issue