diff --git a/PokemonBattleEngine/AI/AIDecisions.cs b/PokemonBattleEngine/AI/AIDecisions.cs
index 3f7ac2f2f..62b63271d 100644
--- a/PokemonBattleEngine/AI/AIDecisions.cs
+++ b/PokemonBattleEngine/AI/AIDecisions.cs
@@ -7,53 +7,39 @@
namespace Kermalis.PokemonBattleEngine.AI
{
- ///
- /// Creates valid decisions for a team in a battle. Decisions may not be valid for custom settings and/or move changes.
- ///
- public static partial class PBEAIManager
+ /// Creates valid decisions for a team in a battle. Decisions may not be valid for custom settings and/or move changes.
+ public static partial class PBEAI
{
- ///
- /// Creates valid actions for a battle turn for a specific team.
- ///
+ /// Creates valid actions for a battle turn for a specific team.
/// The team to create actions for.
/// Thrown when has no active battlers or 's 's is not .
/// Thrown when a Pokémon has no moves, the AI tries to use a move with invalid targets, or 's 's is invalid.
- public static IEnumerable CreateActions(PBETeam team)
+ public static PBETurnAction[] CreateActions(PBETeam team)
{
- if (team.Battle.BattleState != PBEBattleState.WaitingForActions)
+ if (team == null)
{
- throw new InvalidOperationException($"{nameof(team.Battle.BattleState)} must be {PBEBattleState.WaitingForActions} to create actions.");
+ throw new ArgumentNullException(nameof(team));
}
- PBEPokemon[] active = team.ActiveBattlers;
- if (active.Length == 0)
+ if (team.Battle.BattleState != PBEBattleState.WaitingForActions)
{
- throw new InvalidOperationException($"{nameof(team)} must have at least one active battler.");
+ throw new InvalidOperationException($"{nameof(team.Battle.BattleState)} must be {PBEBattleState.WaitingForActions} to create actions.");
}
- PBETeam opposingTeam = team == team.Battle.Teams[0] ? team.Battle.Teams[1] : team.Battle.Teams[0];
- var actions = new PBEAction[team.ActionsRequired.Count];
+ var actions = new PBETurnAction[team.ActionsRequired.Count];
var standBy = new List();
for (int i = 0; i < actions.Length; i++)
{
PBEPokemon pkmn = team.ActionsRequired[i];
- if (Array.TrueForAll(pkmn.Moves, m => m == PBEMove.None))
- {
- throw new ArgumentOutOfRangeException(nameof(pkmn.Moves), $"{pkmn.Nickname} has no moves.");
- }
// If a Pokémon is forced to struggle, it is best that it just stays in until it faints
if (pkmn.IsForcedToStruggle())
{
- actions[i].Decision = PBEDecision.Fight;
- actions[i].FightMove = PBEMove.Struggle;
- actions[i].FightTargets = GetPossibleTargets(pkmn, pkmn.GetMoveTargets(PBEMove.Struggle))[0]; // Seems a little nasty just to select "Self", since this will be RandomFoeSurrounding
+ actions[i] = new PBETurnAction(pkmn.Id, PBEMove.Struggle, GetPossibleTargets(pkmn, pkmn.GetMoveTargets(PBEMove.Struggle))[0]);
}
// If a Pokémon has a temp locked move (Dig, Dive, Shadow Force) it must be used
else if (pkmn.TempLockedMove != PBEMove.None)
{
- actions[i].Decision = PBEDecision.Fight;
- actions[i].FightMove = pkmn.TempLockedMove;
- actions[i].FightTargets = pkmn.TempLockedTargets;
+ actions[i] = new PBETurnAction(pkmn.Id, pkmn.TempLockedMove, pkmn.TempLockedTargets);
}
// The Pokémon is free to switch or fight (unless it cannot switch due to Magnet Pull etc)
else
@@ -62,42 +48,42 @@ public static IEnumerable CreateActions(PBETeam team)
PBEPokemon[] availableForSwitch = team.Party.Except(standBy).Where(p => p.FieldPosition == PBEFieldPosition.None && p.HP > 0).ToArray();
PBEMove[] usableMoves = pkmn.GetUsableMoves();
- var possibleActions = new List<(PBEAction Action, double Score)>();
+ var possibleActions = new List<(PBETurnAction Action, double Score)>();
for (int m = 0; m < usableMoves.Length; m++) // Score moves
{
PBEMove move = usableMoves[m];
PBEType moveType = pkmn.GetMoveType(move);
PBEMoveTarget moveTargets = pkmn.GetMoveTargets(move);
- PBETarget[] possibleTargets = PBEMoveData.IsSpreadMove(moveTargets)
- ? new PBETarget[] { GetSpreadMoveTargets(pkmn, moveTargets) }
+ PBETurnTarget[] possibleTargets = PBEMoveData.IsSpreadMove(moveTargets)
+ ? new PBETurnTarget[] { GetSpreadMoveTargets(pkmn, moveTargets) }
: GetPossibleTargets(pkmn, moveTargets);
- foreach (PBETarget possibleTarget in possibleTargets)
+ foreach (PBETurnTarget possibleTarget in possibleTargets)
{
// TODO: RandomFoeSurrounding (probably just account for the specific effects that use this target type)
var targets = new List();
- if (possibleTarget.HasFlag(PBETarget.AllyLeft))
+ if (possibleTarget.HasFlag(PBETurnTarget.AllyLeft))
{
targets.Add(team.TryGetPokemon(PBEFieldPosition.Left));
}
- if (possibleTarget.HasFlag(PBETarget.AllyCenter))
+ if (possibleTarget.HasFlag(PBETurnTarget.AllyCenter))
{
targets.Add(team.TryGetPokemon(PBEFieldPosition.Center));
}
- if (possibleTarget.HasFlag(PBETarget.AllyRight))
+ if (possibleTarget.HasFlag(PBETurnTarget.AllyRight))
{
targets.Add(team.TryGetPokemon(PBEFieldPosition.Right));
}
- if (possibleTarget.HasFlag(PBETarget.FoeLeft))
+ if (possibleTarget.HasFlag(PBETurnTarget.FoeLeft))
{
- targets.Add(opposingTeam.TryGetPokemon(PBEFieldPosition.Left));
+ targets.Add(team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left));
}
- if (possibleTarget.HasFlag(PBETarget.FoeCenter))
+ if (possibleTarget.HasFlag(PBETurnTarget.FoeCenter))
{
- targets.Add(opposingTeam.TryGetPokemon(PBEFieldPosition.Center));
+ targets.Add(team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center));
}
- if (possibleTarget.HasFlag(PBETarget.FoeRight))
+ if (possibleTarget.HasFlag(PBETurnTarget.FoeRight))
{
- targets.Add(opposingTeam.TryGetPokemon(PBEFieldPosition.Right));
+ targets.Add(team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right));
}
double score = 0.0;
@@ -121,11 +107,11 @@ public static IEnumerable CreateActions(PBETeam team)
// TODO: Favor sleep with Bad Dreams (unless ally)
if (target.Status1 != PBEStatus1.None)
{
- score += target.Team == opposingTeam ? -60 : 0;
+ score += target.Team == team.OpposingTeam ? -60 : 0;
}
else
{
- score += target.Team == opposingTeam ? +40 : -20;
+ score += target.Team == team.OpposingTeam ? +40 : -20;
}
}
}
@@ -191,31 +177,31 @@ public static IEnumerable CreateActions(PBETeam team)
typeEffectiveness *= PBEPokemonData.TypeEffectiveness[moveType][target.KnownType2];
if (typeEffectiveness <= 0.0) // (-infinity, 0.0] Ineffective
{
- score += target.Team == opposingTeam ? -60 : -1;
+ score += target.Team == team.OpposingTeam ? -60 : -1;
}
else if (typeEffectiveness <= 0.25) // (0.0, 0.25] NotVeryEffective
{
- score += target.Team == opposingTeam ? -30 : -5;
+ score += target.Team == team.OpposingTeam ? -30 : -5;
}
else if (typeEffectiveness < 1.0) // (0.25, 1.0) NotVeryEffective
{
- score += target.Team == opposingTeam ? -10 : -10;
+ score += target.Team == team.OpposingTeam ? -10 : -10;
}
else if (typeEffectiveness == 1.0) // [1.0, 1.0] Normal
{
- score += target.Team == opposingTeam ? +10 : -15;
+ score += target.Team == team.OpposingTeam ? +10 : -15;
}
else if (typeEffectiveness < 4.0) // (1.0, 4.0) SuperEffective
{
- score += target.Team == opposingTeam ? +25 : -20;
+ score += target.Team == team.OpposingTeam ? +25 : -20;
}
else // [4.0, infinity) SuperEffective
{
- score += target.Team == opposingTeam ? +40 : -30;
+ score += target.Team == team.OpposingTeam ? +40 : -30;
}
if (pkmn.HasType(moveType) && typeEffectiveness > 0.0) // STAB
{
- score += (pkmn.Ability == PBEAbility.Adaptability ? 15 : 10) * (target.Team == opposingTeam ? +1 : -1);
+ score += (pkmn.Ability == PBEAbility.Adaptability ? 15 : 10) * (target.Team == team.OpposingTeam ? +1 : -1);
}
}
}
@@ -228,7 +214,7 @@ public static IEnumerable CreateActions(PBETeam team)
case PBEMoveEffect.RestoreUserHP:
{
PBEPokemon target = targets[0];
- if (target == null || target.Team == opposingTeam)
+ if (target == null || target.Team == team.OpposingTeam)
{
score -= 100;
}
@@ -325,14 +311,9 @@ public static IEnumerable CreateActions(PBETeam team)
// TODO Moves
break;
}
+ default: throw new ArgumentOutOfRangeException(nameof(PBEMoveData.Effect));
}
- var mAction = new PBEAction
- {
- Decision = PBEDecision.Fight,
- FightMove = move,
- FightTargets = possibleTarget
- };
- possibleActions.Add((mAction, score));
+ possibleActions.Add((new PBETurnAction(pkmn.Id, move, possibleTarget), score));
}
}
if (pkmn.CanSwitchOut())
@@ -344,19 +325,14 @@ public static IEnumerable CreateActions(PBETeam team)
// TODO: Known moves of active battlers
// TODO: Type effectiveness
double score = 0.0;
- var sAction = new PBEAction
- {
- Decision = PBEDecision.SwitchOut,
- SwitchPokemonId = switchPkmn.Id
- };
- possibleActions.Add((sAction, score));
+ possibleActions.Add((new PBETurnAction(pkmn.Id, switchPkmn.Id), score));
}
}
- string ToDebugString((PBEAction Action, double Score) t)
+ string ToDebugString((PBETurnAction Action, double Score) t)
{
string str = "{";
- if (t.Action.Decision == PBEDecision.Fight)
+ if (t.Action.Decision == PBETurnDecision.Fight)
{
str += string.Format("Fight {0} {1}", t.Action.FightMove, t.Action.FightTargets);
}
@@ -367,15 +343,14 @@ string ToDebugString((PBEAction Action, double Score) t)
str += " [" + t.Score + "]}";
return str;
}
- IOrderedEnumerable<(PBEAction Action, double Score)> byScore = possibleActions.OrderByDescending(t => t.Score);
+ IOrderedEnumerable<(PBETurnAction Action, double Score)> byScore = possibleActions.OrderByDescending(t => t.Score);
Debug.WriteLine("{0}'s possible actions: {1}", pkmn.Nickname, byScore.Select(t => ToDebugString(t)).Print());
double bestScore = byScore.First().Score;
actions[i] = byScore.Where(t => t.Score == bestScore).ToArray().RandomElement().Action; // Pick random action of the ones that tied for best score
}
// Action was chosen, finish up for this Pokémon
- actions[i].PokemonId = pkmn.Id;
- if (actions[i].Decision == PBEDecision.SwitchOut)
+ if (actions[i].Decision == PBETurnDecision.SwitchOut)
{
standBy.Add(team.TryGetPokemon(actions[i].SwitchPokemonId));
}
@@ -383,14 +358,16 @@ string ToDebugString((PBEAction Action, double Score) t)
return actions;
}
- ///
- /// Creates valid switches for a battle for a specific team.
- ///
+ /// Creates valid switches for a battle for a specific team.
/// The team to create switches for.
/// Thrown when does not require switch-ins or 's 's is not .
/// Thrown when 's 's is invalid.
- public static IEnumerable<(byte PokemonId, PBEFieldPosition Position)> CreateSwitches(PBETeam team)
+ public static PBESwitchIn[] CreateSwitches(PBETeam team)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
if (team.Battle.BattleState != PBEBattleState.WaitingForSwitchIns)
{
throw new InvalidOperationException($"{nameof(team.Battle.BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to create switch-ins.");
@@ -399,7 +376,6 @@ string ToDebugString((PBEAction Action, double Score) t)
{
throw new InvalidOperationException($"{nameof(team)} must require switch-ins.");
}
- var switches = new List<(byte PokemonId, PBEFieldPosition Position)>(team.SwitchInsRequired);
PBEPokemon[] available = team.Party.Where(p => p.FieldPosition == PBEFieldPosition.None && p.HP > 0).ToArray();
available.Shuffle();
var availablePositions = new List();
@@ -441,9 +417,10 @@ string ToDebugString((PBEAction Action, double Score) t)
}
default: throw new ArgumentOutOfRangeException(nameof(team.Battle.BattleFormat));
}
+ var switches = new PBESwitchIn[team.SwitchInsRequired];
for (int i = 0; i < team.SwitchInsRequired; i++)
{
- switches.Add((available[i].Id, availablePositions[i]));
+ switches[i] = new PBESwitchIn(available[i].Id, availablePositions[i]);
}
return switches;
}
diff --git a/PokemonBattleEngine/AI/AITargets.cs b/PokemonBattleEngine/AI/AITargets.cs
index 4c2e7d4a3..907ad5fed 100644
--- a/PokemonBattleEngine/AI/AITargets.cs
+++ b/PokemonBattleEngine/AI/AITargets.cs
@@ -4,11 +4,11 @@
namespace Kermalis.PokemonBattleEngine.AI
{
- public static partial class PBEAIManager
+ public static partial class PBEAI
{
// TODO: Move these to battle targets and make them public; this file doesn't have to exist
- private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget targets)
+ private static PBETurnTarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget targets)
{
switch (pkmn.Team.Battle.BattleFormat)
{
@@ -20,7 +20,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;
}
else
{
@@ -33,7 +33,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
@@ -44,7 +44,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
@@ -62,7 +62,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -74,7 +74,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -85,7 +85,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight;
}
else
{
@@ -96,11 +96,11 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -118,7 +118,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else
{
@@ -129,7 +129,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else
{
@@ -140,15 +140,15 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;
}
else
{
@@ -159,15 +159,15 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter | PBETarget.FoeLeft | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;
}
else
{
@@ -178,7 +178,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight;
}
else
{
@@ -196,7 +196,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;
}
else
{
@@ -209,7 +209,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
@@ -220,7 +220,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
@@ -234,7 +234,7 @@ private static PBETarget GetSpreadMoveTargets(PBEPokemon pkmn, PBEMoveTarget tar
}
}
- private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget targets)
+ private static PBETurnTarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget targets)
{
switch (pkmn.Team.Battle.BattleFormat)
{
@@ -248,7 +248,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.FoeCenter };
+ return new PBETurnTarget[] { PBETurnTarget.FoeCenter };
}
else
{
@@ -262,7 +262,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter };
}
else
{
@@ -281,11 +281,11 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyLeft };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyRight };
}
else
{
@@ -296,7 +296,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight };
}
else
{
@@ -307,11 +307,11 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyLeft };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft };
}
else
{
@@ -322,7 +322,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.FoeLeft, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };
}
else
{
@@ -334,11 +334,11 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyRight, PBETarget.FoeLeft, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.FoeLeft, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.FoeLeft, PBETurnTarget.FoeRight };
}
else
{
@@ -357,15 +357,15 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyLeft };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyRight };
}
else
{
@@ -376,15 +376,15 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyCenter, PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyCenter, PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight };
}
else
{
@@ -395,11 +395,11 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight };
}
else
{
@@ -410,15 +410,15 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.FoeLeft, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.FoeLeft, PBETarget.FoeCenter };
+ return new PBETurnTarget[] { PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter };
}
else
{
@@ -429,15 +429,15 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyCenter, PBETarget.AllyRight, PBETarget.FoeLeft, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyRight, PBETarget.FoeLeft, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyCenter, PBETarget.FoeLeft, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyCenter, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else
{
@@ -448,15 +448,15 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return new PBETarget[] { PBETarget.AllyCenter, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return new PBETarget[] { PBETarget.AllyLeft, PBETarget.AllyRight, PBETarget.FoeLeft, PBETarget.FoeCenter, PBETarget.FoeRight };
+ return new PBETurnTarget[] { PBETurnTarget.AllyLeft, PBETurnTarget.AllyRight, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter, PBETurnTarget.FoeRight };
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyCenter, PBETarget.FoeLeft, PBETarget.FoeCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter, PBETurnTarget.FoeLeft, PBETurnTarget.FoeCenter };
}
else
{
@@ -476,7 +476,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.FoeCenter };
+ return new PBETurnTarget[] { PBETurnTarget.FoeCenter };
}
else
{
@@ -490,7 +490,7 @@ private static PBETarget[] GetPossibleTargets(PBEPokemon pkmn, PBEMoveTarget tar
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return new PBETarget[] { PBETarget.AllyCenter };
+ return new PBETurnTarget[] { PBETurnTarget.AllyCenter };
}
else
{
diff --git a/PokemonBattleEngine/Battle/Battle.cs b/PokemonBattleEngine/Battle/Battle.cs
index 29e0901ec..fc841e9ae 100644
--- a/PokemonBattleEngine/Battle/Battle.cs
+++ b/PokemonBattleEngine/Battle/Battle.cs
@@ -2,15 +2,22 @@
using Kermalis.PokemonBattleEngine.Data;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Kermalis.PokemonBattleEngine.Battle
{
- // TODO: INPC
+ // TODO: Fully implement INPC
/// Represents a specific Pokémon battle.
- public sealed partial class PBEBattle
+ public sealed partial class PBEBattle : IDisposable, INotifyPropertyChanged
{
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
public delegate void BattleStateChangedEvent(PBEBattle battle);
public event BattleStateChangedEvent OnStateChanged;
public PBEBattleState BattleState { get; private set; }
@@ -20,9 +27,9 @@ public sealed partial class PBEBattle
public PBEBattleFormat BattleFormat { get; }
public PBESettings Settings { get; }
- public PBETeam[] Teams { get; } = new PBETeam[2];
+ public PBETeams Teams { get; }
public List ActiveBattlers { get; } = new List(6);
- private readonly List turnOrder = new List();
+ private readonly List _turnOrder = new List();
public PBEWeather Weather { get; set; }
public byte WeatherCounter { get; set; }
@@ -35,49 +42,59 @@ public sealed partial class PBEBattle
/// The ID of the .
public PBEPokemon TryGetPokemon(byte pkmnId)
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
return Teams.SelectMany(t => t.Party).SingleOrDefault(p => p.Id == pkmnId);
}
- private byte pkmnIdCounter;
/// Creates a new object with the specified and teams. Each team must have equal settings. The battle's settings are set to a copy of the teams' settings. will be .
/// The of the battle.
- /// The object to use to create [0].
- /// The name of the trainer(s) on [0].
- /// The object to use to create [1].
- /// The name of the trainer(s) on [1].
- /// Thrown when or are null.
- /// Thrown when and have unequal or when or are invalid.
- public PBEBattle(PBEBattleFormat battleFormat, PBETeamShell team0Shell, string team0TrainerName, PBETeamShell team1Shell, string team1TrainerName)
+ /// The object to use to create [0].
+ /// The name of the trainer(s) on [0].
+ /// The object to use to create [1].
+ /// The name of the trainer(s) on [1].
+ /// Thrown when or are null.
+ /// Thrown when and have unequal or when or are invalid.
+ public PBEBattle(PBEBattleFormat battleFormat, PBETeamShell team1Shell, string team1TrainerName, PBETeamShell team2Shell, string team2TrainerName)
{
if (battleFormat >= PBEBattleFormat.MAX)
{
throw new ArgumentOutOfRangeException(nameof(battleFormat));
}
- if (team0Shell == null)
- {
- throw new ArgumentNullException(nameof(team0Shell));
- }
- if (string.IsNullOrWhiteSpace(team0TrainerName))
- {
- throw new ArgumentOutOfRangeException(nameof(team0TrainerName));
- }
if (team1Shell == null)
{
throw new ArgumentNullException(nameof(team1Shell));
}
+ if (team2Shell == null)
+ {
+ throw new ArgumentNullException(nameof(team2Shell));
+ }
if (string.IsNullOrWhiteSpace(team1TrainerName))
{
throw new ArgumentOutOfRangeException(nameof(team1TrainerName));
}
- if (!team0Shell.Settings.Equals(team1Shell.Settings))
+ if (string.IsNullOrWhiteSpace(team2TrainerName))
{
- throw new ArgumentOutOfRangeException(nameof(team0Shell.Settings), "Team settings must be equal to each other.");
+ throw new ArgumentOutOfRangeException(nameof(team2TrainerName));
+ }
+ if (team1Shell.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team1Shell));
+ }
+ if (team2Shell.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team2Shell));
+ }
+ if (!team1Shell.Settings.Equals(team2Shell.Settings))
+ {
+ throw new ArgumentOutOfRangeException(nameof(team1Shell.Settings), "Team settings must be equal to each other.");
}
BattleFormat = battleFormat;
- Settings = new PBESettings(team0Shell.Settings);
+ Settings = new PBESettings(team1Shell.Settings);
Settings.MakeReadOnly();
- Teams[0] = new PBETeam(this, 0, team0Shell, team0TrainerName, ref pkmnIdCounter);
- Teams[1] = new PBETeam(this, 1, team1Shell, team1TrainerName, ref pkmnIdCounter);
+ Teams = new PBETeams(this, team1Shell, team1TrainerName, team2Shell, team2TrainerName);
CheckForReadiness();
}
/// Creates a new object with the specified and a copy of the specified . will be .
@@ -96,16 +113,13 @@ public PBEBattle(PBEBattleFormat battleFormat, PBESettings settings)
BattleFormat = battleFormat;
Settings = new PBESettings(settings);
Settings.MakeReadOnly();
-
- Teams[0] = new PBETeam(this, 0);
- Teams[1] = new PBETeam(this, 1);
-
+ Teams = new PBETeams(this);
BattleState = PBEBattleState.WaitingForPlayers;
OnStateChanged?.Invoke(this);
}
private void CheckForReadiness()
{
- if (Array.TrueForAll(Teams, t => t.NumPkmnAlive > 0))
+ if (Teams.All(t => t.NumPkmnAlive > 0))
{
switch (BattleFormat)
{
@@ -113,8 +127,9 @@ private void CheckForReadiness()
{
foreach (PBETeam team in Teams)
{
- team.Party[0].FieldPosition = PBEFieldPosition.Center;
- team.SwitchInQueue.Add(team.Party[0]);
+ PBEPokemon pkmn = team.Party[0];
+ pkmn.FieldPosition = PBEFieldPosition.Center;
+ team.SwitchInQueue.Add(pkmn);
}
break;
}
@@ -122,12 +137,14 @@ private void CheckForReadiness()
{
foreach (PBETeam team in Teams)
{
- team.Party[0].FieldPosition = PBEFieldPosition.Left;
- team.SwitchInQueue.Add(team.Party[0]);
+ PBEPokemon pkmn = team.Party[0];
+ pkmn.FieldPosition = PBEFieldPosition.Left;
+ team.SwitchInQueue.Add(pkmn);
if (team.Party.Count > 1)
{
- team.Party[1].FieldPosition = PBEFieldPosition.Right;
- team.SwitchInQueue.Add(team.Party[1]);
+ pkmn = team.Party[1];
+ pkmn.FieldPosition = PBEFieldPosition.Right;
+ team.SwitchInQueue.Add(pkmn);
}
}
break;
@@ -136,17 +153,20 @@ private void CheckForReadiness()
{
foreach (PBETeam team in Teams)
{
- team.Party[0].FieldPosition = PBEFieldPosition.Left;
- team.SwitchInQueue.Add(team.Party[0]);
+ PBEPokemon pkmn = team.Party[0];
+ pkmn.FieldPosition = PBEFieldPosition.Left;
+ team.SwitchInQueue.Add(pkmn);
if (team.Party.Count > 1)
{
- team.Party[1].FieldPosition = PBEFieldPosition.Center;
- team.SwitchInQueue.Add(team.Party[1]);
+ pkmn = team.Party[1];
+ pkmn.FieldPosition = PBEFieldPosition.Center;
+ team.SwitchInQueue.Add(pkmn);
}
if (team.Party.Count > 2)
{
- team.Party[2].FieldPosition = PBEFieldPosition.Right;
- team.SwitchInQueue.Add(team.Party[2]);
+ pkmn = team.Party[2];
+ pkmn.FieldPosition = PBEFieldPosition.Right;
+ team.SwitchInQueue.Add(pkmn);
}
}
break;
@@ -155,17 +175,20 @@ private void CheckForReadiness()
{
foreach (PBETeam team in Teams)
{
- team.Party[0].FieldPosition = PBEFieldPosition.Center;
- team.SwitchInQueue.Add(team.Party[0]);
+ PBEPokemon pkmn = team.Party[0];
+ pkmn.FieldPosition = PBEFieldPosition.Center;
+ team.SwitchInQueue.Add(pkmn);
if (team.Party.Count > 1)
{
- team.Party[1].FieldPosition = PBEFieldPosition.Left;
- team.SwitchInQueue.Add(team.Party[1]);
+ pkmn = team.Party[1];
+ pkmn.FieldPosition = PBEFieldPosition.Left;
+ team.SwitchInQueue.Add(pkmn);
}
if (team.Party.Count > 2)
{
- team.Party[2].FieldPosition = PBEFieldPosition.Right;
- team.SwitchInQueue.Add(team.Party[2]);
+ pkmn = team.Party[2];
+ pkmn.FieldPosition = PBEFieldPosition.Right;
+ team.SwitchInQueue.Add(pkmn);
}
}
break;
@@ -186,14 +209,6 @@ private void CheckForReadiness()
/// Thrown when 's settings are unequal to 's battle's settings or when is invalid.
public static void CreateTeamParty(PBETeam team, PBETeamShell teamShell, string teamTrainerName)
{
- if (team.Battle.BattleState != PBEBattleState.WaitingForPlayers)
- {
- throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForPlayers} to set a team's party.");
- }
- if (team.Party.Count > 0)
- {
- throw new InvalidOperationException("This team already has its party set.");
- }
if (team == null)
{
throw new ArgumentNullException(nameof(team));
@@ -202,41 +217,82 @@ public static void CreateTeamParty(PBETeam team, PBETeamShell teamShell, string
{
throw new ArgumentNullException(nameof(teamShell));
}
+ if (string.IsNullOrEmpty(teamTrainerName))
+ {
+ throw new ArgumentOutOfRangeException(nameof(teamTrainerName));
+ }
+ if (team.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team));
+ }
+ if (teamShell.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(teamShell));
+ }
if (!teamShell.Settings.Equals(team.Battle.Settings))
{
throw new ArgumentOutOfRangeException(nameof(teamShell), $"\"{nameof(teamShell)}\"'s settings must be equal to the battle's settings.");
}
- if (string.IsNullOrEmpty(teamTrainerName))
+ if (team.Battle.BattleState != PBEBattleState.WaitingForPlayers)
{
- throw new ArgumentOutOfRangeException(nameof(teamTrainerName));
+ throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForPlayers} to set a team's party.");
}
- team.CreateParty(teamShell, teamTrainerName, ref team.Battle.pkmnIdCounter);
+ if (team.Party.Count > 0)
+ {
+ throw new InvalidOperationException("This team already has its party set.");
+ }
+ team.CreateParty(teamShell, teamTrainerName);
team.Battle.CheckForReadiness();
}
/// Begins the battle.
/// Thrown when is not .
public void Begin()
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
if (BattleState != PBEBattleState.ReadyToBegin)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.ReadyToBegin} to begin the battle.");
}
- SwitchesOrActions();
+ lock (_disposeLockObj)
+ {
+ if (!IsDisposed)
+ {
+ foreach (PBETeam team in Teams)
+ {
+ BroadcastTeam(team);
+ }
+ SwitchesOrActions();
+ }
+ }
}
/// Runs a turn.
/// Thrown when is not .
public void RunTurn()
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
if (BattleState != PBEBattleState.ReadyToRunTurn)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.ReadyToRunTurn} to run a turn.");
}
- BattleState = PBEBattleState.Processing;
- OnStateChanged?.Invoke(this);
- DetermineTurnOrder();
- RunActionsInOrder();
- TurnEnded();
+ lock (_disposeLockObj)
+ {
+ if (!IsDisposed)
+ {
+ BattleState = PBEBattleState.Processing;
+ OnStateChanged?.Invoke(this);
+ DetermineTurnOrder();
+ RunActionsInOrder();
+ TurnEnded();
+ }
+ }
}
+
private bool WinCheck()
{
if (Winner != null)
@@ -253,13 +309,13 @@ private void SwitchesOrActions()
BattleState = PBEBattleState.Processing;
OnStateChanged?.Invoke(this);
- IEnumerable teamsWithSwitchIns = Teams.Where(t => t.SwitchInQueue.Count > 0);
- if (teamsWithSwitchIns.Count() > 0)
+ PBETeam[] teamsWithSwitchIns = Teams.Where(t => t.SwitchInQueue.Count > 0).ToArray();
+ if (teamsWithSwitchIns.Length > 0)
{
foreach (PBETeam team in teamsWithSwitchIns)
{
ActiveBattlers.AddRange(team.SwitchInQueue);
- BroadcastPkmnSwitchIn(team, team.SwitchInQueue.Select(p => CreateSwitchInInfo(p)), false);
+ BroadcastPkmnSwitchIn(team, team.SwitchInQueue.Select(p => CreateSwitchInInfo(p)).ToArray(), false);
}
DoSwitchInEffects(teamsWithSwitchIns.SelectMany(t => t.SwitchInQueue));
}
@@ -314,8 +370,8 @@ private void SwitchesOrActions()
default: throw new ArgumentOutOfRangeException(nameof(BattleFormat));
}
}
- teamsWithSwitchIns = Teams.Where(t => t.SwitchInsRequired > 0);
- if (teamsWithSwitchIns.Count() > 0)
+ teamsWithSwitchIns = Teams.Where(t => t.SwitchInsRequired > 0).ToArray();
+ if (teamsWithSwitchIns.Length > 0)
{
BattleState = PBEBattleState.WaitingForSwitchIns;
OnStateChanged?.Invoke(this);
@@ -393,7 +449,7 @@ private void SwitchesOrActions()
}
}
}
- private IEnumerable GetActingOrder(IEnumerable pokemon, bool ignoreItemsThatActivate)
+ private PBEPokemon[] GetActingOrder(IEnumerable pokemon, bool ignoreItemsThatActivate)
{
var evaluated = new List<(PBEPokemon Pokemon, double Speed)>(); // TODO: Full Incense, Lagging Tail, Stall, Quick Claw
foreach (PBEPokemon pkmn in pokemon)
@@ -497,34 +553,34 @@ private IEnumerable GetActingOrder(IEnumerable pokemon,
}
Debug.WriteLine(evaluated.Select(t => $"{t.Pokemon.Team.Id} {t.Pokemon.Nickname} {t.Speed}").Print());
}
- return evaluated.Select(t => t.Pokemon);
+ return evaluated.Select(t => t.Pokemon).ToArray();
}
private void DetermineTurnOrder()
{
- turnOrder.Clear();
- IEnumerable pkmnSwitchingOut = ActiveBattlers.Where(p => p.SelectedAction.Decision == PBEDecision.SwitchOut);
- IEnumerable pkmnFighting = ActiveBattlers.Where(p => p.SelectedAction.Decision == PBEDecision.Fight);
+ _turnOrder.Clear();
+ IEnumerable pkmnSwitchingOut = ActiveBattlers.Where(p => p.TurnAction.Decision == PBETurnDecision.SwitchOut);
+ IEnumerable pkmnFighting = ActiveBattlers.Where(p => p.TurnAction.Decision == PBETurnDecision.Fight);
// Switching happens first:
- turnOrder.AddRange(GetActingOrder(pkmnSwitchingOut, true));
+ _turnOrder.AddRange(GetActingOrder(pkmnSwitchingOut, true));
// Moves:
sbyte GetPrio(PBEPokemon p)
{
- PBEMoveData mData = PBEMoveData.Data[p.SelectedAction.FightMove];
+ PBEMoveData mData = PBEMoveData.Data[p.TurnAction.FightMove];
return (sbyte)PBEUtils.Clamp(mData.Priority + (p.Ability == PBEAbility.Prankster && mData.Category == PBEMoveCategory.Status ? 1 : 0), sbyte.MinValue, sbyte.MaxValue);
}
foreach (sbyte priority in pkmnFighting.Select(p => GetPrio(p)).Distinct().OrderByDescending(p => p))
{
- IEnumerable pkmnWithThisPriority = pkmnFighting.Where(p => GetPrio(p) == priority);
- if (pkmnWithThisPriority.Count() > 0)
+ PBEPokemon[] pkmnWithThisPriority = pkmnFighting.Where(p => GetPrio(p) == priority).ToArray();
+ if (pkmnWithThisPriority.Length > 0)
{
Debug.WriteLine("Priority {0} bracket...", priority);
- turnOrder.AddRange(GetActingOrder(pkmnWithThisPriority, false));
+ _turnOrder.AddRange(GetActingOrder(pkmnWithThisPriority, false));
}
}
}
private void RunActionsInOrder()
{
- foreach (PBEPokemon pkmn in turnOrder.ToArray()) // Copy the list so a faint or ejection does not cause a collection modified exception
+ foreach (PBEPokemon pkmn in _turnOrder.ToArray()) // Copy the list so a faint or ejection does not cause a collection modified exception
{
if (Winner != null) // Do not broadcast winner by calling WinCheck() in here; do it in TurnEnded()
{
@@ -532,19 +588,19 @@ private void RunActionsInOrder()
}
else if (ActiveBattlers.Contains(pkmn))
{
- switch (pkmn.SelectedAction.Decision)
+ switch (pkmn.TurnAction.Decision)
{
- case PBEDecision.Fight:
+ case PBETurnDecision.Fight:
{
- UseMove(pkmn, pkmn.SelectedAction.FightMove, pkmn.SelectedAction.FightTargets);
+ UseMove(pkmn, pkmn.TurnAction.FightMove, pkmn.TurnAction.FightTargets);
break;
}
- case PBEDecision.SwitchOut:
+ case PBETurnDecision.SwitchOut:
{
- SwitchTwoPokemon(pkmn, TryGetPokemon(pkmn.SelectedAction.SwitchPokemonId), false);
+ SwitchTwoPokemon(pkmn, TryGetPokemon(pkmn.TurnAction.SwitchPokemonId), false);
break;
}
- default: throw new ArgumentOutOfRangeException(nameof(pkmn.SelectedAction.Decision));
+ default: throw new ArgumentOutOfRangeException(nameof(pkmn.TurnAction.Decision));
}
}
}
@@ -564,7 +620,7 @@ private void TurnEnded()
return;
}
- // Verified: Reflect then Light Screen then Lucky Chant then Trick Room
+ // Reflect/Light Screen/Lucky Chant/Trick Room are removed in the order they were added, but who cares
foreach (PBETeam team in Teams)
{
if (team.TeamStatus.HasFlag(PBETeamStatus.Reflect))
@@ -609,5 +665,23 @@ private void TurnEnded()
SwitchesOrActions();
}
+
+ private readonly object _disposeLockObj = new object();
+ public bool IsDisposed { get; private set; }
+ public void Dispose()
+ {
+ lock (_disposeLockObj)
+ {
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ OnPropertyChanged(nameof(IsDisposed));
+ foreach (PBETeam team in Teams)
+ {
+ team.Dispose();
+ }
+ }
+ }
+ }
}
}
diff --git a/PokemonBattleEngine/Battle/BattleActions.cs b/PokemonBattleEngine/Battle/BattleActions.cs
index 56954c69c..878a675cb 100644
--- a/PokemonBattleEngine/Battle/BattleActions.cs
+++ b/PokemonBattleEngine/Battle/BattleActions.cs
@@ -3,66 +3,130 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
namespace Kermalis.PokemonBattleEngine.Battle
{
- [StructLayout(LayoutKind.Explicit)]
- public struct PBEAction
+ public sealed class PBETurnAction
{
- [FieldOffset(0)]
- public byte PokemonId;
- [FieldOffset(1)]
- public PBEDecision Decision;
- [FieldOffset(2)]
- public PBEMove FightMove;
- [FieldOffset(4)]
- public PBETarget FightTargets;
- [FieldOffset(4)]
- public byte SwitchPokemonId;
+ public byte PokemonId { get; }
+ public PBETurnDecision Decision { get; }
+ public PBEMove FightMove { get; }
+ public PBETurnTarget FightTargets { get; internal set; } // Internal set because of PBEMoveTarget.RandomFoeSurrounding
+ public byte SwitchPokemonId { get; }
- internal List ToBytes()
+ internal PBETurnAction(BinaryReader r)
+ {
+ PokemonId = r.ReadByte();
+ Decision = (PBETurnDecision)r.ReadByte();
+ switch (Decision)
+ {
+ case PBETurnDecision.Fight:
+ {
+ FightMove = (PBEMove)r.ReadUInt16();
+ FightTargets = (PBETurnTarget)r.ReadByte();
+ break;
+ }
+ case PBETurnDecision.SwitchOut:
+ {
+ SwitchPokemonId = r.ReadByte();
+ break;
+ }
+ default: throw new ArgumentOutOfRangeException(nameof(Decision));
+ }
+ }
+ public PBETurnAction(byte pokemonId, PBEMove fightMove, PBETurnTarget fightTargets)
+ {
+ PokemonId = pokemonId;
+ Decision = PBETurnDecision.Fight;
+ FightMove = fightMove;
+ FightTargets = fightTargets;
+ }
+ public PBETurnAction(byte pokemonId, byte switchPokemonId)
+ {
+ PokemonId = pokemonId;
+ Decision = PBETurnDecision.SwitchOut;
+ SwitchPokemonId = switchPokemonId;
+ }
+
+ internal void ToBytes(List bytes)
{
- var bytes = new List();
bytes.Add(PokemonId);
bytes.Add((byte)Decision);
- bytes.AddRange(BitConverter.GetBytes((ushort)FightMove));
- bytes.Add(SwitchPokemonId);
- return bytes;
+ switch (Decision)
+ {
+ case PBETurnDecision.Fight:
+ {
+ bytes.AddRange(BitConverter.GetBytes((ushort)FightMove));
+ bytes.Add((byte)FightTargets);
+ break;
+ }
+ case PBETurnDecision.SwitchOut:
+ {
+ bytes.Add(SwitchPokemonId);
+ break;
+ }
+ throw new ArgumentOutOfRangeException(nameof(Decision));
+ }
}
- internal static PBEAction FromBytes(BinaryReader r)
+ }
+ public sealed class PBESwitchIn
+ {
+ public byte PokemonId { get; }
+ public PBEFieldPosition Position { get; }
+
+ internal PBESwitchIn(BinaryReader r)
{
- return new PBEAction
- {
- PokemonId = r.ReadByte(),
- Decision = (PBEDecision)r.ReadByte(),
- FightMove = (PBEMove)r.ReadUInt16(),
- SwitchPokemonId = r.ReadByte()
- };
+ PokemonId = r.ReadByte();
+ Position = (PBEFieldPosition)r.ReadByte();
+ }
+ public PBESwitchIn(byte pokemonId, PBEFieldPosition position)
+ {
+ PokemonId = pokemonId;
+ Position = position;
+ }
+
+ internal void ToBytes(List bytes)
+ {
+ bytes.Add(PokemonId);
+ bytes.Add((byte)Position);
}
}
public sealed partial class PBEBattle
{
- ///
- /// Determines whether chosen actions are valid.
- ///
+ /// Determines whether chosen actions are valid.
/// The team the inputted actions belong to.
/// The actions the team wishes to execute.
/// False if the team already chose actions or the actions are illegal, True otherwise.
/// Thrown when is not .
- public static bool AreActionsValid(PBETeam team, IEnumerable actions)
+ public static bool AreActionsValid(PBETeam team, IList actions)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
+ if (actions == null)
+ {
+ throw new ArgumentNullException(nameof(actions));
+ }
+ if (team.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team));
+ }
if (team.Battle.BattleState != PBEBattleState.WaitingForActions)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} to validate actions.");
}
- if (team.ActionsRequired.Count == 0 || actions.Count() != team.ActionsRequired.Count)
+ if (team.ActionsRequired.Count == 0 || actions.Count != team.ActionsRequired.Count)
{
return false;
}
var standBy = new List();
- foreach (PBEAction action in actions)
+ foreach (PBETurnAction action in actions)
{
+ if (action == null)
+ {
+ return false;
+ }
PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId);
if (!team.ActionsRequired.Contains(pkmn))
{
@@ -70,7 +134,7 @@ public static bool AreActionsValid(PBETeam team, IEnumerable actions)
}
switch (action.Decision)
{
- case PBEDecision.Fight:
+ case PBETurnDecision.Fight:
{
if (Array.IndexOf(pkmn.GetUsableMoves(), action.FightMove) == -1
|| (action.FightMove == pkmn.TempLockedMove && action.FightTargets != pkmn.TempLockedTargets)
@@ -81,7 +145,7 @@ public static bool AreActionsValid(PBETeam team, IEnumerable actions)
}
break;
}
- case PBEDecision.SwitchOut:
+ case PBETurnDecision.SwitchOut:
{
if (!pkmn.CanSwitchOut())
{
@@ -102,141 +166,161 @@ public static bool AreActionsValid(PBETeam team, IEnumerable actions)
}
break;
}
+ default: return false;
}
}
return true;
}
- ///
- /// Selects actions if they are valid. Changes the battle state if both teams have selected valid actions.
- ///
+ /// Selects actions if they are valid. Changes the battle state if both teams have selected valid actions.
/// The team the inputted actions belong to.
/// The actions the team wishes to execute.
/// True if the actions are valid and were selected.
/// Thrown when is not .
- public static bool SelectActionsIfValid(PBETeam team, IEnumerable actions)
+ public static bool SelectActionsIfValid(PBETeam team, IList actions)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
+ if (actions == null)
+ {
+ throw new ArgumentNullException(nameof(actions));
+ }
+ if (team.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team));
+ }
if (team.Battle.BattleState != PBEBattleState.WaitingForActions)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} to select actions.");
}
if (AreActionsValid(team, actions))
{
- team.ActionsRequired.Clear();
- foreach (PBEAction action in actions)
+ lock (team.Battle._disposeLockObj)
{
- PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId);
- pkmn.SelectedAction = action;
- switch (pkmn.SelectedAction.Decision)
+ if (!team.Battle.IsDisposed)
{
- case PBEDecision.Fight:
+ team.ActionsRequired.Clear();
+ foreach (PBETurnAction action in actions)
{
- switch (pkmn.GetMoveTargets(pkmn.SelectedAction.FightMove))
+ PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId);
+ if (action.Decision == PBETurnDecision.Fight && pkmn.GetMoveTargets(action.FightMove) == PBEMoveTarget.RandomFoeSurrounding)
{
- case PBEMoveTarget.RandomFoeSurrounding:
+ switch (team.Battle.BattleFormat)
{
- switch (team.Battle.BattleFormat)
+ case PBEBattleFormat.Single:
+ case PBEBattleFormat.Rotation:
{
- case PBEBattleFormat.Single:
- case PBEBattleFormat.Rotation:
- {
- pkmn.SelectedAction.FightTargets = PBETarget.FoeCenter;
- break;
- }
- case PBEBattleFormat.Double:
+ action.FightTargets = PBETurnTarget.FoeCenter;
+ break;
+ }
+ case PBEBattleFormat.Double:
+ {
+ action.FightTargets = PBEUtils.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeRight;
+ break;
+ }
+ case PBEBattleFormat.Triple:
+ {
+ if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeLeft : PBETarget.FoeRight;
- break;
+ action.FightTargets = PBEUtils.RandomBool() ? PBETurnTarget.FoeCenter : PBETurnTarget.FoeRight;
}
- case PBEBattleFormat.Triple:
+ else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- if (pkmn.FieldPosition == PBEFieldPosition.Left)
- {
- pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeCenter : PBETarget.FoeRight;
- }
- else if (pkmn.FieldPosition == PBEFieldPosition.Center)
+ int r; // Keep randomly picking until a non-fainted foe is selected
+ roll:
+ r = PBEUtils.RandomInt(0, 2);
+ if (r == 0)
{
- PBETeam opposingTeam = team == team.Battle.Teams[0] ? team.Battle.Teams[1] : team.Battle.Teams[0];
- int r; // Keep randomly picking until a non-fainted foe is selected
- roll:
- r = PBEUtils.RandomInt(0, 2);
- if (r == 0)
+ if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left) != null)
{
- if (opposingTeam.TryGetPokemon(PBEFieldPosition.Left) != null)
- {
- pkmn.SelectedAction.FightTargets = PBETarget.FoeLeft;
- }
- else
- {
- goto roll;
- }
+ action.FightTargets = PBETurnTarget.FoeLeft;
}
- else if (r == 1)
+ else
+ {
+ goto roll;
+ }
+ }
+ else if (r == 1)
+ {
+ if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center) != null)
{
- if (opposingTeam.TryGetPokemon(PBEFieldPosition.Center) != null)
- {
- pkmn.SelectedAction.FightTargets = PBETarget.FoeCenter;
- }
- else
- {
- goto roll;
- }
+ action.FightTargets = PBETurnTarget.FoeCenter;
}
else
{
- if (opposingTeam.TryGetPokemon(PBEFieldPosition.Right) != null)
- {
- pkmn.SelectedAction.FightTargets = PBETarget.FoeRight;
- }
- else
- {
- goto roll;
- }
+ goto roll;
}
}
else
{
- pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeLeft : PBETarget.FoeCenter;
+ if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right) != null)
+ {
+ action.FightTargets = PBETurnTarget.FoeRight;
+ }
+ else
+ {
+ goto roll;
+ }
}
- break;
}
+ else
+ {
+ action.FightTargets = PBEUtils.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeCenter;
+ }
+ break;
}
- break;
+ default: throw new ArgumentOutOfRangeException(nameof(team.Battle.BattleFormat));
}
}
- break;
+ pkmn.TurnAction = action;
}
+ if (team.Battle.Teams.All(t => t.ActionsRequired.Count == 0))
+ {
+ team.Battle.BattleState = PBEBattleState.ReadyToRunTurn;
+ team.Battle.OnStateChanged?.Invoke(team.Battle);
+ }
+ return true;
}
}
- if (Array.TrueForAll(team.Battle.Teams, t => t.ActionsRequired.Count == 0))
- {
- team.Battle.BattleState = PBEBattleState.ReadyToRunTurn;
- team.Battle.OnStateChanged?.Invoke(team.Battle);
- }
- return true;
}
return false;
}
- ///
- /// Determines whether chosen switches are valid.
- ///
+ /// Determines whether chosen switches are valid.
/// The team the inputted switches belong to.
/// The switches the team wishes to execute.
/// False if the team already chose switches or the switches are illegal, True otherwise.
/// Thrown when is not .
- public static bool AreSwitchesValid(PBETeam team, IEnumerable<(byte PokemonId, PBEFieldPosition Position)> switches)
+ public static bool AreSwitchesValid(PBETeam team, IList switches)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
+ if (switches == null)
+ {
+ throw new ArgumentNullException(nameof(switches));
+ }
+ if (team.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team));
+ }
if (team.Battle.BattleState != PBEBattleState.WaitingForSwitchIns)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to validate switches.");
}
- if (team.SwitchInsRequired == 0 || switches.Count() != team.SwitchInsRequired)
+ if (team.SwitchInsRequired == 0 || switches.Count != team.SwitchInsRequired)
{
return false;
}
- foreach ((byte PokemonId, PBEFieldPosition Position) in switches)
+ foreach (PBESwitchIn s in switches)
{
- PBEPokemon pkmn = team.TryGetPokemon(PokemonId);
+ if (s == null)
+ {
+ return false;
+ }
+ PBEPokemon pkmn = team.TryGetPokemon(s.PokemonId);
if (pkmn == null || pkmn.HP == 0 || pkmn.FieldPosition != PBEFieldPosition.None)
{
return false;
@@ -244,33 +328,49 @@ public static bool AreSwitchesValid(PBETeam team, IEnumerable<(byte PokemonId, P
}
return true;
}
- ///
- /// Selects switches if they are valid. Changes the battle state if both teams have selected valid switches.
- ///
+ /// Selects switches if they are valid. Changes the battle state if both teams have selected valid switches.
/// The team the inputted switches belong to.
/// The switches the team wishes to execute.
/// True if the switches are valid and were selected.
/// Thrown when is not .
- public static bool SelectSwitchesIfValid(PBETeam team, IEnumerable<(byte PokemonId, PBEFieldPosition Position)> switches)
+ public static bool SelectSwitchesIfValid(PBETeam team, IList switches)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
+ if (switches == null)
+ {
+ throw new ArgumentNullException(nameof(switches));
+ }
+ if (team.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(team));
+ }
if (team.Battle.BattleState != PBEBattleState.WaitingForSwitchIns)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to select switches.");
}
if (AreSwitchesValid(team, switches))
{
- team.SwitchInsRequired = 0;
- foreach ((byte PokemonId, PBEFieldPosition Position) in switches)
+ lock (team.Battle._disposeLockObj)
{
- PBEPokemon pkmn = team.TryGetPokemon(PokemonId);
- pkmn.FieldPosition = Position;
- team.SwitchInQueue.Add(pkmn);
- }
- if (Array.TrueForAll(team.Battle.Teams, t => t.SwitchInsRequired == 0))
- {
- team.Battle.SwitchesOrActions();
+ if (!team.Battle.IsDisposed)
+ {
+ team.SwitchInsRequired = 0;
+ foreach (PBESwitchIn s in switches)
+ {
+ PBEPokemon pkmn = team.TryGetPokemon(s.PokemonId);
+ pkmn.FieldPosition = s.Position;
+ team.SwitchInQueue.Add(pkmn);
+ }
+ if (team.Battle.Teams.All(t => t.SwitchInsRequired == 0))
+ {
+ team.Battle.SwitchesOrActions();
+ }
+ return true;
+ }
}
- return true;
}
return false;
}
diff --git a/PokemonBattleEngine/Battle/BattleDamage.cs b/PokemonBattleEngine/Battle/BattleDamage.cs
index 2a605447b..48a23e635 100644
--- a/PokemonBattleEngine/Battle/BattleDamage.cs
+++ b/PokemonBattleEngine/Battle/BattleDamage.cs
@@ -6,9 +6,7 @@ namespace Kermalis.PokemonBattleEngine.Battle
{
public sealed partial class PBEBattle
{
- ///
- /// Gets the influence a stat change has on a stat.
- ///
+ /// Gets the influence a stat change has on a stat.
/// The stat change.
/// True if the stat is or .
public static double GetStatChangeModifier(sbyte change, bool forMissing)
@@ -19,9 +17,7 @@ public static double GetStatChangeModifier(sbyte change, bool forMissing)
return numerator / denominator;
}
- ///
- /// Deals damage to and broadcasts the HP changing and substitute damage.
- ///
+ /// Deals damage to and broadcasts the HP changing and substitute damage.
/// The Pokémon responsible for the damage.
/// The Pokémon receiving the damage.
/// The amount of HP will try to lose.
@@ -81,9 +77,7 @@ private ushort DealDamage(PBEPokemon culprit, PBEPokemon victim, ushort hp, bool
return (ushort)(oldHP - victim.HP);
}
}
- ///
- /// Restores HP to and broadcasts the HP changing if it changes.
- ///
+ /// Restores HP to and broadcasts the HP changing if it changes.
/// The Pokémon receiving the HP.
/// The amount of HP will try to gain.
/// The amount of HP restored.
@@ -502,6 +496,10 @@ private double CalculateBasePower(PBEPokemon user, PBEPokemon[] targets, PBEMove
break;
}
break;
+ case PBEType.None:
+ {
+ break;
+ }
case PBEType.Normal:
switch (user.Item)
{
@@ -613,6 +611,7 @@ private double CalculateBasePower(PBEPokemon user, PBEPokemon[] targets, PBEMove
break;
}
break;
+ default: throw new ArgumentOutOfRangeException(nameof(moveType));
}
// Move-specific power boosts
diff --git a/PokemonBattleEngine/Battle/BattleEffects.cs b/PokemonBattleEngine/Battle/BattleEffects.cs
index be76fe6f6..b80ae866b 100644
--- a/PokemonBattleEngine/Battle/BattleEffects.cs
+++ b/PokemonBattleEngine/Battle/BattleEffects.cs
@@ -8,13 +8,13 @@ namespace Kermalis.PokemonBattleEngine.Battle
{
public sealed partial class PBEBattle
{
- private bool calledFromOtherMove = false;
+ private static readonly PBEStat[] _moodyStats = Enum.GetValues(typeof(PBEStat)).Cast().Except(new[] { PBEStat.HP }).ToArray();
+
+ private bool _calledFromOtherMove = false;
private void DoSwitchInEffects(IEnumerable battlers)
{
- IEnumerable order = GetActingOrder(battlers, true);
-
- foreach (PBEPokemon pkmn in order)
+ foreach (PBEPokemon pkmn in GetActingOrder(battlers, true))
{
// Verified: (Spikes/StealthRock/ToxicSpikes in the order they were applied) before ability
if (pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.Spikes) && !pkmn.HasType(PBEType.Flying) && pkmn.Ability != PBEAbility.Levitate)
@@ -101,8 +101,7 @@ private void DoSwitchInEffects(IEnumerable battlers)
case PBEAbility.Imposter:
{
PBEFieldPosition targetPos = GetPositionAcross(BattleFormat, pkmn.FieldPosition);
- PBETeam opposingTeam = pkmn.Team == Teams[0] ? Teams[1] : Teams[0];
- PBEPokemon target = opposingTeam.TryGetPokemon(targetPos);
+ PBEPokemon target = pkmn.Team.OpposingTeam.TryGetPokemon(targetPos);
if (target != null
&& !target.Status2.HasFlag(PBEStatus2.Disguised)
&& !target.Status2.HasFlag(PBEStatus2.Substitute)
@@ -138,9 +137,7 @@ private void DoSwitchInEffects(IEnumerable battlers)
}
}
}
- ///
- /// Does effects that take place after hitting such as substitute breaking, rough skin, and victim eating its berry.
- ///
+ /// Does effects that take place after hitting such as substitute breaking, rough skin, and victim eating its berry.
/// The Pokémon who used .
/// The Pokémon who was affected by .
/// The move used.
@@ -295,9 +292,7 @@ private void DoPostHitEffects(PBEPokemon user, PBEPokemon victim, PBEMove move,
// TODO: King's Rock, Stench, etc
// TODO?: Cell Battery
}
- ///
- /// Does effects that take place after an attack is completely done such as recoil and life orb.
- ///
+ /// Does effects that take place after an attack is completely done such as recoil and life orb.
/// The Pokémon who used the attack.
/// Whether should damage the user or not.
/// The amount of recoil damage will take.
@@ -324,7 +319,7 @@ private void DoPostAttackedEffects(PBEPokemon user, bool ignoreLifeOrb, ushort r
}
private void DoTurnEndedEffects()
{
- IEnumerable order = GetActingOrder(ActiveBattlers, true);
+ PBEPokemon[] order = GetActingOrder(ActiveBattlers, true);
// Verified: Weather stops before doing damage
if (WeatherCounter > 0)
@@ -577,10 +572,9 @@ private void DoTurnEndedEffects()
{
case PBEAbility.Moody:
{
- IEnumerable allStats = Enum.GetValues(typeof(PBEStat)).Cast().Except(new[] { PBEStat.HP });
- PBEStat[] statsThatCanGoUp = allStats.Where(s => pkmn.GetStatChange(s) < Settings.MaxStatChange).ToArray();
+ PBEStat[] statsThatCanGoUp = _moodyStats.Where(s => pkmn.GetStatChange(s) < Settings.MaxStatChange).ToArray();
PBEStat? upStat = statsThatCanGoUp.Length == 0 ? (PBEStat?)null : statsThatCanGoUp.RandomElement();
- var statsThatCanGoDown = allStats.Where(s => pkmn.GetStatChange(s) > -Settings.MaxStatChange).ToList();
+ var statsThatCanGoDown = _moodyStats.Where(s => pkmn.GetStatChange(s) > -Settings.MaxStatChange).ToList();
if (upStat.HasValue)
{
statsThatCanGoDown.Remove(upStat.Value);
@@ -648,19 +642,23 @@ private void DoTurnEndedEffects()
}
public bool ShouldDoWeatherEffects()
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
// If HP is needed to be above 0, use HPPercentage so clients can continue to use this
// However, I see no instance of this getting called where an ActiveBattler has 0 hp
return !ActiveBattlers.Any(p => p.Ability == PBEAbility.AirLock || p.Ability == PBEAbility.CloudNine);
}
- private void UseMove(PBEPokemon user, PBEMove move, PBETarget requestedTargets)
+ private void UseMove(PBEPokemon user, PBEMove move, PBETurnTarget requestedTargets)
{
- if (!calledFromOtherMove && PreMoveStatusCheck(user, move))
+ if (!_calledFromOtherMove && PreMoveStatusCheck(user, move))
{
if (user.Status2.HasFlag(PBEStatus2.Airborne))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Airborne;
BroadcastStatus2(user, user, PBEStatus2.Airborne, PBEStatusAction.Ended);
@@ -668,7 +666,7 @@ private void UseMove(PBEPokemon user, PBEMove move, PBETarget requestedTargets)
if (user.Status2.HasFlag(PBEStatus2.Underground))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Underground;
BroadcastStatus2(user, user, PBEStatus2.Underground, PBEStatusAction.Ended);
@@ -676,7 +674,7 @@ private void UseMove(PBEPokemon user, PBEMove move, PBETarget requestedTargets)
if (user.Status2.HasFlag(PBEStatus2.Underwater))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Underwater;
BroadcastStatus2(user, user, PBEStatus2.Underwater, PBEStatusAction.Ended);
@@ -1504,14 +1502,14 @@ private void TrySetLoser(PBEPokemon pkmn)
{
if (Winner == null && pkmn.Team.NumPkmnAlive == 0)
{
- Winner = pkmn.Team == Teams[0] ? Teams[1] : Teams[0];
+ Winner = pkmn.Team.OpposingTeam;
}
}
private bool FaintCheck(PBEPokemon pkmn)
{
if (pkmn.HP == 0)
{
- turnOrder.Remove(pkmn);
+ _turnOrder.Remove(pkmn);
ActiveBattlers.Remove(pkmn);
PBEFieldPosition oldPos = pkmn.FieldPosition;
pkmn.FieldPosition = PBEFieldPosition.None;
@@ -1732,7 +1730,14 @@ private void HealingBerryCheck(PBEPokemon pkmn)
private bool HasUsedMoveThisTurn(PBEPokemon pkmn)
{
- return pkmn.ExecutedMoves.Any(e => e.TurnNumber == TurnNumber);
+ for (int i = 0; i < pkmn.ExecutedMoves.Count; i++)
+ {
+ if (pkmn.ExecutedMoves[i].TurnNumber == TurnNumber)
+ {
+ return true;
+ }
+ }
+ return false;
}
private void RecordExecutedMove(PBEPokemon user, PBEMove move, PBEFailReason failReason, IList targets)
{
@@ -1741,19 +1746,22 @@ private void RecordExecutedMove(PBEPokemon user, PBEMove move, PBEFailReason fai
if ((user.Item == PBEItem.ChoiceBand || user.Item == PBEItem.ChoiceScarf || user.Item == PBEItem.ChoiceSpecs) && user.Moves.Contains(move))
{
user.ChoiceLockedMove = move;
- BroadcastMoveLock(user, move, PBETarget.None, PBEMoveLockType.ChoiceItem);
+ BroadcastMoveLock(user, move, PBETurnTarget.None, PBEMoveLockType.ChoiceItem);
}
}
private void PPReduce(PBEPokemon pkmn, PBEMove move)
{
- if (!calledFromOtherMove)
+ if (!_calledFromOtherMove)
{
- int moveIndex = Array.IndexOf(pkmn.Moves, move);
- int amtToReduce = 1;
+ const int amountToReduce = 1;
// TODO: If target is not self and has pressure
- byte oldPP = pkmn.PP[moveIndex];
- pkmn.PP[moveIndex] = (byte)Math.Max(0, pkmn.PP[moveIndex] - amtToReduce);
- BroadcastMovePPChanged(pkmn, move, oldPP, pkmn.PP[moveIndex]);
+ PBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.Moves[move];
+ int oldPP = slot.PP;
+ int newPP = Math.Max(0, oldPP - amountToReduce);
+ int amountReduced = oldPP - newPP;
+ slot.PP = newPP;
+ pkmn.UpdateKnownPP(move, amountReduced);
+ BroadcastMovePPChanged(pkmn, move, amountReduced);
}
}
@@ -2155,7 +2163,7 @@ private PBEPkmnSwitchInPacket.PBESwitchInInfo CreateSwitchInInfo(PBEPokemon pkmn
private void SwitchTwoPokemon(PBEPokemon pkmnLeaving, PBEPokemon pkmnComing, bool forced)
{
PBEFieldPosition pos = pkmnLeaving.FieldPosition;
- turnOrder.Remove(pkmnLeaving);
+ _turnOrder.Remove(pkmnLeaving);
ActiveBattlers.Remove(pkmnLeaving);
PBEPokemon disguisedAsPokemon = pkmnLeaving.Status2.HasFlag(PBEStatus2.Disguised) ? pkmnLeaving.DisguisedAsPokemon : pkmnLeaving;
pkmnLeaving.ClearForSwitch();
@@ -2242,7 +2250,7 @@ private void BasicHit(PBEPokemon user, PBEPokemon[] targets, PBEMove move, ref L
BroadcastEffectiveness(target, moveEffectiveness);
if (success.CriticalHit)
{
- BroadcastMoveCrit();
+ BroadcastMoveCrit(target);
}
hit++;
@@ -2459,7 +2467,6 @@ private void Ef_TryForceTeamStatus(PBEPokemon user, PBEMove move, PBETeamStatus
BroadcastMoveUsed(user, move);
PPReduce(user, move);
- PBETeam opposingTeam = user.Team == Teams[0] ? Teams[1] : Teams[0];
switch (status)
{
case PBETeamStatus.LightScreen:
@@ -2509,11 +2516,11 @@ private void Ef_TryForceTeamStatus(PBEPokemon user, PBEMove move, PBETeamStatus
}
case PBETeamStatus.Spikes:
{
- if (opposingTeam.SpikeCount < 3)
+ if (user.Team.OpposingTeam.SpikeCount < 3)
{
- opposingTeam.TeamStatus |= PBETeamStatus.Spikes;
- opposingTeam.SpikeCount++;
- BroadcastTeamStatus(opposingTeam, PBETeamStatus.Spikes, PBETeamStatusAction.Added);
+ user.Team.OpposingTeam.TeamStatus |= PBETeamStatus.Spikes;
+ user.Team.OpposingTeam.SpikeCount++;
+ BroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.Spikes, PBETeamStatusAction.Added);
failReason = PBEFailReason.None;
}
else
@@ -2524,10 +2531,10 @@ private void Ef_TryForceTeamStatus(PBEPokemon user, PBEMove move, PBETeamStatus
}
case PBETeamStatus.StealthRock:
{
- if (!opposingTeam.TeamStatus.HasFlag(PBETeamStatus.StealthRock))
+ if (!user.Team.OpposingTeam.TeamStatus.HasFlag(PBETeamStatus.StealthRock))
{
- opposingTeam.TeamStatus |= PBETeamStatus.StealthRock;
- BroadcastTeamStatus(opposingTeam, PBETeamStatus.StealthRock, PBETeamStatusAction.Added);
+ user.Team.OpposingTeam.TeamStatus |= PBETeamStatus.StealthRock;
+ BroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.StealthRock, PBETeamStatusAction.Added);
failReason = PBEFailReason.None;
}
else
@@ -2538,11 +2545,11 @@ private void Ef_TryForceTeamStatus(PBEPokemon user, PBEMove move, PBETeamStatus
}
case PBETeamStatus.ToxicSpikes:
{
- if (opposingTeam.ToxicSpikeCount < 2)
+ if (user.Team.OpposingTeam.ToxicSpikeCount < 2)
{
- opposingTeam.TeamStatus |= PBETeamStatus.ToxicSpikes;
- opposingTeam.ToxicSpikeCount++;
- BroadcastTeamStatus(opposingTeam, PBETeamStatus.ToxicSpikes, PBETeamStatusAction.Added);
+ user.Team.OpposingTeam.TeamStatus |= PBETeamStatus.ToxicSpikes;
+ user.Team.OpposingTeam.ToxicSpikeCount++;
+ BroadcastTeamStatus(user.Team.OpposingTeam, PBETeamStatus.ToxicSpikes, PBETeamStatusAction.Added);
failReason = PBEFailReason.None;
}
else
@@ -2831,7 +2838,7 @@ PBEFailReason BeforeDoingDamage(PBEPokemon target)
}
RecordExecutedMove(user, move, failReason, targetSuccess);
}
- private void Ef_Dig(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarget requestedTargets)
+ private void Ef_Dig(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETurnTarget requestedTargets)
{
var targetSuccess = new List();
PBEFailReason failReason;
@@ -2840,7 +2847,7 @@ private void Ef_Dig(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarg
if (user.Status2.HasFlag(PBEStatus2.Underground))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Underground;
BroadcastStatus2(user, user, PBEStatus2.Underground, PBEStatusAction.Ended);
@@ -2874,7 +2881,7 @@ private void Ef_Dig(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarg
}
RecordExecutedMove(user, move, failReason, targetSuccess);
}
- private void Ef_Dive(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarget requestedTargets)
+ private void Ef_Dive(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETurnTarget requestedTargets)
{
var targetSuccess = new List();
PBEFailReason failReason;
@@ -2883,7 +2890,7 @@ private void Ef_Dive(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETar
if (user.Status2.HasFlag(PBEStatus2.Underwater))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Underwater;
BroadcastStatus2(user, user, PBEStatus2.Underwater, PBEStatusAction.Ended);
@@ -2924,7 +2931,7 @@ private void Ef_Fail(PBEPokemon user, PBEMove move)
BroadcastMoveFailed(user, user, PBEFailReason.Default);
RecordExecutedMove(user, move, PBEFailReason.Default, Array.Empty());
}
- private void Ef_Fly(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarget requestedTargets)
+ private void Ef_Fly(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETurnTarget requestedTargets)
{
var targetSuccess = new List();
PBEFailReason failReason;
@@ -2933,7 +2940,7 @@ private void Ef_Fly(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBETarg
if (user.Status2.HasFlag(PBEStatus2.Airborne))
{
user.TempLockedMove = PBEMove.None;
- user.TempLockedTargets = PBETarget.None;
+ user.TempLockedTargets = PBETurnTarget.None;
BroadcastMoveLock(user, user.TempLockedMove, user.TempLockedTargets, PBEMoveLockType.Temporary);
user.Status2 &= ~PBEStatus2.Airborne;
BroadcastStatus2(user, user, PBEStatus2.Airborne, PBEStatusAction.Ended);
@@ -3055,8 +3062,8 @@ private void Ef_SuckerPunch(PBEPokemon user, PBEPokemon[] targets, PBEMove move)
failReason = PBEFailReason.None;
PBEFailReason BeforeDoingDamage(PBEPokemon target)
{
- if (target.SelectedAction.Decision != PBEDecision.Fight
- || PBEMoveData.Data[target.SelectedAction.FightMove].Category == PBEMoveCategory.Status
+ if (target.TurnAction.Decision != PBETurnDecision.Fight
+ || PBEMoveData.Data[target.TurnAction.FightMove].Category == PBEMoveCategory.Status
|| HasUsedMoveThisTurn(target))
{
BroadcastMoveFailed(user, target, PBEFailReason.Default);
@@ -3596,18 +3603,18 @@ private void Ef_Curse(PBEPokemon user, PBEPokemon[] targets, PBEMove move)
if (target == user) // Just gained the Ghost type after selecting the move, so get a random target
{
PBEFieldPosition prioritizedPos = GetPositionAcross(BattleFormat, user.FieldPosition);
- PBETarget moveTarget;
+ PBETurnTarget moveTarget;
if (prioritizedPos == PBEFieldPosition.Left)
{
- moveTarget = PBETarget.FoeLeft;
+ moveTarget = PBETurnTarget.FoeLeft;
}
else if (prioritizedPos == PBEFieldPosition.Center)
{
- moveTarget = PBETarget.FoeCenter;
+ moveTarget = PBETurnTarget.FoeCenter;
}
else
{
- moveTarget = PBETarget.FoeRight;
+ moveTarget = PBETurnTarget.FoeRight;
}
PBEPokemon[] runtimeTargets = GetRuntimeTargets(user, moveTarget, false);
@@ -3806,9 +3813,9 @@ private void Ef_Metronome(PBEPokemon user, PBEMove move)
RecordExecutedMove(user, move, PBEFailReason.None, Array.Empty());
PBEMove calledMove = PBEMoveData.Data.Where(t => !t.Value.Flags.HasFlag(PBEMoveFlag.BlockedByMetronome)).Select(t => t.Key).ToArray().RandomElement();
- calledFromOtherMove = true;
+ _calledFromOtherMove = true;
UseMove(user, calledMove, GetRandomTargetForMetronome(user, calledMove));
- calledFromOtherMove = false;
+ _calledFromOtherMove = false;
}
private void Ef_PsychUp(PBEPokemon user, PBEPokemon[] targets, PBEMove move)
{
diff --git a/PokemonBattleEngine/Battle/BattleEvents.cs b/PokemonBattleEngine/Battle/BattleEvents.cs
index 012721bdc..9c62cdf66 100644
--- a/PokemonBattleEngine/Battle/BattleEvents.cs
+++ b/PokemonBattleEngine/Battle/BattleEvents.cs
@@ -2,7 +2,6 @@
using Kermalis.PokemonBattleEngine.Data;
using Kermalis.PokemonBattleEngine.Packets;
using System;
-using System.Collections.Generic;
using System.Linq;
namespace Kermalis.PokemonBattleEngine.Battle
@@ -50,9 +49,9 @@ private void BroadcastItem(PBEPokemon itemHolder, PBEPokemon pokemon2, PBEItem i
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
- private void BroadcastMoveCrit()
+ private void BroadcastMoveCrit(PBEPokemon victim)
{
- var p = new PBEMoveCritPacket();
+ var p = new PBEMoveCritPacket(victim);
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
@@ -68,7 +67,7 @@ private void BroadcastMoveFailed(PBEPokemon moveUser, PBEPokemon pokemon2, PBEFa
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
- private void BroadcastMoveLock(PBEPokemon moveUser, PBEMove lockedMove, PBETarget lockedTargets, PBEMoveLockType moveLockType)
+ private void BroadcastMoveLock(PBEPokemon moveUser, PBEMove lockedMove, PBETurnTarget lockedTargets, PBEMoveLockType moveLockType)
{
var p = new PBEMoveLockPacket(moveUser, lockedMove, lockedTargets, moveLockType);
Events.Add(p);
@@ -80,18 +79,18 @@ private void BroadcastMoveMissed(PBEPokemon moveUser, PBEPokemon pokemon2)
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
- private void BroadcastMovePPChanged(PBEPokemon moveUser, PBEMove move, byte oldValue, byte newValue)
+ private void BroadcastMovePPChanged(PBEPokemon moveUser, PBEMove move, int amountReduced)
{
- var p = new PBEMovePPChangedPacket(moveUser.FieldPosition, moveUser.Team, move, oldValue, newValue);
+ var p = new PBEMovePPChangedPacket(moveUser.FieldPosition, moveUser.Team, move, amountReduced);
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
private void BroadcastMoveUsed(PBEPokemon moveUser, PBEMove move)
{
bool reveal;
- if (!calledFromOtherMove && moveUser.Moves.Contains(move) && !moveUser.KnownMoves.Contains(move))
+ if (!_calledFromOtherMove && moveUser.Moves.Contains(move) && !moveUser.KnownMoves.Contains(move))
{
- moveUser.KnownMoves[Array.IndexOf(moveUser.KnownMoves, PBEMove.MAX)] = move;
+ moveUser.KnownMoves[PBEMove.MAX].Move = move;
reveal = true;
}
else
@@ -136,7 +135,7 @@ private void BroadcastPkmnStatChanged(PBEPokemon pokemon, PBEStat stat, sbyte ol
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
- private void BroadcastPkmnSwitchIn(PBETeam team, IEnumerable switchIns, bool forced)
+ private void BroadcastPkmnSwitchIn(PBETeam team, PBEPkmnSwitchInPacket.PBESwitchInInfo[] switchIns, bool forced)
{
var p = new PBEPkmnSwitchInPacket(team, switchIns, forced);
Events.Add(p);
@@ -246,6 +245,12 @@ private void BroadcastAutoCenter(byte pokemon1Id, PBEFieldPosition pokemon1Posit
Events.Add(p);
OnNewEvent?.Invoke(this, p);
}
+ private void BroadcastTeam(PBETeam team)
+ {
+ var p = new PBETeamPacket(team);
+ Events.Add(p);
+ OnNewEvent?.Invoke(this, p);
+ }
private void BroadcastSwitchInRequest(PBETeam team)
{
var p = new PBESwitchInRequestPacket(team);
@@ -266,13 +271,25 @@ private void BroadcastWinner(PBETeam winningTeam)
}
- ///
- /// Writes battle events to in English.
- ///
+ /// Writes battle events to in English.
/// The battle that belongs to.
/// The battle event packet.
+ /// Thrown when or are null.
public static void ConsoleBattleEventHandler(PBEBattle battle, INetPacket packet)
{
+ if (battle == null)
+ {
+ throw new ArgumentNullException(nameof(battle));
+ }
+ if (packet == null)
+ {
+ throw new ArgumentNullException(nameof(packet));
+ }
+ if (battle.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(battle));
+ }
+
string NameForTrainer(PBEPokemon pkmn)
{
return pkmn == null ? string.Empty : $"{pkmn.Team.TrainerName}'s {pkmn.KnownNickname}";
@@ -590,9 +607,10 @@ string NameForTrainer(PBEPokemon pkmn)
Console.WriteLine(message, NameForTrainer(itemHolder), NameForTrainer(pokemon2), PBELocalizedString.GetItemName(ip.Item).English);
break;
}
- case PBEMoveCritPacket _:
+ case PBEMoveCritPacket mcp:
{
- Console.WriteLine("A critical hit!");
+ PBEPokemon victim = mcp.VictimTeam.TryGetPokemon(mcp.Victim);
+ Console.WriteLine("A critical hit on {0}!", NameForTrainer(victim));
break;
}
case PBEMoveEffectivenessPacket mep:
@@ -602,12 +620,15 @@ string NameForTrainer(PBEPokemon pkmn)
switch (mep.Effectiveness)
{
case PBEEffectiveness.Ineffective: message = "It doesn't affect {0}..."; break;
- case PBEEffectiveness.NotVeryEffective: message = "It's not very effective..."; break;
- case PBEEffectiveness.Normal: message = "It's normally effective."; break;
- case PBEEffectiveness.SuperEffective: message = "It's super effective!"; break;
+ case PBEEffectiveness.NotVeryEffective: message = "It's not very effective on {0}..."; break;
+ case PBEEffectiveness.Normal: message = null; break;
+ case PBEEffectiveness.SuperEffective: message = "It's super effective on {0}!"; break;
default: throw new ArgumentOutOfRangeException(nameof(mep.Effectiveness));
}
- Console.WriteLine(message, NameForTrainer(victim));
+ if (message != null)
+ {
+ Console.WriteLine(message, NameForTrainer(victim));
+ }
break;
}
case PBEMoveFailedPacket mfp:
@@ -642,8 +663,7 @@ string NameForTrainer(PBEPokemon pkmn)
case PBEMovePPChangedPacket mpcp:
{
PBEPokemon moveUser = mpcp.MoveUserTeam.TryGetPokemon(mpcp.MoveUser);
- int change = mpcp.NewValue - mpcp.OldValue;
- Console.WriteLine("{0}'s {1} {3} {2} PP!", NameForTrainer(moveUser), PBELocalizedString.GetMoveName(mpcp.Move).English, Math.Abs(change), change <= 0 ? "lost" : "gained");
+ Console.WriteLine("{0}'s {1} {3} {2} PP!", NameForTrainer(moveUser), PBELocalizedString.GetMoveName(mpcp.Move).English, Math.Abs(mpcp.AmountReduced), mpcp.AmountReduced >= 0 ? "lost" : "gained");
break;
}
case PBEMoveUsedPacket mup:
@@ -743,7 +763,7 @@ string NameForTrainer(PBEPokemon pkmn)
{
if (!psip.Forced)
{
- Console.WriteLine("{1} sent out {0}!", psip.SwitchIns.Select(s => s.Nickname).Andify(), psip.Team.TrainerName);
+ Console.WriteLine("{1} sent out {0}!", psip.SwitchIns.Select(s => s.Nickname).ToArray().Andify(), psip.Team.TrainerName);
}
break;
}
@@ -1196,12 +1216,12 @@ string NameForTrainer(PBEPokemon pkmn)
}
case PBETurnBeganPacket tbp:
{
- Console.WriteLine("Turn {0} is starting. ({1})", tbp.TurnNumber, tbp.Time);
+ Console.WriteLine("Turn {0} is starting.", tbp.TurnNumber);
break;
}
case PBEWinnerPacket win:
{
- Console.WriteLine("{0} defeated {1}!", win.WinningTeam.TrainerName, (win.WinningTeam == battle.Teams[0] ? battle.Teams[1] : battle.Teams[0]).TrainerName);
+ Console.WriteLine("{0} defeated {1}!", win.WinningTeam.TrainerName, win.WinningTeam.OpposingTeam.TrainerName);
break;
}
}
diff --git a/PokemonBattleEngine/Battle/BattleMoveset.cs b/PokemonBattleEngine/Battle/BattleMoveset.cs
new file mode 100644
index 000000000..8d3791b12
--- /dev/null
+++ b/PokemonBattleEngine/Battle/BattleMoveset.cs
@@ -0,0 +1,282 @@
+using Kermalis.PokemonBattleEngine.Data;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+
+namespace Kermalis.PokemonBattleEngine.Battle
+{
+ public sealed class PBEBattleMoveset : IReadOnlyList
+ {
+ public sealed class PBEBattleMovesetSlot : INotifyPropertyChanged
+ {
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private PBEMove _move;
+ public PBEMove Move
+ {
+ get => _move;
+ set
+ {
+ if (_move != value)
+ {
+ _move = value;
+ OnPropertyChanged(nameof(Move));
+ }
+ }
+ }
+ private int _pp;
+ public int PP
+ {
+ get => _pp;
+ set
+ {
+ if (_pp != value)
+ {
+ _pp = value;
+ OnPropertyChanged(nameof(PP));
+ }
+ }
+ }
+ private int _maxPP;
+ public int MaxPP
+ {
+ get => _maxPP;
+ set
+ {
+ if (_maxPP != value)
+ {
+ _maxPP = value;
+ OnPropertyChanged(nameof(MaxPP));
+ }
+ }
+ }
+
+ internal PBEBattleMovesetSlot()
+ {
+ _move = PBEMove.MAX;
+ }
+ internal PBEBattleMovesetSlot(PBEMove move, int pp, int maxPP)
+ {
+ _move = move;
+ _pp = pp;
+ _maxPP = maxPP;
+ }
+ }
+
+ private readonly PBEBattleMovesetSlot[] _list;
+ public int Count => _list.Length;
+ public PBEBattleMovesetSlot this[int index]
+ {
+ get
+ {
+ if (index >= _list.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ return _list[index];
+ }
+ }
+ public PBEBattleMovesetSlot this[PBEMove move]
+ {
+ get
+ {
+ for (int i = 0; i < _list.Length; i++)
+ {
+ PBEBattleMovesetSlot slot = _list[i];
+ if (slot.Move == move)
+ {
+ return slot;
+ }
+ }
+ return null;
+ }
+ }
+
+ internal PBEBattleMoveset(PBESettings settings)
+ {
+ _list = new PBEBattleMovesetSlot[settings.NumMoves];
+ for (int i = 0; i < _list.Length; i++)
+ {
+ _list[i] = new PBEBattleMovesetSlot();
+ }
+ }
+ internal PBEBattleMoveset(PBEMoveset moveset)
+ {
+ _list = new PBEBattleMovesetSlot[moveset.Settings.NumMoves];
+ for (int i = 0; i < _list.Length; i++)
+ {
+ PBEMoveset.PBEMovesetSlot slot = moveset[i];
+ PBEMove move = slot.Move;
+ int pp;
+ if (move != PBEMove.None)
+ {
+ byte tier = PBEMoveData.Data[move].PPTier;
+ pp = Math.Max(1, (tier * moveset.Settings.PPMultiplier) + (tier * slot.PPUps));
+ }
+ else
+ {
+ pp = 0;
+ }
+ _list[i] = new PBEBattleMovesetSlot(move, pp, pp);
+ }
+ }
+ internal PBEBattleMoveset(PBEBattleMoveset other)
+ {
+ _list = new PBEBattleMovesetSlot[other._list.Length];
+ for (int i = 0; i < _list.Length; i++)
+ {
+ PBEBattleMovesetSlot oSlot = other._list[i];
+ _list[i] = new PBEBattleMovesetSlot(oSlot.Move, oSlot.PP, oSlot.MaxPP);
+ }
+ }
+
+ public static int GetTransformPP(PBESettings settings, PBEMove move)
+ {
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ else if (move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
+ {
+ throw new ArgumentOutOfRangeException(nameof(move));
+ }
+ else if (move == PBEMove.None)
+ {
+ return 0;
+ }
+ else
+ {
+ return PBEMoveData.Data[move].PPTier == 0 ? 1 : settings.PPMultiplier;
+ }
+ }
+ public static int GetNonTransformPP(PBESettings settings, PBEMove move, byte ppUps)
+ {
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ else if (move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
+ {
+ throw new ArgumentOutOfRangeException(nameof(move));
+ }
+ else if (move == PBEMove.None)
+ {
+ return 0;
+ }
+ else
+ {
+ byte tier = PBEMoveData.Data[move].PPTier;
+ return Math.Max(1, (tier * settings.PPMultiplier) + (tier * ppUps));
+ }
+ }
+
+ internal static void DoTransform(PBEPokemon user, PBEPokemon target)
+ {
+ PBEBattleMoveset targetKnownBackup = null;
+ if (user.Team != target.Team)
+ {
+ targetKnownBackup = new PBEBattleMoveset(target.KnownMoves);
+ }
+ PBESettings settings = user.Team.Battle.Settings;
+ for (int i = 0; i < settings.NumMoves; i++)
+ {
+ PBEBattleMovesetSlot userMove = user.Moves._list[i];
+ PBEBattleMovesetSlot userKnownMove = user.KnownMoves._list[i];
+ PBEBattleMovesetSlot targetMove = target.Moves._list[i];
+ PBEBattleMovesetSlot targetKnownMove = target.KnownMoves._list[i];
+ PBEMove move;
+ int pp;
+ if (user.Team == target.Team)
+ {
+ move = targetMove.Move;
+ pp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);
+ userMove.Move = move;
+ userMove.PP = pp;
+ userMove.MaxPP = pp;
+
+ move = targetKnownMove.Move;
+ pp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);
+ userKnownMove.Move = move;
+ userKnownMove.PP = 0;
+ userKnownMove.MaxPP = pp;
+ }
+ else
+ {
+ move = targetMove.Move;
+ pp = move == PBEMove.MAX ? 0 : GetTransformPP(settings, move);
+ userMove.Move = move;
+ userMove.PP = pp;
+ userMove.MaxPP = pp;
+ targetKnownMove.Move = move;
+ PBEBattleMovesetSlot bSlot = targetKnownBackup[move];
+ if (bSlot == null)
+ {
+ targetKnownMove.PP = 0;
+ targetKnownMove.MaxPP = 0;
+ }
+ else
+ {
+ targetKnownMove.PP = bSlot.PP;
+ targetKnownMove.MaxPP = bSlot.MaxPP;
+ }
+ userKnownMove.Move = move;
+ userKnownMove.PP = 0;
+ userKnownMove.MaxPP = pp;
+ }
+ }
+ }
+ internal ReadOnlyCollection ForTransformPacket()
+ {
+ var moves = new PBEMove[_list.Length];
+ for (int i = 0; i < _list.Length; i++)
+ {
+ moves[i] = _list[i].Move;
+ }
+ return new ReadOnlyCollection(moves);
+ }
+ internal void Reset(PBEBattleMoveset other)
+ {
+ for (int i = 0; i < _list.Length; i++)
+ {
+ PBEBattleMovesetSlot slot = _list[i];
+ PBEBattleMovesetSlot oSlot = other._list[i];
+ slot.Move = oSlot.Move;
+ slot.PP = oSlot.PP;
+ slot.MaxPP = oSlot.MaxPP;
+ }
+ }
+ internal void SetUnknown()
+ {
+ for (int i = 0; i < _list.Length; i++)
+ {
+ PBEBattleMovesetSlot slot = _list[i];
+ slot.Move = PBEMove.MAX;
+ slot.PP = 0;
+ slot.MaxPP = 0;
+ }
+ }
+
+ public bool Contains(PBEMove move)
+ {
+ return this[move] != null;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < _list.Length; i++)
+ {
+ yield return _list[i];
+ }
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/PokemonBattleEngine/Battle/BattleReplay.cs b/PokemonBattleEngine/Battle/BattleReplay.cs
index d2798f876..bb3726026 100644
--- a/PokemonBattleEngine/Battle/BattleReplay.cs
+++ b/PokemonBattleEngine/Battle/BattleReplay.cs
@@ -15,11 +15,23 @@ public sealed partial class PBEBattle
public void SaveReplay()
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
// "12-30-2020 11-59-59 PM - Team 1 vs Team 2.pbereplay"
SaveReplay(PBEUtils.ToSafeFileName(new string(string.Format("{0} - {1} vs {2}", DateTime.Now.ToLocalTime(), Teams[0].TrainerName, Teams[1].TrainerName).Take(200).ToArray())) + ".pbereplay");
}
public void SaveReplay(string path)
{
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
if (BattleState != PBEBattleState.Ended)
{
throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.Ended} to save a replay.");
@@ -28,12 +40,9 @@ public void SaveReplay(string path)
var bytes = new List();
bytes.AddRange(BitConverter.GetBytes(CurrentReplayVersion));
- bytes.AddRange(Settings.ToBytes());
+ Settings.ToBytes(bytes);
bytes.Add((byte)BattleFormat);
- bytes.AddRange(Teams[0].ToBytes());
- bytes.AddRange(Teams[1].ToBytes());
-
bytes.AddRange(BitConverter.GetBytes(Events.Count));
for (int i = 0; i < Events.Count; i++)
{
@@ -76,20 +85,20 @@ private PBEBattle(BinaryReader r)
Settings = new PBESettings(r);
Settings.MakeReadOnly();
BattleFormat = (PBEBattleFormat)r.ReadByte();
-
- Teams[0] = new PBETeam(this, 0);
- Teams[0].FromBytes(r);
- Teams[1] = new PBETeam(this, 1);
- Teams[1].FromBytes(r);
+ Teams = new PBETeams(this);
var packetProcessor = new PBEPacketProcessor(this);
int numEvents = r.ReadInt32();
for (int i = 0; i < numEvents; i++)
{
INetPacket packet = packetProcessor.CreatePacket(r.ReadBytes(r.ReadInt16()));
- if (packet is PBEWinnerPacket wp)
+ switch (packet)
{
- Winner = wp.WinningTeam;
+ case PBEWinnerPacket wp:
+ {
+ Winner = wp.WinningTeam;
+ break;
+ }
}
Events.Add(packet);
}
diff --git a/PokemonBattleEngine/Battle/BattleTargets.cs b/PokemonBattleEngine/Battle/BattleTargets.cs
index fc0512034..cf56303fc 100644
--- a/PokemonBattleEngine/Battle/BattleTargets.cs
+++ b/PokemonBattleEngine/Battle/BattleTargets.cs
@@ -7,9 +7,7 @@ namespace Kermalis.PokemonBattleEngine.Battle
{
public sealed partial class PBEBattle
{
- ///
- /// Gets the position across from the inputted position for a specific battle format.
- ///
+ /// Gets the position across from the inputted position for a specific battle format.
/// The battle format.
/// The position.
/// Thrown when is invalid or is invalid for .
@@ -63,21 +61,27 @@ public static PBEFieldPosition GetPositionAcross(PBEBattleFormat battleFormat, P
throw new ArgumentOutOfRangeException(nameof(position));
}
}
- default: throw new ArgumentOutOfRangeException(nameof(BattleFormat));
+ default: throw new ArgumentOutOfRangeException(nameof(battleFormat));
}
}
- ///
- /// Gets the Pokémon surrounding .
- ///
+ /// Gets the Pokémon surrounding .
/// The Pokémon to check.
/// True if allies should be included, False otherwise.
/// True if foes should be included, False otherwise.
/// Thrown when 's 's is invalid or 's is invalid for 's 's .
- public static IEnumerable GetRuntimeSurrounding(PBEPokemon pkmn, bool includeAllies, bool includeFoes)
+ public static List GetRuntimeSurrounding(PBEPokemon pkmn, bool includeAllies, bool includeFoes)
{
+ if (pkmn == null)
+ {
+ throw new ArgumentNullException(nameof(pkmn));
+ }
+ if (!includeAllies && !includeFoes)
+ {
+ throw new ArgumentException($"\"{nameof(includeAllies)}\" and \"{nameof(includeFoes)}\" were false.");
+ }
PBEPokemon[] allies = pkmn.Team.ActiveBattlers.Where(p => p != pkmn).ToArray();
- PBEPokemon[] foes = (pkmn.Team == pkmn.Team.Battle.Teams[0] ? pkmn.Team.Battle.Teams[1] : pkmn.Team.Battle.Teams[0]).ActiveBattlers;
+ PBEPokemon[] foes = pkmn.Team.OpposingTeam.ActiveBattlers;
switch (pkmn.Team.Battle.BattleFormat)
{
case PBEBattleFormat.Single:
@@ -180,53 +184,49 @@ public static IEnumerable GetRuntimeSurrounding(PBEPokemon pkmn, boo
}
}
- ///
- /// Gets all Pokémon that will be hit.
- ///
+ /// Gets all Pokémon that will be hit.
/// The Pokémon that will act.
/// The targets the Pokémon wishes to hit.
/// Whether the move can hit far Pokémon in a triple battle.
- private static PBEPokemon[] GetRuntimeTargets(PBEPokemon user, PBETarget requestedTargets, bool canHitFarCorners)
+ private static PBEPokemon[] GetRuntimeTargets(PBEPokemon user, PBETurnTarget requestedTargets, bool canHitFarCorners)
{
- PBETeam opposingTeam = user.Team == user.Team.Battle.Teams[0] ? user.Team.Battle.Teams[1] : user.Team.Battle.Teams[0];
-
var targets = new List();
- if (requestedTargets.HasFlag(PBETarget.AllyLeft))
+ if (requestedTargets.HasFlag(PBETurnTarget.AllyLeft))
{
targets.Add(user.Team.TryGetPokemon(PBEFieldPosition.Left));
}
- if (requestedTargets.HasFlag(PBETarget.AllyCenter))
+ if (requestedTargets.HasFlag(PBETurnTarget.AllyCenter))
{
targets.Add(user.Team.TryGetPokemon(PBEFieldPosition.Center));
}
- if (requestedTargets.HasFlag(PBETarget.AllyRight))
+ if (requestedTargets.HasFlag(PBETurnTarget.AllyRight))
{
targets.Add(user.Team.TryGetPokemon(PBEFieldPosition.Right));
}
- if (requestedTargets.HasFlag(PBETarget.FoeLeft))
+ if (requestedTargets.HasFlag(PBETurnTarget.FoeLeft))
{
- PBEPokemon pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Left);
+ PBEPokemon pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left);
if (pkmn == null)
{
if (user.Team.Battle.BattleFormat == PBEBattleFormat.Double)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
}
else if (user.Team.Battle.BattleFormat == PBEBattleFormat.Triple)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Center);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center);
// Center fainted as well and user can reach far right
if (pkmn == null && (user.FieldPosition != PBEFieldPosition.Right || canHitFarCorners))
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
}
}
}
targets.Add(pkmn);
}
- if (requestedTargets.HasFlag(PBETarget.FoeCenter))
+ if (requestedTargets.HasFlag(PBETurnTarget.FoeCenter))
{
- PBEPokemon pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Center);
+ PBEPokemon pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center);
// Target fainted, fallback to its teammate
if (pkmn == null)
{
@@ -234,26 +234,26 @@ private static PBEPokemon[] GetRuntimeTargets(PBEPokemon user, PBETarget request
{
if (user.FieldPosition == PBEFieldPosition.Left)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
// Right fainted as well and user can reach far left
if (pkmn == null && (user.FieldPosition != PBEFieldPosition.Left || canHitFarCorners))
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Left);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left);
}
}
else if (user.FieldPosition == PBEFieldPosition.Right)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Left);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left);
// Left fainted as well and user can reach far right
if (pkmn == null && (user.FieldPosition != PBEFieldPosition.Right || canHitFarCorners))
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
}
}
else // Center
{
- PBEPokemon oppLeft = opposingTeam.TryGetPokemon(PBEFieldPosition.Left),
- oppRight = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ PBEPokemon oppLeft = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left),
+ oppRight = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
// Left is dead but not right
if (oppLeft == null && oppRight != null)
{
@@ -274,23 +274,23 @@ private static PBEPokemon[] GetRuntimeTargets(PBEPokemon user, PBETarget request
}
targets.Add(pkmn);
}
- if (requestedTargets.HasFlag(PBETarget.FoeRight))
+ if (requestedTargets.HasFlag(PBETurnTarget.FoeRight))
{
- PBEPokemon pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Right);
+ PBEPokemon pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right);
// Target fainted, fallback to its teammate
if (pkmn == null)
{
if (user.Team.Battle.BattleFormat == PBEBattleFormat.Double)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Left);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left);
}
else if (user.Team.Battle.BattleFormat == PBEBattleFormat.Triple)
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Center);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center);
// Center fainted as well and user can reach far left
if (pkmn == null && (user.FieldPosition != PBEFieldPosition.Left || canHitFarCorners))
{
- pkmn = opposingTeam.TryGetPokemon(PBEFieldPosition.Left);
+ pkmn = user.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left);
}
}
}
@@ -299,16 +299,18 @@ private static PBEPokemon[] GetRuntimeTargets(PBEPokemon user, PBETarget request
return targets.Where(p => p != null).Distinct().ToArray(); // Remove duplicate targets
}
- ///
- /// Determines whether chosen targets are valid for a given move.
- ///
+ /// Determines whether chosen targets are valid for a given move.
/// The Pokémon that will act.
/// The move the Pokémon wishes to use.
/// The targets bitfield to validate.
/// Thrown when , , 's , or 's 's is invalid.
- public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targets)
+ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETurnTarget targets)
{
- if (move == PBEMove.None || move >= PBEMove.MAX)
+ if (pkmn == null)
+ {
+ throw new ArgumentNullException(nameof(pkmn));
+ }
+ if (move == PBEMove.None || move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
{
throw new ArgumentOutOfRangeException(nameof(move));
}
@@ -323,7 +325,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == (PBETarget.AllyCenter | PBETarget.FoeCenter);
+ return targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter);
}
else
{
@@ -339,7 +341,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.FoeCenter;
+ return targets == PBETurnTarget.FoeCenter;
}
else
{
@@ -354,7 +356,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyCenter;
+ return targets == PBETurnTarget.AllyCenter;
}
else
{
@@ -372,7 +374,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);
}
else
{
@@ -384,7 +386,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.FoeLeft | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);
}
else
{
@@ -395,7 +397,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyLeft | PBETarget.AllyRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight);
}
else
{
@@ -406,11 +408,11 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == (PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyLeft | PBETarget.FoeLeft | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight);
}
else
{
@@ -422,11 +424,11 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyLeft;
+ return targets == PBETurnTarget.AllyLeft;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyRight;
}
else
{
@@ -437,7 +439,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight;
}
else
{
@@ -448,11 +450,11 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyLeft;
+ return targets == PBETurnTarget.AllyLeft;
}
else
{
@@ -463,7 +465,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.FoeLeft || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;
}
else
{
@@ -475,11 +477,11 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyRight || targets == PBETarget.FoeLeft || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.FoeLeft || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeRight;
}
else
{
@@ -497,7 +499,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else
{
@@ -508,7 +510,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else
{
@@ -519,15 +521,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == (PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == (PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.FoeLeft | PBETarget.FoeCenter);
+ return targets == (PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter);
}
else
{
@@ -538,15 +540,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == (PBETarget.AllyCenter | PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == (PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight);
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyCenter | PBETarget.FoeLeft | PBETarget.FoeCenter);
+ return targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter);
}
else
{
@@ -557,7 +559,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight);
+ return targets == (PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight);
}
else
{
@@ -569,15 +571,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyLeft;
+ return targets == PBETurnTarget.AllyLeft;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyCenter;
+ return targets == PBETurnTarget.AllyCenter;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyRight;
}
else
{
@@ -588,15 +590,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyCenter;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyCenter || targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyCenter || targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight;
}
else
{
@@ -607,11 +609,11 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyCenter;
+ return targets == PBETurnTarget.AllyCenter;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight;
}
else
{
@@ -622,15 +624,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter;
+ return targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter;
}
else
{
@@ -641,15 +643,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyCenter || targets == PBETarget.AllyRight || targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyRight || targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyCenter || targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else
{
@@ -660,15 +662,15 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return targets == PBETarget.AllyCenter || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return targets == PBETarget.AllyLeft || targets == PBETarget.AllyRight || targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter || targets == PBETarget.FoeRight;
+ return targets == PBETurnTarget.AllyLeft || targets == PBETurnTarget.AllyRight || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter || targets == PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyCenter || targets == PBETarget.FoeLeft || targets == PBETarget.FoeCenter;
+ return targets == PBETurnTarget.AllyCenter || targets == PBETurnTarget.FoeLeft || targets == PBETurnTarget.FoeCenter;
}
else
{
@@ -686,7 +688,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == (PBETarget.AllyCenter | PBETarget.FoeCenter);
+ return targets == (PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter);
}
else
{
@@ -702,7 +704,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.FoeCenter;
+ return targets == PBETurnTarget.FoeCenter;
}
else
{
@@ -717,7 +719,7 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return targets == PBETarget.AllyCenter;
+ return targets == PBETurnTarget.AllyCenter;
}
else
{
@@ -731,14 +733,20 @@ public static bool AreTargetsValid(PBEPokemon pkmn, PBEMove move, PBETarget targ
}
}
- ///
- /// Gets a random target a move can hit when called by .
- ///
+ /// Gets a random target a move can hit when called by .
/// The Pokémon using .
/// The move being called.
/// Thrown when , 's , or 's 's is invalid.
- public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove calledMove)
+ public static PBETurnTarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove calledMove)
{
+ if (pkmn == null)
+ {
+ throw new ArgumentNullException(nameof(pkmn));
+ }
+ if (calledMove == PBEMove.None || calledMove >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), calledMove))
+ {
+ throw new ArgumentOutOfRangeException(nameof(calledMove));
+ }
PBEMoveTarget possibleTargets = pkmn.GetMoveTargets(calledMove);
switch (pkmn.Team.Battle.BattleFormat)
{
@@ -750,7 +758,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;
}
else
{
@@ -767,7 +775,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
@@ -781,7 +789,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
@@ -799,7 +807,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -811,7 +819,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -822,7 +830,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight;
}
else
{
@@ -833,11 +841,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.FoeLeft | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.FoeLeft | PBETurnTarget.FoeRight;
}
else
{
@@ -848,11 +856,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
else
{
@@ -865,11 +873,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (PBEUtils.RandomBool())
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
}
else
@@ -881,11 +889,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else
{
@@ -901,11 +909,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (PBEUtils.RandomBool())
{
- return PBETarget.FoeLeft;
+ return PBETurnTarget.FoeLeft;
}
else
{
- return PBETarget.FoeRight;
+ return PBETurnTarget.FoeRight;
}
}
else
@@ -924,7 +932,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else
{
@@ -935,7 +943,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else
{
@@ -946,15 +954,15 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeLeft | PBETarget.FoeCenter;
+ return PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;
}
else
{
@@ -965,15 +973,15 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyLeft | PBETarget.AllyRight | PBETarget.FoeLeft | PBETarget.FoeCenter | PBETarget.FoeRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyRight | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter | PBETurnTarget.FoeRight;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter | PBETarget.FoeLeft | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeLeft | PBETurnTarget.FoeCenter;
}
else
{
@@ -984,7 +992,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyLeft | PBETarget.AllyCenter | PBETarget.AllyRight;
+ return PBETurnTarget.AllyLeft | PBETurnTarget.AllyCenter | PBETurnTarget.AllyRight;
}
else
{
@@ -995,15 +1003,15 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
else
{
@@ -1016,11 +1024,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (PBEUtils.RandomBool())
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
@@ -1028,26 +1036,26 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
int val = PBEUtils.RandomInt(0, 2);
if (val == 0)
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else if (val == 1)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
if (PBEUtils.RandomBool())
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
}
else
@@ -1059,22 +1067,22 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
{
if (PBEUtils.RandomBool())
{
- return PBETarget.AllyLeft;
+ return PBETurnTarget.AllyLeft;
}
else
{
- return PBETarget.AllyRight;
+ return PBETurnTarget.AllyRight;
}
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
@@ -1089,11 +1097,11 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (PBEUtils.RandomBool())
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
- return PBETarget.FoeRight;
+ return PBETurnTarget.FoeRight;
}
}
else if (pkmn.FieldPosition == PBEFieldPosition.Center)
@@ -1101,26 +1109,26 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
int val = PBEUtils.RandomInt(0, 2);
if (val == 0)
{
- return PBETarget.FoeLeft;
+ return PBETurnTarget.FoeLeft;
}
else if (val == 1)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
- return PBETarget.FoeRight;
+ return PBETurnTarget.FoeRight;
}
}
else if (pkmn.FieldPosition == PBEFieldPosition.Right)
{
if (PBEUtils.RandomBool())
{
- return PBETarget.FoeLeft;
+ return PBETurnTarget.FoeLeft;
}
else
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
}
else
@@ -1135,15 +1143,15 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
int val = PBEUtils.RandomInt(0, 2);
if (val == 0)
{
- return PBETarget.FoeLeft;
+ return PBETurnTarget.FoeLeft;
}
else if (val == 1)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
- return PBETarget.FoeRight;
+ return PBETurnTarget.FoeRight;
}
}
else
@@ -1162,7 +1170,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter | PBETarget.FoeCenter;
+ return PBETurnTarget.AllyCenter | PBETurnTarget.FoeCenter;
}
else
{
@@ -1179,7 +1187,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.FoeCenter;
+ return PBETurnTarget.FoeCenter;
}
else
{
@@ -1193,7 +1201,7 @@ public static PBETarget GetRandomTargetForMetronome(PBEPokemon pkmn, PBEMove cal
{
if (pkmn.FieldPosition == PBEFieldPosition.Left || pkmn.FieldPosition == PBEFieldPosition.Center || pkmn.FieldPosition == PBEFieldPosition.Right)
{
- return PBETarget.AllyCenter;
+ return PBETurnTarget.AllyCenter;
}
else
{
diff --git a/PokemonBattleEngine/Battle/ExecutedMove.cs b/PokemonBattleEngine/Battle/ExecutedMove.cs
index 78c4a75fc..47751736c 100644
--- a/PokemonBattleEngine/Battle/ExecutedMove.cs
+++ b/PokemonBattleEngine/Battle/ExecutedMove.cs
@@ -16,6 +16,8 @@ public sealed class PBETargetSuccess
public bool CriticalHit { get; set; }
public PBEFailReason FailReason { get; set; }
public bool Missed { get; set; }
+
+ internal PBETargetSuccess() { }
}
public ushort TurnNumber { get; }
@@ -23,7 +25,7 @@ public sealed class PBETargetSuccess
public PBEFailReason FailReason { get; }
public ReadOnlyCollection Targets { get; }
- public PBEExecutedMove(ushort turnNumber, PBEMove move, PBEFailReason failReason, IList targets)
+ internal PBEExecutedMove(ushort turnNumber, PBEMove move, PBEFailReason failReason, IList targets)
{
TurnNumber = turnNumber;
Move = move;
diff --git a/PokemonBattleEngine/Battle/Pokemon.cs b/PokemonBattleEngine/Battle/Pokemon.cs
index 05102745f..7eb537fdd 100644
--- a/PokemonBattleEngine/Battle/Pokemon.cs
+++ b/PokemonBattleEngine/Battle/Pokemon.cs
@@ -17,73 +17,47 @@ public sealed class PBEPokemon
/// The Pokémon's ID in its battle.
public byte Id { get; }
- ///
- /// The Pokémon's nickname with its gender attached.
- ///
+ /// The Pokémon's with its attached.
public string NameWithGender => Nickname + GenderSymbol;
- ///
- /// The Pokémon's gender as a string.
- ///
+ /// The Pokémon's as a .
public string GenderSymbol => Gender == PBEGender.Female ? "♀" : Gender == PBEGender.Male ? "♂" : string.Empty;
- ///
- /// The Pokémon's known nickname with its known gender attached.
- ///
+ /// The Pokémon's with its attached.
public string KnownNameWithKnownGender => KnownNickname + KnownGenderSymbol;
- ///
- /// The Pokémon's known gender as a string.
- ///
+ /// The Pokémon's as a .
public string KnownGenderSymbol => KnownGender == PBEGender.Female ? "♀" : KnownGender == PBEGender.Male ? "♂" : string.Empty;
- ///
- /// The Pokémon's current HP.
- ///
+ /// The Pokémon's current HP.
public ushort HP { get; set; }
- ///
- /// The Pokémon's maximum HP.
- ///
+ /// The Pokémon's maximum HP.
public ushort MaxHP { get; set; }
- ///
- /// The Pokémon's current HP as a percentage.
- ///
+ /// The Pokémon's current HP as a percentage.
public double HPPercentage { get; set; }
- ///
- /// The Pokémon's attack stat.
- ///
+ /// The Pokémon's attack stat.
public ushort Attack { get; set; }
public sbyte AttackChange { get; set; }
- ///
- /// The Pokémon's defense stat.
- ///
+ /// The Pokémon's defense stat.
public ushort Defense { get; set; }
public sbyte DefenseChange { get; set; }
- ///
- /// The Pokémon's special attack stat.
- ///
+ /// The Pokémon's special attack stat.
public ushort SpAttack { get; set; }
public sbyte SpAttackChange { get; set; }
- ///
- /// The Pokémon's special defense stat.
- ///
+ /// The Pokémon's special defense stat.
public ushort SpDefense { get; set; }
public sbyte SpDefenseChange { get; set; }
- ///
- /// The Pokémon's speed stat.
- ///
+ /// The Pokémon's speed stat.
public ushort Speed { get; set; }
public sbyte SpeedChange { get; set; }
public sbyte AccuracyChange { get; set; }
public sbyte EvasionChange { get; set; }
- public PBEEffortValueCollection EffortValues { get; set; }
- public PBEIndividualValueCollection IndividualValues { get; set; }
+ public PBEEffortValues EffortValues { get; }
+ public PBEIndividualValues IndividualValues { get; }
public byte Friendship { get; set; }
/// The Pokémon's level.
public byte Level { get; set; }
/// The Pokémon's nature.
public PBENature Nature { get; set; }
- /// The moves the Pokémon had upon entering battle.
- public PBEMove[] OriginalMoves { get; set; }
- /// The PP-Ups the Pokémon had upon entering battle.
- public byte[] OriginalPPUps { get; set; }
+ /// The moveset the Pokémon had upon entering battle.
+ public PBEMoveset OriginalMoveset { get; set; }
/// The Pokémon's field position.
public PBEFieldPosition FieldPosition { get; set; }
@@ -104,11 +78,9 @@ public sealed class PBEPokemon
/// The item the Pokémon is known to have.
public PBEItem KnownItem { get; set; }
/// The moves the Pokémon currently has.
- public PBEMove[] Moves { get; set; }
+ public PBEBattleMoveset Moves { get; }
/// The moves the Pokémon is known to have.
- public PBEMove[] KnownMoves { get; set; }
- public byte[] PP { get; set; }
- public byte[] MaxPP { get; set; }
+ public PBEBattleMoveset KnownMoves { get; }
/// The nickname the Pokémon normally has.
public string Nickname { get; set; }
/// The nickname the Pokémon is known to have.
@@ -137,79 +109,49 @@ public sealed class PBEPokemon
public double KnownWeight { get; set; }
#region Statuses
- ///
- /// The counter used for and .
- ///
+ /// The counter used for and .
public byte Status1Counter { get; set; }
- ///
- /// The amount of turns the Pokémon will sleep for before waking.
- ///
+ /// The amount of turns the Pokémon will sleep for before waking.
public byte SleepTurns { get; set; }
- ///
- /// The counter used for .
- ///
+ /// The counter used for .
public byte ConfusionCounter { get; set; }
- ///
- /// The amount of turns the Pokémon will be confused for before snapping out of it.
- ///
+ /// The amount of turns the Pokémon will be confused for before snapping out of it.
public byte ConfusionTurns { get; set; }
- ///
- /// The Pokémon that is disguised as.
- ///
+ /// The Pokémon that is disguised as.
public PBEPokemon DisguisedAsPokemon { get; set; }
- ///
- /// The Pokémon that is bound to.
- ///
+ /// The Pokémon that is bound to.
public PBEPokemon InfatuatedWithPokemon { get; set; }
- ///
- /// The position to return HP to on .
- ///
+ /// The position to return HP to on .
public PBEFieldPosition SeededPosition { get; set; }
- ///
- /// The team responsible for .
- ///
+ /// The team responsible for .
public PBETeam SeededTeam { get; set; }
- ///
- /// The amount of HP the Pokémon's has left.
- ///
+ /// The amount of HP the Pokémon's has left.
public ushort SubstituteHP { get; set; }
- public PBEMove[] PreTransformMoves { get; set; }
- public byte[] PreTransformPP { get; set; }
- public byte[] PreTransformMaxPP { get; set; }
+ public PBEBattleMoveset TransformBackupMoves { get; }
#endregion
#region Actions
- public PBEAction SelectedAction; // Must be a field
+ public PBETurnAction TurnAction { get; set; }
public List ExecutedMoves { get; } = new List();
- ///
- /// The move the Pokémon is forced to use by multi-turn moves.
- ///
+ /// The move the Pokémon is forced to use by multi-turn moves.
public PBEMove TempLockedMove { get; set; }
- ///
- /// The targets the Pokémon is forced to target by multi-turn moves.
- ///
- public PBETarget TempLockedTargets { get; set; }
- ///
- /// The move the Pokémon is forced to use by its choice item.
- ///
+ /// The targets the Pokémon is forced to target by multi-turn moves.
+ public PBETurnTarget TempLockedTargets { get; set; }
+ /// The move the Pokémon is forced to use by its choice item.
public PBEMove ChoiceLockedMove { get; set; }
#endregion
#region Special Flags
- ///
- /// True if the Pokémon was originally but was , therefore forcing it to remain as when switching out.
- ///
+ /// True if the Pokémon was originally but was , therefore forcing it to remain as when switching out.
public bool Shaymin_CannotChangeBackToSkyForm { get; set; }
- ///
- /// True if the Pokémon was present at the start of the turn, which would allow to activate.
- ///
+ /// True if the Pokémon was present at the start of the turn, which would allow to activate.
public bool SpeedBoost_AbleToSpeedBoostThisTurn { get; set; }
#endregion
internal PBEPokemon(BinaryReader r, PBETeam team)
{
Team = team;
- SelectedAction.PokemonId = Id = r.ReadByte();
+ Id = r.ReadByte();
Species = OriginalSpecies = KnownSpecies = (PBESpecies)r.ReadUInt32();
var pData = PBEPokemonData.GetData(Species);
KnownType1 = Type1 = pData.Type1;
@@ -225,42 +167,21 @@ internal PBEPokemon(BinaryReader r, PBETeam team)
Gender = KnownGender = (PBEGender)r.ReadByte();
Item = OriginalItem = (PBEItem)r.ReadUInt16();
KnownItem = (PBEItem)ushort.MaxValue;
- EffortValues = new PBEEffortValueCollection(team.Battle.Settings, r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte());
- IndividualValues = new PBEIndividualValueCollection(team.Battle.Settings, r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte(), r.ReadByte());
+ EffortValues = new PBEEffortValues(Team.Battle.Settings, r);
+ IndividualValues = new PBEIndividualValues(Team.Battle.Settings, r);
SetStats();
HP = MaxHP;
HPPercentage = 1D;
- OriginalMoves = new PBEMove[team.Battle.Settings.NumMoves];
- OriginalPPUps = new byte[team.Battle.Settings.NumMoves];
- for (int i = 0; i < team.Battle.Settings.NumMoves; i++)
- {
- OriginalMoves[i] = (PBEMove)r.ReadUInt16();
- OriginalPPUps[i] = r.ReadByte();
- }
- Moves = (PBEMove[])OriginalMoves.Clone();
- KnownMoves = new PBEMove[Team.Battle.Settings.NumMoves];
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
- {
- KnownMoves[i] = PBEMove.MAX;
- }
- PP = new byte[team.Battle.Settings.NumMoves];
- MaxPP = new byte[team.Battle.Settings.NumMoves];
- for (int i = 0; i < team.Battle.Settings.NumMoves; i++)
- {
- PBEMove move = Moves[i];
- if (move != PBEMove.None)
- {
- byte tier = PBEMoveData.Data[move].PPTier;
- PP[i] = MaxPP[i] = (byte)Math.Max(1, (tier * team.Battle.Settings.PPMultiplier) + (tier * OriginalPPUps[i]));
- }
- }
- team.Party.Add(this);
+ OriginalMoveset = new PBEMoveset(Species, Level, Team.Battle.Settings, r);
+ Moves = new PBEBattleMoveset(OriginalMoveset);
+ KnownMoves = new PBEBattleMoveset(Team.Battle.Settings);
+ TransformBackupMoves = new PBEBattleMoveset(Team.Battle.Settings);
+ Team.Party.Add(this);
}
- // Stats & PP are set from the shell info
internal PBEPokemon(PBETeam team, byte id, PBEPokemonShell shell)
{
Team = team;
- SelectedAction.PokemonId = Id = id;
+ Id = id;
Species = OriginalSpecies = KnownSpecies = shell.Species;
var pData = PBEPokemonData.GetData(Species);
KnownType1 = Type1 = pData.Type1;
@@ -276,35 +197,28 @@ internal PBEPokemon(PBETeam team, byte id, PBEPokemonShell shell)
Gender = KnownGender = shell.Gender;
Item = OriginalItem = shell.Item;
KnownItem = (PBEItem)ushort.MaxValue;
- EffortValues = new PBEEffortValueCollection(team.Battle.Settings, shell.EffortValues);
- IndividualValues = new PBEIndividualValueCollection(team.Battle.Settings, shell.IndividualValues);
+ EffortValues = new PBEEffortValues(shell.EffortValues);
+ IndividualValues = new PBEIndividualValues(shell.IndividualValues);
SetStats();
HP = MaxHP;
HPPercentage = 1D;
- OriginalMoves = shell.Moveset.MoveSlots.Select(m => m.Move).ToArray();
- OriginalPPUps = shell.Moveset.MoveSlots.Select(m => m.PPUps).ToArray();
- Moves = (PBEMove[])OriginalMoves.Clone();
- KnownMoves = new PBEMove[Team.Battle.Settings.NumMoves];
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
- {
- KnownMoves[i] = PBEMove.MAX;
- }
- PP = new byte[team.Battle.Settings.NumMoves];
- MaxPP = new byte[team.Battle.Settings.NumMoves];
- for (int i = 0; i < team.Battle.Settings.NumMoves; i++)
- {
- PBEMove move = Moves[i];
- if (move != PBEMove.None)
- {
- byte tier = PBEMoveData.Data[move].PPTier;
- PP[i] = MaxPP[i] = (byte)Math.Max(1, (tier * team.Battle.Settings.PPMultiplier) + (tier * OriginalPPUps[i]));
- }
- }
+ OriginalMoveset = new PBEMoveset(shell.Moveset);
+ Moves = new PBEBattleMoveset(OriginalMoveset);
+ KnownMoves = new PBEBattleMoveset(Team.Battle.Settings);
+ TransformBackupMoves = new PBEBattleMoveset(Team.Battle.Settings);
team.Party.Add(this);
}
// This constructor is to define a remote Pokémon
public PBEPokemon(PBETeam team, PBEPkmnSwitchInPacket.PBESwitchInInfo info)
{
+ if (team == null)
+ {
+ throw new ArgumentNullException(nameof(team));
+ }
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
Team = team;
Id = info.PokemonId;
FieldPosition = info.FieldPosition;
@@ -316,14 +230,9 @@ public PBEPokemon(PBETeam team, PBEPkmnSwitchInPacket.PBESwitchInInfo info)
KnownAbility = Ability = OriginalAbility = PBEAbility.MAX;
KnownGender = Gender = info.Gender;
KnownItem = Item = OriginalItem = (PBEItem)ushort.MaxValue;
- Moves = new PBEMove[Team.Battle.Settings.NumMoves];
- KnownMoves = new PBEMove[Team.Battle.Settings.NumMoves];
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
- {
- KnownMoves[i] = Moves[i] = PBEMove.MAX;
- }
- PP = new byte[Team.Battle.Settings.NumMoves];
- MaxPP = new byte[Team.Battle.Settings.NumMoves];
+ Moves = new PBEBattleMoveset(Team.Battle.Settings);
+ KnownMoves = new PBEBattleMoveset(Team.Battle.Settings);
+ TransformBackupMoves = new PBEBattleMoveset(Team.Battle.Settings);
KnownNickname = Nickname = info.Nickname;
KnownShiny = Shiny = info.Shiny;
KnownSpecies = Species = OriginalSpecies = info.Species;
@@ -377,10 +286,7 @@ public void ClearForSwitch()
KnownAbility = PBEAbility.MAX;
KnownGender = Gender;
KnownItem = (PBEItem)ushort.MaxValue;
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
- {
- KnownMoves[i] = PBEMove.MAX;
- }
+ KnownMoves.SetUnknown();
KnownNickname = Nickname;
KnownShiny = Shiny;
KnownType1 = Type1 = pData.Type1;
@@ -402,19 +308,15 @@ public void ClearForSwitch()
SeededPosition = PBEFieldPosition.None;
SeededTeam = null;
SubstituteHP = 0;
- if (Status2.HasFlag(PBEStatus2.Transformed))
+ if (Id != byte.MaxValue && Status2.HasFlag(PBEStatus2.Transformed))
{
- Moves = (PBEMove[])PreTransformMoves.Clone();
- PP = (byte[])PreTransformPP.Clone();
- MaxPP = (byte[])PreTransformMaxPP.Clone();
- PreTransformMoves = null;
- PreTransformPP = null;
- PreTransformMaxPP = null;
+ Moves.Reset(TransformBackupMoves);
+ TransformBackupMoves.SetUnknown();
}
Status2 = PBEStatus2.None;
TempLockedMove = ChoiceLockedMove = PBEMove.None;
- TempLockedTargets = PBETarget.None;
+ TempLockedTargets = PBETurnTarget.None;
ExecutedMoves.Clear();
@@ -431,6 +333,10 @@ public void ClearForSwitch()
/// Frees the Pokémon of its .
public void Transform(PBEPokemon target)
{
+ if (target == null)
+ {
+ throw new ArgumentNullException(nameof(target));
+ }
if (Team != target.Team)
{
KnownAbility = target.KnownAbility = Ability = target.Ability;
@@ -464,36 +370,43 @@ public void Transform(PBEPokemon target)
SpeedChange = target.SpeedChange;
AccuracyChange = target.AccuracyChange;
EvasionChange = target.EvasionChange;
- PreTransformMoves = (PBEMove[])Moves.Clone();
- PreTransformPP = (byte[])PP.Clone();
- PreTransformMaxPP = (byte[])MaxPP.Clone();
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
+ TransformBackupMoves.Reset(Moves);
+ PBEBattleMoveset.DoTransform(this, target);
+ if (!Moves.Contains(ChoiceLockedMove))
{
- if (Team != target.Team)
- {
- KnownMoves[i] = target.KnownMoves[i] = Moves[i] = target.Moves[i];
- }
- else
+ ChoiceLockedMove = PBEMove.None;
+ }
+ Status2 |= PBEStatus2.Transformed;
+ }
+ public void UpdateKnownPP(PBEMove move, int amountReduced)
+ {
+ if (move == PBEMove.None || move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
+ {
+ throw new ArgumentOutOfRangeException(nameof(move));
+ }
+ PBEBattleMoveset.PBEBattleMovesetSlot knownSlot = KnownMoves[move];
+ knownSlot.PP += amountReduced;
+ if (knownSlot.MaxPP == 0)
+ {
+ if (Status2.HasFlag(PBEStatus2.Transformed))
{
- Moves[i] = target.Moves[i];
- KnownMoves[i] = target.KnownMoves[i];
+ knownSlot.MaxPP = PBEBattleMoveset.GetTransformPP(Team.Battle.Settings, move);
}
- if (Id != byte.MaxValue) // Don't set PP if this is a client's unknown remote Pokémon
+ else if (Team.Battle.Settings.MaxPPUps == 0 || knownSlot.PP > PBEBattleMoveset.GetNonTransformPP(Team.Battle.Settings, move, (byte)(Team.Battle.Settings.MaxPPUps - 1)))
{
- PP[i] = MaxPP[i] = (byte)(Moves[i] == PBEMove.None ? 0 : PBEMoveData.Data[Moves[i]].PPTier == 0 ? 1 : Team.Battle.Settings.PPMultiplier);
+ knownSlot.MaxPP = PBEBattleMoveset.GetNonTransformPP(Team.Battle.Settings, move, Team.Battle.Settings.MaxPPUps);
}
}
- if (!Moves.Contains(ChoiceLockedMove))
- {
- ChoiceLockedMove = PBEMove.None;
- }
- Status2 |= PBEStatus2.Transformed;
}
/// Returns True if the Pokémon has , False otherwise.
/// The type to check.
public bool HasType(PBEType type)
{
+ if (type >= PBEType.MAX)
+ {
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
return Type1 == type || Type2 == type;
}
public bool HasCancellingAbility()
@@ -562,14 +475,12 @@ public sbyte SetStatChange(PBEStat stat, int value)
default: throw new ArgumentOutOfRangeException(nameof(stat));
}
}
- ///
- /// Gets the type that a move will become when used by this Pokémon.
- ///
+ /// Gets the type that a move will become when used by this Pokémon.
/// The move to check.
/// Thrown when is invalid.
public PBEType GetMoveType(PBEMove move)
{
- if (move == PBEMove.None || move >= PBEMove.MAX)
+ if (move == PBEMove.None || move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
{
throw new ArgumentOutOfRangeException(nameof(move));
}
@@ -640,14 +551,12 @@ public PBEType GetMoveType(PBEMove move)
}
}
}
- ///
- /// Gets the possible targets that a move can target when used by this Pokémon.
- ///
+ /// Gets the possible targets that a move can target when used by this Pokémon.
/// The move to check.
/// Thrown when is invalid.
public PBEMoveTarget GetMoveTargets(PBEMove move)
{
- if (move == PBEMove.None || move >= PBEMove.MAX)
+ if (move == PBEMove.None || move >= PBEMove.MAX || !Enum.IsDefined(typeof(PBEMove), move))
{
throw new ArgumentOutOfRangeException(nameof(move));
}
@@ -667,17 +576,15 @@ public PBEMoveTarget GetMoveTargets(PBEMove move)
return PBEMoveData.Data[move].Targets;
}
}
- ///
- /// Returns True if the Pokémon is only able to use , False otherwise.
- ///
+ /// Returns True if the Pokémon is only able to use , False otherwise.
public bool IsForcedToStruggle()
{
if (TempLockedMove != PBEMove.None) // Temp locked moves deduct PP on the first turn and don't on the second, so having a temp locked move means it is supposed to be used again for the second turn
{
return false;
}
- else if ((ChoiceLockedMove != PBEMove.None && PP[Array.IndexOf(Moves, ChoiceLockedMove)] == 0) // If the choice locked move has 0 pp, it is forced to struggle
- || Array.TrueForAll(PP, p => p == 0) // If all moves have 0 pp, then the user is forced to struggle
+ else if ((ChoiceLockedMove != PBEMove.None && Moves[ChoiceLockedMove].PP == 0) // If the choice locked move has 0 pp, it is forced to struggle
+ || Moves.All(s => s.PP == 0) // If all moves have 0 pp, then the user is forced to struggle
)
{
return true;
@@ -687,9 +594,7 @@ public bool IsForcedToStruggle()
return false;
}
}
- ///
- /// Returns True if the Pokémon can switch out. Does not check if the Pokémon is on the field or if there are available Pokémon to switch into.
- ///
+ /// Returns True if the Pokémon can switch out. Does not check if the Pokémon is on the field or if there are available Pokémon to switch into.
public bool CanSwitchOut()
{
return TempLockedMove == PBEMove.None;
@@ -697,17 +602,29 @@ public bool CanSwitchOut()
// TODO: Make different public versions that use Known*? AIs should not be able to cheat
public bool CanBecomeBurnedBy(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return Status1 == PBEStatus1.None
&& !HasType(PBEType.Fire)
&& !(Ability == PBEAbility.WaterVeil && !other.HasCancellingAbility());
}
public bool CanBecomeConfusedBy(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return !Status2.HasFlag(PBEStatus2.Confused)
&& !(Ability == PBEAbility.OwnTempo && !other.HasCancellingAbility());
}
public bool CanBecomeFrozenBy(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return Status1 == PBEStatus1.None
&& !HasType(PBEType.Ice)
&& !(Ability == PBEAbility.MagmaArmor && !other.HasCancellingAbility());
@@ -715,17 +632,29 @@ public bool CanBecomeFrozenBy(PBEPokemon other)
/// Returns True if the Pokémon can become with .
public bool CanBecomeInfatuatedWith(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return !Status2.HasFlag(PBEStatus2.Infatuated)
&& ((Gender == PBEGender.Male && other.Gender == PBEGender.Female) || (Gender == PBEGender.Female && other.Gender == PBEGender.Male))
&& !(Ability == PBEAbility.Oblivious && !other.HasCancellingAbility());
}
public bool CanBecomeParalyzedBy(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return Status1 == PBEStatus1.None
&& !(Ability == PBEAbility.Limber && !other.HasCancellingAbility());
}
public bool CanBecomePoisonedBy(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return Status1 == PBEStatus1.None
&& !HasType(PBEType.Poison)
&& !HasType(PBEType.Steel)
@@ -733,11 +662,19 @@ public bool CanBecomePoisonedBy(PBEPokemon other)
}
public bool CanFallAsleepFrom(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return Status1 == PBEStatus1.None
&& !(Ability == PBEAbility.Insomnia && !other.HasCancellingAbility());
}
public bool CanFlinchFrom(PBEPokemon other)
{
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
return !Status2.HasFlag(PBEStatus2.Flinching)
&& !(Ability == PBEAbility.InnerFocus && !other.HasCancellingAbility());
}
@@ -761,9 +698,10 @@ public PBEMove[] GetUsableMoves()
{
for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
{
- if (PP[i] > 0)
+ PBEBattleMoveset.PBEBattleMovesetSlot slot = Moves[i];
+ if (slot.PP > 0)
{
- usableMoves.Add(Moves[i]);
+ usableMoves.Add(slot.Move);
}
}
}
@@ -788,12 +726,11 @@ public ushort GetProtectionChance()
return chance;
}
- internal List ToBytes()
+ internal void ToBytes(List bytes)
{
- var bytes = new List();
bytes.Add(Id);
bytes.AddRange(BitConverter.GetBytes((uint)OriginalSpecies));
- bytes.AddRange(PBEUtils.StringToBytes(Nickname));
+ PBEUtils.StringToBytes(bytes, Nickname);
bytes.Add(Level);
bytes.Add(Friendship);
bytes.Add((byte)(Shiny ? 1 : 0));
@@ -801,14 +738,9 @@ internal List ToBytes()
bytes.Add((byte)Nature);
bytes.Add((byte)Gender);
bytes.AddRange(BitConverter.GetBytes((ushort)OriginalItem));
- bytes.AddRange(EffortValues.Select(ev => ev.Value));
- bytes.AddRange(IndividualValues.Select(iv => iv.Value));
- for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
- {
- bytes.AddRange(BitConverter.GetBytes((ushort)OriginalMoves[i]));
- bytes.Add(OriginalPPUps[i]);
- }
- return bytes;
+ EffortValues.ToBytes(bytes);
+ IndividualValues.ToBytes(bytes);
+ OriginalMoveset.ToBytes(bytes);
}
// Will only be accurate for the host
@@ -817,7 +749,18 @@ public override string ToString()
var sb = new StringBuilder();
sb.AppendLine($"{Nickname}/{Species} {GenderSymbol} Lv.{Level}");
sb.AppendLine($"HP: {HP}/{MaxHP} ({HPPercentage:P2})");
- sb.AppendLine($"Types: {PBELocalizedString.GetTypeName(Type1).English}/{PBELocalizedString.GetTypeName(Type2).English}");
+ sb.Append($"Types: {PBELocalizedString.GetTypeName(Type1).English}");
+ if (Type2 != PBEType.None)
+ {
+ sb.Append($"/{PBELocalizedString.GetTypeName(Type2).English}");
+ }
+ sb.AppendLine();
+ sb.Append($"Known types: {PBELocalizedString.GetTypeName(KnownType1).English}");
+ if (KnownType2 != PBEType.None)
+ {
+ sb.Append($"/{PBELocalizedString.GetTypeName(KnownType2).English}");
+ }
+ sb.AppendLine();
sb.AppendLine($"Position: {Team.TrainerName}'s {FieldPosition}");
sb.AppendLine($"Status1: {Status1}");
if (Status1 == PBEStatus1.Asleep)
@@ -888,23 +831,68 @@ public override string ToString()
sb.AppendLine($"Known ability: {(KnownAbility == PBEAbility.MAX ? "???" : PBELocalizedString.GetAbilityName(KnownAbility).English)}");
sb.AppendLine($"Item: {PBELocalizedString.GetItemName(Item).English}");
sb.AppendLine($"Known item: {(KnownItem == (PBEItem)ushort.MaxValue ? "???" : PBELocalizedString.GetItemName(KnownItem).English)}");
- if (Array.IndexOf(Moves, PBEMove.Frustration) != -1 || Array.IndexOf(Moves, PBEMove.Return) != -1)
+ if (Moves.Contains(PBEMove.Frustration) || Moves.Contains(PBEMove.Return))
{
- sb.AppendLine($"Friendship: {Friendship} ({Friendship / (double)byte.MaxValue:P2})");
+ sb.AppendLine($"Friendship: {Friendship} ({Friendship / byte.MaxValue:P2})");
}
- if (Array.IndexOf(Moves, PBEMove.HiddenPower) != -1)
+ if (Moves.Contains(PBEMove.HiddenPower))
{
sb.AppendLine($"{PBELocalizedString.GetMoveName(PBEMove.HiddenPower).English}: {PBELocalizedString.GetTypeName(IndividualValues.HiddenPowerType).English}:{IndividualValues.HiddenPowerBasePower}");
}
- string[] moveStrs = new string[Moves.Length];
- for (int i = 0; i < moveStrs.Length; i++)
+ sb.Append("Moves: ");
+ for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
+ {
+ PBEBattleMoveset.PBEBattleMovesetSlot slot = Moves[i];
+ PBEMove move = slot.Move;
+ if (i > 0)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(PBELocalizedString.GetMoveName(slot.Move).English);
+ if (move != PBEMove.None)
+ {
+ sb.Append($" ({slot.PP}/{slot.MaxPP})");
+ }
+ }
+ sb.AppendLine();
+ sb.Append("Known moves: ");
+ for (int i = 0; i < Team.Battle.Settings.NumMoves; i++)
{
- moveStrs[i] = $"{PBELocalizedString.GetMoveName(Moves[i]).English} {PP[i]}/{MaxPP[i]}";
+ PBEBattleMoveset.PBEBattleMovesetSlot slot = KnownMoves[i];
+ PBEMove move = slot.Move;
+ int pp = slot.PP;
+ int maxPP = slot.MaxPP;
+ if (i > 0)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(move == PBEMove.MAX ? "???" : PBELocalizedString.GetMoveName(move).English);
+ if (move != PBEMove.None && move != PBEMove.MAX)
+ {
+ sb.Append($" ({pp}{(maxPP == 0 ? ")" : $"/{maxPP})")}");
+ }
}
- sb.AppendLine($"Moves: {string.Join(", ", moveStrs)}");
- sb.AppendLine($"Usable moves: {string.Join(", ", GetUsableMoves().Select(m => PBELocalizedString.GetMoveName(m).English))}");
- sb.Append($"Known moves: {string.Join(", ", KnownMoves.Select(m => m == PBEMove.MAX ? "???" : PBELocalizedString.GetMoveName(m).English))}");
+ sb.AppendLine();
+ sb.Append($"Usable moves: {string.Join(", ", GetUsableMoves().Select(m => PBELocalizedString.GetMoveName(m).English))}");
return sb.ToString();
}
+
+ internal bool IsDisposed { get; private set; }
+ internal void Dispose()
+ {
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ if (Id != byte.MaxValue)
+ {
+ EffortValues.CanDispose = true;
+ EffortValues.Dispose();
+ IndividualValues.CanDispose = true;
+ IndividualValues.Dispose();
+ OriginalMoveset.CanDispose = true;
+ OriginalMoveset.Dispose();
+ }
+ }
+ }
}
}
diff --git a/PokemonBattleEngine/Battle/Team.cs b/PokemonBattleEngine/Battle/Team.cs
index 356a5312c..2583287b9 100644
--- a/PokemonBattleEngine/Battle/Team.cs
+++ b/PokemonBattleEngine/Battle/Team.cs
@@ -1,4 +1,6 @@
using Kermalis.PokemonBattleEngine.Data;
+using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -6,6 +8,50 @@
namespace Kermalis.PokemonBattleEngine.Battle
{
+ public sealed class PBETeams : IReadOnlyList
+ {
+ private readonly PBETeam _team1;
+ private readonly PBETeam _team2;
+
+ public int Count => 2;
+ public PBETeam this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0: return _team1;
+ case 1: return _team2;
+ default: throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+ }
+
+ internal PBETeams(PBEBattle battle, PBETeamShell team1Shell, string team1TrainerName, PBETeamShell team2Shell, string team2TrainerName)
+ {
+ _team1 = new PBETeam(battle, 0, team1Shell, team1TrainerName);
+ _team2 = new PBETeam(battle, 1, team2Shell, team2TrainerName);
+ _team1.OpposingTeam = _team2;
+ _team2.OpposingTeam = _team1;
+ }
+ internal PBETeams(PBEBattle battle)
+ {
+ _team1 = new PBETeam(battle, 0);
+ _team2 = new PBETeam(battle, 1);
+ _team1.OpposingTeam = _team2;
+ _team2.OpposingTeam = _team1;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ yield return _team1;
+ yield return _team2;
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
// TODO: INPC
/// Represents a team in a specific .
public sealed class PBETeam
@@ -14,7 +60,8 @@ public sealed class PBETeam
public PBEBattle Battle { get; }
public byte Id { get; }
public string TrainerName { get; set; } // Setter is public because a client cannot submit the opponent's team
- public List Party { get; private set; }
+ public PBEList Party { get; }
+ public PBETeam OpposingTeam { get; internal set; }
public PBEPokemon[] ActiveBattlers => Battle.ActiveBattlers.Where(p => p.Team == this).OrderBy(p => p.FieldPosition).ToArray();
public int NumPkmnAlive => Party.Count(p => p.HP > 0);
@@ -33,25 +80,25 @@ public sealed class PBETeam
public bool MonFaintedLastTurn { get; set; }
public bool MonFaintedThisTurn { get; set; }
- internal PBETeam(PBEBattle battle, byte id, PBETeamShell shell, string trainerName, ref byte pkmnIdCounter)
+ internal PBETeam(PBEBattle battle, byte id, PBETeamShell shell, string trainerName)
{
Battle = battle;
Id = id;
- CreateParty(shell, trainerName, ref pkmnIdCounter);
+ Party = new PBEList(Battle.Settings.MaxPartySize);
+ CreateParty(shell, trainerName);
}
internal PBETeam(PBEBattle battle, byte id)
{
Battle = battle;
Id = id;
- Party = new List(Battle.Settings.MaxPartySize);
+ Party = new PBEList(Battle.Settings.MaxPartySize);
}
- internal void CreateParty(PBETeamShell shell, string trainerName, ref byte pkmnIdCounter)
+ internal void CreateParty(PBETeamShell shell, string trainerName)
{
TrainerName = trainerName;
- Party = new List(Battle.Settings.MaxPartySize);
for (int i = 0; i < shell.Count; i++)
{
- new PBEPokemon(this, pkmnIdCounter++, shell[i]);
+ new PBEPokemon(this, (byte)((Id * Battle.Settings.MaxPartySize) + i), shell[i]);
}
}
@@ -59,22 +106,49 @@ internal void CreateParty(PBETeamShell shell, string trainerName, ref byte pkmnI
/// The of the .
public PBEPokemon TryGetPokemon(PBEFieldPosition pos)
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
return ActiveBattlers.SingleOrDefault(p => p.FieldPosition == pos);
}
/// Gets a specific by its ID.
/// The ID of the .
public PBEPokemon TryGetPokemon(byte pkmnId)
{
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
return Party.SingleOrDefault(p => p.Id == pkmnId);
}
- internal List ToBytes()
+ public bool Remove(PBEPokemon pokemon)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (pokemon.IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(pokemon));
+ }
+ bool b = Party.Remove(pokemon);
+ if (b)
+ {
+ pokemon.Dispose();
+ }
+ return b;
+ }
+
+ internal void ToBytes(List bytes)
{
- var bytes = new List();
- bytes.AddRange(PBEUtils.StringToBytes(TrainerName));
+ PBEUtils.StringToBytes(bytes, TrainerName);
bytes.Add((byte)Party.Count);
- bytes.AddRange(Party.SelectMany(p => p.ToBytes()));
- return bytes;
+ for (int i = 0; i < (sbyte)Party.Count; i++)
+ {
+ Party[i].ToBytes(bytes);
+ }
}
internal void FromBytes(BinaryReader r)
{
@@ -96,5 +170,18 @@ public override string ToString()
sb.AppendLine($"NumPkmnOnField: {NumPkmnOnField}");
return sb.ToString();
}
+
+ internal bool IsDisposed { get; private set; }
+ internal void Dispose()
+ {
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ for (int i = 0; i < Party.Count; i++)
+ {
+ Party[i].Dispose();
+ }
+ }
+ }
}
}
diff --git a/PokemonBattleEngine/Data/EffortValueCollection.cs b/PokemonBattleEngine/Data/EffortValueCollection.cs
deleted file mode 100644
index 9971296ef..000000000
--- a/PokemonBattleEngine/Data/EffortValueCollection.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-
-namespace Kermalis.PokemonBattleEngine.Data
-{
- public sealed class PBEEffortValueCollection : IEnumerable, INotifyPropertyChanged
- {
- public sealed class PBEEffortValue : INotifyPropertyChanged
- {
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- private readonly PBEEffortValueCollection parent;
-
- public PBEStat Stat { get; }
- private byte value;
- public byte Value
- {
- get => value;
- set
- {
- if (this.value != value)
- {
- parent.Set(Stat, value);
- }
- }
- }
-
- internal PBEEffortValue(PBEEffortValueCollection parent, PBEStat stat)
- {
- this.parent = parent;
- Stat = stat;
- }
-
- internal void Update(byte value)
- {
- this.value = value;
- OnPropertyChanged(nameof(Value));
- }
- }
-
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- public PBESettings Settings { get; }
- private PBEEffortValue[] evs;
-
- public ushort StatTotal
- {
- get
- {
- ushort total = 0;
- for (int i = 0; i < 6; i++)
- {
- total += evs[i].Value;
- }
- return total;
- }
- }
-
- public PBEEffortValueCollection(PBESettings settings, bool randomize)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- CreateEVs();
- if (randomize)
- {
- Randomize();
- }
- }
- public PBEEffortValueCollection(PBESettings settings, byte hp = 0, byte attack = 0, byte defense = 0, byte spAttack = 0, byte spDefense = 0, byte speed = 0)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- CreateEVs();
- TrySet(hp, attack, defense, spAttack, spDefense, speed);
- }
- public PBEEffortValueCollection(PBESettings settings, PBEEffortValueCollection other)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- CreateEVs();
- TrySet(other[PBEStat.HP].Value, other[PBEStat.Attack].Value, other[PBEStat.Defense].Value, other[PBEStat.SpAttack].Value, other[PBEStat.SpDefense].Value, other[PBEStat.Speed].Value);
- }
- private void CreateEVs()
- {
- evs = new PBEEffortValue[6]
- {
- new PBEEffortValue(this, PBEStat.HP),
- new PBEEffortValue(this, PBEStat.Attack),
- new PBEEffortValue(this, PBEStat.Defense),
- new PBEEffortValue(this, PBEStat.SpAttack),
- new PBEEffortValue(this, PBEStat.SpDefense),
- new PBEEffortValue(this, PBEStat.Speed)
- };
- }
- private void UpdateEV(PBEEffortValue ev, byte value)
- {
- if (ev.Value != value)
- {
- ev.Update(value);
- OnPropertyChanged(nameof(StatTotal));
- }
- }
- private void TrySet(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)
- {
- Set(PBEStat.HP, hp);
- Set(PBEStat.Attack, attack);
- Set(PBEStat.Defense, defense);
- Set(PBEStat.SpAttack, spAttack);
- Set(PBEStat.SpDefense, spDefense);
- Set(PBEStat.Speed, speed);
- }
-
- public PBEEffortValue this[PBEStat stat]
- {
- get
- {
- int statIndex = (int)stat;
- if (statIndex >= 6)
- {
- throw new ArgumentOutOfRangeException(nameof(stat));
- }
- else
- {
- return evs[statIndex];
- }
- }
- }
-
- public void Set(PBEStat stat, byte value)
- {
- int statIndex = (int)stat;
- if (statIndex >= 6)
- {
- throw new ArgumentOutOfRangeException(nameof(stat));
- }
- PBEEffortValue ev = evs[statIndex];
- ushort oldTotal = StatTotal;
- int newTotal = oldTotal - ev.Value + value;
- if (newTotal > Settings.MaxTotalEVs)
- {
- UpdateEV(ev, (byte)(value - (newTotal - Settings.MaxTotalEVs)));
- }
- else
- {
- UpdateEV(ev, value);
- }
- }
- public void Randomize()
- {
- if (Settings.MaxTotalEVs != 0)
- {
- byte[] vals = new byte[6];
- int[] a = Enumerable.Repeat(0, 6 - 1)
- .Select(x => PBEUtils.RandomInt(1, Settings.MaxTotalEVs - 1))
- .Concat(new int[] { Settings.MaxTotalEVs })
- .OrderBy(x => x)
- .ToArray();
- ushort total = 0;
- for (int i = 0; i < 6; i++)
- {
- byte b = (byte)Math.Min(byte.MaxValue, a[i] - total);
- vals[i] = b;
- total += b;
- }
- // This "while" will fix the issue where the speed stat was supposed to be above 255
- var notMax = new List(5);
- while (total != Settings.MaxTotalEVs)
- {
- notMax.Clear();
- for (int i = 0; i < 6; i++)
- {
- if (vals[i] != byte.MaxValue)
- {
- notMax.Add(i);
- }
- }
- int index = notMax.RandomElement();
- byte old = vals[index];
- byte b = (byte)Math.Min(byte.MaxValue, old + (Settings.MaxTotalEVs - total));
- vals[index] = b;
- total += (byte)(b - old);
- }
- for (int i = 0; i < 6; i++)
- {
- UpdateEV(evs[i], vals[i]);
- }
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- for (int i = 0; i < 6; i++)
- {
- yield return evs[i];
- }
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
- {
- switch (e.PropertyName)
- {
- case nameof(Settings.MaxTotalEVs):
- {
- ushort oldTotal = StatTotal;
- if (oldTotal > Settings.MaxTotalEVs)
- {
- for (int i = 0; i < 6; i++)
- {
- PBEEffortValue ev = evs[i];
- UpdateEV(ev, (byte)(Settings.MaxTotalEVs * ((double)ev.Value / oldTotal)));
- }
- }
- break;
- }
- }
- }
- }
-}
diff --git a/PokemonBattleEngine/Data/EffortValues.cs b/PokemonBattleEngine/Data/EffortValues.cs
new file mode 100644
index 000000000..3cde1fac4
--- /dev/null
+++ b/PokemonBattleEngine/Data/EffortValues.cs
@@ -0,0 +1,295 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+
+namespace Kermalis.PokemonBattleEngine.Data
+{
+ public sealed class PBEEffortValues : IDisposable, IEnumerable, INotifyPropertyChanged
+ {
+ public sealed class PBEEffortValue : INotifyPropertyChanged
+ {
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private readonly PBEEffortValues _parent;
+
+ public PBEStat Stat { get; }
+ private byte _value;
+ public byte Value
+ {
+ get => _value;
+ set
+ {
+ if (_parent.IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (_value != value)
+ {
+ ushort oldTotal = _parent.StatTotal;
+ int newTotal = oldTotal - _value + value;
+ if (newTotal > _parent.Settings.MaxTotalEVs)
+ {
+ byte newValue = (byte)(value - (newTotal - _parent.Settings.MaxTotalEVs));
+ if (_value != newValue)
+ {
+ Update(newValue);
+ }
+ }
+ else
+ {
+ Update(value);
+ }
+ }
+ }
+ }
+
+ internal PBEEffortValue(PBEEffortValues parent, PBEStat stat, byte value)
+ {
+ _parent = parent;
+ Stat = stat;
+ _value = value;
+ }
+
+ private void Update(byte newValue)
+ {
+ _value = newValue;
+ OnPropertyChanged(nameof(Value));
+ _parent.OnPropertyChanged(nameof(_parent.StatTotal));
+ }
+ }
+
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public PBESettings Settings { get; }
+ private PBEEffortValue[] _evs;
+ public PBEEffortValue this[PBEStat stat]
+ {
+ get
+ {
+ int statIndex = (int)stat;
+ if (statIndex >= 6)
+ {
+ throw new ArgumentOutOfRangeException(nameof(stat));
+ }
+ return _evs[statIndex];
+ }
+ }
+
+ public ushort StatTotal
+ {
+ get
+ {
+ ushort total = 0;
+ for (int i = 0; i < 6; i++)
+ {
+ total += _evs[i].Value;
+ }
+ return total;
+ }
+ }
+
+ internal PBEEffortValues(PBESettings settings, BinaryReader r)
+ {
+ byte hp = r.ReadByte();
+ byte attack = r.ReadByte();
+ byte defense = r.ReadByte();
+ byte spAttack = r.ReadByte();
+ byte spDefense = r.ReadByte();
+ byte speed = r.ReadByte();
+ if (hp + attack + defense + spAttack + spDefense + speed > settings.MaxTotalEVs)
+ {
+ throw new InvalidDataException();
+ }
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateEVs(hp, attack, defense, spAttack, spDefense, speed);
+ }
+ internal PBEEffortValues(PBESettings settings, JToken jToken)
+ {
+ byte hp = jToken[nameof(PBEStat.HP)].Value();
+ byte attack = jToken[nameof(PBEStat.Attack)].Value();
+ byte defense = jToken[nameof(PBEStat.Defense)].Value();
+ byte spAttack = jToken[nameof(PBEStat.SpAttack)].Value();
+ byte spDefense = jToken[nameof(PBEStat.SpDefense)].Value();
+ byte speed = jToken[nameof(PBEStat.Speed)].Value();
+ if (hp + attack + defense + spAttack + spDefense + speed > settings.MaxTotalEVs)
+ {
+ throw new ArgumentOutOfRangeException(nameof(PBEPokemonShell.EffortValues), $"Effort values total must not exceed \"{nameof(settings.MaxTotalEVs)}\" ({settings.MaxTotalEVs})");
+ }
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateEVs(hp, attack, defense, spAttack, spDefense, speed);
+ }
+ internal PBEEffortValues(PBEEffortValues other)
+ {
+ Settings = other.Settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateEVs(other[PBEStat.HP].Value, other[PBEStat.Attack].Value, other[PBEStat.Defense].Value, other[PBEStat.SpAttack].Value, other[PBEStat.SpDefense].Value, other[PBEStat.Speed].Value);
+ }
+ public PBEEffortValues(PBESettings settings, bool randomize)
+ {
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ _canDispose = true;
+ CreateEVs(0, 0, 0, 0, 0, 0);
+ if (randomize)
+ {
+ Randomize();
+ }
+ }
+
+ private void CreateEVs(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)
+ {
+ _evs = new PBEEffortValue[6]
+ {
+ new PBEEffortValue(this, PBEStat.HP, hp),
+ new PBEEffortValue(this, PBEStat.Attack, attack),
+ new PBEEffortValue(this, PBEStat.Defense, defense),
+ new PBEEffortValue(this, PBEStat.SpAttack, spAttack),
+ new PBEEffortValue(this, PBEStat.SpDefense, spDefense),
+ new PBEEffortValue(this, PBEStat.Speed, speed)
+ };
+ }
+ private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Settings.MaxTotalEVs):
+ {
+ ushort oldTotal = StatTotal;
+ if (oldTotal > Settings.MaxTotalEVs)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ PBEEffortValue ev = _evs[i];
+ ev.Value = (byte)(Settings.MaxTotalEVs * ((double)ev.Value / oldTotal));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private bool _canDispose;
+ public bool CanDispose
+ {
+ get => _canDispose;
+ internal set
+ {
+ if (_canDispose != value)
+ {
+ _canDispose = value;
+ OnPropertyChanged(nameof(CanDispose));
+ }
+ }
+ }
+ public bool IsDisposed { get; private set; }
+ public void Dispose()
+ {
+ if (!_canDispose)
+ {
+ throw new InvalidOperationException();
+ }
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ OnPropertyChanged(nameof(IsDisposed));
+ Settings.PropertyChanged -= OnSettingsChanged;
+ }
+ }
+
+ public void Randomize()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (Settings.MaxTotalEVs != 0)
+ {
+ byte[] vals = new byte[6];
+ int[] a = Enumerable.Repeat(0, 6 - 1)
+ .Select(x => PBEUtils.RandomInt(1, Settings.MaxTotalEVs - 1))
+ .Concat(new int[] { Settings.MaxTotalEVs })
+ .OrderBy(x => x)
+ .ToArray();
+ ushort total = 0;
+ for (int i = 0; i < 6; i++)
+ {
+ byte b = (byte)Math.Min(byte.MaxValue, a[i] - total);
+ vals[i] = b;
+ total += b;
+ }
+ // This "while" will fix the issue where the speed stat was supposed to be above 255
+ var notMax = new List(5);
+ while (total != Settings.MaxTotalEVs)
+ {
+ notMax.Clear();
+ for (int i = 0; i < 6; i++)
+ {
+ if (vals[i] != byte.MaxValue)
+ {
+ notMax.Add(i);
+ }
+ }
+ int index = notMax.RandomElement();
+ byte old = vals[index];
+ byte b = (byte)Math.Min(byte.MaxValue, old + (Settings.MaxTotalEVs - total));
+ vals[index] = b;
+ total += (byte)(b - old);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ _evs[i].Value = vals[i];
+ }
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ yield return _evs[i];
+ }
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ internal void ToBytes(List bytes)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ bytes.Add(_evs[i].Value);
+ }
+ }
+ internal void ToJson(JsonTextWriter w)
+ {
+ w.WriteStartObject();
+ for (int i = 0; i < 6; i++)
+ {
+ PBEEffortValue ev = _evs[i];
+ w.WritePropertyName(ev.Stat.ToString());
+ w.WriteValue(ev.Value);
+ }
+ w.WriteEndObject();
+ }
+ }
+}
diff --git a/PokemonBattleEngine/Data/Enums.cs b/PokemonBattleEngine/Data/Enums.cs
index 2cdfdf11b..7d6d4e971 100644
--- a/PokemonBattleEngine/Data/Enums.cs
+++ b/PokemonBattleEngine/Data/Enums.cs
@@ -15,696 +15,380 @@ public enum PBEGender : byte
/// Invalid gender.
MAX
}
- ///
- /// Represents a Pokémon species' ratio.
- ///
+ /// Represents a Pokémon species' ratio.
public enum PBEGenderRatio : byte
{
- ///
- /// The species is 87.5% male, 12.5% female.
- ///
+ /// The species is 87.5% male, 12.5% female.
M7_F1 = 0x1F,
- ///
- /// The species is 75% male, 25% female.
- ///
+ /// The species is 75% male, 25% female.
M3_F1 = 0x3F,
- ///
- /// The species is 50% male, 50% female.
- ///
+ /// The species is 50% male, 50% female.
M1_F1 = 0x7F,
- ///
- /// The species is 25% male, 75% female.
- ///
+ /// The species is 25% male, 75% female.
M1_F3 = 0xBF,
- ///
- /// The species is 0% male, 100% female.
- ///
+ /// The species is 0% male, 100% female.
M0_F1 = 0xFE,
- ///
- /// The species is genderless.
- ///
+ /// The species is genderless.
M0_F0 = 0xFF,
- ///
- /// The species is 100% male, 0% female.
- ///
+ /// The species is 100% male, 0% female.
M1_F0 = 0x00
}
- ///
- /// Represents a Pokémon stat.
- ///
+ /// Represents a Pokémon stat.
public enum PBEStat : byte
{
- ///
- /// Hit points.
- ///
- HP = 0,
- ///
- /// Attack.
- ///
- Attack = 1,
- ///
- /// Defense.
- ///
- Defense = 2,
- ///
- /// Special Attack.
- ///
- SpAttack = 3,
- ///
- /// Special Defense.
- ///
- SpDefense = 4,
- ///
- /// Speed.
- ///
- Speed = 5,
- ///
- /// Accuracy.
- ///
+ /// Hit points.
+ HP,
+ /// Attack.
+ Attack,
+ /// Defense.
+ Defense,
+ /// Special Attack.
+ SpAttack,
+ /// Special Defense.
+ SpDefense,
+ /// Speed.
+ Speed,
+ /// Accuracy.
Accuracy,
- ///
- /// Evasion.
- ///
+ /// Evasion.
Evasion
}
- ///
- /// Represents the effectiveness of a move against a target.
- ///
+ /// Represents the effectiveness of a move against a target.
public enum PBEEffectiveness : byte
{
- ///
- /// The move does not affect the target.
- ///
+ /// The move does not affect the target.
Ineffective,
- ///
- /// The move does less damage to the target.
- ///
+ /// The move does less damage to the target.
NotVeryEffective,
- ///
- /// The move affects the target as usual.
- ///
+ /// The move affects the target as usual.
Normal,
- ///
- /// The move does more damage to the target.
- ///
+ /// The move does more damage to the target.
SuperEffective
}
- ///
- /// Represents the format of a specific battle.
- ///
+ /// Represents the format of a specific battle.
public enum PBEBattleFormat : byte
{
- ///
- /// A 1v1 battle. Each Pokémon is able to use moves or switch out for another Pokémon.
- ///
+ /// A 1v1 battle. Each Pokémon is able to use moves or switch out for another Pokémon.
Single,
- ///
- /// A 2v2 battle where all Pokémon are able to use moves or switch out for another Pokémon.
- ///
+ /// A 2v2 battle where all Pokémon are able to use moves or switch out for another Pokémon.
Double,
- ///
- /// A 3v3 battle where all Pokémon are able to use moves, shift positions with a teammate, or switch out for another Pokémon.
- ///
+ /// A 3v3 battle where all Pokémon are able to use moves, shift positions with a teammate, or switch out for another Pokémon.
Triple,
- ///
- /// A 3v3 battle where only the front Pokémon are able to force a team rotation, use a move, or switch out for another Pokémon.
- ///
- ///
- /// Team rotation does not take up a turn and can be done once per turn.
- ///
+ /// A 3v3 battle where only the front Pokémon are able to force a team rotation, use a move, or switch out for another Pokémon.
+ /// Team rotation does not take up a turn and can be done once per turn.
Rotation,
- ///
- /// Invalid battle format.
- ///
+ /// Invalid battle format.
MAX
}
- ///
- /// Represents the current state of a specific battle.
- ///
+ /// Represents the current state of a specific battle.
public enum PBEBattleState : byte
{
- ///
- /// The battle is waiting for team shells.
- ///
+ /// The battle is waiting for team shells.
WaitingForPlayers,
- ///
- /// The battle is ready to begin.
- ///
+ /// The battle is ready to begin.
ReadyToBegin,
- ///
- /// The battle is waiting for players to send actions.
- ///
+ /// The battle is waiting for players to send actions.
WaitingForActions,
- ///
- /// The battle is ready to run a turn.
- ///
+ /// The battle is ready to run a turn.
ReadyToRunTurn,
- ///
- /// The battle is processing.
- ///
+ /// The battle is processing.
Processing,
- ///
- /// The battle is waiting for players to send switch-ins.
- ///
+ /// The battle is waiting for players to send switch-ins.
WaitingForSwitchIns,
- ///
- /// The battle ended.
- ///
+ /// The battle ended.
Ended
}
- ///
- /// Represents the weather in a specific battle.
- ///
+ /// Represents the weather in a specific battle.
public enum PBEWeather : byte
{
- ///
- /// There is no weather.
- ///
+ /// There is no weather.
None,
- ///
- /// It is hailing.
- ///
+ /// It is hailing.
Hailstorm,
- ///
- /// The sunlight is harsh.
- ///
+ /// The sunlight is harsh.
HarshSunlight,
- ///
- /// It is raining.
- ///
+ /// It is raining.
Rain,
- ///
- /// A sandstorm is brewing.
- ///
+ /// A sandstorm is brewing.
Sandstorm
}
- ///
- /// Represents a position on the battle field.
- ///
+ /// Represents a position on the battle field.
public enum PBEFieldPosition : byte
{
- ///
- /// A Pokémon is not on the field.
- ///
+ /// A Pokémon is not on the field.
None,
- ///
- /// The Pokémon to a player's left in a Double, Triple, or Rotation battle.
- ///
+ /// The Pokémon to a player's left in a Double, Triple, or Rotation battle.
Left,
- ///
- /// The Pokémon in the center of the field in a Single, Triple, or Rotation battle.
- ///
+ /// The Pokémon in the center of the field in a Single, Triple, or Rotation battle.
Center,
- ///
- /// The Pokémon to a player's right in a Double, Triple, or Rotation battle.
- ///
+ /// The Pokémon to a player's right in a Double, Triple, or Rotation battle.
Right
}
- ///
- /// Represents a 's targets.
- ///
+ /// Represents a 's targets.
[Flags]
- public enum PBETarget : byte
+ public enum PBETurnTarget : byte
{
- ///
- /// The Pokémon has not chosen any targets.
- ///
+ /// The Pokémon has not chosen any targets.
None,
- ///
- /// The move is targetting the player's left Pokémon.
- ///
+ /// The move is targetting the player's left Pokémon.
AllyLeft = 1 << 0,
- ///
- /// The move is targetting the player's center Pokémon.
- ///
+ /// The move is targetting the player's center Pokémon.
AllyCenter = 1 << 1,
- ///
- /// The move is targetting the player's right Pokémon.
- ///
+ /// The move is targetting the player's right Pokémon.
AllyRight = 1 << 2,
- ///
- /// The move is targetting the opponent's left Pokémon.
- ///
+ /// The move is targetting the opponent's left Pokémon.
FoeLeft = 1 << 3,
- ///
- /// The move is targetting the opponent's center Pokémon.
- ///
+ /// The move is targetting the opponent's center Pokémon.
FoeCenter = 1 << 4,
- ///
- /// The move is targetting the opponent's right Pokémon.
- ///
+ /// The move is targetting the opponent's right Pokémon.
FoeRight = 1 << 5
}
- ///
- /// Represents a Pokémon's decision for a turn.
- ///
- public enum PBEDecision : byte
+ /// Represents a Pokémon's decision for a turn.
+ public enum PBETurnDecision : byte
{
- ///
- /// The Pokémon has not made a decision.
- ///
+ /// The Pokémon has not made a decision.
None,
- ///
- /// The Pokémon has chosen to use a move.
- ///
+ /// The Pokémon has chosen to use a move.
Fight,
- ///
- /// The Pokémon has chosen to switch out for another Pokémon.
- ///
+ /// The Pokémon has chosen to switch out for another Pokémon.
SwitchOut
}
- ///
- /// Represents a specific 's category.
- ///
+ /// Represents a specific 's category.
public enum PBEMoveCategory : byte
{
- ///
- /// The move deals no damage.
- ///
+ /// The move deals no damage.
Status,
- ///
- /// The move deals physical damage using the Attack and Defense stats.
- ///
+ /// The move deals physical damage using the Attack and Defense stats.
Physical,
- ///
- /// The move deals special damage using the Special Attack and Special Defense stats.
- ///
+ /// The move deals special damage using the Special Attack and Special Defense stats.
Special,
- ///
- /// Invalid category.
- ///
+ /// Invalid category.
MAX
}
- ///
- /// Represents the various methods in which a Pokémon can learn a .
- ///
+ /// Represents the various methods in which a Pokémon can learn a .
[Flags]
public enum PBEMoveObtainMethod : ulong
{
- ///
- /// There is no way to learn this move.
- ///
+ /// There is no way to learn this move.
None,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Ruby Version and Pokémon Sapphire Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Ruby Version and Pokémon Sapphire Version.
LevelUp_RSColoXD = 1uL << 0,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Fire Red Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Fire Red Version.
LevelUp_FR = 1uL << 1,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Leaf Green Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Leaf Green Version.
LevelUp_LG = 1uL << 2,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Emerald Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Emerald Version.
LevelUp_E = 1uL << 3,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Diamond Version and Pokémon Pearl Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Diamond Version and Pokémon Pearl Version.
LevelUp_DP = 1uL << 4,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Platinum Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Platinum Version.
LevelUp_Pt = 1uL << 5,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
LevelUp_HGSS = 1uL << 6,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Black Version and Pokémon White Version.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Black Version and Pokémon White Version.
LevelUp_BW = 1uL << 7,
- ///
- /// The move can be learned by levelling up a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.
- ///
+ /// The move can be learned by levelling up a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.
LevelUp_B2W2 = 1uL << 8,
- ///
- /// The move can be learned by using a technical machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.
- ///
+ /// The move can be learned by using a technical machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.
TM_RSFRLGEColoXD = 1uL << 9,
- ///
- /// The move can be learned by using a technical machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
- ///
+ /// The move can be learned by using a technical machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
TM_DPPt = 1uL << 10,
- ///
- /// The move can be learned by using a technical machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
- ///
+ /// The move can be learned by using a technical machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
TM_HGSS = 1uL << 11,
- ///
- /// The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version and Pokémon White Version.
- ///
+ /// The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version and Pokémon White Version.
TM_BW = 1uL << 12,
- ///
- /// The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.
- ///
+ /// The move can be learned by using a technical machine on a Pokémon in Pokémon Black Version 2 and Pokémon White Version 2.
TM_B2W2 = 1uL << 13,
- ///
- /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.
- ///
+ /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, Pokémon Emerald Version, Pokémon Colosseum, and Pokémon XD: Gale of Darkness.
HM_RSFRLGEColoXD = 1uL << 14,
- ///
- /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
- ///
+ /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
HM_DPPt = 1uL << 15,
- ///
- /// The move can be learned by using a hidden machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
- ///
+ /// The move can be learned by using a hidden machine on a Pokémon in Pokémon HeartGold Version and Pokémon SoulSilver Version.
HM_HGSS = 1uL << 16,
- ///
- /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.
- ///
+ /// The move can be learned by using a hidden machine on a Pokémon in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.
HM_BWB2W2 = 1uL << 17,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Fire Red Version and Pokémon Leaf Green Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Fire Red Version and Pokémon Leaf Green Version.
MoveTutor_FRLG = 1uL << 18,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Emerald Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Emerald Version.
MoveTutor_E = 1uL << 19,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon XD: Gale of Darkness.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon XD: Gale of Darkness.
MoveTutor_XD = 1uL << 20,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Diamond Version and Pokémon Pearl Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Diamond Version and Pokémon Pearl Version.
MoveTutor_DP = 1uL << 21,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Platinum Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Platinum Version.
MoveTutor_Pt = 1uL << 22,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon HeartGold Version and Pokémon SoulSilver Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon HeartGold Version and Pokémon SoulSilver Version.
MoveTutor_HGSS = 1uL << 23,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Black Version and Pokémon White Version.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Black Version and Pokémon White Version.
MoveTutor_BW = 1uL << 24,
- ///
- /// The move can be taught to a Pokémon by a move tutor in Pokémon Black Version 2 and Pokémon White Version 2.
- ///
+ /// The move can be taught to a Pokémon by a move tutor in Pokémon Black Version 2 and Pokémon White Version 2.
MoveTutor_B2W2 = 1uL << 25,
- ///
- /// The move can be learned by hatching a Pokémon egg in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, and Pokémon Emerald Version.
- ///
+ /// The move can be learned by hatching a Pokémon egg in Pokémon Ruby Version, Pokémon Sapphire Version, Pokémon Fire Red Version, Pokémon Leaf Green Version, and Pokémon Emerald Version.
EggMove_RSFRLGE = 1uL << 26,
- ///
- /// The move can be learned by hatching a Pokémon egg in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
- ///
+ /// The move can be learned by hatching a Pokémon egg in Pokémon Diamond Version, Pokémon Pearl Version, and Pokémon Platinum Version.
EggMove_DPPt = 1uL << 27,
- ///
- /// The move can be learned by hatching a Pokémon egg in Pokémon HeartGold Version and Pokémon SoulSilver Version.
- ///
+ /// The move can be learned by hatching a Pokémon egg in Pokémon HeartGold Version and Pokémon SoulSilver Version.
EggMove_HGSS = 1uL << 28,
- ///
- /// The move can be learned by hatching a Pokémon egg in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.
- ///
+ /// The move can be learned by hatching a Pokémon egg in Pokémon Black Version, Pokémon White Version, Pokémon Black Version 2, and Pokémon White Version 2.
EggMove_BWB2W2 = 1uL << 29,
- ///
- /// The move is known by a Pokémon when found in the Dream World.
- ///
+ /// The move is known by a Pokémon when found in the Dream World.
DreamWorld = 1uL << 30,
- ///
- /// The move can be learned by hatching a Pokémon egg under special conditions.
- ///
+ /// The move can be learned by hatching a Pokémon egg under special conditions.
EggMove_Special = 1uL << 31,
- ///
- /// The move is learned by a Pokémon when changing forms.
- ///
+ /// The move is learned by a Pokémon when changing forms.
Form = 1uL << 32
}
- ///
- /// Represents a specific Pokémon's non-volatile status.
- ///
+ /// Represents a specific Pokémon's non-volatile status.
public enum PBEStatus1 : byte
{
- ///
- /// The Pokémon has no status.
- ///
+ /// The Pokémon has no status.
None,
- ///
- /// The Pokémon is asleep.
- ///
+ /// The Pokémon is asleep.
Asleep,
- ///
- /// The Pokémon is badly poisoned.
- ///
+ /// The Pokémon is badly poisoned.
BadlyPoisoned,
- ///
- /// The Pokémon is burned.
- ///
+ /// The Pokémon is burned.
Burned,
- ///
- /// The Pokémon is frozen.
- ///
+ /// The Pokémon is frozen.
Frozen,
- ///
- /// The Pokémon is paralyzed.
- ///
+ /// The Pokémon is paralyzed.
Paralyzed,
- ///
- /// The Pokémon is poisoned.
- ///
+ /// The Pokémon is poisoned.
Poisoned
}
- ///
- /// Represents a specific Pokémon's volatile status.
- ///
+ /// Represents a specific Pokémon's volatile status.
[Flags]
public enum PBEStatus2 : uint
{
- ///
- /// The Pokémon has no status.
- ///
+ /// The Pokémon has no status.
None,
- ///
- /// The Pokémon is high up in the air.
- /// A move will miss against the Pokémon unless it has or either Pokémon has .
+ /// The Pokémon is high up in the air.A move will miss against the Pokémon unless it has or either Pokémon has .
///
Airborne = 1 << 0,
- ///
- /// The Pokémon is confused and may hurt itself instead of execute its chosen move.
- ///
+ /// The Pokémon is confused and may hurt itself instead of execute its chosen move.
Confused = 1 << 1,
- ///
- /// The Pokémon is cursed and will take damage at the end of each turn.
- ///
+ /// The Pokémon is cursed and will take damage at the end of each turn.
Cursed = 1 << 2,
- ///
- /// The Pokémon is disguised as with .
- ///
+ /// The Pokémon is disguised as with .
Disguised = 1 << 3,
- ///
- /// The Pokémon is flinching and will be unable to move this turn.
- ///
+ /// The Pokémon is flinching and will be unable to move this turn.
Flinching = 1 << 4,
- ///
- /// The Pokémon will gain a power boost due to .
- ///
+ /// The Pokémon will gain a power boost due to .
HelpingHand = 1 << 5,
- ///
- /// The Pokémon is infatuated with and may be unable to move this turn.
- ///
+ /// The Pokémon is infatuated with and may be unable to move this turn.
Infatuated = 1 << 6,
- ///
- /// The Pokémon is seeded and HP will be stolen at the end of each turn.
- ///
+ /// The Pokémon is seeded and HP will be stolen at the end of each turn.
LeechSeed = 1 << 7,
- ///
- /// The Pokémon is protected from moves this turn.
- ///
+ /// The Pokémon is protected from moves this turn.
Protected = 1 << 8,
- ///
- /// The Pokémon is under the effect of or and has a higher chance of landing critical hits.
- ///
+ /// The Pokémon is under the effect of or and has a higher chance of landing critical hits.
Pumped = 1 << 9,
- ///
- /// The Pokémon is behind a substitute that will take damage on behalf of the Pokémon and prevent most moves from affecting the Pokémon.
- ///
+ /// The Pokémon is behind a substitute that will take damage on behalf of the Pokémon and prevent most moves from affecting the Pokémon.
Substitute = 1 << 10,
- ///
- /// The Pokémon is unable to use the same move two times in a row.
- ///
+ /// The Pokémon is unable to use the same move two times in a row.
Tormented = 1 << 11, // TODO
- ///
- /// The Pokémon is transformed into another Pokémon.
- ///
+ /// The Pokémon is transformed into another Pokémon.
Transformed = 1 << 12,
- ///
- /// The Pokémon is underground.
- /// A move will miss against the Pokémon unless it has or either Pokémon has .
+ /// The Pokémon is underground.A move will miss against the Pokémon unless it has or either Pokémon has .
/// The Pokémon will take double damage from and .
///
Underground = 1 << 13,
- ///
- /// The Pokémon is underwater.
- /// A move will miss against the Pokémon unless it has or either Pokémon has .
+ /// The Pokémon is underwater.A move will miss against the Pokémon unless it has or either Pokémon has .
/// The Pokémon will take double damage from and .
///
Underwater = 1 << 14
}
- ///
- /// Represents a specific 's status.
- ///
+ /// Represents a specific 's status.
[Flags]
public enum PBEBattleStatus : byte
{
- ///
- /// The battle has no status.
- ///
+ /// The battle has no status.
None,
- ///
- /// The acting order of Pokémon in this battle is reversed.
- ///
+ /// The acting order of Pokémon in this battle is reversed.
TrickRoom = 1 << 0 // TODO: Full Incense, Lagging Tail, Stall, Quick Claw
}
- ///
- /// Represents a specific 's status.
- ///
+ /// Represents a specific 's status.
[Flags]
public enum PBETeamStatus : byte
{
- ///
- /// The team has no status.
- ///
+ /// The team has no status.
None,
- ///
- /// The team will take less damage from moves.
- ///
+ /// The team will take less damage from moves.
LightScreen = 1 << 0,
- ///
- /// The team is shielded from critical hits.
- ///
+ /// The team is shielded from critical hits.
LuckyChant = 1 << 1,
- ///
- /// The team will take less damage from moves.
- ///
+ /// The team will take less damage from moves.
Reflect = 1 << 2,
- ///
- /// Grounded Pokémon that switch in will take damage.
- /// The amount of damage is based on .
+ /// Grounded Pokémon that switch in will take damage.The amount of damage is based on .
///
Spikes = 1 << 3, // TODO: Gravity, magnet rise, magic guard, iron ball, baton pass with ingrain, air balloon
- ///
- /// Pokémon that switch in will take damage.
- /// The amount of damage is based on the effectiveness of on the Pokémon.
+ /// Pokémon that switch in will take damage.The amount of damage is based on the effectiveness of on the Pokémon.
///
StealthRock = 1 << 4, // TODO: magic guard, castform transforms after taking damage
- ///
- /// Grounded Pokémon that switch in will be if is 1 or if it is 2.
- /// Grounded Pokémon will remove toxic spikes.
+ /// Grounded Pokémon that switch in will be if is 1 or if it is 2.Grounded Pokémon will remove toxic spikes.
///
ToxicSpikes = 1 << 5, // TODO: Gravity, immunity, leaf guard, magic guard, iron ball, baton pass with ingrain, air balloon, synchronize with roar/whirlwind
- ///
- /// The team is protected from spread moves for a turn.
- ///
+ /// The team is protected from spread moves for a turn.
WideGuard = 1 << 6
}
- ///
- /// Represents an action regarding a .
- ///
+ /// Represents an action regarding a .
public enum PBEAbilityAction : byte
{
- ///
- /// The ability was changed.
- ///
+ /// The ability was changed.
Changed = 0,
- ///
- /// The ability caused a Pokémon to change its appearance.
- ///
+ /// The ability caused a Pokémon to change its appearance.
ChangedAppearance = 1,
- ///
- /// The ability changed a Pokémon stats.
- ///
+ /// The ability changed a Pokémon stats.
ChangedStats = 2,
- ///
- /// The ability changed a Pokémon's or .
- ///
+ /// The ability changed a Pokémon's or .
ChangedStatus = 3,
- ///
- /// The ability was involved with damage.
- ///
+ /// The ability was involved with damage.
Damage = 4,
- ///
- /// The ability prevented a Pokémon from being inflicted with a or .
- ///
+ /// The ability prevented a Pokémon from being inflicted with a or .
PreventedStatus = 5,
- ///
- /// The ability restored a Pokémon's HP.
- ///
+ /// The ability restored a Pokémon's HP.
RestoredHP = 6,
- ///
- /// The ability was involved with weather.
- ///
+ /// The ability was involved with weather.
Weather = 7
}
- ///
- /// Represents an action regarding a .
- ///
+ /// Represents an action regarding a .
public enum PBEItemAction : byte
{
- ///
- /// The item caused a Pokémon's or to change.
- ///
+ /// The item caused a Pokémon's or to change.
ChangedStatus = 0,
- ///
- /// The item was consumed by a Pokémon.
- ///
+ /// The item was consumed by a Pokémon.
Consumed = 1,
- ///
- /// The item was involved with damage.
- ///
+ /// The item was involved with damage.
Damage = 2,
- ///
- /// The item restored HP to a Pokémon.
- ///
+ /// The item restored HP to a Pokémon.
RestoredHP = 3
}
- ///
- /// Represents an action regarding a or .
- ///
+ /// Represents an action regarding a or .
public enum PBEStatusAction : byte
{
- ///
- /// The status activated its main effect.
- ///
+ /// The status activated its main effect.
///
/// prevented movement.
///
Activated = 0,
- ///
- /// The status was added to a Pokémon.
- ///
+ /// The status was added to a Pokémon.
///
/// The Pokémon became .
///
Added = 1,
- ///
- /// The status caused a Pokémon to be immobile.
- ///
+ /// The status caused a Pokémon to be immobile.
CausedImmobility = 2,
- ///
- /// The status was cured from a Pokémon.
- ///
+ /// The status was cured from a Pokémon.
///
/// cured a Pokémon of .
///
Cured = 3,
- ///
- /// The status was involved with damage.
- ///
+ /// The status was involved with damage.
///
/// A Pokémon's took damage.
///
Damage = 4,
- ///
- /// The status has ended.
- ///
+ /// The status has ended.
///
/// A Pokémon with regained its senses.
///
@@ -716,110 +400,66 @@ public enum PBEBattleStatusAction : byte
Cleared = 1,
Ended = 2
}
- ///
- /// Represents an action regarding a .
- ///
+ /// Represents an action regarding a .
public enum PBETeamStatusAction : byte
{
- ///
- /// The status was added to a team.
- ///
+ /// The status was added to a team.
///
/// The team set up .
///
Added = 0,
- ///
- /// The status was removed from a team.
- ///
+ /// The status was removed from a team.
///
/// An opponent used and destroyed .
///
Cleared = 1,
- ///
- /// The status caused a Pokémon to take damage.
- ///
+ /// The status caused a Pokémon to take damage.
///
/// An Pokémon switched in and took damage from .
///
Damage = 2,
- ///
- /// The status ended.
- ///
+ /// The status ended.
///
/// wore off.
///
Ended = 3
}
- ///
- /// Represents the reason for a move failing.
- ///
+ /// Represents the reason for a move failing.
public enum PBEFailReason : byte
{
- ///
- /// The move did not fail.
- ///
+ /// The move did not fail.
None = 0,
- ///
- /// The move failed because the target already has .
- ///
+ /// The move failed because the target already has .
AlreadyAsleep = 1,
- ///
- /// The move failed because the target already has .
- ///
+ /// The move failed because the target already has .
AlreadyBurned = 2,
- ///
- /// The move failed because the target already has .
- ///
+ /// The move failed because the target already has .
AlreadyConfused = 3,
- ///
- /// The move failed because the target already has .
- ///
+ /// The move failed because the target already has .
AlreadyParalyzed = 4,
- ///
- /// The move failed because the target already has or .
- ///
+ /// The move failed because the target already has or .
AlreadyPoisoned = 5,
- ///
- /// The move failed because the target already has .
- ///
+ /// The move failed because the target already has .
AlreadySubstituted = 6,
- ///
- /// General failure.
- ///
+ /// General failure.
Default = 7,
- ///
- /// The move tried to heal a Pokémon's HP when it was already full.
- ///
+ /// The move tried to heal a Pokémon's HP when it was already full.
HPFull = 8,
- ///
- /// The move failed because the Pokémon was immune to the move.
- ///
+ /// The move failed because the Pokémon was immune to the move.
Ineffective = 9,
- ///
- /// The move was used when there were no available targets to hit.
- ///
+ /// The move was used when there were no available targets to hit.
NoTarget = 10,
- ///
- /// The one-hit-knockout move failed because the target was a higher level than the user.
- ///
+ /// The one-hit-knockout move failed because the target was a higher level than the user.
OneHitKnockoutUnaffected = 11
}
- ///
- /// Represents an action regarding a .
- ///
+ /// Represents an action regarding a .
public enum PBEWeatherAction : byte
{
- ///
- /// The weather was added to the battle.
- ///
+ /// The weather was added to the battle.
Added = 0,
- ///
- /// The weather caused a Pokémon to take damage.
- ///
+ /// The weather caused a Pokémon to take damage.
CausedDamage = 1,
- ///
- /// The weather was removed from the battle.
- ///
+ /// The weather was removed from the battle.
Ended = 2
}
public enum PBESpecialMessage : byte
@@ -857,7 +497,8 @@ public enum PBEType : byte
Psychic,
Rock,
Steel,
- Water
+ Water,
+ MAX
}
/// Represents a specific Pokémon's nature.
public enum PBENature : byte
@@ -915,132 +556,80 @@ public enum PBENature : byte
/// Invalid nature.
MAX = 25
}
- ///
- /// Represents a specific Pokémon's held item.
- ///
+ /// Represents a specific Pokémon's held item.
public enum PBEItem : ushort
{
- ///
- /// No item.
- ///
+ /// No item.
None = 0,
AbsorbBulb = 545, // TODO
AdamantOrb = 135,
AguavBerry = 162, // TODO
AirBalloon = 541, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
AmuletCoin = 223,
- ///
- /// No effect.
- ///
+ /// No effect.
Antidote = 18,
ApicotBerry = 205, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ArmorFossil = 104,
AspearBerry = 153, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
Awakening = 21,
BabiriBerry = 199, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
BalmMushroom = 580,
BelueBerry = 183, // TODO
BerryJuice = 43, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
BigMushroom = 87,
- ///
- /// No effect.
- ///
+ /// No effect.
BigNugget = 581,
- ///
- /// No effect.
- ///
+ /// No effect.
BigPearl = 89,
BigRoot = 296,
BindingBand = 544, // TODO
BlackBelt = 241,
- ///
- /// No effect.
- ///
+ /// No effect.
BlackFlute = 68,
BlackGlasses = 240,
BlackSludge = 281,
- ///
- /// No effect.
- ///
+ /// No effect.
BlkApricorn = 491,
- ///
- /// No effect.
- ///
+ /// No effect.
BluApricorn = 486,
- ///
- /// No effect.
- ///
+ /// No effect.
BlueFlute = 65,
- ///
- /// No effect.
- ///
+ /// No effect.
BlueScarf = 261,
- ///
- /// No effect.
- ///
+ /// No effect.
BlueShard = 73,
BlukBerry = 165, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
BridgeMailD = 145,
- ///
- /// No effect.
- ///
+ /// No effect.
BridgeMailM = 148,
- ///
- /// No effect.
- ///
+ /// No effect.
BridgeMailS = 144,
- ///
- /// No effect.
- ///
+ /// No effect.
BridgeMailT = 146,
- ///
- /// No effect.
- ///
+ /// No effect.
BridgeMailV = 147,
BrightPowder = 213,
BugGem = 558,
BurnDrive = 118,
- ///
- /// No effect.
- ///
+ /// No effect.
BurnHeal = 19,
- ///
- /// No effect.
- ///
+ /// No effect.
Calcium = 49,
- ///
- /// No effect.
- ///
+ /// No effect.
Carbos = 48,
- ///
- /// No effect.
- ///
+ /// No effect.
Casteliacone = 591,
CellBattery = 546, // TODO
Charcoal = 249,
ChartiBerry = 195, // TODO
CheriBerry = 149, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
CherishBall = 16,
ChestoBerry = 150, // TODO
ChilanBerry = 200, // TODO
@@ -1049,264 +638,158 @@ public enum PBEItem : ushort
ChoiceScarf = 287,
ChoiceSpecs = 297,
ChopleBerry = 189, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ClawFossil = 100,
- ///
- /// No effect.
- ///
+ /// No effect.
CleanseTag = 224,
- ///
- /// No effect.
- ///
+ /// No effect.
CleverWing = 569,
CobaBerry = 192, // TODO
ColburBerry = 198, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
CometShard = 583,
CornnBerry = 175, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
CoverFossil = 572,
CustapBerry = 210, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
DampMulch = 96,
DampRock = 285,
DarkGem = 562,
- ///
- /// No effect.
- ///
+ /// No effect.
DawnStone = 109,
DeepSeaScale = 227,
DeepSeaTooth = 226,
DestinyKnot = 280,
- ///
- /// No effect.
- ///
+ /// No effect.
DireHit = 56,
- ///
- /// No effect.
- ///
+ /// No effect.
DiveBall = 7,
- ///
- /// No effect.
- ///
+ /// No effect.
DomeFossil = 102,
DouseDrive = 116,
DracoPlate = 311,
DragonFang = 250,
DragonGem = 561,
- ///
- /// No effect.
- ///
+ /// No effect.
DragonScale = 235,
DreadPlate = 312,
- ///
- /// No effect.
- ///
+ /// No effect.
DreamBall = 576,
- ///
- /// No effect.
- ///
+ /// No effect.
DubiousDisc = 324,
DurinBerry = 182, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
DuskBall = 13,
- ///
- /// No effect.
- ///
+ /// No effect.
DuskStone = 108,
EarthPlate = 305,
EjectButton = 547, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
Electirizer = 322,
ElectricGem = 550,
- ///
- /// No effect.
- ///
+ /// No effect.
Elixir = 40,
- ///
- /// No effect.
- ///
+ /// No effect.
EnergyPowder = 34,
- ///
- /// No effect.
- ///
+ /// No effect.
EnergyRoot = 35,
EnigmaBerry = 208, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
EscapeRope = 78,
- ///
- /// No effect.
- ///
+ /// No effect.
Ether = 38,
- ///
- /// No effect.
- ///
+ /// No effect.
Everstone = 229,
Eviolite = 538,
ExpertBelt = 268,
- ///
- /// No effect.
- ///
+ /// No effect.
ExpShare = 216,
- ///
- /// No effect.
- ///
+ /// No effect.
FastBall = 492,
- ///
- /// No effect.
- ///
+ /// No effect.
FavoredMail = 138,
FightingGem = 553,
FigyBerry = 159, // TODO
FireGem = 548,
- ///
- /// No effect.
- ///
+ /// No effect.
FireStone = 82,
FistPlate = 303,
- ///
- /// The Pokémon contracts at the end of each turn if it has no other and it does not have .
- ///
+ /// The Pokémon contracts at the end of each turn if it has no other and it does not have .
FlameOrb = 273,
FlamePlate = 298,
FloatStone = 539, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
FluffyTail = 64,
FlyingGem = 556,
FocusBand = 230,
FocusSash = 275,
- ///
- /// No effect.
- ///
+ /// No effect.
FreshWater = 30,
- ///
- /// No effect.
- ///
+ /// No effect.
FriendBall = 497,
- ///
- /// No effect.
- ///
+ /// No effect.
FullHeal = 27,
FullIncense = 316, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
FullRestore = 23,
GanlonBerry = 202, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
GeniusWing = 568,
GhostGem = 560,
- ///
- /// No effect.
- ///
+ /// No effect.
GooeyMulch = 98,
GrassGem = 551,
- ///
- /// No effect.
- ///
+ /// No effect.
GreatBall = 3,
- ///
- /// No effect.
- ///
+ /// No effect.
GreenScarf = 263,
- ///
- /// No effect.
- ///
+ /// No effect.
GreenShard = 75,
- ///
- /// No effect.
- ///
+ /// No effect.
GreetMail = 137,
GrepaBerry = 173, // TODO
GripClaw = 286, // TODO
GriseousOrb = 112,
- ///
- /// No effect.
- ///
+ /// No effect.
GrnApricorn = 488,
GroundGem = 555,
- ///
- /// No effect.
- ///
+ /// No effect.
GrowthMulch = 95,
- ///
- /// No effect.
- ///
+ /// No effect.
GuardSpec = 55,
HabanBerry = 197, // TODO
HardStone = 238,
- ///
- /// No effect.
- ///
+ /// No effect.
HealBall = 14,
- ///
- /// No effect.
- ///
+ /// No effect.
HealPowder = 36,
- ///
- /// No effect.
- ///
+ /// No effect.
HealthWing = 565,
- ///
- /// No effect.
- ///
+ /// No effect.
HeartScale = 93,
HeatRock = 284,
- ///
- /// No effect.
- ///
+ /// No effect.
HeavyBall = 495,
- ///
- /// No effect.
- ///
+ /// No effect.
HelixFossil = 101,
HondewBerry = 172, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
Honey = 94,
- ///
- /// No effect.
- ///
+ /// No effect.
HPUp = 45,
- ///
- /// No effect.
- ///
+ /// No effect.
HyperPotion = 25,
IapapaBerry = 163, // TODO
IceGem = 552,
- ///
- /// No effect.
- ///
+ /// No effect.
IceHeal = 20,
IciclePlate = 302,
IcyRock = 282,
- ///
- /// No effect.
- ///
+ /// No effect.
InquiryMail = 141,
InsectPlate = 308,
- ///
- /// No effect.
- ///
+ /// No effect.
Iron = 47,
IronBall = 278, // TODO
IronPlate = 313,
@@ -1317,87 +800,53 @@ public enum PBEItem : ushort
KingsRock = 221, // TODO
LaggingTail = 279, // TODO
LansatBerry = 206, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
LavaCookie = 42,
LaxIncense = 255,
- ///
- /// No effect.
- ///
+ /// No effect.
LeafStone = 85,
Leftovers = 234,
- ///
- /// No effect.
- ///
+ /// No effect.
Lemonade = 32,
LeppaBerry = 154, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
LevelBall = 493,
LiechiBerry = 201, // TODO
LifeOrb = 270,
LightBall = 236,
LightClay = 269,
- ///
- /// No effect.
- ///
+ /// No effect.
LikeMail = 142,
- ///
- /// No effect.
- ///
+ /// No effect.
LoveBall = 496,
- ///
- /// No effect.
- ///
+ /// No effect.
LuckIncense = 319,
- ///
- /// No effect.
- ///
+ /// No effect.
LuckyEgg = 231,
LuckyPunch = 256,
LumBerry = 157, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
LureBall = 494,
LustrousOrb = 136,
- ///
- /// No effect.
- ///
+ /// No effect.
LuxuryBall = 11,
MachoBrace = 215,
- ///
- /// No effect.
- ///
+ /// No effect.
Magmarizer = 323,
Magnet = 242,
MagoBerry = 161, // TODO
MagostBerry = 176, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
MasterBall = 1,
- ///
- /// No effect.
- ///
+ /// No effect.
MaxElixir = 41,
- ///
- /// No effect.
- ///
+ /// No effect.
MaxEther = 39,
- ///
- /// No effect.
- ///
+ /// No effect.
MaxPotion = 24,
- ///
- /// No effect.
- ///
+ /// No effect.
MaxRepel = 77,
- ///
- /// No effect.
- ///
+ /// No effect.
MaxRevive = 29,
MeadowPlate = 301,
MentalHerb = 219, // TODO
@@ -1407,116 +856,70 @@ public enum PBEItem : ushort
MicleBerry = 209, // TODO
MindPlate = 307,
MiracleSeed = 239,
- ///
- /// No effect.
- ///
+ /// No effect.
MoomooMilk = 33,
- ///
- /// No effect.
- ///
+ /// No effect.
MoonBall = 498,
- ///
- /// No effect.
- ///
+ /// No effect.
MoonStone = 81,
MuscleBand = 266,
- ///
- /// No effect.
- ///
+ /// No effect.
MuscleWing = 566,
MysticWater = 243,
NanabBerry = 166, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
NestBall = 8,
- ///
- /// No effect.
- ///
+ /// No effect.
NetBall = 6,
NeverMeltIce = 246,
NomelBerry = 178, // TODO
NormalGem = 564,
- ///
- /// No effect.
- ///
+ /// No effect.
Nugget = 92,
OccaBerry = 184, // TODO
OddIncense = 314,
- ///
- /// No effect.
- ///
+ /// No effect.
OddKeystone = 111,
- ///
- /// No effect.
- ///
+ /// No effect.
OldAmber = 103,
- ///
- /// No effect.
- ///
+ /// No effect.
OldGateau = 54,
OranBerry = 155, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
OvalStone = 110,
PamtreBerry = 180, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ParalyzHeal = 22,
- ///
- /// No effect.
- ///
+ /// No effect.
ParkBall = 500,
PasshoBerry = 185, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
PassOrb = 575,
PayapaBerry = 193, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
Pearl = 88,
- ///
- /// No effect.
- ///
+ /// No effect.
PearlString = 582,
PechaBerry = 151, // TODO
PersimBerry = 156, // TODO
PetayaBerry = 204, // TODO
PinapBerry = 168, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
PinkScarf = 262,
- ///
- /// No effect.
- ///
+ /// No effect.
PlumeFossil = 573,
- ///
- /// No effect.
- ///
+ /// No effect.
PnkApricorn = 489,
PoisonBarb = 245,
PoisonGem = 554,
- ///
- /// No effect.
- ///
+ /// No effect.
PokeBall = 4,
- ///
- /// No effect.
- ///
+ /// No effect.
PokeDoll = 63,
- ///
- /// No effect.
- ///
+ /// No effect.
PokeToy = 577,
PomegBerry = 169, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
Potion = 17,
PowerAnklet = 293,
PowerBand = 292,
@@ -1525,158 +928,90 @@ public enum PBEItem : ushort
PowerHerb = 271,
PowerLens = 291,
PowerWeight = 294,
- ///
- /// No effect.
- ///
+ /// No effect.
PPMax = 53,
- ///
- /// No effect.
- ///
+ /// No effect.
PPUp = 51,
- ///
- /// No effect.
- ///
+ /// No effect.
PremierBall = 12,
- ///
- /// No effect.
- ///
+ /// No effect.
PrettyWing = 571,
- ///
- /// No effect.
- ///
+ /// No effect.
PrismScale = 537,
- ///
- /// No effect.
- ///
+ /// No effect.
Protector = 321,
- ///
- /// No effect.
- ///
+ /// No effect.
Protein = 46,
PsychicGem = 557,
- ///
- /// No effect.
- ///
+ /// No effect.
PureIncense = 320,
QualotBerry = 171, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
QuickBall = 15,
QuickClaw = 217, // TODO
QuickPowder = 274,
RabutaBerry = 177, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
RageCandyBar = 504,
- ///
- /// No effect.
- ///
+ /// No effect.
RareBone = 106,
- ///
- /// No effect.
- ///
+ /// No effect.
RareCandy = 50,
RawstBerry = 152, // TODO
RazorClaw = 326,
RazorFang = 327, // TODO
RazzBerry = 164, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ReaperCloth = 325,
- ///
- /// No effect.
- ///
+ /// No effect.
RedApricorn = 485,
RedCard = 542, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
RedFlute = 67,
- ///
- /// No effect.
- ///
+ /// No effect.
RedScarf = 260,
- ///
- /// No effect.
- ///
+ /// No effect.
RedShard = 72,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicBand = 588,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicCopper = 584,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicCrown = 590,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicGold = 586,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicSilver = 585,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicStatue = 589,
- ///
- /// No effect.
- ///
+ /// No effect.
RelicVase = 587,
- ///
- /// No effect.
- ///
+ /// No effect.
RepeatBall = 9,
- ///
- /// No effect.
- ///
+ /// No effect.
Repel = 79,
- ///
- /// No effect.
- ///
+ /// No effect.
ReplyMail = 143,
- ///
- /// No effect.
- ///
+ /// No effect.
ResistWing = 567,
- ///
- /// No effect.
- ///
+ /// No effect.
RevivalHerb = 37,
- ///
- /// No effect.
- ///
+ /// No effect.
Revive = 28,
RindoBerry = 187, // TODO
RingTarget = 543, // TODO
RockGem = 559,
RockIncense = 315,
RockyHelmet = 540,
- ///
- /// No effect.
- ///
+ /// No effect.
RootFossil = 99,
RoseIncense = 318,
RowapBerry = 212, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
RSVPMail = 139,
- ///
- /// No effect.
- ///
+ /// No effect.
SacredAsh = 44,
- ///
- /// No effect.
- ///
+ /// No effect.
SafariBall = 5,
SalacBerry = 203, // TODO
ScopeLens = 232,
@@ -1684,199 +1019,121 @@ public enum PBEItem : ushort
SharpBeak = 244,
ShedShell = 295, // TODO
ShellBell = 253, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ShinyStone = 107,
- ///
- /// No effect.
- ///
+ /// No effect.
ShoalSalt = 70,
- ///
- /// No effect.
- ///
+ /// No effect.
ShoalShell = 71,
ShockDrive = 117,
ShucaBerry = 191, // TODO
SilkScarf = 251,
SilverPowder = 222,
SitrusBerry = 158,
- ///
- /// No effect.
- ///
+ /// No effect.
SkullFossil = 105,
SkyPlate = 306,
- ///
- /// No effect.
- ///
+ /// No effect.
SmokeBall = 228,
SmoothRock = 283,
- ///
- /// No effect.
- ///
+ /// No effect.
SodaPop = 31,
SoftSand = 237,
- ///
- /// No effect.
- ///
+ /// No effect.
SootheBell = 218,
SoulDew = 225,
SpellTag = 247,
SpelonBerry = 179, // TODO
SplashPlate = 299,
SpookyPlate = 310,
- ///
- /// No effect.
- ///
+ /// No effect.
SportBall = 499,
- ///
- /// No effect.
- ///
+ /// No effect.
StableMulch = 97,
- ///
- /// No effect.
- ///
+ /// No effect.
Stardust = 90,
StarfBerry = 207, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
StarPiece = 91,
SteelGem = 563,
Stick = 259,
StickyBarb = 288, // TODO
StonePlate = 309,
- ///
- /// No effect.
- ///
+ /// No effect.
SunStone = 80,
- ///
- /// No effect.
- ///
+ /// No effect.
SuperPotion = 26,
- ///
- /// No effect.
- ///
+ /// No effect.
SuperRepel = 76,
- ///
- /// No effect.
- ///
+ /// No effect.
SweetHeart = 134,
- ///
- /// No effect.
- ///
+ /// No effect.
SwiftWing = 570,
TamatoBerry = 174, // TODO
TangaBerry = 194, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
ThanksMail = 140,
ThickClub = 258,
- ///
- /// No effect.
- ///
+ /// No effect.
Thunderstone = 83,
- ///
- /// No effect.
- ///
+ /// No effect.
TimerBall = 10,
- ///
- /// No effect.
- ///
+ /// No effect.
TinyMushroom = 86,
- ///
- /// The Pokémon contracts at the end of each turn if it has no other and it does not have or .
- ///
+ /// The Pokémon contracts at the end of each turn if it has no other and it does not have or .
ToxicOrb = 272,
ToxicPlate = 304,
TwistedSpoon = 248,
- ///
- /// No effect.
- ///
+ /// No effect.
UltraBall = 2,
UpGrade = 252,
WacanBerry = 186, // TODO
WaterGem = 549,
- ///
- /// No effect.
- ///
+ /// No effect.
WaterStone = 84,
WatmelBerry = 181, // TODO
WaveIncense = 317,
WepearBerry = 167, // TODO
WhiteHerb = 214, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
WhiteFlute = 69,
WideLens = 265,
WikiBerry = 160, // TODO
WiseGlasses = 267,
- ///
- /// No effect.
- ///
+ /// No effect.
WhtApricorn = 490,
- ///
- /// No effect.
- ///
+ /// No effect.
XAccuracy = 60,
- ///
- /// No effect.
- ///
+ /// No effect.
XAttack = 57,
- ///
- /// No effect.
- ///
+ /// No effect.
XDefend = 58,
- ///
- /// No effect.
- ///
+ /// No effect.
XSpecial = 61,
- ///
- /// No effect.
- ///
+ /// No effect.
XSpDef = 62,
- ///
- /// No effect.
- ///
+ /// No effect.
XSpeed = 59,
YacheBerry = 188, // TODO
- ///
- /// No effect.
- ///
+ /// No effect.
YellowFlute = 66,
- ///
- /// No effect.
- ///
+ /// No effect.
YellowScarf = 264,
- ///
- /// No effect.
- ///
+ /// No effect.
YellowShard = 74,
- ///
- /// No effect.
- ///
+ /// No effect.
YlwApricorn = 487,
ZapPlate = 300,
- ///
- /// No effect.
- ///
+ /// No effect.
Zinc = 52,
ZoomLens = 276 // TODO
}
- ///
- /// Represents a specific Pokémon's special ability.
- ///
+ /// Represents a specific Pokémon's special ability.
public enum PBEAbility : byte
{
- ///
- /// The Pokémon's ability was suppressed with .
- ///
+ /// The Pokémon's ability was suppressed with .
None = 0,
- ///
- /// The Pokémon has a stronger same-type-attack-bonus.
- ///
+ /// The Pokémon has a stronger same-type-attack-bonus.
Adaptability = 91,
Aftermath = 106, // TODO
AirLock = 76,
@@ -1885,25 +1142,17 @@ public enum PBEAbility : byte
Anticipation = 107, // TODO
ArenaTrap = 71, // TODO
BadDreams = 123, // TODO
- ///
- /// The Pokémon takes no critical hits.
- ///
+ /// The Pokémon takes no critical hits.
BattleArmor = 4,
BigPecks = 145, // TODO
- ///
- /// When the Pokémon has low HP, its Fire-type moves get a power boost.
- ///
+ /// When the Pokémon has low HP, its Fire-type moves get a power boost.
Blaze = 66,
- ///
- /// The Pokémon gets a speed boost in harsh sunlight.
- ///
+ /// The Pokémon gets a speed boost in harsh sunlight.
Chlorophyll = 34,
ClearBody = 29, // TODO
CloudNine = 13,
ColorChange = 16, // TODO
- ///
- /// The Pokémon accuracy is boosted.
- ///
+ /// The Pokémon accuracy is boosted.
Compoundeyes = 14,
Contrary = 126, // TODO
CursedBody = 130, // TODO
@@ -1912,20 +1161,14 @@ public enum PBEAbility : byte
Defeatist = 129,
Defiant = 128, // TODO
Download = 88, // TODO
- ///
- /// The Pokémon changes the weather to infinite rain.
- ///
+ /// The Pokémon changes the weather to infinite rain.
Drizzle = 2,
- ///
- /// The Pokémon changes the weather to infinite harsh sunlight.
- ///
+ /// The Pokémon changes the weather to infinite harsh sunlight.
Drought = 70,
DrySkin = 87, // TODO
EarlyBird = 48,
EffectSpore = 27,
- ///
- /// The Pokémon takes less damage from incoming super-effective moves.
- ///
+ /// The Pokémon takes less damage from incoming super-effective moves.
Filter = 111,
FlameBody = 49,
FlareBoost = 138, // TODO
@@ -1936,76 +1179,52 @@ public enum PBEAbility : byte
FriendGuard = 132, // TODO
Frisk = 119, // TODO
Gluttony = 82, // TODO
- ///
- /// The Pokémon's attack is boosted when it is afflicted with a and the damage reduction from is not applied.
- ///
+ /// The Pokémon's attack is boosted when it is afflicted with a and the damage reduction from is not applied.
Guts = 62,
Harvest = 139, // TODO
Healer = 131,
- ///
- /// The Pokémon takes less damage from Fire-type moves and from a burn.
- ///
+ /// The Pokémon takes less damage from Fire-type moves and from a burn.
Heatproof = 85,
HeavyMetal = 134, // TODO
- ///
- /// No effect in battle.
- ///
+ /// No effect in battle.
HoneyGather = 118,
- ///
- /// The Pokémon's attack is boosted.
- ///
+ /// The Pokémon's attack is boosted.
HugePower = 37,
- ///
- /// The Pokémon's attack is boosted, but its accuracy is lower for physical moves.
- ///
+ /// The Pokémon's attack is boosted, but its accuracy is lower for physical moves.
Hustle = 55,
Hydration = 93, // TODO
HyperCutter = 52, // TODO
- ///
- /// In a hailstorm, the Pokémon takes no damage from the hailstorm and restores HP at the end of each turn.
- ///
+ /// In a hailstorm, the Pokémon takes no damage from the hailstorm and restores HP at the end of each turn.
IceBody = 115,
- ///
- /// No effect in battle.
- ///
+ /// No effect in battle.
Illuminate = 35,
Illusion = 149,
Immunity = 17,
- ///
- /// The Pokémon transforms into the foe across from it when switching in.
- ///
+ /// The Pokémon transforms into the foe across from it when switching in.
Imposter = 150,
Infiltrator = 151, // TODO
InnerFocus = 39,
Insomnia = 15,
Intimidate = 22, // TODO
IronBarbs = 160,
- ///
- /// The power of moves with is increased.
- ///
+ /// The power of moves with is increased.
IronFist = 89,
Justified = 154,
KeenEye = 51, // TODO
Klutz = 103, // TODO
LeafGuard = 102, // TODO
- ///
- /// The Pokémon is immune to Ground-type moves and most entry hazards.
- ///
+ /// The Pokémon is immune to Ground-type moves and most entry hazards.
Levitate = 26,
LightMetal = 135, // TODO
Lightningrod = 31, // TODO
- ///
- /// The Pokémon cannot be paralyzed.
- ///
+ /// The Pokémon cannot be paralyzed.
Limber = 7,
LiquidOoze = 64,
MagicBounce = 156, // TODO
MagicGuard = 98, // TODO
MagmaArmor = 40,
MagnetPull = 42, // TODO
- ///
- /// The Pokémon's defense is boosted when it is afflicted with a .
- ///
+ /// The Pokémon's defense is boosted when it is afflicted with a .
MarvelScale = 63,
Minus = 58,
MoldBreaker = 104, // TODO
@@ -2013,25 +1232,17 @@ public enum PBEAbility : byte
MotorDrive = 78, // TODO
Moxie = 153, // TODO
Multiscale = 136, // TODO
- ///
- /// No effect in battle.
- ///
+ /// No effect in battle.
Multitype = 121,
Mummy = 152,
NaturalCure = 30,
- ///
- /// The Pokémon will always hit and always get hit unless protection is used.
- ///
+ /// The Pokémon will always hit and always get hit unless protection is used.
NoGuard = 99,
Normalize = 96,
Oblivious = 12,
- ///
- /// The Pokémon takes no damage from a hailstorm or sandstorm.
- ///
+ /// The Pokémon takes no damage from a hailstorm or sandstorm.
Overcoat = 142,
- ///
- /// When the Pokémon has low HP, its Grass-type moves get a power boost.
- ///
+ /// When the Pokémon has low HP, its Grass-type moves get a power boost.
Overgrow = 65,
OwnTempo = 20,
Pickpocket = 124, // TODO
@@ -2042,14 +1253,10 @@ public enum PBEAbility : byte
PoisonTouch = 143, // TODO
Prankster = 158,
Pressure = 46, // TODO
- ///
- /// The Pokémon's attack is boosted.
- ///
+ /// The Pokémon's attack is boosted.
PurePower = 74,
QuickFeet = 95,
- ///
- /// In rain, the Pokémon restores HP at the end of each turn.
- ///
+ /// In rain, the Pokémon restores HP at the end of each turn.
RainDish = 44,
Rattled = 155,
Reckless = 120,
@@ -2057,25 +1264,15 @@ public enum PBEAbility : byte
Rivalry = 79, // TODO
RockHead = 69,
RoughSkin = 24,
- ///
- /// No effect in battle.
- ///
+ /// No effect in battle.
RunAway = 50,
- ///
- /// In a sandstorm, the Pokémon takes no damage from the sandstorm and its Rock-, Ground-, and Steel-type moves get a power boost.
- ///
+ /// In a sandstorm, the Pokémon takes no damage from the sandstorm and its Rock-, Ground-, and Steel-type moves get a power boost.
SandForce = 159,
- ///
- /// The Pokémon gets a speed boost in a sandstorm.
- ///
+ /// The Pokémon gets a speed boost in a sandstorm.
SandRush = 146,
- ///
- /// The Pokémon changes the weather to an infinite sandstorm.
- ///
+ /// The Pokémon changes the weather to an infinite sandstorm.
SandStream = 45,
- ///
- /// In a sandstorm, the Pokémon takes no damage from the sandstorm and gets a 20% evasion boost.
- ///
+ /// In a sandstorm, the Pokémon takes no damage from the sandstorm and gets a 20% evasion boost.
SandVeil = 8,
SapSipper = 157, // TODO
Scrappy = 113, // TODO
@@ -2083,36 +1280,22 @@ public enum PBEAbility : byte
ShadowTag = 23, // TODO
ShedSkin = 61,
SheerForce = 125, // TODO
- ///
- /// The Pokémon takes no critical hits.
- ///
+ /// The Pokémon takes no critical hits.
ShellArmor = 75,
ShieldDust = 19, // TODO
- ///
- /// The Pokémon's stat changes are doubled.
- ///
+ /// The Pokémon's stat changes are doubled.
Simple = 86,
SkillLink = 92, // TODO
SlowStart = 112, // TODO
- ///
- /// The Pokémon deals more damage when landing critical hits.
- ///
+ /// The Pokémon deals more damage when landing critical hits.
Sniper = 97,
- ///
- /// In a hailstorm, the Pokémon takes no damage from the hailstorm and gets a 20% evasion boost.
- ///
+ /// In a hailstorm, the Pokémon takes no damage from the hailstorm and gets a 20% evasion boost.
SnowCloak = 81,
- ///
- /// The Pokémon changes the weather to an infinite hailstorm.
- ///
+ /// The Pokémon changes the weather to an infinite hailstorm.
SnowWarning = 117,
- ///
- /// In harsh sunlight, the Pokémon gets a special attack boost and takes damage at the end of each turn.
- ///
+ /// In harsh sunlight, the Pokémon gets a special attack boost and takes damage at the end of each turn.
SolarPower = 94,
- ///
- /// The Pokémon takes less damage from incoming super-effective moves.
- ///
+ /// The Pokémon takes less damage from incoming super-effective moves.
SolidRock = 116,
Soundproof = 43, // TODO
SpeedBoost = 3,
@@ -2124,34 +1307,22 @@ public enum PBEAbility : byte
StormDrain = 114, // TODO
Sturdy = 5,
SuctionCups = 21, // TODO
- ///
- /// The Pokémon is more likely to land critical hits.
- ///
+ /// The Pokémon is more likely to land critical hits.
SuperLuck = 105,
- ///
- /// When the Pokémon has low HP, its Bug-type moves get a power boost.
- ///
+ /// When the Pokémon has low HP, its Bug-type moves get a power boost.
Swarm = 68,
- ///
- /// The Pokémon gets a speed boost in rain.
- ///
+ /// The Pokémon gets a speed boost in rain.
SwiftSwim = 33,
Synchronize = 28, // TODO
TangledFeet = 77,
Technician = 101, // TODO
Telepathy = 140, // TODO
Teravolt = 164, // TODO
- ///
- /// The Pokémon takes less damage from Ice- and Fire-type moves.
- ///
+ /// The Pokémon takes less damage from Ice- and Fire-type moves.
ThickFat = 47,
- ///
- /// The Pokémon deals double damage with outgoing not-very-effective moves.
- ///
+ /// The Pokémon deals double damage with outgoing not-very-effective moves.
TintedLens = 110,
- ///
- /// When the Pokémon has low HP, its Water-type moves get a power boost.
- ///
+ /// When the Pokémon has low HP, its Water-type moves get a power boost.
Torrent = 67,
ToxicBoost = 137,
Trace = 36, // TODO
@@ -2167,20 +1338,14 @@ public enum PBEAbility : byte
WaterVeil = 41,
WeakArmor = 133,
WhiteSmoke = 73, // TODO
- ///
- /// The Pokémon is immune to all damaging moves except for moves that would deal super-effective damage.
- ///
+ /// The Pokémon is immune to all damaging moves except for moves that would deal super-effective damage.
WonderGuard = 25, // TODO: Mold Breaker, Teravolt, Turboblaze, Role Play, Skill Swap
WonderSkin = 147,
ZenMode = 161, // TODO
- ///
- /// Invalid ability.
- ///
+ /// Invalid ability.
MAX = 165,
}
- ///
- /// Represents a specific Pokémon species.
- ///
+ /// Represents a specific Pokémon species.
public enum PBESpecies : uint
{
Abomasnow = 460,
@@ -2931,76 +2096,42 @@ public enum PBEMoveTarget : byte
SingleSurrounding, // Single battler surrounding (Ex. Tackle)
Varies // Possible targets vary (Ex. Curse)
}
- ///
- /// Represents a specific 's flags.
- ///
+ /// Represents a specific 's flags.
[Flags]
public enum PBEMoveFlag : ushort
{
- ///
- /// The move has no flags.
- ///
+ /// The move has no flags.
None,
- ///
- /// The move's power is boosted by .
- ///
+ /// The move's power is boosted by .
AffectedByIronFist = 1 << 0,
- ///
- /// The move is blocked by and .
- ///
+ /// The move is blocked by and .
AffectedByMagicCoat = 1 << 1,
- ///
- /// The move can be copied by .
- ///
+ /// The move can be copied by .
AffectedByMirrorMove = 1 << 2,
- ///
- /// The move is blocked by , , and .
- ///
+ /// The move is blocked by , , and .
AffectedByProtect = 1 << 3,
- ///
- /// The move's power is boosted by .
- ///
+ /// The move's power is boosted by .
AffectedByReckless = 1 << 4,
- ///
- /// The move can be stolen by .
- ///
+ /// The move can be stolen by .
AffectedBySnatch = 1 << 5,
- ///
- /// The move always lands a critical hit.
- ///
+ /// The move always lands a critical hit.
AlwaysCrit = 1 << 6,
BlockedByMetronome = 1 << 7,
- ///
- /// The move removes from the user.
- ///
+ /// The move removes from the user.
DefrostsUser = 1 << 8,
- ///
- /// The move has a higher chance of landing a critical hit.
- ///
+ /// The move has a higher chance of landing a critical hit.
HighCritChance = 1 << 9,
- ///
- /// The move can hit targets.
- ///
+ /// The move can hit targets.
HitsAirborne = 1 << 10,
- ///
- /// The move can hit targets.
- ///
+ /// The move can hit targets.
HitsUnderground = 1 << 11,
- ///
- /// The move can hit targets.
- ///
+ /// The move can hit targets.
HitsUnderwater = 1 << 12,
- ///
- /// The user makes contact with the target, causing it to take damage from the target's , , and .
- ///
+ /// The user makes contact with the target, causing it to take damage from the target's , , and .
MakesContact = 1 << 13,
- ///
- /// The move is blocked by .
- ///
+ /// The move is blocked by .
SoundBased = 1 << 14,
- ///
- /// The move does not consume a held gem.
- ///
+ /// The move does not consume a held gem.
UnaffectedByGems = 1 << 15
}
public enum PBEMoveEffect : byte
@@ -3497,9 +2628,7 @@ public enum PBEMove : ushort
XScissor = 404,
ZapCannon = 192,
ZenHeadbutt = 428,
- ///
- /// Invalid move.
- ///
+ /// Invalid move.
MAX = 560
}
}
diff --git a/PokemonBattleEngine/Data/IndividualValueCollection.cs b/PokemonBattleEngine/Data/IndividualValueCollection.cs
deleted file mode 100644
index a7bd774cf..000000000
--- a/PokemonBattleEngine/Data/IndividualValueCollection.cs
+++ /dev/null
@@ -1,213 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-
-namespace Kermalis.PokemonBattleEngine.Data
-{
- public sealed class PBEIndividualValueCollection : IEnumerable, INotifyPropertyChanged
- {
- public sealed class PBEIndividualValue : INotifyPropertyChanged
- {
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- private readonly PBEIndividualValueCollection parent;
-
- public PBEStat Stat { get; }
- private byte value;
- public byte Value
- {
- get => value;
- set
- {
- if (this.value != value)
- {
- parent.Set(Stat, value);
- }
- }
- }
-
- internal PBEIndividualValue(PBEIndividualValueCollection parent, PBEStat stat)
- {
- this.parent = parent;
- Stat = stat;
- }
-
- internal void Update(byte value)
- {
- this.value = value;
- OnPropertyChanged(nameof(Value));
- }
- }
-
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- public PBESettings Settings { get; }
- private PBEIndividualValue[] ivs;
-
- private PBEType hiddenPowerType;
- public PBEType HiddenPowerType
- {
- get => hiddenPowerType;
- private set
- {
- if (hiddenPowerType != value)
- {
- hiddenPowerType = value;
- OnPropertyChanged(nameof(HiddenPowerType));
- }
- }
- }
- private byte hiddenPowerBasePower;
- public byte HiddenPowerBasePower
- {
- get => hiddenPowerBasePower;
- private set
- {
- if (hiddenPowerBasePower != value)
- {
- hiddenPowerBasePower = value;
- OnPropertyChanged(nameof(HiddenPowerBasePower));
- }
- }
- }
-
- public PBEIndividualValueCollection(PBESettings settings, bool randomize)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- Settings = settings;
- CreateIVs();
- if (randomize)
- {
- Randomize();
- }
- }
- public PBEIndividualValueCollection(PBESettings settings, byte hp = 0, byte attack = 0, byte defense = 0, byte spAttack = 0, byte spDefense = 0, byte speed = 0)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- CreateIVs();
- TrySet(hp, attack, defense, spAttack, spDefense, speed);
- }
- public PBEIndividualValueCollection(PBESettings settings, PBEIndividualValueCollection other)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- if (other == null)
- {
- throw new ArgumentNullException(nameof(other));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- CreateIVs();
- TrySet(other[PBEStat.HP].Value, other[PBEStat.Attack].Value, other[PBEStat.Defense].Value, other[PBEStat.SpAttack].Value, other[PBEStat.SpDefense].Value, other[PBEStat.Speed].Value);
- }
- private void CreateIVs()
- {
- ivs = new PBEIndividualValue[6]
- {
- new PBEIndividualValue(this, PBEStat.HP),
- new PBEIndividualValue(this, PBEStat.Attack),
- new PBEIndividualValue(this, PBEStat.Defense),
- new PBEIndividualValue(this, PBEStat.SpAttack),
- new PBEIndividualValue(this, PBEStat.SpDefense),
- new PBEIndividualValue(this, PBEStat.Speed)
- };
- UpdateHiddenPower();
- }
- private void UpdateHiddenPower()
- {
- HiddenPowerType = PBEPokemonData.GetHiddenPowerType(this[PBEStat.HP].Value, this[PBEStat.Attack].Value, this[PBEStat.Defense].Value, this[PBEStat.SpAttack].Value, this[PBEStat.SpDefense].Value, this[PBEStat.Speed].Value);
- HiddenPowerBasePower = PBEPokemonData.GetHiddenPowerBasePower(this[PBEStat.HP].Value, this[PBEStat.Attack].Value, this[PBEStat.Defense].Value, this[PBEStat.SpAttack].Value, this[PBEStat.SpDefense].Value, this[PBEStat.Speed].Value);
- }
- private void TrySet(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)
- {
- Set(PBEStat.HP, hp);
- Set(PBEStat.Attack, attack);
- Set(PBEStat.Defense, defense);
- Set(PBEStat.SpAttack, spAttack);
- Set(PBEStat.SpDefense, spDefense);
- Set(PBEStat.Speed, speed);
- }
-
- public PBEIndividualValue this[PBEStat stat]
- {
- get
- {
- int statIndex = (int)stat;
- if (statIndex >= 6)
- {
- throw new ArgumentOutOfRangeException(nameof(stat));
- }
- else
- {
- return ivs[statIndex];
- }
- }
- }
-
- public void Set(PBEStat stat, byte value)
- {
- int statIndex = (int)stat;
- if (statIndex >= 6)
- {
- throw new ArgumentOutOfRangeException(nameof(stat));
- }
- byte newVal = Math.Min(value, Settings.MaxIVs);
- PBEIndividualValue iv = ivs[statIndex];
- if (iv.Value != newVal)
- {
- iv.Update(newVal);
- UpdateHiddenPower();
- }
- }
- public void Randomize()
- {
- for (int i = 0; i < 6; i++)
- {
- ivs[i].Value = (byte)PBEUtils.RandomInt(0, Settings.MaxIVs);
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- for (int i = 0; i < 6; i++)
- {
- yield return ivs[i];
- }
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
- {
- switch (e.PropertyName)
- {
- case nameof(Settings.MaxIVs):
- {
- TrySet(this[PBEStat.HP].Value, this[PBEStat.Attack].Value, this[PBEStat.Defense].Value, this[PBEStat.SpAttack].Value, this[PBEStat.SpDefense].Value, this[PBEStat.Speed].Value);
- break;
- }
- }
- }
- }
-}
diff --git a/PokemonBattleEngine/Data/IndividualValues.cs b/PokemonBattleEngine/Data/IndividualValues.cs
new file mode 100644
index 000000000..1772f8515
--- /dev/null
+++ b/PokemonBattleEngine/Data/IndividualValues.cs
@@ -0,0 +1,283 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+
+namespace Kermalis.PokemonBattleEngine.Data
+{
+ public sealed class PBEIndividualValues : IDisposable, IEnumerable, INotifyPropertyChanged
+ {
+ public sealed class PBEIndividualValue : INotifyPropertyChanged
+ {
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private readonly PBEIndividualValues _parent;
+
+ public PBEStat Stat { get; }
+ private byte _value;
+ public byte Value
+ {
+ get => _value;
+ set
+ {
+ if (_parent.IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (_value != value)
+ {
+ if (value > _parent.Settings.MaxIVs)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value));
+ }
+ _value = value;
+ OnPropertyChanged(nameof(Value));
+ _parent.UpdateHiddenPower();
+ }
+ }
+ }
+
+ internal PBEIndividualValue(PBEIndividualValues parent, PBEStat stat, byte value)
+ {
+ _parent = parent;
+ Stat = stat;
+ _value = value;
+ }
+ }
+
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public PBESettings Settings { get; }
+ private PBEIndividualValue[] _ivs;
+ public PBEIndividualValue this[PBEStat stat]
+ {
+ get
+ {
+ int statIndex = (int)stat;
+ if (statIndex >= 6)
+ {
+ throw new ArgumentOutOfRangeException(nameof(stat));
+ }
+ return _ivs[statIndex];
+ }
+ }
+
+ private PBEType _hiddenPowerType;
+ public PBEType HiddenPowerType
+ {
+ get => _hiddenPowerType;
+ private set
+ {
+ if (_hiddenPowerType != value)
+ {
+ _hiddenPowerType = value;
+ OnPropertyChanged(nameof(HiddenPowerType));
+ }
+ }
+ }
+ private byte _hiddenPowerBasePower;
+ public byte HiddenPowerBasePower
+ {
+ get => _hiddenPowerBasePower;
+ private set
+ {
+ if (_hiddenPowerBasePower != value)
+ {
+ _hiddenPowerBasePower = value;
+ OnPropertyChanged(nameof(HiddenPowerBasePower));
+ }
+ }
+ }
+
+ internal PBEIndividualValues(PBESettings settings, BinaryReader r)
+ {
+ void Validate(byte val)
+ {
+ if (val > settings.MaxIVs)
+ {
+ throw new InvalidDataException();
+ }
+ }
+ byte hp = r.ReadByte();
+ Validate(hp);
+ byte attack = r.ReadByte();
+ Validate(attack);
+ byte defense = r.ReadByte();
+ Validate(defense);
+ byte spAttack = r.ReadByte();
+ Validate(spAttack);
+ byte spDefense = r.ReadByte();
+ Validate(spDefense);
+ byte speed = r.ReadByte();
+ Validate(speed);
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateIVs(hp, attack, defense, spAttack, spDefense, speed);
+ }
+ internal PBEIndividualValues(PBESettings settings, JToken jToken)
+ {
+ void Validate(byte val, string name)
+ {
+ if (val > settings.MaxIVs)
+ {
+ throw new ArgumentOutOfRangeException(nameof(PBEPokemonShell.IndividualValues), $"\"{name}\" individual value must not exceed \"{nameof(settings.MaxIVs)}\" ({settings.MaxIVs})");
+ }
+ }
+ byte hp = jToken[nameof(PBEStat.HP)].Value();
+ Validate(hp, nameof(PBEStat.HP));
+ byte attack = jToken[nameof(PBEStat.Attack)].Value();
+ Validate(attack, nameof(PBEStat.Attack));
+ byte defense = jToken[nameof(PBEStat.Defense)].Value();
+ Validate(defense, nameof(PBEStat.Defense));
+ byte spAttack = jToken[nameof(PBEStat.SpAttack)].Value();
+ Validate(spAttack, nameof(PBEStat.SpAttack));
+ byte spDefense = jToken[nameof(PBEStat.SpDefense)].Value();
+ Validate(spDefense, nameof(PBEStat.SpDefense));
+ byte speed = jToken[nameof(PBEStat.Speed)].Value();
+ Validate(speed, nameof(PBEStat.Speed));
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateIVs(hp, attack, defense, spAttack, spDefense, speed);
+ }
+ internal PBEIndividualValues(PBEIndividualValues other)
+ {
+ Settings = other.Settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ CreateIVs(other[PBEStat.HP].Value, other[PBEStat.Attack].Value, other[PBEStat.Defense].Value, other[PBEStat.SpAttack].Value, other[PBEStat.SpDefense].Value, other[PBEStat.Speed].Value);
+ }
+ public PBEIndividualValues(PBESettings settings, bool randomize)
+ {
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ _canDispose = true;
+ CreateIVs(0, 0, 0, 0, 0, 0);
+ if (randomize)
+ {
+ Randomize();
+ }
+ }
+
+ private void CreateIVs(byte hp, byte attack, byte defense, byte spAttack, byte spDefense, byte speed)
+ {
+ _ivs = new PBEIndividualValue[6]
+ {
+ new PBEIndividualValue(this, PBEStat.HP, hp),
+ new PBEIndividualValue(this, PBEStat.Attack, attack),
+ new PBEIndividualValue(this, PBEStat.Defense, defense),
+ new PBEIndividualValue(this, PBEStat.SpAttack, spAttack),
+ new PBEIndividualValue(this, PBEStat.SpDefense, spDefense),
+ new PBEIndividualValue(this, PBEStat.Speed, speed)
+ };
+ UpdateHiddenPower();
+ }
+ private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Settings.MaxIVs):
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ PBEIndividualValue iv = _ivs[i];
+ if (iv.Value > Settings.MaxIVs)
+ {
+ iv.Value = Settings.MaxIVs;
+ }
+ }
+ break;
+ }
+ }
+ }
+ private void UpdateHiddenPower()
+ {
+ HiddenPowerType = PBEPokemonData.GetHiddenPowerType(this[PBEStat.HP].Value, this[PBEStat.Attack].Value, this[PBEStat.Defense].Value, this[PBEStat.SpAttack].Value, this[PBEStat.SpDefense].Value, this[PBEStat.Speed].Value);
+ HiddenPowerBasePower = PBEPokemonData.GetHiddenPowerBasePower(this[PBEStat.HP].Value, this[PBEStat.Attack].Value, this[PBEStat.Defense].Value, this[PBEStat.SpAttack].Value, this[PBEStat.SpDefense].Value, this[PBEStat.Speed].Value);
+ }
+
+ private bool _canDispose;
+ public bool CanDispose
+ {
+ get => _canDispose;
+ internal set
+ {
+ if (_canDispose != value)
+ {
+ _canDispose = value;
+ OnPropertyChanged(nameof(CanDispose));
+ }
+ }
+ }
+ public bool IsDisposed { get; private set; }
+ public void Dispose()
+ {
+ if (!_canDispose)
+ {
+ throw new InvalidOperationException();
+ }
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ OnPropertyChanged(nameof(IsDisposed));
+ Settings.PropertyChanged -= OnSettingsChanged;
+ }
+ }
+
+ public void Randomize()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ _ivs[i].Value = (byte)PBEUtils.RandomInt(0, Settings.MaxIVs);
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ yield return _ivs[i];
+ }
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ internal void ToBytes(List bytes)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ bytes.Add(_ivs[i].Value);
+ }
+ }
+ internal void ToJson(JsonTextWriter w)
+ {
+ w.WriteStartObject();
+ for (int i = 0; i < 6; i++)
+ {
+ PBEIndividualValue iv = _ivs[i];
+ w.WritePropertyName(iv.Stat.ToString());
+ w.WriteValue(iv.Value);
+ }
+ w.WriteEndObject();
+ }
+ }
+}
diff --git a/PokemonBattleEngine/Data/ItemData.cs b/PokemonBattleEngine/Data/ItemData.cs
index 940fa0f9a..e07276e28 100644
--- a/PokemonBattleEngine/Data/ItemData.cs
+++ b/PokemonBattleEngine/Data/ItemData.cs
@@ -5,17 +5,11 @@ namespace Kermalis.PokemonBattleEngine.Data
{
public sealed class PBEItemData
{
- ///
- /// The power has when the user is holding this item. 0 will cause the move to fail.
- ///
+ /// The power has when the user is holding this item. 0 will cause the move to fail.
public byte FlingPower { get; }
- ///
- /// The power has when the user is holding this item. 0 will cause the move to fail.
- ///
+ /// The power has when the user is holding this item. 0 will cause the move to fail.
public byte NaturalGiftPower { get; }
- ///
- /// The type becomes when the user is holding this item.
- ///
+ /// The type becomes when the user is holding this item.
public PBEType NaturalGiftType { get; }
private PBEItemData(byte flingPower = 0, byte naturalGiftPower = 0, PBEType naturalGiftType = PBEType.None)
diff --git a/PokemonBattleEngine/Data/LegalityChecker.cs b/PokemonBattleEngine/Data/LegalityChecker.cs
index 4af858430..839de4f89 100644
--- a/PokemonBattleEngine/Data/LegalityChecker.cs
+++ b/PokemonBattleEngine/Data/LegalityChecker.cs
@@ -10,8 +10,10 @@ public static class PBELegalityChecker
// TODO: Include generation?
// TODO: Sketch
// TODO: Same goals as MoveLegalityCheck
- public static PBEMove[] GetLegalMoves(PBESpecies species, byte level)
+ public static PBEMove[] GetLegalMoves(PBESpecies species, byte level, PBESettings settings)
{
+ PBEPokemonShell.ValidateSpecies(species);
+ PBEPokemonShell.ValidateLevel(level, settings);
var evolutionChain = new List();
void AddPreEvolutions(PBESpecies sp)
{
@@ -43,48 +45,34 @@ void AddPreEvolutions(PBESpecies sp)
// TODO: Check if HMs were transferred
// TODO: Check events for moves
// TODO: EggMove_Special
- public static void MoveLegalityCheck(PBESpecies species, byte level, IEnumerable moves, PBESettings settings)
+ public static void MoveLegalityCheck(PBEMoveset moveset)
{
// Validate basic move rules first
- if (moves == null || moves.Count() != settings.NumMoves)
+ if (moveset == null)
{
- throw new ArgumentOutOfRangeException(nameof(moves), $"A Pokémon must have exactly {settings.NumMoves} moves.");
- }
- if (moves.Any(m => moves.Count(m2 => m != PBEMove.None && m == m2) > 1))
- {
- throw new ArgumentOutOfRangeException(nameof(moves), $"A Pokémon cannot have duplicate moves other than {PBEMove.None}.");
- }
- if (moves.All(m => m == PBEMove.None))
- {
- throw new ArgumentOutOfRangeException(nameof(moves), $"A Pokémon must have at least one move other than {PBEMove.None}.");
- }
- if (species == PBESpecies.Keldeo_Resolute && !moves.Contains(PBEMove.SecretSword))
- {
- throw new ArgumentOutOfRangeException(nameof(moves), $"{species} must have {PBEMove.SecretSword}.");
+ throw new ArgumentNullException(nameof(moveset));
}
// Combine all moves from pre-evolutions
- IEnumerable evolutionChain = PBEPokemonData.GetData(species).PreEvolutions.Concat(new[] { species });
+ var evolutionChain = new List();
+ void AddPreEvolutions(PBESpecies sp)
+ {
+ foreach (PBESpecies pkmn in PBEPokemonData.GetData(sp).PreEvolutions)
+ {
+ AddPreEvolutions(pkmn);
+ }
+ evolutionChain.Add(sp);
+ }
+ AddPreEvolutions(moveset.Species);
var levelUp = new List<(PBEMove Move, byte Level, PBEMoveObtainMethod ObtainMethod)>();
var other = new List<(PBEMove Move, PBEMoveObtainMethod ObtainMethod)>();
foreach (PBESpecies pkmn in evolutionChain)
{
var pData = PBEPokemonData.GetData(pkmn);
- levelUp.AddRange(pData.LevelUpMoves.Where(t => t.Level <= level));
+ levelUp.AddRange(pData.LevelUpMoves.Where(t => t.Level <= moveset.Level));
other.AddRange(pData.OtherMoves);
}
- // TODO:
- PBEMove[] allAsMoves = GetLegalMoves(species, level);
-
- // Check if there's a move it cannot possibly learn
- foreach (PBEMove m in moves)
- {
- if (m != PBEMove.None && !allAsMoves.Contains(m))
- {
- throw new ArgumentOutOfRangeException(nameof(moves), $"{species} cannot learn {m}.");
- }
- }
// Check generational rules
bool HasGen3Method(PBEMoveObtainMethod method)
@@ -127,7 +115,7 @@ bool HasGen5Method(PBEMoveObtainMethod method)
|| method.HasFlag(PBEMoveObtainMethod.EggMove_BWB2W2);
}
- IEnumerable<(PBEMove Move, PBEMoveObtainMethod ObtainMethod)> movesAsObtainMethods = levelUp.Where(t => moves.Contains(t.Move)).Select(t => (t.Move, t.ObtainMethod)).Union(other.Where(t => moves.Contains(t.Move)));
+ IEnumerable<(PBEMove Move, PBEMoveObtainMethod ObtainMethod)> movesAsObtainMethods = levelUp.Where(t => moveset.Contains(t.Move)).Select(t => (t.Move, t.ObtainMethod)).Union(other.Where(t => moveset.Contains(t.Move)));
// Check to see where the Pokémon DEFINITELY has been
bool definitelyBeenInGeneration3 = false,
diff --git a/PokemonBattleEngine/Data/LocalizedString.cs b/PokemonBattleEngine/Data/LocalizedString.cs
index 10d6bf7f2..b89d2cf98 100644
--- a/PokemonBattleEngine/Data/LocalizedString.cs
+++ b/PokemonBattleEngine/Data/LocalizedString.cs
@@ -9,24 +9,24 @@ public sealed class PBELocalizedString
public delegate void PBECultureChangedHandler(CultureInfo oldPBECultureInfo);
public static event PBECultureChangedHandler PBECultureChanged;
- private static CultureInfo pbeCulture;
+ private static CultureInfo _pbeCulture;
public static CultureInfo PBECulture
{
- get => pbeCulture;
+ get => _pbeCulture;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
- if (!pbeCulture.Equals(value))
+ if (!_pbeCulture.Equals(value))
{
if (!IsCultureValid(value))
{
throw new ArgumentOutOfRangeException(nameof(value));
}
- CultureInfo oldPBECultureInfo = pbeCulture;
- pbeCulture = value;
+ CultureInfo oldPBECultureInfo = _pbeCulture;
+ _pbeCulture = value;
PBECultureChanged?.Invoke(oldPBECultureInfo);
}
}
@@ -35,7 +35,7 @@ public static CultureInfo PBECulture
static PBELocalizedString()
{
CultureInfo cultureInfo = CultureInfo.CurrentUICulture;
- pbeCulture = IsCultureValid(cultureInfo) ? cultureInfo : CultureInfo.GetCultureInfo("en-US");
+ _pbeCulture = IsCultureValid(cultureInfo) ? cultureInfo : CultureInfo.GetCultureInfo("en-US");
}
public string English { get; }
@@ -61,6 +61,10 @@ private PBELocalizedString(SearchResult result)
public string FromCultureInfo(CultureInfo cultureInfo)
{
+ if (cultureInfo == null)
+ {
+ throw new ArgumentNullException(nameof(cultureInfo));
+ }
string id = cultureInfo.TwoLetterISOLanguageName;
switch (id)
{
@@ -76,13 +80,17 @@ public string FromCultureInfo(CultureInfo cultureInfo)
}
public static bool IsCultureValid(CultureInfo cultureInfo)
{
+ if (cultureInfo == null)
+ {
+ throw new ArgumentNullException(nameof(cultureInfo));
+ }
string id = cultureInfo.TwoLetterISOLanguageName;
return id == "en" || id == "fr" || id == "de" || id == "it" || id == "ja" || id == "ko" || id == "es";
}
public override string ToString()
{
- return FromCultureInfo(pbeCulture);
+ return FromCultureInfo(_pbeCulture);
}
#region Database Querying
@@ -99,8 +107,8 @@ private class SearchResult
public string Korean { get; set; }
public string Spanish { get; set; }
}
- private const string queryText = "SELECT * FROM {0} WHERE StrCmp(English,'{1}') OR StrCmp(French,'{1}') OR StrCmp(German,'{1}') OR StrCmp(Italian,'{1}') OR StrCmp(Japanese_Kana,'{1}') OR StrCmp(Japanese_Kanji,'{1}') OR StrCmp(Korean,'{1}') OR StrCmp(Spanish,'{1}')";
- private const string queryId = "SELECT * FROM {0} WHERE Id={1}";
+ private const string QueryText = "SELECT * FROM {0} WHERE StrCmp(English,'{1}') OR StrCmp(French,'{1}') OR StrCmp(German,'{1}') OR StrCmp(Italian,'{1}') OR StrCmp(Japanese_Kana,'{1}') OR StrCmp(Japanese_Kanji,'{1}') OR StrCmp(Korean,'{1}') OR StrCmp(Spanish,'{1}')";
+ private const string QueryId = "SELECT * FROM {0} WHERE Id={1}";
private static bool GetEnumValue(string value, out TEnum result) where TEnum : struct
{
foreach (TEnum v in Enum.GetValues(typeof(TEnum)))
@@ -118,7 +126,7 @@ private static bool GetEnumValue(string value, out TEnum result) where TE
public static PBEAbility? GetAbilityByName(string abilityName)
{
PBEAbility ability;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "AbilityNames", abilityName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "AbilityNames", abilityName));
if (results.Count == 1)
{
ability = (PBEAbility)results[0].Id;
@@ -135,7 +143,7 @@ public static PBELocalizedString GetAbilityDescription(PBEAbility ability)
{
throw new ArgumentOutOfRangeException(nameof(ability));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "AbilityDescriptions", (byte)ability))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "AbilityDescriptions", (byte)ability))[0]);
}
public static PBELocalizedString GetAbilityName(PBEAbility ability)
{
@@ -143,12 +151,12 @@ public static PBELocalizedString GetAbilityName(PBEAbility ability)
{
throw new ArgumentOutOfRangeException(nameof(ability));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "AbilityNames", (byte)ability))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "AbilityNames", (byte)ability))[0]);
}
public static PBEGender? GetGenderByName(string genderName)
{
PBEGender gender;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "GenderNames", genderName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "GenderNames", genderName));
if (results.Count == 1)
{
gender = (PBEGender)results[0].Id;
@@ -165,12 +173,12 @@ public static PBELocalizedString GetGenderName(PBEGender gender)
{
throw new ArgumentOutOfRangeException(nameof(gender));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "GenderNames", (byte)gender))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "GenderNames", (byte)gender))[0]);
}
public static PBEItem? GetItemByName(string itemName)
{
PBEItem item;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "ItemNames", itemName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "ItemNames", itemName));
if (results.Count == 1)
{
item = (PBEItem)results[0].Id;
@@ -187,7 +195,7 @@ public static PBELocalizedString GetItemDescription(PBEItem item)
{
throw new ArgumentOutOfRangeException(nameof(item));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "ItemDescriptions", (ushort)item))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "ItemDescriptions", (ushort)item))[0]);
}
public static PBELocalizedString GetItemName(PBEItem item)
{
@@ -195,12 +203,12 @@ public static PBELocalizedString GetItemName(PBEItem item)
{
throw new ArgumentOutOfRangeException(nameof(item));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "ItemNames", (ushort)item))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "ItemNames", (ushort)item))[0]);
}
public static PBEMove? GetMoveByName(string moveName)
{
PBEMove move;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "MoveNames", moveName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "MoveNames", moveName));
if (results.Count == 1)
{
move = (PBEMove)results[0].Id;
@@ -217,7 +225,7 @@ public static PBELocalizedString GetMoveDescription(PBEMove move)
{
throw new ArgumentOutOfRangeException(nameof(move));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "MoveDescriptions", (ushort)move))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "MoveDescriptions", (ushort)move))[0]);
}
public static PBELocalizedString GetMoveName(PBEMove move)
{
@@ -225,12 +233,12 @@ public static PBELocalizedString GetMoveName(PBEMove move)
{
throw new ArgumentOutOfRangeException(nameof(move));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "MoveNames", (ushort)move))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "MoveNames", (ushort)move))[0]);
}
public static PBENature? GetNatureByName(string natureName)
{
PBENature nature;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "NatureNames", natureName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "NatureNames", natureName));
if (results.Count == 1)
{
nature = (PBENature)results[0].Id;
@@ -247,12 +255,12 @@ public static PBELocalizedString GetNatureName(PBENature nature)
{
throw new ArgumentOutOfRangeException(nameof(nature));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "NatureNames", (byte)nature))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "NatureNames", (byte)nature))[0]);
}
public static PBESpecies? GetSpeciesByName(string speciesName)
{
PBESpecies species;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "SpeciesNames", speciesName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "SpeciesNames", speciesName));
if (results.Count == 1)
{
species = (PBESpecies)results[0].Id;
@@ -270,7 +278,7 @@ public static PBELocalizedString GetSpeciesCategory(PBESpecies species)
{
throw new ArgumentOutOfRangeException(nameof(species));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "SpeciesCategories", speciesId))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "SpeciesCategories", speciesId))[0]);
}
public static PBELocalizedString GetSpeciesEntry(PBESpecies species)
{
@@ -279,7 +287,7 @@ public static PBELocalizedString GetSpeciesEntry(PBESpecies species)
{
throw new ArgumentOutOfRangeException(nameof(species));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "SpeciesEntries", speciesId))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "SpeciesEntries", speciesId))[0]);
}
public static PBELocalizedString GetSpeciesName(PBESpecies species)
{
@@ -288,12 +296,12 @@ public static PBELocalizedString GetSpeciesName(PBESpecies species)
{
throw new ArgumentOutOfRangeException(nameof(species));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "SpeciesNames", speciesId))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "SpeciesNames", speciesId))[0]);
}
public static PBEStat? GetStatByName(string statName)
{
PBEStat stat;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "StatNames", statName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "StatNames", statName));
if (results.Count == 1)
{
stat = (PBEStat)results[0].Id;
@@ -310,12 +318,12 @@ public static PBELocalizedString GetStatName(PBEStat stat)
{
throw new ArgumentOutOfRangeException(nameof(stat));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "StatNames", (byte)stat))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "StatNames", (byte)stat))[0]);
}
public static PBEType? GetTypeByName(string typeName)
{
PBEType type;
- List results = PBEUtils.QueryDatabase(string.Format(queryText, "TypeNames", typeName));
+ List results = PBEUtils.QueryDatabase(string.Format(QueryText, "TypeNames", typeName));
if (results.Count == 1)
{
type = (PBEType)results[0].Id;
@@ -328,11 +336,11 @@ public static PBELocalizedString GetStatName(PBEStat stat)
}
public static PBELocalizedString GetTypeName(PBEType type)
{
- if (!Enum.IsDefined(typeof(PBEType), type))
+ if (type >= PBEType.MAX)
{
throw new ArgumentOutOfRangeException(nameof(type));
}
- return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(queryId, "TypeNames", (byte)type))[0]);
+ return new PBELocalizedString(PBEUtils.QueryDatabase(string.Format(QueryId, "TypeNames", (byte)type))[0]);
}
#endregion
diff --git a/PokemonBattleEngine/Data/Moveset.cs b/PokemonBattleEngine/Data/Moveset.cs
new file mode 100644
index 000000000..3985a5fe2
--- /dev/null
+++ b/PokemonBattleEngine/Data/Moveset.cs
@@ -0,0 +1,640 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+
+namespace Kermalis.PokemonBattleEngine.Data
+{
+ public sealed class PBEMoveset : IDisposable, INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList
+ {
+ public sealed class PBEMovesetSlot : INotifyPropertyChanged
+ {
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private readonly PBEMoveset _parent;
+ private readonly int _index;
+
+ public PBEAlphabeticalList Allowed { get; }
+ private PBEMove _move;
+ public PBEMove Move
+ {
+ get => _move;
+ set
+ {
+ if (_parent.IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ PBEMove old = _move;
+ if (old != value)
+ {
+ if (!Enum.IsDefined(typeof(PBEMove), value))
+ {
+ throw new ArgumentOutOfRangeException(nameof(value));
+ }
+ if (!_isMoveEditable)
+ {
+ throw new InvalidOperationException($"Slot {_index}'s move cannot be changed because there is no move in slot {_index - 1}.");
+ }
+ if (!Allowed.Contains(value))
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), $"Slot {_index} does not allow {value}.");
+ }
+ if (value != PBEMove.None)
+ {
+ // If "move" is in another slot, place "slotIndex"'s old move at the other slot
+ for (int i = 0; i < _parent.Settings.NumMoves; i++)
+ {
+ if (i != _index)
+ {
+ PBEMovesetSlot iSlot = _parent[i];
+ if (iSlot.Move == value)
+ {
+ // If slot 0 is Snore and slot 3 is None but is trying to become Snore, do nothing because the first Snore is in an earlier slot and swapping None to an earlier slot makes no sense
+ if (old == PBEMove.None && i < _index)
+ {
+ goto finish;
+ }
+ else
+ {
+ UpdateMove(value);
+ iSlot.UpdateMove(old);
+ goto editables;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // If "move" is None and a slot after "slotIndex" is not None, then place None at the other slot instead and place the other slot's move at "slotIndex"
+ for (int i = _parent.Settings.NumMoves - 1; i > _index; i--)
+ {
+ PBEMovesetSlot iSlot = _parent[i];
+ if (iSlot.Move != PBEMove.None)
+ {
+ UpdateMove(iSlot.Move);
+ iSlot.UpdateMove(PBEMove.None);
+ goto editables;
+ }
+ }
+ }
+ // This gets reached if:
+ // "move" is not None and there is no other slot with "move"
+ // "move" is None and there is no slot after "slotIndex" with a move
+ UpdateMove(value);
+ editables:
+ _parent.SetEditables();
+ finish:
+ ;
+ }
+ }
+ }
+ private bool _isMoveEditable;
+ public bool IsMoveEditable
+ {
+ get => _isMoveEditable;
+ internal set
+ {
+ if (_isMoveEditable != value)
+ {
+ _isMoveEditable = value;
+ OnPropertyChanged(nameof(IsMoveEditable));
+ }
+ }
+ }
+ private byte _ppUps;
+ public byte PPUps
+ {
+ get => _ppUps;
+ set
+ {
+ if (_parent.IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (_ppUps != value)
+ {
+ if (value > _parent.Settings.MaxPPUps)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), $"\"{nameof(value)}\" cannot exceed \"{nameof(_parent.Settings.MaxPPUps)}\" ({_parent.Settings.MaxPPUps}).");
+ }
+ if (!IsPPUpsEditable)
+ {
+ throw new InvalidOperationException($"Slot {_index}'s PP-Ups cannot be changed because it has no move.");
+ }
+ UpdatePPUps(value);
+ }
+ }
+ }
+ private bool _isPPUpsEditable;
+ public bool IsPPUpsEditable
+ {
+ get => _isPPUpsEditable;
+ private set
+ {
+ if (_isPPUpsEditable != value)
+ {
+ _isPPUpsEditable = value;
+ OnPropertyChanged(nameof(IsPPUpsEditable));
+ }
+ }
+ }
+
+ internal PBEMovesetSlot(PBEMoveset parent, int index)
+ {
+ _parent = parent;
+ _index = index;
+ _isMoveEditable = index < 2;
+ Allowed = new PBEAlphabeticalList(new[] { PBEMove.None });
+ }
+
+ private void UpdateMove(PBEMove move)
+ {
+ if (_move != move)
+ {
+ PBEMove old = _move;
+ _move = move;
+ OnPropertyChanged(nameof(Move));
+ if (_move == PBEMove.None)
+ {
+ UpdatePPUps(0);
+ }
+ IsPPUpsEditable = _move != PBEMove.None;
+ }
+ }
+ private void UpdatePPUps(byte ppUps)
+ {
+ if (_ppUps != ppUps)
+ {
+ _ppUps = ppUps;
+ OnPropertyChanged(nameof(PPUps));
+ }
+ }
+ }
+
+ private void FireEvents(NotifyCollectionChangedEventArgs e)
+ {
+ OnPropertyChanged(nameof(Count));
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(e);
+ }
+ private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke(this, e);
+ }
+ private void OnPropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ }
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private PBESpecies _species;
+ public PBESpecies Species
+ {
+ get => _species;
+ set
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (_species != value)
+ {
+ PBEPokemonShell.ValidateSpecies(value);
+ _species = value;
+ OnPropertyChanged(nameof(Species));
+ SetAlloweds();
+ }
+ }
+ }
+ private byte _level;
+ public byte Level
+ {
+ get => _level;
+ set
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ if (_level != value)
+ {
+ PBEPokemonShell.ValidateLevel(value, Settings);
+ _level = value;
+ OnPropertyChanged(nameof(Level));
+ SetAlloweds();
+ }
+ }
+ }
+ public PBESettings Settings { get; }
+ private readonly List _list;
+ public int Count => _list.Count;
+ public PBEMovesetSlot this[int index]
+ {
+ get
+ {
+ if (index >= _list.Count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ return _list[index];
+ }
+ }
+ public PBEMovesetSlot this[PBEMove move]
+ {
+ get
+ {
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ if (slot.Move == move)
+ {
+ return slot;
+ }
+ }
+ return null;
+ }
+ }
+
+ internal PBEMoveset(PBESpecies species, byte level, PBESettings settings, BinaryReader r)
+ {
+ if (r.ReadByte() != settings.NumMoves)
+ {
+ throw new InvalidDataException();
+ }
+ _species = species;
+ _level = level;
+ Settings = settings;
+ _list = new List(Settings.NumMoves);
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ Add(new PBEMovesetSlot(this, i));
+ }
+ SetAlloweds();
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ var move = (PBEMove)r.ReadUInt16();
+ slot.Move = move;
+ if (slot.Move != move)
+ {
+ throw new InvalidDataException();
+ }
+ byte ppUps = r.ReadByte();
+ slot.PPUps = ppUps;
+ if (slot.PPUps != ppUps)
+ {
+ throw new InvalidDataException();
+ }
+ }
+ Settings.PropertyChanged += OnSettingsChanged;
+ }
+ internal PBEMoveset(PBESpecies species, byte level, PBESettings settings, JArray jArray)
+ {
+ if (jArray.Count != settings.NumMoves)
+ {
+ throw new ArgumentOutOfRangeException(nameof(PBEPokemonShell.Moveset), $"Moveset count must be equal to \"{nameof(settings.NumMoves)}\" ({settings.NumMoves}).");
+ }
+ _species = species;
+ _level = level;
+ Settings = settings;
+ _list = new List(Settings.NumMoves);
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ Add(new PBEMovesetSlot(this, i));
+ }
+ SetAlloweds();
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ JToken jToken = jArray[i];
+ PBEMove move = PBELocalizedString.GetMoveByName(jToken[nameof(PBEMovesetSlot.Move)].Value()).Value;
+ slot.Move = move;
+ if (slot.Move != move)
+ {
+ throw new ArgumentOutOfRangeException(nameof(PBEPokemonShell.Moveset), "Invalid moves.");
+ }
+ byte ppUps = jToken[nameof(PBEMovesetSlot.PPUps)].Value();
+ slot.PPUps = ppUps;
+ if (slot.PPUps != ppUps)
+ {
+ throw new ArgumentOutOfRangeException(nameof(PBEPokemonShell.Moveset), "Invalid PP-Ups.");
+ }
+ }
+ Settings.PropertyChanged += OnSettingsChanged;
+ }
+ internal PBEMoveset(PBEMoveset other)
+ {
+ _species = other._species;
+ _level = other._level;
+ Settings = other.Settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ _list = new List(Settings.NumMoves);
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ Add(new PBEMovesetSlot(this, i));
+ }
+ SetAlloweds();
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ PBEMovesetSlot oSlot = other[i];
+ slot.Move = oSlot.Move;
+ slot.PPUps = oSlot.PPUps;
+ }
+ }
+ /// Creates a new object with the specified traits.
+ /// The species of the Pokémon that this moveset will be built for.
+ /// The level of the Pokémon that this moveset will be built for.
+ /// The settings that will be used to evaluate the .
+ /// True if should be called, False if the move slots use their default values.
+ public PBEMoveset(PBESpecies species, byte level, PBESettings settings, bool randomize)
+ {
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ PBEPokemonShell.ValidateLevel(level, settings);
+ _level = level;
+ PBEPokemonShell.ValidateSpecies(species);
+ _species = species;
+ Settings = settings;
+ Settings.PropertyChanged += OnSettingsChanged;
+ _canDispose = true;
+ _list = new List(Settings.NumMoves);
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ Add(new PBEMovesetSlot(this, i));
+ }
+ SetAlloweds();
+ if (randomize)
+ {
+ Randomize();
+ }
+ }
+
+ private void Add(PBEMovesetSlot item)
+ {
+ int index = _list.Count;
+ _list.Insert(index, item);
+ FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
+ }
+ private bool Remove(PBEMovesetSlot item)
+ {
+ int index = _list.IndexOf(item);
+ bool b = index != -1;
+ if (b)
+ {
+ _list.RemoveAt(index);
+ FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
+ item.Allowed.Dispose();
+ }
+ return b;
+ }
+ private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Settings.MaxLevel):
+ {
+ if (_level > Settings.MaxLevel)
+ {
+ Level = Settings.MaxLevel;
+ }
+ break;
+ }
+ case nameof(Settings.MaxPPUps):
+ {
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ if (slot.PPUps > Settings.MaxPPUps)
+ {
+ slot.PPUps = Settings.MaxPPUps;
+ }
+ }
+ break;
+ }
+ case nameof(Settings.MinLevel):
+ {
+ if (_level < Settings.MinLevel)
+ {
+ Level = Settings.MinLevel;
+ }
+ break;
+ }
+ case nameof(Settings.NumMoves):
+ {
+ int oldCount = _list.Count;
+ if (Settings.NumMoves != oldCount)
+ {
+ if (Settings.NumMoves > oldCount)
+ {
+ if (Settings.NumMoves > _list.Capacity)
+ {
+ _list.Capacity = Settings.NumMoves;
+ }
+ int numToAdd = Settings.NumMoves - oldCount;
+ for (int i = 0; i < numToAdd; i++)
+ {
+ Add(new PBEMovesetSlot(this, oldCount + i));
+ }
+ }
+ else
+ {
+ int numToRemove = oldCount - Settings.NumMoves;
+ for (int i = 0; i < numToRemove; i++)
+ {
+ Remove(_list[oldCount - 1 - i]);
+ }
+ }
+ SetAlloweds();
+ }
+ break;
+ }
+ }
+ }
+
+ private void SetAlloweds()
+ {
+ int i;
+ PBEMove[] legalMoves = PBELegalityChecker.GetLegalMoves(_species, _level, Settings);
+ var allowed = new List(legalMoves.Length + 1);
+ allowed.AddRange(legalMoves);
+ if (_species == PBESpecies.Keldeo_Resolute)
+ {
+ PBEMovesetSlot slot = _list[0];
+ slot.Allowed.Reset(new[] { PBEMove.SecretSword });
+ if (slot.Move != PBEMove.SecretSword)
+ {
+ slot.Move = PBEMove.SecretSword;
+ }
+ allowed.Remove(PBEMove.SecretSword);
+ i = 1;
+ }
+ else
+ {
+ i = 0;
+ }
+ for (; i < Settings.NumMoves; i++)
+ {
+ if (i == 1)
+ {
+ allowed.Add(PBEMove.None);
+ }
+ PBEMovesetSlot slot = _list[i];
+ slot.Allowed.Reset(allowed);
+ if (!allowed.Contains(slot.Move))
+ {
+ PBEMove m = allowed[0];
+ slot.Move = m;
+ }
+ }
+ SetEditables();
+ }
+ private void SetEditables()
+ {
+ for (int i = 2; i < Settings.NumMoves; i++)
+ {
+ _list[i].IsMoveEditable = _list[i - 1].Move != PBEMove.None;
+ }
+ }
+
+ private bool _canDispose;
+ public bool CanDispose
+ {
+ get => _canDispose;
+ internal set
+ {
+ if (_canDispose != value)
+ {
+ _canDispose = value;
+ OnPropertyChanged(nameof(CanDispose));
+ }
+ }
+ }
+ public bool IsDisposed { get; private set; }
+ public void Dispose()
+ {
+ if (!_canDispose)
+ {
+ throw new InvalidOperationException();
+ }
+ if (!IsDisposed)
+ {
+ IsDisposed = true;
+ OnPropertyChanged(nameof(IsDisposed));
+ Settings.PropertyChanged -= OnSettingsChanged;
+ for (int i = 0; i < _list.Count; i++)
+ {
+ _list[i].Allowed.Dispose();
+ }
+ }
+ }
+
+ /// Sets every move slot excluding the first to with 0 PP-Ups.
+ public void Clear()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ for (int i = 1; i < Settings.NumMoves; i++)
+ {
+ _list[i].Move = PBEMove.None;
+ }
+ SetEditables();
+ }
+ public bool Contains(PBEMove move)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ return this[move] != null;
+ }
+ /// Randomizes the move and PP-Ups in each slot without creating duplicate moves.
+ public void Randomize()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ var blacklist = new List(Settings.NumMoves) { PBEMove.None };
+ for (int i = 0; i < Settings.NumMoves; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ PBEMove[] allowed = slot.Allowed.Except(blacklist).ToArray();
+ if (allowed.Length == 0)
+ {
+ for (int j = i; j < Settings.NumMoves; j++)
+ {
+ _list[j].Move = PBEMove.None;
+ }
+ break;
+ }
+ else
+ {
+ PBEMove move = allowed.RandomElement();
+ if (i < Settings.NumMoves - 1)
+ {
+ blacklist.Add(move);
+ }
+ slot.Move = move;
+ slot.PPUps = (byte)PBEUtils.RandomInt(0, Settings.MaxPPUps);
+ }
+ }
+ SetEditables();
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < _list.Count; i++)
+ {
+ yield return _list[i];
+ }
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ internal void ToBytes(List bytes)
+ {
+ byte amt = (byte)_list.Count;
+ bytes.Add(amt);
+ for (int i = 0; i < amt; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ bytes.AddRange(BitConverter.GetBytes((ushort)slot.Move));
+ bytes.Add(slot.PPUps);
+ }
+ }
+ internal void ToJson(JsonTextWriter w)
+ {
+ w.WriteStartArray();
+ for (int i = 0; i < _list.Count; i++)
+ {
+ PBEMovesetSlot slot = _list[i];
+ w.WriteStartObject();
+ w.WritePropertyName(nameof(PBEMovesetSlot.Move));
+ w.WriteValue(slot.Move.ToString());
+ w.WritePropertyName(nameof(PBEMovesetSlot.PPUps));
+ w.WriteValue(slot.PPUps);
+ w.WriteEndObject();
+ }
+ w.WriteEndArray();
+ }
+ }
+}
diff --git a/PokemonBattleEngine/Data/MovesetBuilder.cs b/PokemonBattleEngine/Data/MovesetBuilder.cs
deleted file mode 100644
index 07ca6709b..000000000
--- a/PokemonBattleEngine/Data/MovesetBuilder.cs
+++ /dev/null
@@ -1,398 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-
-namespace Kermalis.PokemonBattleEngine.Data
-{
- public sealed class PBEMovesetBuilder : INotifyPropertyChanged
- {
- public sealed class PBEMoveSlot : INotifyPropertyChanged
- {
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- private readonly PBEMovesetBuilder parent;
- private readonly int slotIndex;
-
- public PBEAlphabeticalList Allowed { get; }
- private PBEMove move;
- public PBEMove Move
- {
- get => move;
- set => parent.Set(slotIndex, value, null);
- }
- public bool IsMoveEditable { get; private set; }
- private byte ppUps;
- public byte PPUps
- {
- get => ppUps;
- set => parent.Set(slotIndex, null, value);
- }
- public bool IsPPUpsEditable => move != PBEMove.None;
-
- internal PBEMoveSlot(PBEMovesetBuilder parent, int slotIndex)
- {
- this.parent = parent;
- this.slotIndex = slotIndex;
- IsMoveEditable = slotIndex < 2;
- Allowed = new PBEAlphabeticalList(new[] { PBEMove.None });
- }
-
- internal void Update(PBEMove? move, byte? ppUps)
- {
- if (move.HasValue && this.move != move.Value)
- {
- PBEMove old = this.move;
- this.move = move.Value;
- OnPropertyChanged(nameof(Move));
- if ((old == PBEMove.None && this.move != PBEMove.None) || (old != PBEMove.None && this.move == PBEMove.None))
- {
- OnPropertyChanged(nameof(IsPPUpsEditable));
- }
- }
- if (ppUps.HasValue && this.ppUps != ppUps.Value)
- {
- this.ppUps = ppUps.Value;
- OnPropertyChanged(nameof(PPUps));
- }
- }
- internal void SetEditable(bool value)
- {
- if (value != IsMoveEditable)
- {
- IsMoveEditable = value;
- OnPropertyChanged(nameof(IsMoveEditable));
- }
- }
- internal void RemoveIfNotAllowed()
- {
- if (!Allowed.Contains(move))
- {
- Move = Allowed[0];
- }
- }
- internal void RemoveIfNotAllowedSilent()
- {
- if (!Allowed.Contains(move))
- {
- PBEMove m = Allowed[0];
- Update(m, m == PBEMove.None ? 0 : (byte?)null);
- }
- }
- }
-
- private void OnPropertyChanged(string property)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
- }
- public event PropertyChangedEventHandler PropertyChanged;
-
- private PBESpecies species;
- public PBESpecies Species
- {
- get => species;
- set
- {
- if (species != value)
- {
- PBEPokemonShell.ValidateSpecies(value);
- species = value;
- OnPropertyChanged(nameof(Species));
- SetAlloweds();
- }
- }
- }
- private byte level;
- public byte Level
- {
- get => level;
- set
- {
- if (level != value)
- {
- PBEPokemonShell.ValidateLevel(value, Settings);
- level = value;
- OnPropertyChanged(nameof(Level));
- SetAlloweds();
- }
- }
- }
- public PBESettings Settings { get; }
- public PBEList MoveSlots { get; }
-
- /// Creates a new object with the specified traits.
- /// The species of the Pokémon that this moveset will be built for.
- /// The level of the Pokémon that this moveset will be built for.
- /// The settings that will be used to evaluate the .
- /// True if should be called, False if the move slots use their default values.
- public PBEMovesetBuilder(PBESpecies species, byte level, PBESettings settings, bool randomize)
- {
- if (settings == null)
- {
- throw new ArgumentNullException(nameof(settings));
- }
- Settings = settings;
- Settings.PropertyChanged += OnSettingsChanged;
- PBEPokemonShell.ValidateLevel(level, Settings);
- this.level = level;
- PBEPokemonShell.ValidateSpecies(species);
- this.species = species;
- MoveSlots = new PBEList(Settings.NumMoves);
- for (int i = 0; i < Settings.NumMoves; i++)
- {
- MoveSlots.Add(new PBEMoveSlot(this, i));
- }
- SetAlloweds();
- if (randomize)
- {
- Randomize();
- }
- }
-
- private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
- {
- switch (e.PropertyName)
- {
- case nameof(Settings.MaxLevel):
- {
- if (level > Settings.MaxLevel)
- {
- Level = Settings.MaxLevel;
- }
- break;
- }
- case nameof(Settings.MaxPPUps):
- {
- for (int i = 0; i < Settings.NumMoves; i++)
- {
- PBEMoveSlot slot = MoveSlots[i];
- if (slot.PPUps > Settings.MaxPPUps)
- {
- slot.Update(null, Settings.MaxPPUps);
- }
- }
- break;
- }
- case nameof(Settings.MinLevel):
- {
- if (level < Settings.MinLevel)
- {
- Level = Settings.MinLevel;
- }
- break;
- }
- case nameof(Settings.NumMoves):
- {
- int oldCount = MoveSlots.Count;
- if (Settings.NumMoves != oldCount)
- {
- if (Settings.NumMoves > oldCount)
- {
- int numToAdd = Settings.NumMoves - oldCount;
- for (int i = 0; i < numToAdd; i++)
- {
- MoveSlots.Add(new PBEMoveSlot(this, oldCount + i));
- }
- }
- else
- {
- int numToRemove = oldCount - Settings.NumMoves;
- for (int i = 0; i < numToRemove; i++)
- {
- MoveSlots.RemoveAt(oldCount - 1 - i);
- }
- }
- SetAlloweds();
- SetEditables();
- }
- break;
- }
- }
- }
-
- private void SetAlloweds()
- {
- int i;
- PBEMove[] legalMoves = PBELegalityChecker.GetLegalMoves(species, level);
- var allowed = new List(legalMoves.Length + 1);
- allowed.AddRange(legalMoves);
- if (species == PBESpecies.Keldeo_Resolute)
- {
- PBEMoveSlot slot = MoveSlots[0];
- slot.Allowed.Reset(new[] { PBEMove.SecretSword });
- slot.RemoveIfNotAllowedSilent();
- allowed.Remove(PBEMove.SecretSword);
- i = 1;
- }
- else
- {
- i = 0;
- }
- for (; i < Settings.NumMoves; i++)
- {
- if (i == 1)
- {
- allowed.Add(PBEMove.None);
- }
- PBEMoveSlot slot = MoveSlots[i];
- slot.Allowed.Reset(allowed);
- slot.RemoveIfNotAllowedSilent();
- }
- }
- private void SetEditables()
- {
- for (int i = 2; i < Settings.NumMoves; i++)
- {
- MoveSlots[i].SetEditable(MoveSlots[i - 1].Move != PBEMove.None);
- }
- }
-
- /// Sets every move slot excluding the first to with 0 PP-Ups.
- public void Clear()
- {
- for (int i = 1; i < Settings.NumMoves; i++)
- {
- MoveSlots[i].Update(PBEMove.None, 0);
- }
- SetEditables();
- }
- /// Randomizes the move and PP-Ups in each slot without creating duplicate moves.
- public void Randomize()
- {
- var blacklist = new List(Settings.NumMoves) { PBEMove.None };
- for (int i = 0; i < Settings.NumMoves; i++)
- {
- PBEMoveSlot slot = MoveSlots[i];
- PBEMove[] allowed = slot.Allowed.Except(blacklist).ToArray();
- if (allowed.Length == 0)
- {
- for (int j = i; j < Settings.NumMoves; j++)
- {
- MoveSlots[j].Update(PBEMove.None, 0);
- }
- break;
- }
- else
- {
- PBEMove move = allowed.RandomElement();
- if (i < Settings.NumMoves - 1)
- {
- blacklist.Add(move);
- }
- slot.Update(move, (byte)PBEUtils.RandomInt(0, Settings.MaxPPUps));
- }
- }
- SetEditables();
- }
- /// Sets a specific move slot's move and/or PP-Ups. If using a for loop to set all values and the moveset is not already cleared, you should call first so that move indexes do not move behind your iterator as you change moves.
- /// The index of the slot to change.
- /// The move if it needs changing, null if the current move will remain unchanged.
- /// The PP-Ups if it needs changing, null if the current PP-Ups will remain unchanged.
- public void Set(int slotIndex, PBEMove? move, byte? ppUps)
- {
- if (slotIndex < 0 || slotIndex >= Settings.NumMoves)
- {
- throw new ArgumentOutOfRangeException(nameof(slotIndex));
- }
- if (move.HasValue || ppUps.HasValue)
- {
- PBEMoveSlot slot = MoveSlots[slotIndex];
- if (move.HasValue)
- {
- PBEMove mVal = move.Value;
- PBEMove old = slot.Move;
- if (old != mVal)
- {
- if (!Enum.IsDefined(typeof(PBEMove), move))
- {
- throw new ArgumentOutOfRangeException(nameof(move));
- }
- if (!slot.IsMoveEditable)
- {
- throw new InvalidOperationException($"Slot {slotIndex}'s move cannot be changed because there is no move in slot {slotIndex - 1}.");
- }
- if (!slot.Allowed.Contains(mVal))
- {
- throw new ArgumentOutOfRangeException(nameof(move), $"Slot {slotIndex} does not allow {mVal}.");
- }
- void UpdateSlot()
- {
- slot.Update(mVal, mVal == PBEMove.None ? 0 : (byte?)null);
- }
- if (mVal != PBEMove.None)
- {
- // If "move" is in another slot, place "slotIndex"'s old move at the other slot
- for (int i = 0; i < Settings.NumMoves; i++)
- {
- if (i != slotIndex)
- {
- PBEMoveSlot iSlot = MoveSlots[i];
- if (iSlot.Move == mVal)
- {
- // If slot 0 is Snore and slot 3 is None but is trying to become Snore, do nothing because the first Snore is in an earlier slot and swapping None to an earlier slot makes no sense
- if (old == PBEMove.None && i < slotIndex)
- {
- goto finish;
- }
- else
- {
- UpdateSlot();
- iSlot.Update(old, old == PBEMove.None ? 0 : (byte?)null);
- goto editables;
- }
- }
- }
- }
- }
- else
- {
- // If "move" is None and a slot after "slotIndex" is not None, then place None at the other slot instead and place the other slot's move at "slotIndex"
- for (int i = Settings.NumMoves - 1; i > slotIndex; i--)
- {
- PBEMoveSlot iSlot = MoveSlots[i];
- if (iSlot.Move != PBEMove.None)
- {
- slot.Update(iSlot.Move, null);
- iSlot.Update(PBEMove.None, 0);
- goto editables;
- }
- }
- }
- // This gets reached if:
- // "move" is not None and there is no other slot with "move"
- // "move" is None and there is no slot after "slotIndex" with a move
- UpdateSlot();
- editables:
- SetEditables();
- finish:
- ;
- }
- }
- if (ppUps.HasValue)
- {
- byte pVal = ppUps.Value;
- if (slot.PPUps != pVal)
- {
- if (pVal > Settings.MaxPPUps)
- {
- throw new ArgumentOutOfRangeException(nameof(ppUps), $"\"{nameof(ppUps)}\" cannot exceed \"{nameof(Settings.MaxPPUps)}\" ({Settings.MaxPPUps}).");
- }
- if (!slot.IsPPUpsEditable)
- {
- throw new InvalidOperationException($"Slot {slotIndex}'s PP-Ups cannot be changed because it has no move.");
- }
- slot.Update(null, pVal);
- }
- }
- }
- else
- {
- throw new ArgumentException($"\"{nameof(move)}\" or \"{nameof(ppUps)}\" has to have a value to set.");
- }
- }
- }
-}
diff --git a/PokemonBattleEngine/Data/PBEAlphabeticalList.cs b/PokemonBattleEngine/Data/PBEAlphabeticalList.cs
index c8ac2b4f8..f92fb4f5c 100644
--- a/PokemonBattleEngine/Data/PBEAlphabeticalList.cs
+++ b/PokemonBattleEngine/Data/PBEAlphabeticalList.cs
@@ -8,7 +8,7 @@
namespace Kermalis.PokemonBattleEngine.Data
{
- public sealed class PBEAlphabeticalList : IDisposable, INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList
+ public sealed class PBEAlphabeticalList : INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList
{
private sealed class PBEAlphabeticalListEntry
{
@@ -33,12 +33,6 @@ public PBEAlphabeticalListEntry(T key)
}
}
- private void FireEvents(NotifyCollectionChangedEventArgs e)
- {
- OnPropertyChanged(nameof(Count));
- OnPropertyChanged("Item[]");
- OnCollectionChanged(e);
- }
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
@@ -50,13 +44,23 @@ private void OnPropertyChanged(string property)
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
- private PBEAlphabeticalListEntry[] list;
- public int Count => list.Length;
- public T this[int index] => list[index].Key;
+ private PBEAlphabeticalListEntry[] _list;
+ public int Count => _list.Length;
+ public T this[int index]
+ {
+ get
+ {
+ if (index >= _list.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ return _list[index].Key;
+ }
+ }
internal PBEAlphabeticalList()
{
- list = Array.Empty>();
+ _list = Array.Empty>();
PBELocalizedString.PBECultureChanged += OnCultureChanged;
}
internal PBEAlphabeticalList(IEnumerable collection)
@@ -69,30 +73,52 @@ private void OnCultureChanged(CultureInfo oldPBECultureInfo)
{
if (!oldPBECultureInfo.TwoLetterISOLanguageName.Equals(PBELocalizedString.PBECulture.TwoLetterISOLanguageName))
{
- Sort();
+ Sort(_list);
}
}
- private void Sort()
+ private void Sort(PBEAlphabeticalListEntry[] old)
{
- Array.Sort(list, (x, y) => x.Value.ToString().CompareTo(y.Value.ToString()));
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ if (old == null || old == _list)
+ {
+ old = (PBEAlphabeticalListEntry[])_list.Clone();
+ }
+ Array.Sort(_list, (x, y) => x.Value.ToString().CompareTo(y.Value.ToString()));
+ if (!_list.SequenceEqual(old))
+ {
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
}
+ private bool _isDisposed = false;
+ internal void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+ PBELocalizedString.PBECultureChanged -= OnCultureChanged;
+ }
+ }
internal void Reset(IEnumerable collection)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
+ PBEAlphabeticalListEntry[] old = _list;
if (collection is PBEAlphabeticalList other)
{
- list = (PBEAlphabeticalListEntry[])other.list.Clone();
+ _list = (PBEAlphabeticalListEntry[])other._list.Clone();
}
else
{
- list = collection.Select(t => new PBEAlphabeticalListEntry(t)).ToArray();
+ _list = collection.Select(t => new PBEAlphabeticalListEntry(t)).ToArray();
+ }
+ if (old != null && old.Length != _list.Length)
+ {
+ OnPropertyChanged(nameof(Count));
}
- Sort();
+ Sort(old);
}
public bool Contains(T item)
@@ -103,9 +129,9 @@ public int IndexOf(T item)
{
if (item != null)
{
- for (int i = 0; i < list.Length; i++)
+ for (int i = 0; i < _list.Length; i++)
{
- if (item.Equals(list[i].Key))
+ if (item.Equals(_list[i].Key))
{
return i;
}
@@ -114,15 +140,11 @@ public int IndexOf(T item)
return -1;
}
- public void Dispose()
- {
- PBELocalizedString.PBECultureChanged -= OnCultureChanged;
- }
public IEnumerator GetEnumerator()
{
- for (int i = 0; i < list.Length; i++)
+ for (int i = 0; i < _list.Length; i++)
{
- yield return list[i].Key;
+ yield return _list[i].Key;
}
}
IEnumerator IEnumerable.GetEnumerator()
diff --git a/PokemonBattleEngine/Data/PBEList.cs b/PokemonBattleEngine/Data/PBEList.cs
index 0e9261930..05b4a39e6 100644
--- a/PokemonBattleEngine/Data/PBEList.cs
+++ b/PokemonBattleEngine/Data/PBEList.cs
@@ -3,17 +3,12 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
+using System.Linq;
namespace Kermalis.PokemonBattleEngine.Data
{
public sealed class PBEList : INotifyCollectionChanged, INotifyPropertyChanged, IReadOnlyList
{
- private void FireEvents(NotifyCollectionChangedEventArgs e)
- {
- OnPropertyChanged(nameof(Count));
- OnPropertyChanged("Item[]");
- OnCollectionChanged(e);
- }
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
@@ -25,52 +20,70 @@ private void OnPropertyChanged(string property)
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
- private readonly List list;
- public int Count => list.Count;
- public T this[int index] => list[index];
+ private readonly List _list;
+ public int Count => _list.Count;
+ public T this[int index]
+ {
+ get
+ {
+ if (index >= _list.Count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ return _list[index];
+ }
+ }
internal PBEList()
{
- list = new List();
+ _list = new List();
}
internal PBEList(int capacity)
{
- list = new List(capacity);
+ _list = new List(capacity);
}
internal void Add(T item)
{
- int index = list.Count;
- list.Insert(index, item);
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
+ int index = _list.Count;
+ _list.Insert(index, item);
+ OnPropertyChanged(nameof(Count));
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
internal void Insert(int index, T item)
{
- list.Insert(index, item);
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
+ _list.Insert(index, item);
+ OnPropertyChanged(nameof(Count));
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
internal bool Remove(T item)
{
- int index = list.IndexOf(item);
+ int index = _list.IndexOf(item);
bool b = index != -1;
if (b)
{
- list.RemoveAt(index);
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
+ _list.RemoveAt(index);
+ OnPropertyChanged(nameof(Count));
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
return b;
}
internal void RemoveAt(int index)
{
- if (index < 0 || index >= list.Count)
+ if (index < 0 || index >= _list.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
else
{
- T item = list[index];
- list.RemoveAt(index);
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
+ T item = _list[index];
+ _list.RemoveAt(index);
+ OnPropertyChanged(nameof(Count));
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
}
internal void Reset(IEnumerable collection)
@@ -79,27 +92,39 @@ internal void Reset(IEnumerable collection)
{
throw new ArgumentNullException(nameof(collection));
}
- list.Clear();
- list.AddRange(collection);
- FireEvents(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ int oldCount = _list.Count;
+ if (!_list.SequenceEqual(collection))
+ {
+ _list.Clear();
+ _list.AddRange(collection);
+ if (oldCount != _list.Count)
+ {
+ OnPropertyChanged(nameof(Count));
+ }
+ OnPropertyChanged("Item[]");
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
}
public bool Contains(T item)
{
- return list.IndexOf(item) != -1;
+ return _list.IndexOf(item) != -1;
}
public int IndexOf(T item)
{
- return list.IndexOf(item);
+ return _list.IndexOf(item);
}
public IEnumerator GetEnumerator()
{
- return list.GetEnumerator();
+ for (int i = 0; i < _list.Count; i++)
+ {
+ yield return _list[i];
+ }
}
IEnumerator IEnumerable.GetEnumerator()
{
- return list.GetEnumerator();
+ return GetEnumerator();
}
}
}
diff --git a/PokemonBattleEngine/Data/PokemonData.cs b/PokemonBattleEngine/Data/PokemonData.cs
index dc005c1b0..c2ecdae5e 100644
--- a/PokemonBattleEngine/Data/PokemonData.cs
+++ b/PokemonBattleEngine/Data/PokemonData.cs
@@ -38,10 +38,18 @@ private PBEPokemonData(byte[] baseStats,
public bool HasAbility(PBEAbility ability)
{
+ if (ability >= PBEAbility.MAX)
+ {
+ throw new ArgumentOutOfRangeException(nameof(ability));
+ }
return Abilities.Contains(ability);
}
public bool HasType(PBEType type)
{
+ if (type >= PBEType.MAX)
+ {
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
return Type1 == type || Type2 == type;
}
@@ -494,12 +502,23 @@ public bool HasType(PBEType type)
PBEType.Dark // 1.5625 %
});
- ///
- /// Calculates a species's stats based on its level, IVs, EVs, and nature.
- ///
+ /// Calculates a species's stats based on its level, IVs, EVs, and nature.
/// Thrown when is invalid.
public static ushort CalculateStat(PBEStat stat, PBESpecies species, PBENature nature, byte evs, byte ivs, byte level, PBESettings settings)
{
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ if (!Enum.IsDefined(typeof(PBESpecies), species))
+ {
+ throw new ArgumentOutOfRangeException(nameof(species));
+ }
+ PBEPokemonShell.ValidateLevel(level, settings);
+ if (ivs > settings.MaxIVs)
+ {
+ throw new ArgumentOutOfRangeException(nameof(ivs));
+ }
switch (stat)
{
case PBEStat.HP:
@@ -521,6 +540,15 @@ public static ushort CalculateStat(PBEStat stat, PBESpecies species, PBENature n
}
public static void GetStatRange(PBEStat stat, PBESpecies species, byte level, PBESettings settings, out ushort low, out ushort high)
{
+ if (settings == null)
+ {
+ throw new ArgumentNullException(nameof(settings));
+ }
+ if (!Enum.IsDefined(typeof(PBESpecies), species))
+ {
+ throw new ArgumentOutOfRangeException(nameof(species));
+ }
+ PBEPokemonShell.ValidateLevel(level, settings);
switch (stat)
{
case PBEStat.HP:
@@ -595,6 +623,10 @@ private class SearchResult
}
public static PBEPokemonData GetData(PBESpecies species)
{
+ if (!Enum.IsDefined(typeof(PBESpecies), species))
+ {
+ throw new ArgumentOutOfRangeException(nameof(species));
+ }
string json = PBEUtils.QueryDatabase($"SELECT * FROM PokemonData WHERE Id={(uint)species}")[0].Json;
using (var reader = new JsonTextReader(new StringReader(json)))
{
diff --git a/PokemonBattleEngine/Data/PokemonShell.cs b/PokemonBattleEngine/Data/PokemonShell.cs
index 3462409d2..67d7c1fe0 100644
--- a/PokemonBattleEngine/Data/PokemonShell.cs
+++ b/PokemonBattleEngine/Data/PokemonShell.cs
@@ -8,9 +8,7 @@
namespace Kermalis.PokemonBattleEngine.Data
{
- // TODO: What should happen if you continue using this after it was removed from a PBETeamShell?
- // This should be able to be used aside from team shells (which would help event pokemon be represented again)
- public sealed class PBEPokemonShell : INotifyPropertyChanged
+ public sealed class PBEPokemonShell : IDisposable, INotifyPropertyChanged
{
public static PBEAlphabeticalList AllSpecies { get; } = new PBEAlphabeticalList(
Enum.GetValues(typeof(PBESpecies))
@@ -38,292 +36,231 @@ private void OnPropertyChanged(string property)
}
public event PropertyChangedEventHandler PropertyChanged;
- private readonly PBETeamShell parent;
+ public PBESettings Settings { get; }
- private PBEPokemonData pData;
+ private PBEPokemonData _pData;
public PBEAlphabeticalList SelectableAbilities { get; } = new PBEAlphabeticalList();
public PBEAlphabeticalList SelectableGenders { get; } = new PBEAlphabeticalList();
public PBEAlphabeticalList SelectableItems { get; } = new PBEAlphabeticalList();
- private PBESpecies species;
+ private PBESpecies _species;
public PBESpecies Species
{
- get => species;
+ get => _species;
set
{
- if (species != value)
+ if (_species != value)
{
ValidateSpecies(value);
- PBESpecies old = species;
- species = value;
+ PBESpecies old = _species;
+ _species = value;
OnPropertyChanged(nameof(Species));
OnSpeciesChanged(old);
}
}
}
- private string nickname;
+ private string _nickname;
public string Nickname
{
- get => nickname;
+ get => _nickname;
set
{
- if (nickname != value)
+ if (_nickname != value)
{
- ValidateNickname(value, parent.Settings);
- nickname = value;
+ ValidateNickname(value, Settings);
+ _nickname = value;
OnPropertyChanged(nameof(Nickname));
}
}
}
- private byte level;
+ private byte _level;
public byte Level
{
- get => level;
+ get => _level;
set
{
- if (level != value)
+ if (_level != value)
{
- ValidateLevel(value, parent.Settings);
- level = value;
+ ValidateLevel(value, Settings);
+ _level = value;
OnPropertyChanged(nameof(Level));
Moveset.Level = value;
}
}
}
- private byte friendship;
+ private byte _friendship;
public byte Friendship
{
- get => friendship;
+ get => _friendship;
set
{
- if (value != friendship)
+ if (value != _friendship)
{
- friendship = value;
+ _friendship = value;
OnPropertyChanged(nameof(Friendship));
}
}
}
- private bool shiny;
+ private bool _shiny;
public bool Shiny
{
- get => shiny;
+ get => _shiny;
set
{
- if (value != shiny)
+ if (value != _shiny)
{
- shiny = value;
+ _shiny = value;
OnPropertyChanged(nameof(Shiny));
}
}
}
- private PBEAbility ability;
+ private PBEAbility _ability;
public PBEAbility Ability
{
- get => ability;
+ get => _ability;
set
{
- if (value != ability)
+ if (value != _ability)
{
ValidateAbility(value);
- ability = value;
+ _ability = value;
OnPropertyChanged(nameof(Ability));
}
}
}
- private PBENature nature;
+ private PBENature _nature;
public PBENature Nature
{
- get => nature;
+ get => _nature;
set
{
- if (value != nature)
+ if (value != _nature)
{
ValidateNature(value);
- nature = value;
+ _nature = value;
OnPropertyChanged(nameof(Nature));
}
}
}
- private PBEGender gender;
+ private PBEGender _gender;
public PBEGender Gender
{
- get => gender;
+ get => _gender;
set
{
- if (value != gender)
+ if (value != _gender)
{
ValidateGender(value);
- gender = value;
+ _gender = value;
OnPropertyChanged(nameof(Gender));
}
}
}
- private PBEItem item;
+ private PBEItem _item;
public PBEItem Item
{
- get => item;
+ get => _item;
set
{
- if (value != item)
+ if (value != _item)
{
ValidateItem(value);
- item = value;
+ _item = value;
OnPropertyChanged(nameof(Item));
}
}
}
- public PBEEffortValueCollection EffortValues { get; private set; }
- public PBEIndividualValueCollection IndividualValues { get; private set; }
- public PBEMovesetBuilder Moveset { get; private set; }
+ public PBEEffortValues EffortValues { get; }
+ public PBEIndividualValues IndividualValues { get; }
+ public PBEMoveset Moveset { get; }
- internal PBEPokemonShell(BinaryReader r, PBETeamShell parent)
+ internal PBEPokemonShell(PBESettings settings, BinaryReader r)
{
- this.parent = parent;
+ Settings = settings;
var species = (PBESpecies)r.ReadUInt32();
ValidateSpecies(species);
- this.species = species;
+ _species = species;
SetSelectable();
string nickname = PBEUtils.StringFromBytes(r);
- ValidateNickname(nickname, parent.Settings);
- this.nickname = nickname;
+ ValidateNickname(nickname, Settings);
+ _nickname = nickname;
byte level = r.ReadByte();
- ValidateLevel(level, parent.Settings);
- this.level = level;
- friendship = r.ReadByte();
- shiny = r.ReadBoolean();
+ ValidateLevel(level, Settings);
+ _level = level;
+ _friendship = r.ReadByte();
+ _shiny = r.ReadBoolean();
var ability = (PBEAbility)r.ReadByte();
ValidateAbility(ability);
- this.ability = ability;
+ _ability = ability;
var nature = (PBENature)r.ReadByte();
ValidateNature(nature);
- this.nature = nature;
+ _nature = nature;
var gender = (PBEGender)r.ReadByte();
ValidateGender(gender);
- this.gender = gender;
+ _gender = gender;
var item = (PBEItem)r.ReadUInt16();
ValidateItem(item);
- this.item = item;
- byte hpEV = r.ReadByte();
- byte attackEV = r.ReadByte();
- byte defenseEV = r.ReadByte();
- byte spAttackEV = r.ReadByte();
- byte spDefenseEV = r.ReadByte();
- byte speedEV = r.ReadByte();
- if (hpEV + attackEV + defenseEV + spAttackEV + spDefenseEV + speedEV > parent.Settings.MaxTotalEVs)
- {
- throw new ArgumentOutOfRangeException(nameof(EffortValues), $"Total must not exceed \"{nameof(parent.Settings.MaxTotalEVs)}\" ({parent.Settings.MaxTotalEVs})");
- }
- EffortValues = new PBEEffortValueCollection(parent.Settings, hpEV, attackEV, defenseEV, spAttackEV, spDefenseEV, speedEV);
- void ValidateIV(byte val, string name)
- {
- if (val > parent.Settings.MaxIVs)
- {
- throw new ArgumentOutOfRangeException(nameof(IndividualValues), $"\"{name}\" value must not exceed \"{nameof(parent.Settings.MaxIVs)}\" ({parent.Settings.MaxIVs})");
- }
- }
- byte hpIV = r.ReadByte();
- ValidateIV(hpIV, nameof(PBEStat.HP));
- byte attackIV = r.ReadByte();
- ValidateIV(attackIV, nameof(PBEStat.Attack));
- byte defenseIV = r.ReadByte();
- ValidateIV(defenseIV, nameof(PBEStat.Defense));
- byte spAttackIV = r.ReadByte();
- ValidateIV(spAttackIV, nameof(PBEStat.SpAttack));
- byte spDefenseIV = r.ReadByte();
- ValidateIV(spDefenseIV, nameof(PBEStat.SpDefense));
- byte speedIV = r.ReadByte();
- ValidateIV(speedIV, nameof(PBEStat.Speed));
- IndividualValues = new PBEIndividualValueCollection(parent.Settings, hpIV, attackIV, defenseIV, spAttackIV, spDefenseIV, speedIV);
- Moveset = new PBEMovesetBuilder(species, level, parent.Settings, false);
- for (int i = 0; i < parent.Settings.NumMoves; i++)
- {
- var move = (PBEMove)r.ReadUInt16();
- byte ppUps = r.ReadByte();
- Moveset.Set(i, move, ppUps); // "Set()" will throw its own exceptions for invalid moves and pp-ups
- // The following check is for the case where identical moves were stored in the same moveset, therefore forcing "Set()" to overwrite one with PBEMove.None
- PBEMovesetBuilder.PBEMoveSlot slot = Moveset.MoveSlots[i];
- if (slot.Move != move || slot.PPUps != ppUps)
- {
- throw new InvalidDataException("Invalid moveset.");
- }
- }
+ _item = item;
+ Settings.PropertyChanged += OnSettingsChanged;
+ EffortValues = new PBEEffortValues(Settings, r);
+ IndividualValues = new PBEIndividualValues(Settings, r);
+ Moveset = new PBEMoveset(species, level, Settings, r);
}
- internal PBEPokemonShell(JToken jObj, PBETeamShell parent)
+ internal PBEPokemonShell(PBESettings settings, JToken jToken)
{
- this.parent = parent;
- friendship = jObj[nameof(Friendship)].Value();
- shiny = jObj[nameof(Shiny)].Value();
- byte level = jObj[nameof(Level)].Value();
- ValidateLevel(level, parent.Settings);
- this.level = level;
- string nickname = jObj[nameof(Nickname)].Value();
- ValidateNickname(nickname, parent.Settings);
- this.nickname = nickname;
- PBENature nature = PBELocalizedString.GetNatureByName(jObj[nameof(Nature)].Value()).Value;
+ Settings = settings;
+ _friendship = jToken[nameof(Friendship)].Value();
+ _shiny = jToken[nameof(Shiny)].Value();
+ byte level = jToken[nameof(Level)].Value();
+ ValidateLevel(level, Settings);
+ _level = level;
+ string nickname = jToken[nameof(Nickname)].Value();
+ ValidateNickname(nickname, Settings);
+ _nickname = nickname;
+ PBENature nature = PBELocalizedString.GetNatureByName(jToken[nameof(Nature)].Value()).Value;
ValidateNature(nature);
- this.nature = nature;
- PBESpecies species = PBELocalizedString.GetSpeciesByName(jObj[nameof(Species)].Value()).Value;
+ _nature = nature;
+ PBESpecies species = PBELocalizedString.GetSpeciesByName(jToken[nameof(Species)].Value()).Value;
ValidateSpecies(species);
- this.species = species;
+ _species = species;
SetSelectable();
- PBEAbility ability = PBELocalizedString.GetAbilityByName(jObj[nameof(Ability)].Value()).Value;
+ PBEAbility ability = PBELocalizedString.GetAbilityByName(jToken[nameof(Ability)].Value()).Value;
ValidateAbility(ability);
- this.ability = ability;
- PBEGender gender = PBELocalizedString.GetGenderByName(jObj[nameof(Gender)].Value()).Value;
+ _ability = ability;
+ PBEGender gender = PBELocalizedString.GetGenderByName(jToken[nameof(Gender)].Value