More moves implemented

This commit is contained in:
Deukhoofd 2025-02-01 15:00:22 +01:00
parent 3a75493912
commit 00fe08dcd4
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
50 changed files with 1146 additions and 139 deletions

View File

@ -115,6 +115,10 @@ public interface IBattle : IScriptSource, IDeepCloneable
/// </summary> /// </summary>
StringKey? WeatherName { get; } StringKey? WeatherName { get; }
void SetTerrain(StringKey? terrainName);
StringKey? TerrainName { get; }
/// <summary> /// <summary>
/// Gets the turn choices of the previous turn. This is a list of lists, where each list represents the choices /// Gets the turn choices of the previous turn. This is a list of lists, where each list represents the choices
/// for a single turn. The outer list is ordered from oldest to newest turn. /// for a single turn. The outer list is ordered from oldest to newest turn.
@ -267,6 +271,8 @@ public class BattleImpl : ScriptSource, IBattle
return false; return false;
var preventMove = false; var preventMove = false;
choice.RunScriptHook(script => script.PreventMoveSelection(moveChoice, ref preventMove)); choice.RunScriptHook(script => script.PreventMoveSelection(moveChoice, ref preventMove));
if (preventMove)
return false;
} }
return true; return true;
@ -358,6 +364,26 @@ public class BattleImpl : ScriptSource, IBattle
/// <inheritdoc /> /// <inheritdoc />
public StringKey? WeatherName => _weatherScript.Script?.Name; public StringKey? WeatherName => _weatherScript.Script?.Name;
private readonly ScriptContainer _terrainScript = new();
/// <inheritdoc />
public void SetTerrain(StringKey? terrainName)
{
if (terrainName.HasValue)
{
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Terrain, terrainName.Value, null, out var script))
throw new InvalidOperationException($"Terrain script {terrainName} not found.");
_terrainScript.Set(script);
}
else
{
_terrainScript.Clear();
}
}
/// <inheritdoc />
public StringKey? TerrainName => _terrainScript.Script?.Name;
private readonly List<IReadOnlyList<ITurnChoice>> _previousTurnChoices = new(); private readonly List<IReadOnlyList<ITurnChoice>> _previousTurnChoices = new();
@ -383,12 +409,13 @@ public class BattleImpl : ScriptSource, IBattle
} }
/// <inheritdoc /> /// <inheritdoc />
public override int ScriptCount => 2; public override int ScriptCount => 3;
/// <inheritdoc /> /// <inheritdoc />
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts) public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
{ {
scripts.Add(_weatherScript); scripts.Add(_weatherScript);
scripts.Add(_terrainScript);
scripts.Add(Volatile); scripts.Add(Volatile);
} }

View File

@ -16,6 +16,7 @@ public class BattleChoiceQueue : IDeepCloneable
{ {
private readonly ITurnChoice?[] _choices; private readonly ITurnChoice?[] _choices;
private int _currentIndex; private int _currentIndex;
public ITurnChoice? LastRanChoice { get; private set; }
/// <inheritdoc cref="BattleChoiceQueue"/> /// <inheritdoc cref="BattleChoiceQueue"/>
public BattleChoiceQueue(ITurnChoice[] choices) public BattleChoiceQueue(ITurnChoice[] choices)
@ -36,6 +37,7 @@ public class BattleChoiceQueue : IDeepCloneable
var choice = _choices[_currentIndex]; var choice = _choices[_currentIndex];
_choices[_currentIndex] = null; _choices[_currentIndex] = null;
_currentIndex++; _currentIndex++;
LastRanChoice = choice;
return choice; return choice;
} }
@ -99,4 +101,6 @@ public class BattleChoiceQueue : IDeepCloneable
} }
internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices; internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices;
public ITurnChoice? Where(Func<ITurnChoice, bool> predicate) => _choices.WhereNotNull().FirstOrDefault(predicate);
} }

View File

@ -36,6 +36,7 @@ internal static class MoveTurnExecutor
var targetType = moveData.Target; var targetType = moveData.Target;
var targets = TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType); var targets = TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType);
moveChoice.RunScriptHook(x => x.ChangeTargets(moveChoice, ref targets));
byte numberOfHits = 1; byte numberOfHits = 1;
moveChoice.RunScriptHook(x => x.ChangeNumberOfHits(moveChoice, ref numberOfHits)); moveChoice.RunScriptHook(x => x.ChangeNumberOfHits(moveChoice, ref numberOfHits));
@ -152,8 +153,11 @@ internal static class MoveTurnExecutor
var blockIncomingHit = false; var blockIncomingHit = false;
target.RunScriptHook(x => x.BlockIncomingHit(executingMove, target, hitIndex, ref blockIncomingHit)); target.RunScriptHook(x => x.BlockIncomingHit(executingMove, target, hitIndex, ref blockIncomingHit));
executingMove.RunScriptHook(x => x.BlockOutgoingHit(executingMove, target, hitIndex, ref blockIncomingHit));
if (blockIncomingHit) if (blockIncomingHit)
break; break;
if (executingMove.GetHitData(target, hitIndex).HasFailed)
break;
if (useMove.Category == MoveCategory.Status) if (useMove.Category == MoveCategory.Status)
{ {
var secondaryEffect = useMove.SecondaryEffect; var secondaryEffect = useMove.SecondaryEffect;

View File

@ -34,6 +34,8 @@ public interface IMoveChoice : ITurnChoice
ScriptContainer Script { get; set; } ScriptContainer Script { get; set; }
Dictionary<StringKey, object?>? AdditionalData { get; } Dictionary<StringKey, object?>? AdditionalData { get; }
IScriptSet Volatile { get; }
} }
/// <inheritdoc cref="IMoveChoice"/> /// <inheritdoc cref="IMoveChoice"/>
@ -76,10 +78,17 @@ public class MoveChoice : TurnChoice, IMoveChoice
public Dictionary<StringKey, object?>? AdditionalData { get; } public Dictionary<StringKey, object?>? AdditionalData { get; }
/// <inheritdoc /> /// <inheritdoc />
public override int ScriptCount => 1 + User.ScriptCount; public IScriptSet Volatile { get; } = new ScriptSet();
/// <inheritdoc /> /// <inheritdoc />
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts) => scripts.Add(Script); public override int ScriptCount => 2 + User.ScriptCount;
/// <inheritdoc />
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
{
scripts.Add(Volatile);
scripts.Add(Script);
}
/// <inheritdoc /> /// <inheritdoc />
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts)

View File

@ -122,6 +122,13 @@ public abstract class Script : IDeepCloneable
{ {
} }
/// <summary>
/// Changes the targets of a move choice. This allows for changing the targets of a move before the move starts.
/// </summary>
public virtual void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
{
}
/// <summary> /// <summary>
/// This function allows you to change a move into a multi-hit move. The number of hits set here /// This function allows you to change a move into a multi-hit move. The number of hits set here
/// gets used as the number of hits. If set to 0, this will behave as if the move missed on its /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its
@ -261,6 +268,14 @@ public abstract class Script : IDeepCloneable
{ {
} }
/// <summary>
/// This function allows a script to bypass evasion stat boosts for a move hit.
/// If this is true, the move will handle the evasion stat boosts as if the target has no positive stat boosts.
/// </summary>
public virtual void BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass)
{
}
/// <summary> /// <summary>
/// This function allows a script to bypass offensive stat boosts for a move hit. /// This function allows a script to bypass offensive stat boosts for a move hit.
/// If this is true, the damage will be calculated as if the user has no negative offensive stat boosts. Positive /// If this is true, the damage will be calculated as if the user has no negative offensive stat boosts. Positive
@ -513,6 +528,21 @@ public abstract class Script : IDeepCloneable
{ {
} }
public virtual void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
}
/// <summary>
/// Custom triggers for scripts. This allows scripts to run custom events that are not part of the
/// standard battle flow.
/// </summary>
/// <param name="eventName">
/// The name of the event that is triggered. This should be unique for each different event. Overriding scripts
/// should validate the event name is one they should handle.
/// </param>
/// <param name="parameters">
/// The parameters that are passed to the event. This can be null if no parameters are passed.
/// </param>
public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters) public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
{ {
} }

View File

@ -17,41 +17,48 @@ public enum ScriptCategory
/// </summary> /// </summary>
Move = 0, Move = 0,
/// <summary>
/// A volatile script effect that is attached to a move choice.
/// </summary>
MoveVolatile = 1,
/// <summary> /// <summary>
/// An ability script. Scripts in this category are always abilities, and therefore always /// An ability script. Scripts in this category are always abilities, and therefore always
/// attached to a Pokemon. /// attached to a Pokemon.
/// </summary> /// </summary>
Ability = 1, Ability = 2,
/// <summary> /// <summary>
/// A non volatile status script. Scripts in this category are always non volatile statuses, and /// A non volatile status script. Scripts in this category are always non volatile statuses, and
/// therefore always attached to a Pokemon. /// therefore always attached to a Pokemon.
/// </summary> /// </summary>
Status = 2, Status = 3,
/// <summary> /// <summary>
/// A volatile status script. Scripts in this category are always volatile status effects, and /// A volatile status script. Scripts in this category are always volatile status effects, and
/// therefore always attached to a Pokemon. /// therefore always attached to a Pokemon.
/// </summary> /// </summary>
Pokemon = 3, Pokemon = 4,
/// <summary> /// <summary>
/// A script that can be attached to an entire side. /// A script that can be attached to an entire side.
/// </summary> /// </summary>
Side = 4, Side = 5,
/// <summary> /// <summary>
/// A script that can be attached to the entire battle. /// A script that can be attached to the entire battle.
/// </summary> /// </summary>
Battle = 5, Battle = 6,
/// <summary> /// <summary>
/// A special script for weather, for use on battles. /// A special script for weather, for use on battles.
/// </summary> /// </summary>
Weather = 6, Weather = 7,
Terrain = 8,
/// <summary> /// <summary>
/// A special script for held items. As they're part of a held item, they're attached to a Pokemon. /// A special script for held items. As they're part of a held item, they're attached to a Pokemon.
/// </summary> /// </summary>
ItemBattleTrigger = 7, ItemBattleTrigger = 9,
} }

View File

@ -49,4 +49,11 @@ public static class NumericHelpers
var result = value * multiplier; var result = value * multiplier;
return result > short.MaxValue ? short.MaxValue : (short)result; return result > short.MaxValue ? short.MaxValue : (short)result;
} }
public static uint MultiplyOrMax(this uint value, uint multiplier)
{
var result = (ulong)value * multiplier;
return result > uint.MaxValue ? uint.MaxValue : (uint)result;
}
} }

View File

@ -1337,7 +1337,11 @@
"snatch" "snatch"
], ],
"effect": { "effect": {
"name": "bulk_up" "name": "change_multiple_user_stat_boosts",
"parameters": {
"attack": 1,
"defense": 1
}
} }
}, },
{ {
@ -1426,7 +1430,11 @@
"snatch" "snatch"
], ],
"effect": { "effect": {
"name": "calm_mind" "name": "change_multiple_user_stat_boosts",
"parameters": {
"specialAttack": 1,
"specialDefense": 1
}
} }
}, },
{ {
@ -1676,9 +1684,10 @@
"mirror" "mirror"
], ],
"effect": { "effect": {
"name": "change_user_defense", "name": "change_multiple_user_stat_boosts",
"parameters": { "parameters": {
"amount": -1 "defense": -1,
"specialDefense": -1
} }
} }
}, },
@ -1695,7 +1704,12 @@
"snatch" "snatch"
], ],
"effect": { "effect": {
"name": "coil" "name": "change_multiple_user_stat_boosts",
"parameters": {
"attack": 1,
"defense": 1,
"accuracy": 1
}
} }
}, },
{ {
@ -1917,7 +1931,11 @@
"snatch" "snatch"
], ],
"effect": { "effect": {
"name": "cosmic_power" "name": "change_multiple_user_stat_boosts",
"parameters": {
"defense": 1,
"specialDefense": 1
}
} }
}, },
{ {
@ -1973,7 +1991,10 @@
"flags": [ "flags": [
"contact", "contact",
"protect" "protect"
] ],
"effect": {
"name": "counter"
}
}, },
{ {
"name": "covet", "name": "covet",
@ -1988,7 +2009,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "covet"
}
}, },
{ {
"name": "crabhammer", "name": "crabhammer",
@ -2014,7 +2038,10 @@
"priority": 3, "priority": 3,
"target": "AllAlly", "target": "AllAlly",
"category": "status", "category": "status",
"flags": [] "flags": [],
"effect": {
"name": "crafty_shield"
}
}, },
{ {
"name": "cross_chop", "name": "cross_chop",
@ -2044,7 +2071,14 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 10,
"parameters": {
"status": "poisoned"
}
}
}, },
{ {
"name": "crunch", "name": "crunch",
@ -2060,7 +2094,14 @@
"protect", "protect",
"mirror", "mirror",
"bite" "bite"
] ],
"effect": {
"name": "change_target_defense",
"chance": 20,
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "crush_claw", "name": "crush_claw",
@ -2075,7 +2116,14 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_defense",
"chance": 50,
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "crush_grip", "name": "crush_grip",
@ -2090,7 +2138,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "crush_grip"
}
}, },
{ {
"name": "curse", "name": "curse",
@ -2103,7 +2154,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "curse"
}
}, },
{ {
"name": "cut", "name": "cut",
@ -2134,7 +2188,11 @@
"mirror", "mirror",
"distance", "distance",
"pulse" "pulse"
] ],
"effect": {
"name": "flinch",
"chance": 20
}
}, },
{ {
"name": "dark_void", "name": "dark_void",
@ -2149,7 +2207,13 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"parameters": {
"status": "sleep"
}
}
}, },
{ {
"name": "darkest_lariat", "name": "darkest_lariat",
@ -2164,7 +2228,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "darkest_lariat"
}
}, },
{ {
"name": "dazzling_gleam", "name": "dazzling_gleam",
@ -2191,7 +2258,14 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "change_multiple_user_stat_boosts",
"parameters": {
"defense": 1,
"specialDefense": 1
}
}
}, },
{ {
"name": "defense_curl", "name": "defense_curl",
@ -2204,7 +2278,13 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "change_user_defense",
"parameters": {
"amount": 1
}
}
}, },
{ {
"name": "defog", "name": "defog",
@ -2220,7 +2300,10 @@
"reflectable", "reflectable",
"mirror", "mirror",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "defog"
}
}, },
{ {
"name": "destiny_bond", "name": "destiny_bond",
@ -2233,7 +2316,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "destiny_bond"
}
}, },
{ {
"name": "detect", "name": "detect",
@ -2244,7 +2330,10 @@
"priority": 4, "priority": 4,
"target": "Self", "target": "Self",
"category": "status", "category": "status",
"flags": [] "flags": [],
"effect": {
"name": "protect"
}
}, },
{ {
"name": "devastating_drake__physical", "name": "devastating_drake__physical",
@ -2280,7 +2369,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "raise_user_defense",
"chance": 50,
"parameters": {
"amount": 2
}
}
}, },
{ {
"name": "dig", "name": "dig",
@ -2297,7 +2393,10 @@
"protect", "protect",
"mirror", "mirror",
"nonskybattle" "nonskybattle"
] ],
"effect": {
"name": "dig"
}
}, },
{ {
"name": "disable", "name": "disable",
@ -2344,7 +2443,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 30,
"parameters": {
"status": "paralyzed"
}
}
}, },
{ {
"name": "dive", "name": "dive",
@ -2361,7 +2467,10 @@
"protect", "protect",
"mirror", "mirror",
"nonskybattle" "nonskybattle"
] ],
"effect": {
"name": "dive"
}
}, },
{ {
"name": "dizzy_punch", "name": "dizzy_punch",
@ -2377,7 +2486,11 @@
"protect", "protect",
"mirror", "mirror",
"punch" "punch"
] ],
"effect": {
"name": "confuse",
"chance": 20
}
}, },
{ {
"name": "doom_desire", "name": "doom_desire",
@ -2388,7 +2501,10 @@
"priority": 0, "priority": 0,
"target": "Any", "target": "Any",
"category": "special", "category": "special",
"flags": [] "flags": [],
"effect": {
"name": "doom_desire"
}
}, },
{ {
"name": "double_edge", "name": "double_edge",
@ -2403,7 +2519,13 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "recoil",
"parameters": {
"amount": 33.333
}
}
}, },
{ {
"name": "double_hit", "name": "double_hit",
@ -2418,7 +2540,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "2_hit_move"
}
}, },
{ {
"name": "double_kick", "name": "double_kick",
@ -2433,7 +2558,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "2_hit_move"
}
}, },
{ {
"name": "double_slap", "name": "double_slap",
@ -2448,7 +2576,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "2_5_hit_move"
}
}, },
{ {
"name": "double_team", "name": "double_team",
@ -2461,7 +2592,13 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "change_user_evasion",
"parameters": {
"amount": 1
}
}
}, },
{ {
"name": "draco_meteor", "name": "draco_meteor",
@ -2475,7 +2612,13 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "change_user_special_attack",
"parameters": {
"amount": -2
}
}
}, },
{ {
"name": "dragon_ascent", "name": "dragon_ascent",
@ -2491,7 +2634,14 @@
"protect", "protect",
"mirror", "mirror",
"distance" "distance"
] ],
"effect": {
"name": "change_multiple_user_stat_boosts",
"parameters": {
"defense": 1,
"specialDefense": 1
}
}
}, },
{ {
"name": "dragon_breath", "name": "dragon_breath",
@ -2505,7 +2655,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 30,
"parameters": {
"status": "paralyzed"
}
}
}, },
{ {
"name": "dragon_claw", "name": "dragon_claw",
@ -2534,7 +2691,14 @@
"flags": [ "flags": [
"snatch", "snatch",
"dance" "dance"
] ],
"effect": {
"name": "change_multiple_user_stat_boosts",
"parameters": {
"attack": 1,
"speed": 1
}
}
}, },
{ {
"name": "dragon_hammer", "name": "dragon_hammer",
@ -2579,7 +2743,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "dragon_rage"
}
}, },
{ {
"name": "dragon_rush", "name": "dragon_rush",
@ -2594,7 +2761,11 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "flinch",
"chance": 20
}
}, },
{ {
"name": "dragon_tail", "name": "dragon_tail",
@ -2609,7 +2780,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "circle_throw"
}
}, },
{ {
"name": "drain_punch", "name": "drain_punch",
@ -2626,7 +2800,13 @@
"mirror", "mirror",
"punch", "punch",
"heal" "heal"
] ],
"effect": {
"name": "drain",
"parameters": {
"drain_modifier": 0.5
}
}
}, },
{ {
"name": "draining_kiss", "name": "draining_kiss",
@ -2642,7 +2822,13 @@
"protect", "protect",
"mirror", "mirror",
"heal" "heal"
] ],
"effect": {
"name": "drain",
"parameters": {
"drain_modifier": 0.75
}
}
}, },
{ {
"name": "dream_eater", "name": "dream_eater",
@ -2657,7 +2843,10 @@
"protect", "protect",
"mirror", "mirror",
"heal" "heal"
] ],
"effect": {
"name": "dream_eater"
}
}, },
{ {
"name": "drill_peck", "name": "drill_peck",
@ -2703,7 +2892,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "2_hit_move"
}
}, },
{ {
"name": "dynamic_punch", "name": "dynamic_punch",
@ -2719,7 +2911,10 @@
"protect", "protect",
"mirror", "mirror",
"punch" "punch"
] ],
"effect": {
"name": "confuse"
}
}, },
{ {
"name": "earth_power", "name": "earth_power",
@ -2734,7 +2929,14 @@
"protect", "protect",
"mirror", "mirror",
"nonskybattle" "nonskybattle"
] ],
"effect": {
"name": "change_target_special_defense",
"chance": 10,
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "earthquake", "name": "earthquake",
@ -2748,7 +2950,9 @@
"flags": [ "flags": [
"protect", "protect",
"mirror", "mirror",
"nonskybattle" "nonskybattle",
"hit_underground",
"effective_against_underground"
] ]
}, },
{ {
@ -2765,7 +2969,10 @@
"mirror", "mirror",
"sound", "sound",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "echoed_voice"
}
}, },
{ {
"name": "eerie_impulse", "name": "eerie_impulse",
@ -2780,7 +2987,13 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_special_attack",
"parameters": {
"amount": -2
}
}
}, },
{ {
"name": "egg_bomb", "name": "egg_bomb",
@ -2808,7 +3021,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"nonskybattle" "nonskybattle"
] ],
"effect": {
"name": "electric_terrain"
}
}, },
{ {
"name": "electrify", "name": "electrify",
@ -2822,7 +3038,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "electrify"
}
}, },
{ {
"name": "electro_ball", "name": "electro_ball",
@ -2837,7 +3056,10 @@
"protect", "protect",
"mirror", "mirror",
"ballistics" "ballistics"
] ],
"effect": {
"name": "electro_ball"
}
}, },
{ {
"name": "electroweb", "name": "electroweb",
@ -2851,7 +3073,13 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_speed",
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "embargo", "name": "embargo",

View File

@ -55,6 +55,10 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
executingMove.RunScriptHook(x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier)); executingMove.RunScriptHook(x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier));
var modifiedAccuracy = (int)(moveAccuracy * accuracyModifier); var modifiedAccuracy = (int)(moveAccuracy * accuracyModifier);
var targetEvasion = target.StatBoost.Evasion; var targetEvasion = target.StatBoost.Evasion;
var ignoreEvasion = false;
executingMove.RunScriptHook(x => x.BypassEvasionStatBoosts(executingMove, target, hitIndex, ref ignoreEvasion));
if (ignoreEvasion)
targetEvasion = 0;
var userAccuracy = executingMove.User.StatBoost.Accuracy; var userAccuracy = executingMove.User.StatBoost.Accuracy;
var difference = targetEvasion - userAccuracy; var difference = targetEvasion - userAccuracy;
var statModifier = difference switch var statModifier = difference switch

View File

@ -0,0 +1,17 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
[Script(ScriptCategory.MoveVolatile, "electrify")]
public class ElectrifyEffect : Script {
/// <inheritdoc />
public override void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier moveType)
{
var battleData = target.BattleData;
if (battleData == null)
return;
if (!battleData.Battle.Library.StaticLibrary.Types.TryGetTypeIdentifier("electric", out var electricType))
return;
moveType = electricType;
}
}

View File

@ -1,15 +0,0 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "bulk_up")]
public class BulkUp : 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);
}
}

View File

@ -1,15 +0,0 @@
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);
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "change_multiple_user_stat_boosts")]
public class ChangeMultipleUserStatBoosts : Script
{
private Dictionary<Statistic, sbyte> _statBoosts = new();
/// <inheritdoc />
public override void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
{
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}
foreach (var par in parameters)
{
if (!Enum.TryParse<Statistic>(par.Key, true, out var stat))
continue;
if (par.Value is sbyte value)
{
_statBoosts[stat] = value;
}
}
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId batchId = new();
foreach (var stat in _statBoosts!)
{
move.User.ChangeStatBoost(stat.Key, stat.Value, true, batchId);
}
}
}

View File

@ -77,3 +77,19 @@ public class ChangeUserSpeed : ChangeUserStats
{ {
} }
} }
[Script(ScriptCategory.Move, "change_user_accuracy")]
public class ChangeUserAccuracy : ChangeUserStats
{
public ChangeUserAccuracy() : base(Statistic.Accuracy)
{
}
}
[Script(ScriptCategory.Move, "change_user_evasion")]
public class ChangeUserEvasion : ChangeUserStats
{
public ChangeUserEvasion() : base(Statistic.Evasion)
{
}
}

View File

@ -4,10 +4,10 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class ChipAway : Script public class ChipAway : Script
{ {
/// <inheritdoc /> /// <inheritdoc />
public override void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass) public override void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass) =>
{
bypass = true; bypass = true;
}
// TODO: bypass evasion stat. /// <inheritdoc />
public override void BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass)
=> bypass = true;
} }

View File

@ -1,16 +0,0 @@
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);
}
}

View File

@ -1,15 +0,0 @@
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);
}
}

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "counter")]
public class Counter : Script
{
/// <inheritdoc />
public override void OnBeforeTurnStart(ITurnChoice choice)
{
choice.User.Volatile.Add(new CounterHelperEffect());
}
/// <inheritdoc />
public override void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
{
var counterHelper = moveChoice.User.Volatile.Get<CounterHelperEffect>();
var lastHitBy = counterHelper?.LastHitBy;
if (lastHitBy == null)
{
moveChoice.Fail();
return;
}
targets = [lastHitBy];
}
/// <inheritdoc />
public override void ChangeDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
var counterHelper = move.User.Volatile.Get<CounterHelperEffect>();
if (counterHelper == null || counterHelper.LastHitBy == null || counterHelper.LastHitBy != target)
{
move.GetHitData(target, hit).Fail();
return;
}
var lastDamage = counterHelper.LastDamage;
damage = lastDamage.MultiplyOrMax(2);
}
}

View File

@ -0,0 +1,15 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "covet")]
public class Covet : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (target.HeldItem == null)
return;
if (move.User.HeldItem != null)
return;
_ = move.User.SetHeldItem(target.RemoveHeldItem());
}
}

View File

@ -0,0 +1,17 @@
using PkmnLib.Plugin.Gen7.Scripts.Side;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "crafty_shield")]
public class CraftyShield : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
var side = battleData.Battle.Sides[battleData.SideIndex];
side.VolatileScripts.Add(new CraftyShieldEffect());
}
}

View File

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

View File

@ -0,0 +1,34 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "curse")]
public class Curse : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
if (!typeLibrary.TryGetTypeIdentifier("ghost", out var ghostType))
return;
if (move.User.Types.Contains(ghostType))
{
move.User.Damage(move.User.CurrentHealth / 2, DamageSource.Misc);
if (move.User.CurrentHealth == 0)
return;
target.Volatile.Add(new GhostCurseEffect(target));
}
else
{
EventBatchId batchId = new();
move.User.ChangeStatBoost(Statistic.Speed, -1, true, batchId);
move.User.ChangeStatBoost(Statistic.Defense, 1, true, batchId);
move.User.ChangeStatBoost(Statistic.Attack, 1, true, batchId);
}
}
}

View File

@ -0,0 +1,13 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "darkest_lariat")]
public class DarkestLariat : Script
{
/// <inheritdoc />
public override void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass) =>
bypass = true;
/// <inheritdoc />
public override void
BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass) => bypass = true;
}

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "defog")]
public class Defog : Script
{
public static HashSet<StringKey> DefoggedEffects = new()
{
"mist",
"light_screen",
"reflect",
"safe_guard",
"spikes",
"toxic_spikes",
"stealth_rock",
};
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (target.BattleData == null)
return;
var targetSide = target.BattleData.Battle.Sides[target.BattleData.SideIndex];
foreach (var effect in DefoggedEffects)
{
targetSide.VolatileScripts.Remove(effect);
}
}
}

View File

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

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "dig")]
public class Dig : Script
{
/// <inheritdoc />
public override void PreventMove(IExecutingMove move, ref bool prevent)
{
if (move.User.Volatile.Contains<DigEffect>())
return;
move.User.Volatile.Add(new DigEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dig_charge", new Dictionary<string, object>()
{
{ "user", move.User }
}));
prevent = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.Volatile.Remove(ScriptUtils.ResolveName<DigEffect>());
}
}

View File

@ -0,0 +1,28 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "disable")]
public class Disable : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
var choiceQueue = battleData.Battle.PreviousTurnChoices;
var lastMove = choiceQueue
.SelectMany(x => x)
.OfType<IMoveChoice>()
.LastOrDefault(x => x.User == target);
if (lastMove == null)
{
move.GetHitData(target, hit).Fail();
return;
}
var lastMoveName = lastMove.ChosenMove.MoveData.Name;
target.Volatile.Add(new DisableEffect(lastMoveName));
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "dive")]
public class Dive : Script
{
/// <inheritdoc />
public override void PreventMove(IExecutingMove move, ref bool prevent)
{
if (move.User.Volatile.Contains<DiveEffect>())
return;
move.User.Volatile.Add(new DigEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dive_charge", new Dictionary<string, object>()
{
{ "user", move.User }
}));
prevent = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.Volatile.Remove(ScriptUtils.ResolveName<DiveEffect>());
}
}

View File

@ -0,0 +1,31 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.Side;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "doom_desire")]
public class DoomDesire : Script
{
/// <inheritdoc />
public override void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
var battleData = target.BattleData;
if (battleData == null)
return;
var side = battleData.Battle.Sides[battleData.SideIndex];
var effect = side.VolatileScripts.Get<DoomDesireEffect>();
if (effect != null && effect.HasTarget(battleData.Position))
{
executingMove.GetHitData(target, hitIndex).Fail();
return;
}
if (effect == null)
{
effect = new DoomDesireEffect(side);
side.VolatileScripts.Add(effect);
}
effect.AddTarget(battleData.Position, executingMove.GetHitData(target, hitIndex).Damage);
block = true;
}
}

View File

@ -2,14 +2,14 @@ using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves; namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "close_combat")] [Script(ScriptCategory.Move, "dragon_ascent")]
public class CloseCombat : Script public class DragonAscent : Script
{ {
/// <inheritdoc /> /// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{ {
EventBatchId eventBatchId = new(); EventBatchId batchId = new();
move.User.ChangeStatBoost(Statistic.Defense, -1, true, eventBatchId); move.User.ChangeStatBoost(Statistic.Defense, -1, true, batchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, eventBatchId); move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, batchId);
} }
} }

View File

@ -0,0 +1,11 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "dragon_rage")]
public class DragonRage : Script
{
/// <inheritdoc />
public override void ChangeDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
damage = 40;
}
}

View File

@ -0,0 +1,23 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "dream_eater")]
public class DreamEater : Script
{
/// <inheritdoc />
public override void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
if (!target.HasStatus("asleep"))
block = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var user = move.User;
var damage = move.GetHitData(target, hit).Damage;
var healed = (uint)(damage * 0.5f);
if (move.User.HasHeldItem("big_root"))
healed = (uint)(healed * 1.3f);
user.Heal(healed, false);
}
}

View File

@ -0,0 +1,31 @@
using PkmnLib.Plugin.Gen7.Scripts.Side;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "echoed_voice")]
public class EchoedVoice : Script
{
/// <inheritdoc />
public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
var side = battleData.Battle.Sides[battleData.SideIndex];
var echoedVoiceData = side.VolatileScripts.Get<EchoedVoiceData>();
if (echoedVoiceData == null)
return;
modifier *= 2;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = move.User.BattleData;
if (battleData == null)
return;
var side = battleData.Battle.Sides[battleData.SideIndex];
side.VolatileScripts.Add(new EchoedVoiceData());
}
}

View File

@ -0,0 +1,12 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "electric_terrain")]
public class ElectricTerrain : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = move.User.BattleData;
battleData?.Battle.SetTerrain(ScriptUtils.ResolveName<ElectricTerrain>());
}
}

View File

@ -0,0 +1,23 @@
using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "electrify")]
public class Electrify : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var choiceQueue = target.BattleData?.Battle.ChoiceQueue;
if (choiceQueue == null)
return;
if (choiceQueue.Where(x => x is IMoveChoice moveChoice && moveChoice.User == target) is not IMoveChoice choice)
{
move.GetHitData(target, hit).Fail();
return;
}
choice.Volatile.Add(new ElectrifyEffect());
}
}

View File

@ -0,0 +1,23 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "electro_ball")]
public class ElectroBall : Script
{
/// <inheritdoc />
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
{
var user = move.User;
var targetSpeed = target.BoostedStats.Speed;
var userSpeed = user.BoostedStats.Speed;
var ratio = (float) userSpeed / targetSpeed;
basePower = ratio switch
{
> 4 => 150,
> 3 => 120,
> 2 => 80,
> 1 => 60,
_ => 40
};
}
}

View File

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

View File

@ -0,0 +1,20 @@
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "counterhelper")]
public class CounterHelperEffect : Script
{
public IPokemon? LastHitBy { get; private set; }
public uint LastDamage { get; private set; }
/// <inheritdoc />
public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
{
if (move.UseMove.Category == MoveCategory.Physical)
{
LastHitBy = move.User;
LastDamage = move.GetHitData(target, hit).Damage;
}
}
}

View File

@ -0,0 +1,22 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "destiny_bond")]
public class DestinyBondEffect : Script
{
/// <inheritdoc />
public override void OnFaint(IPokemon pokemon, DamageSource source)
{
if (source == DamageSource.MoveDamage)
{
if (pokemon.BattleData?.Battle.ChoiceQueue?.LastRanChoice is not IMoveChoice lastChoice)
return;
lastChoice.User.Damage(lastChoice.User.BoostedStats.Hp * 10, DamageSource.Misc);
}
}
/// <inheritdoc />
public override void OnBeforeMove(IExecutingMove move)
{
RemoveSelf();
}
}

View File

@ -0,0 +1,34 @@
using PkmnLib.Plugin.Gen7.Scripts.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "dig")]
public class DigEffect : Script
{
private readonly IPokemon _owner;
public DigEffect(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, "dig", opposingSideIndex, position);
}
/// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
if (!executingMove.UseMove.HasFlag("hit_underground"))
block = true;
}
/// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
if (!move.UseMove.HasFlag("effective_against_underground"))
damage *= 2;
}
}

View File

@ -0,0 +1,21 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "disable")]
public class DisableEffect : Script
{
private readonly StringKey _move;
public DisableEffect(StringKey move)
{
_move = move;
}
/// <inheritdoc />
public override void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
{
if (choice.ChosenMove.MoveData.Name == _move)
prevent = true;
}
}

View File

@ -0,0 +1,34 @@
using PkmnLib.Plugin.Gen7.Scripts.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "dive")]
public class DiveEffect : Script
{
private readonly IPokemon _owner;
public DiveEffect(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, "dive", opposingSideIndex, position);
}
/// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
if (!executingMove.UseMove.HasFlag("hit_underwater"))
block = true;
}
/// <inheritdoc />
public override void ChangeIncomingDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
if (!move.UseMove.HasFlag("effective_against_underwater"))
damage *= 2;
}
}

View File

@ -0,0 +1,18 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "ghostcurse")]
public class GhostCurseEffect : Script
{
private IPokemon _pokemon;
public GhostCurseEffect(IPokemon pokemon)
{
_pokemon = pokemon;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
_pokemon.Damage(_pokemon.CurrentHealth / 4, DamageSource.Misc);
}
}

View File

@ -1,6 +1,6 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
public abstract class ProtectionEffectScript : Script public class ProtectionEffectScript : Script
{ {
/// <inheritdoc /> /// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block) public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)

View File

@ -0,0 +1,14 @@
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
[Script(ScriptCategory.Side, "crafty_shield")]
public class CraftyShieldEffect : Script
{
/// <inheritdoc />
public override void StopBeforeMove(IExecutingMove move, ref bool stop)
{
if (move.UseMove.Category == MoveCategory.Status)
stop = true;
}
}

View File

@ -0,0 +1,63 @@
using System.Collections.Generic;
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
[Script(ScriptCategory.Side, "doom_desire_effect")]
public class DoomDesireEffect : Script
{
private class Target
{
public byte Position { get; }
public uint Damage { get; }
public int Turns { get; set; }
public Target(byte position, uint damage)
{
Position = position;
Damage = damage;
Turns = 3;
}
}
private readonly IBattleSide _side;
private List<Target> _targets = new();
public DoomDesireEffect(IBattleSide side)
{
_side = side;
}
public void AddTarget(byte position, uint damage)
{
_targets.Add(new Target(position, damage));
}
public bool HasTarget(byte position)
{
return _targets.Exists(x => x.Position == position);
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
var toRemove = new List<Target>();
foreach (var v in _targets)
{
v.Turns--;
if (v.Turns == 0)
{
var pokemon = _side.Pokemon[v.Position];
pokemon?.Damage(v.Damage, DamageSource.Misc);
toRemove.Add(v);
}
}
foreach (var v in toRemove)
{
_targets.Remove(v);
}
if (_targets.Count == 0)
{
RemoveSelf();
}
}
}

View File

@ -0,0 +1,14 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
/// <summary>
/// Just here to indicate that a Pokemon on this side has used Echoed Voice.
/// </summary>
[Script(ScriptCategory.Side, "echoed_voice_data")]
public class EchoedVoiceData : Script
{
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
RemoveSelf();
}
}

View File

@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Status;
[Script(ScriptCategory.Status, "sleep")]
public class Sleep : Script
{
}

View File

@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Terrain;
[Script(ScriptCategory.Terrain, "electric_terrain")]
public class ElectricTerrain : Script
{
// TODO: Implement Electric Terrain
}