Imlements baneful bunker, data fixes

This commit is contained in:
Deukhoofd 2025-01-10 12:55:25 +01:00
parent 6434f9925c
commit 92ab67ddf8
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
12 changed files with 209 additions and 17 deletions

View File

@ -325,6 +325,7 @@ public class BattleImpl : ScriptSource, IBattle
{ {
_weatherScript.Clear(); _weatherScript.Clear();
} }
// TODO: Trigger weather change script hooks
} }
private IScriptSet Volatile { get; } = new ScriptSet(); private IScriptSet Volatile { get; } = new ScriptSet();

View File

@ -44,8 +44,7 @@ internal static class MoveTurnExecutor
return; return;
} }
var executingMove = new ExecutingMoveImpl(targets, numberOfHits, moveChoice.User, chosenMove, moveData, var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, moveData, moveChoice);
moveChoice.Script);
var prevented = false; var prevented = false;
executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented)); executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented));
@ -151,6 +150,10 @@ internal static class MoveTurnExecutor
break; break;
} }
var blockIncomingHit = false;
target.RunScriptHook(x => x.BlockIncomingHit(executingMove, target, hitIndex, ref blockIncomingHit));
if (blockIncomingHit)
break;
if (useMove.Category == MoveCategory.Status) if (useMove.Category == MoveCategory.Status)
{ {
var secondaryEffect = useMove.SecondaryEffect; var secondaryEffect = useMove.SecondaryEffect;

View File

@ -1,3 +1,4 @@
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Static; using PkmnLib.Static;
using PkmnLib.Static.Moves; using PkmnLib.Static.Moves;
@ -133,6 +134,11 @@ public interface IExecutingMove : IScriptSource
/// Gets the targets of this move. /// Gets the targets of this move.
/// </summary> /// </summary>
IReadOnlyList<IPokemon?> Targets { get; } IReadOnlyList<IPokemon?> Targets { get; }
/// <summary>
/// The underlying move choice.
/// </summary>
IMoveChoice MoveChoice { get; }
} }
/// <inheritdoc cref="IExecutingMove"/> /// <inheritdoc cref="IExecutingMove"/>
@ -142,15 +148,14 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
private readonly IHitData[] _hits; private readonly IHitData[] _hits;
/// <inheritdoc cref="ExecutingMoveImpl"/> /// <inheritdoc cref="ExecutingMoveImpl"/>
public ExecutingMoveImpl(IReadOnlyList<IPokemon?> targets, byte numberOfHits, IPokemon user, ILearnedMove chosenMove, public ExecutingMoveImpl(IReadOnlyList<IPokemon?> targets, byte numberOfHits, ILearnedMove chosenMove,
IMoveData useMove, ScriptContainer script) IMoveData useMove, IMoveChoice moveChoice)
{ {
_targets = targets; _targets = targets;
NumberOfHits = numberOfHits; NumberOfHits = numberOfHits;
User = user;
ChosenMove = chosenMove; ChosenMove = chosenMove;
UseMove = useMove; UseMove = useMove;
Script = script; MoveChoice = moveChoice;
var totalHits = targets.Count * numberOfHits; var totalHits = targets.Count * numberOfHits;
_hits = new IHitData[totalHits]; _hits = new IHitData[totalHits];
@ -167,7 +172,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
public byte NumberOfHits { get; } public byte NumberOfHits { get; }
/// <inheritdoc /> /// <inheritdoc />
public IPokemon User { get; } public IPokemon User => MoveChoice.User;
/// <inheritdoc /> /// <inheritdoc />
public ILearnedMove ChosenMove { get; } public ILearnedMove ChosenMove { get; }
@ -176,7 +181,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
public IMoveData UseMove { get; } public IMoveData UseMove { get; }
/// <inheritdoc /> /// <inheritdoc />
public ScriptContainer Script { get; } public ScriptContainer Script => MoveChoice.Script;
/// <inheritdoc /> /// <inheritdoc />
public IHitData GetHitData(IPokemon target, byte hit) public IHitData GetHitData(IPokemon target, byte hit)
@ -215,6 +220,9 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyList<IPokemon?> Targets => _targets.ToList(); public IReadOnlyList<IPokemon?> Targets => _targets.ToList();
/// <inheritdoc />
public IMoveChoice MoveChoice { get; }
/// <inheritdoc /> /// <inheritdoc />
public override int ScriptCount => 1 + User.ScriptCount; public override int ScriptCount => 1 + User.ScriptCount;

View File

@ -313,6 +313,10 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// </summary> /// </summary>
void LearnMove(StringKey moveName, MoveLearnMethod method, byte index); void LearnMove(StringKey moveName, MoveLearnMethod method, byte index);
/// <summary>
/// Adds a non-volatile status to the Pokemon.
/// </summary>
void SetStatus(StringKey status);
/// <summary> /// <summary>
/// Removes the current non-volatile status from the Pokemon. /// Removes the current non-volatile status from the Pokemon.
/// </summary> /// </summary>
@ -926,6 +930,14 @@ public class PokemonImpl : ScriptSource, IPokemon
_learnedMoves[index] = new LearnedMoveImpl(move, method); _learnedMoves[index] = new LearnedMoveImpl(move, method);
} }
/// <inheritdoc />
public void SetStatus(StringKey status)
{
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Status, status, null, out var statusScript))
throw new KeyNotFoundException($"Status script {status} not found");
StatusScript.Set(statusScript);
}
/// <inheritdoc /> /// <inheritdoc />
public void ClearStatus() => StatusScript.Clear(); public void ClearStatus() => StatusScript.Clear();

View File

@ -497,4 +497,8 @@ public abstract class Script : IDeepCloneable
public virtual void ChangeCatchRateBonus(IPokemon pokemon, IItem pokeball, ref byte modifier) public virtual void ChangeCatchRateBonus(IPokemon pokemon, IItem pokeball, ref byte modifier)
{ {
} }
public virtual void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
}
} }

View File

@ -482,7 +482,7 @@
"category": "status", "category": "status",
"flags": [], "flags": [],
"effect": { "effect": {
"name": "Assist" "name": "assist"
} }
}, },
{ {
@ -564,7 +564,7 @@
"type": "fighting", "type": "fighting",
"power": 80, "power": 80,
"pp": 20, "pp": 20,
"accuracy": 0, "accuracy": 255,
"priority": 0, "priority": 0,
"target": "Any", "target": "Any",
"category": "special", "category": "special",
@ -608,7 +608,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "aurora_veil"
}
}, },
{ {
"name": "autotomize", "name": "autotomize",
@ -623,7 +626,7 @@
"snatch" "snatch"
], ],
"effect": { "effect": {
"name": "automize" "name": "autotomize"
} }
}, },
{ {
@ -670,11 +673,14 @@
"type": "poison", "type": "poison",
"power": 0, "power": 0,
"pp": 10, "pp": 10,
"accuracy": 0, "accuracy": 255,
"priority": 4, "priority": 4,
"target": "Self", "target": "Self",
"category": "status", "category": "status",
"flags": [] "flags": [],
"effect": {
"name": "baneful_bunker"
}
}, },
{ {
"name": "barrage", "name": "barrage",
@ -689,7 +695,10 @@
"protect", "protect",
"mirror", "mirror",
"ballistics" "ballistics"
] ],
"effect": {
"name": "2_5_hit_move"
}
}, },
{ {
"name": "barrier", "name": "barrier",
@ -702,7 +711,13 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "change_target_defense",
"parameters": {
"amount": 2
}
}
}, },
{ {
"name": "baton_pass", "name": "baton_pass",

View File

@ -0,0 +1,15 @@
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "baneful_bunker")]
public class BanefulBunker : ProtectionScript
{
/// <inheritdoc />
protected override ProtectionEffectScript GetEffectScript()
{
return new BanefulBunkerEffect();
}
}

View File

@ -0,0 +1,47 @@
using System;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public abstract class ProtectionScript : Script
{
protected abstract ProtectionEffectScript GetEffectScript();
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = target.BattleData;
if (battleData == null)
return;
// If the user goes last in the turn, the move will fail
if (battleData.Battle.ChoiceQueue is not null && !battleData.Battle.ChoiceQueue.HasNext())
{
move.GetHitData(target, hit).Fail();
return;
}
var failure = target.Volatile.Get<ProtectionFailureScript>();
if (failure == null)
{
failure = new ProtectionFailureScript();
target.Volatile.Add(failure);
}
var successive = failure.ProtectTurns;
// Each time, the chance of success is divided by 3.
var chance = 1.0 / Math.Pow(3, successive);
var random = target.BattleData!.Battle.Random.GetFloat();
if (random < chance)
{
failure.ProtectTurns++;
failure.UsedProtect = true;
target.Volatile.Add(GetEffectScript());
}
else
{
move.GetHitData(target, hit).Fail();
}
}
}

View File

@ -0,0 +1,17 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
public class BanefulBunkerEffect : ProtectionEffectScript
{
/// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
base.BlockIncomingHit(executingMove, target, hitIndex, ref block);
if (executingMove.UseMove.Category != MoveCategory.Status && executingMove.UseMove.HasFlag("contact"))
{
executingMove.User.SetStatus("poisoned");
}
}
}

View File

@ -0,0 +1,28 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
public abstract class ProtectionEffectScript : Script
{
/// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
if (target.BattleData == null)
return;
var originalTarget = executingMove.MoveChoice.TargetPosition;
var targetPosition = target.BattleData.Position;
// We only want to block the hit if it's explicitly targeting the Pokemon.
if (targetPosition != originalTarget)
return;
if (executingMove.UseMove.Target is MoveTarget.All or MoveTarget.SelfUse or MoveTarget.AllAlly
or MoveTarget.AllAdjacent)
return;
block = true;
}
}

View File

@ -0,0 +1,32 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
/// <summary>
/// Attached script for <see cref="ProtectionEffectScript"/> to keep track of the consecutive turns of using Protect,
/// or protect-like moves.
/// </summary>
[Script(ScriptCategory.Pokemon, "protection_failure")]
public class ProtectionFailureScript : Script
{
public int ProtectTurns { get; set; }
public bool UsedProtect { get; set; }
/// <inheritdoc />
public override void OnBeforeTurnStart(ITurnChoice choice)
{
UsedProtect = false;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (!UsedProtect)
{
RemoveSelf();
}
}
}

View File

@ -0,0 +1,10 @@
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
namespace PkmnLib.Plugin.Gen7.Scripts.Status;
[Script(ScriptCategory.Status, "poisoned")]
public class Poisoned : Script
{
// TODO: Implement the Poisoned status effect.
}