This commit is contained in:
@@ -179,13 +179,8 @@ public static class TurnRunner
|
||||
var battleData = user.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
IPokemon? target = null;
|
||||
if (itemChoice is { TargetSide: not null, TargetPosition: not null })
|
||||
{
|
||||
var side = battle.Sides[itemChoice.TargetSide.Value];
|
||||
target = side.Pokemon[itemChoice.TargetPosition.Value];
|
||||
}
|
||||
var target = itemChoice.GetTargetPokemon(battle);
|
||||
battle.EventHook.Invoke(new ItemUseEvent(user, itemChoice.Item));
|
||||
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user, battle.EventHook);
|
||||
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user, user, battle, battle.EventHook);
|
||||
}
|
||||
}
|
||||
@@ -351,6 +351,21 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
if (switchChoice.SwitchTo == switchChoice.User)
|
||||
return false;
|
||||
}
|
||||
else if (choice is IItemChoice itemChoice)
|
||||
{
|
||||
if (!Library.ScriptResolver.TryResolveBattleItemScript(itemChoice.Item, out var itemScript))
|
||||
return false;
|
||||
if (!itemScript.IsItemUsable)
|
||||
return false;
|
||||
if (itemScript.TargetType != ItemTargetType.None)
|
||||
{
|
||||
var target = itemChoice.GetTargetPokemon(this);
|
||||
if (target is null || !itemScript.IsTargetValid(target))
|
||||
return false;
|
||||
if (!itemScript.TargetType.IsValidTarget(this, choice.User, target))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
@@ -11,40 +11,67 @@ public interface IItemChoice : ITurnChoice
|
||||
/// <summary>
|
||||
/// The item that is used.
|
||||
/// </summary>
|
||||
public IItem Item { get; }
|
||||
IItem Item { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The side the move is targeted at.
|
||||
/// The target Pokémon of the item, if any.
|
||||
/// </summary>
|
||||
byte? TargetSide { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The position the move is targeted at.
|
||||
/// </summary>
|
||||
byte? TargetPosition { get; }
|
||||
IPokemon? GetTargetPokemon(IBattle battle);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IItemChoice"/>
|
||||
public class ItemChoice : TurnChoice, IItemChoice
|
||||
{
|
||||
/// <inheritdoc cref="ItemChoice"/>
|
||||
public ItemChoice(IPokemon user, IItem item, byte? targetSide, byte? targetPosition) : base(user)
|
||||
private ItemChoice(IPokemon user, IItem item, byte? targetSide, byte? targetPosition, IPokemon? targetPokemon) :
|
||||
base(user)
|
||||
{
|
||||
Item = item;
|
||||
TargetSide = targetSide;
|
||||
TargetPosition = targetPosition;
|
||||
TargetPokemon = targetPokemon;
|
||||
}
|
||||
|
||||
public static ItemChoice CreateWithoutTarget(IPokemon user, IItem item) =>
|
||||
new(user, item, null, null, null);
|
||||
|
||||
public static ItemChoice CreateForOpponent(IPokemon user, IItem item, byte targetSide, byte targetPosition) =>
|
||||
new(user, item, targetSide, targetPosition, null);
|
||||
|
||||
public static ItemChoice CreateForPartyMember(IPokemon user, IItem item, IPokemon targetPokemon) =>
|
||||
new(user, item, null, null, targetPokemon);
|
||||
|
||||
/// <summary>
|
||||
/// The item that is used.
|
||||
/// </summary>
|
||||
public IItem Item { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte? TargetSide { get; }
|
||||
public IPokemon? GetTargetPokemon(IBattle battle)
|
||||
{
|
||||
if (TargetPokemon != null)
|
||||
return TargetPokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte? TargetPosition { get; }
|
||||
if (!TargetSide.HasValue || !TargetPosition.HasValue)
|
||||
return null;
|
||||
var side = battle.Sides[TargetSide.Value];
|
||||
return side.Pokemon[TargetPosition.Value];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The target side of the item, if any.
|
||||
/// </summary>
|
||||
private byte? TargetSide { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target position of the item, if any.
|
||||
/// </summary>
|
||||
private byte? TargetPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target Pokémon of the item, if any. This is used for party members.
|
||||
/// </summary>
|
||||
private IPokemon? TargetPokemon { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ScriptCount => User.ScriptCount;
|
||||
|
||||
67
PkmnLib.Dynamic/Models/ItemTargetType.cs
Normal file
67
PkmnLib.Dynamic/Models/ItemTargetType.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
namespace PkmnLib.Dynamic.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Types of targets an item can have.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ItemTargetType
|
||||
{
|
||||
/// <summary>
|
||||
/// The item does not require a target.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The item targets Pokémon that are part of the user's own team.
|
||||
/// </summary>
|
||||
OwnPokemon = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The item targets allied Pokémon.
|
||||
/// </summary>
|
||||
AllyPokemon = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The item targets opposing Pokémon.
|
||||
/// </summary>
|
||||
FoePokemon = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for ItemTargetType.
|
||||
/// </summary>
|
||||
public static class ItemTargetTypeHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the given target is valid based on the ItemTargetType.
|
||||
/// </summary>
|
||||
public static bool IsValidTarget(this ItemTargetType targetType, IBattle battle, IPokemon user, IPokemon target)
|
||||
{
|
||||
if (targetType == ItemTargetType.None)
|
||||
return true;
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.OwnPokemon))
|
||||
{
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
||||
if (userParty is not null && targetParty is not null && userParty == targetParty)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.AllyPokemon))
|
||||
{
|
||||
if (user.BattleData?.BattleSide == target.BattleData?.BattleSide)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.FoePokemon))
|
||||
{
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
||||
if (userParty is not null && targetParty is not null && userParty != targetParty)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public abstract class ItemScript : IDeepCloneable
|
||||
/// <summary>
|
||||
/// Returns whether the item requires a target to be used.
|
||||
/// </summary>
|
||||
public virtual bool RequiresTarget => false;
|
||||
public virtual ItemTargetType TargetType => ItemTargetType.None;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the item is usable on the given target.
|
||||
|
||||
@@ -29,7 +29,7 @@ public abstract class PokeballScript : ItemScript
|
||||
public override bool IsItemUsable => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RequiresTarget => true;
|
||||
public override ItemTargetType TargetType => ItemTargetType.FoePokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsTargetValid(IPokemon target) =>
|
||||
|
||||
@@ -100,8 +100,8 @@ public static class ScriptExecution
|
||||
/// <summary>
|
||||
/// Executes a script on an item.
|
||||
/// </summary>
|
||||
public static void RunItemScript(this IItem item, ScriptResolver scriptResolver, IPokemon? target,
|
||||
EventHook eventHook)
|
||||
public static void RunItemScript(this IItem item, ScriptResolver scriptResolver, IPokemon? target, IPokemon user,
|
||||
IBattle battle, EventHook eventHook)
|
||||
{
|
||||
if (!scriptResolver.TryResolveBattleItemScript(item, out var itemScript))
|
||||
{
|
||||
@@ -111,16 +111,20 @@ public static class ScriptExecution
|
||||
return;
|
||||
|
||||
itemScript.OnInitialize(item.BattleEffect!.Parameters);
|
||||
var requiresTarget = itemScript.RequiresTarget;
|
||||
if (requiresTarget)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target), "Item script requires a target.");
|
||||
itemScript.OnUseWithTarget(target, eventHook);
|
||||
}
|
||||
else
|
||||
var targetType = itemScript.TargetType;
|
||||
if (targetType == ItemTargetType.None)
|
||||
{
|
||||
itemScript.OnUse(eventHook);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target), "Item script requires a target.");
|
||||
if (!targetType.IsValidTarget(battle, user, target))
|
||||
throw new InvalidOperationException("Item script target is not valid for the required target type.");
|
||||
if (!itemScript.IsTargetValid(target))
|
||||
throw new InvalidOperationException("Item script target is not valid.");
|
||||
itemScript.OnUseWithTarget(target, eventHook);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2672,6 +2672,7 @@
|
||||
{
|
||||
"name": "hyper_potion",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 1500,
|
||||
"additionalData": {
|
||||
@@ -3549,6 +3550,7 @@
|
||||
{
|
||||
"name": "max_potion",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 2500,
|
||||
"additionalData": {
|
||||
@@ -3567,6 +3569,7 @@
|
||||
{
|
||||
"name": "max_revive",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 4000,
|
||||
"additionalData": {
|
||||
@@ -4584,6 +4587,7 @@
|
||||
{
|
||||
"name": "potion",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 200,
|
||||
"additionalData": {
|
||||
@@ -5230,6 +5234,7 @@
|
||||
{
|
||||
"name": "revival_herb",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 2800,
|
||||
"additionalData": {
|
||||
@@ -5239,6 +5244,7 @@
|
||||
{
|
||||
"name": "revive",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 2000,
|
||||
"additionalData": {
|
||||
@@ -6185,6 +6191,7 @@
|
||||
{
|
||||
"name": "super_potion",
|
||||
"itemType": "Medicine",
|
||||
"battleType": "Healing",
|
||||
"flags": [],
|
||||
"price": 700,
|
||||
"additionalData": {
|
||||
|
||||
@@ -14,7 +14,7 @@ public class EvolutionItem : ItemScript
|
||||
public override bool IsItemUsable => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RequiresTarget => true;
|
||||
public override ItemTargetType TargetType => ItemTargetType.OwnPokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsTargetValid(IPokemon target)
|
||||
|
||||
@@ -14,7 +14,7 @@ public class HealingItem : ItemScript
|
||||
public override bool IsItemUsable => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RequiresTarget => true;
|
||||
public override ItemTargetType TargetType => ItemTargetType.OwnPokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
|
||||
|
||||
@@ -20,6 +20,7 @@ public class BugBite : Script, IScriptOnSecondaryEffect
|
||||
}
|
||||
|
||||
_ = target.ForceSetHeldItem(null);
|
||||
targetHeldItem.RunItemScript(battleData.Battle.Library.ScriptResolver, user, move.Battle.EventHook);
|
||||
targetHeldItem.RunItemScript(battleData.Battle.Library.ScriptResolver, user, user, battleData.Battle,
|
||||
move.Battle.EventHook);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user