/// <summary>Determines whether chosen switches are valid.</summary> /// <param name="team">The team the inputted switches belong to.</param> /// <param name="switches">The switches the team wishes to execute.</param> /// <returns>False if the team already chose switches or the switches are illegal, True otherwise.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForSwitchIns"/>.</exception> public static bool AreSwitchesValid(PBETeam team, IList <PBESwitchIn> switches) { if (team == null) { throw new ArgumentNullException(nameof(team)); } if (switches == null || switches.Any(s => s == 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) { return(false); } var verified = new List <PBEPokemon>(team.SwitchInsRequired); foreach (PBESwitchIn s in switches) { PBEPokemon pkmn = team.TryGetPokemon(s.PokemonId); if (pkmn == null || pkmn.HP == 0 || pkmn.FieldPosition != PBEFieldPosition.None || verified.Contains(pkmn)) { return(false); } verified.Add(pkmn); } return(true); }
/// <summary>Selects switches if they are valid. Changes the battle state if both teams have selected valid switches.</summary> /// <param name="team">The team the inputted switches belong to.</param> /// <param name="switches">The switches the team wishes to execute.</param> /// <returns>True if the switches are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForSwitchIns"/>.</exception> public static bool SelectSwitchesIfValid(PBETeam team, IList <PBESwitchIn> switches) { if (AreSwitchesValid(team, switches)) { lock (team.Battle._disposeLockObj) { 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(false); }
/// <summary> /// Transforms into <paramref name="target"/> and sets <see cref="PBEStatus2.Transformed"/>. /// </summary> /// <param name="target">The Pokémon to transform into.</param> /// <remarks>Frees the Pokémon of its <see cref="ChoiceLockedMove"/>.</remarks> public void Transform(PBEPokemon target) { if (Team != target.Team) { KnownAbility = target.KnownAbility = Ability = target.Ability; KnownType1 = target.KnownType1 = Type1 = target.Type1; KnownType2 = target.KnownType2 = Type2 = target.Type2; KnownWeight = target.KnownWeight = Weight = target.Weight; } else { Ability = target.Ability; KnownAbility = target.KnownAbility; Type1 = target.Type1; KnownType1 = target.KnownType1; Type2 = target.Type2; KnownType2 = target.KnownType2; Weight = target.Weight; KnownWeight = target.KnownWeight; } KnownGender = target.KnownGender = target.Gender; KnownShiny = target.KnownShiny = target.Shiny; KnownSpecies = target.KnownSpecies = Species = target.Species; Attack = target.Attack; Defense = target.Defense; SpAttack = target.SpAttack; SpDefense = target.SpDefense; Speed = target.Speed; AttackChange = target.AttackChange; DefenseChange = target.DefenseChange; SpAttackChange = target.SpAttackChange; SpDefenseChange = target.SpDefenseChange; 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++) { if (Team != target.Team) { KnownMoves[i] = target.KnownMoves[i] = Moves[i] = target.Moves[i]; } else { Moves[i] = target.Moves[i]; KnownMoves[i] = target.KnownMoves[i]; } if (Id != byte.MaxValue) // Don't set PP if this is a client's unknown remote Pokémon { PP[i] = MaxPP[i] = (byte)(Moves[i] == PBEMove.None ? 0 : PBEMoveData.Data[Moves[i]].PPTier == 0 ? 1 : Team.Battle.Settings.PPMultiplier); } } if (!Moves.Contains(ChoiceLockedMove)) { ChoiceLockedMove = PBEMove.None; } Status2 |= PBEStatus2.Transformed; }
public bool CanBecomePoisonedBy(PBEPokemon other) { return(Status1 == PBEStatus1.None && !HasType(PBEType.Poison) && !HasType(PBEType.Steel) && !(Ability == PBEAbility.Immunity && !other.HasCancellingAbility())); }
private double CalculateSpDefense(PBEPokemon user, PBEPokemon target, double initialSpDefense) { double spDefense = initialSpDefense; if (target.Item == PBEItem.DeepSeaScale && target.OriginalSpecies == PBESpecies.Clamperl) { spDefense *= 2.0; } if (target.Item == PBEItem.SoulDew && (target.OriginalSpecies == PBESpecies.Latias || target.OriginalSpecies == PBESpecies.Latios)) { spDefense *= 1.5; } if (target.Item == PBEItem.Eviolite && PBEPokemonData.GetData(target.OriginalSpecies).Evolutions.Count > 0) { spDefense *= 1.5; } if (ShouldDoWeatherEffects()) { if (Weather == PBEWeather.Sandstorm && target.HasType(PBEType.Rock)) { spDefense *= 1.5; } if (!user.HasCancellingAbility() && Weather == PBEWeather.HarshSunlight && Array.Exists(target.Team.ActiveBattlers, p => p.Ability == PBEAbility.FlowerGift)) { spDefense *= 1.5; } } return(spDefense); }
private void TypeCheck(PBEPokemon user, PBEPokemon target, PBEType moveType, out PBEEffectiveness moveEffectiveness, ref double moveEffectivenessMultiplier, bool ignoreWonderGuard) { double m = PBEPokemonData.TypeEffectiveness[moveType][target.Type1]; m *= PBEPokemonData.TypeEffectiveness[moveType][target.Type2]; if (m <= 0) // (-infinity, 0] { moveEffectiveness = PBEEffectiveness.Ineffective; } else if (m < 1) // (0, 1) { moveEffectiveness = PBEEffectiveness.NotVeryEffective; } else if (m == 1.0) // [1, 1] { moveEffectiveness = PBEEffectiveness.Normal; } else // (1, infinity) { moveEffectiveness = PBEEffectiveness.SuperEffective; } moveEffectivenessMultiplier *= m; if (moveEffectiveness != PBEEffectiveness.Ineffective) { if ((target.Ability == PBEAbility.Levitate && moveType == PBEType.Ground) || (!ignoreWonderGuard && target.Ability == PBEAbility.WonderGuard && moveEffectiveness != PBEEffectiveness.SuperEffective)) { moveEffectiveness = PBEEffectiveness.Ineffective; moveEffectivenessMultiplier = 0; BroadcastAbility(target, target, target.Ability, PBEAbilityAction.Damage); } } }
/// <summary> /// Transforms into <paramref name="target"/> and sets <see cref="PBEStatus2.Transformed"/>. /// </summary> /// <param name="target">The Pokémon to transform into.</param> /// <remarks>Frees the Pokémon of its <see cref="ChoiceLockedMove"/>.</remarks> public void Transform(PBEPokemon target) { VisualSpecies = target.Shell.Species; VisualShiny = target.Shell.Shiny; VisualGender = target.Shell.Gender; Ability = target.Ability; Type1 = target.Type1; Type2 = target.Type2; Weight = target.Weight; Attack = target.Attack; Defense = target.Defense; SpAttack = target.SpAttack; SpDefense = target.SpDefense; Speed = target.Speed; AttackChange = target.AttackChange; DefenseChange = target.DefenseChange; SpAttackChange = target.SpAttackChange; SpDefenseChange = target.SpDefenseChange; SpeedChange = target.SpeedChange; AccuracyChange = target.AccuracyChange; EvasionChange = target.EvasionChange; Moves = (PBEMove[])target.Moves.Clone(); if (Id != byte.MaxValue) { for (int i = 0; i < Moves.Length; i++) { PP[i] = MaxPP[i] = (byte)(Moves[i] == PBEMove.None ? 0 : PBEMoveData.Data[Moves[i]].PPTier == 0 ? 1 : Team.Battle.Settings.PPMultiplier); } } if (!Moves.Contains(ChoiceLockedMove)) { ChoiceLockedMove = PBEMove.None; } Status2 |= PBEStatus2.Transformed; }
private double CalculateAttack(PBEPokemon user, PBEPokemon target, PBEType moveType, double initialAttack) { double attack = initialAttack; if (user.Ability == PBEAbility.HugePower || user.Ability == PBEAbility.PurePower) { attack *= 2.0; } if (user.Item == PBEItem.ThickClub && (user.OriginalSpecies == PBESpecies.Cubone || user.OriginalSpecies == PBESpecies.Marowak)) { attack *= 2.0; } if (user.Item == PBEItem.LightBall && user.OriginalSpecies == PBESpecies.Pikachu) { attack *= 2.0; } if (moveType == PBEType.Fire && user.Ability == PBEAbility.Blaze && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (moveType == PBEType.Grass && user.Ability == PBEAbility.Overgrow && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (moveType == PBEType.Water && user.Ability == PBEAbility.Torrent && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (user.Ability == PBEAbility.Hustle) { attack *= 1.5; } if (user.Ability == PBEAbility.Guts && user.Status1 != PBEStatus1.None) { attack *= 1.5; } if (user.Item == PBEItem.ChoiceBand) { attack *= 1.5; } if (!user.HasCancellingAbility() && ShouldDoWeatherEffects() && Weather == PBEWeather.HarshSunlight && Array.Exists(user.Team.ActiveBattlers, p => p.Ability == PBEAbility.FlowerGift)) { attack *= 1.5; } if ((moveType == PBEType.Fire || moveType == PBEType.Ice) && target.Ability == PBEAbility.ThickFat && !user.HasCancellingAbility()) { attack *= 0.5; } if (user.Ability == PBEAbility.Defeatist && user.HP <= user.MaxHP / 2) { attack *= 0.5; } if (user.Ability == PBEAbility.SlowStart && user.SlowStart_HinderTurnsLeft > 0) { attack *= 0.5; } return(attack); }
/// <summary> /// Determines whether chosen actions are valid. /// </summary> /// <param name="team">The team the inputted actions belong to.</param> /// <param name="actions">The actions the team wishes to execute.</param> /// <returns>False if the team already chose actions or the actions are illegal, True otherwise.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForActions"/>.</exception> public static bool AreActionsValid(PBETeam team, IEnumerable <PBEAction> actions) { 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) { return(false); } var standBy = new List <PBEPokemon>(); foreach (PBEAction action in actions) { PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId); if (!team.ActionsRequired.Contains(pkmn)) { return(false); } switch (action.Decision) { case PBEDecision.Fight: { if (Array.IndexOf(pkmn.GetUsableMoves(), action.FightMove) == -1 || (action.FightMove == pkmn.TempLockedMove && action.FightTargets != pkmn.TempLockedTargets) || !AreTargetsValid(pkmn, action.FightMove, action.FightTargets) ) { return(false); } break; } case PBEDecision.SwitchOut: { if (!pkmn.CanSwitchOut()) { return(false); } PBEPokemon switchPkmn = team.TryGetPokemon(action.SwitchPokemonId); if (switchPkmn == null || switchPkmn.HP == 0 || switchPkmn.FieldPosition != PBEFieldPosition.None || // Also takes care of trying to switch into yourself standBy.Contains(switchPkmn) ) { return(false); } else { standBy.Add(switchPkmn); } break; } } } return(true); }
/// <summary>Deals damage to <paramref name="victim"/> and broadcasts the HP changing and substitute damage.</summary> /// <param name="culprit">The Pokémon responsible for the damage.</param> /// <param name="victim">The Pokémon receiving the damage.</param> /// <param name="hp">The amount of HP <paramref name="victim"/> will try to lose.</param> /// <param name="ignoreSubstitute">Whether the damage should ignore <paramref name="victim"/>'s <see cref="PBEStatus2.Substitute"/>.</param> /// <param name="ignoreSturdy">Whether the damage should ignore <paramref name="victim"/>'s <see cref="PBEAbility.Sturdy"/>, <see cref="PBEItem.FocusBand"/>, or <see cref="PBEItem.FocusSash"/>.</param> /// <returns>The amount of damage dealt.</returns> private ushort DealDamage(PBEPokemon culprit, PBEPokemon victim, int hp, bool ignoreSubstitute, bool ignoreSturdy = false) { if (hp < 1) { hp = 1; } if (!ignoreSubstitute && victim.Status2.HasFlag(PBEStatus2.Substitute)) { ushort oldHP = victim.SubstituteHP; victim.SubstituteHP = (ushort)Math.Max(0, victim.SubstituteHP - hp); ushort damageAmt = (ushort)(oldHP - victim.SubstituteHP); BroadcastStatus2(victim, culprit, PBEStatus2.Substitute, PBEStatusAction.Damage); return(damageAmt); } else { ushort oldHP = victim.HP; double oldPercentage = victim.HPPercentage; victim.HP = (ushort)Math.Max(0, victim.HP - hp); bool sturdyHappened = false, focusBandHappened = false, focusSashHappened = false; if (!ignoreSturdy && victim.HP == 0) { // TODO: Endure if (oldHP == victim.MaxHP && victim.Ability == PBEAbility.Sturdy && !culprit.HasCancellingAbility()) { sturdyHappened = true; victim.HP = 1; } else if (victim.Item == PBEItem.FocusBand && PBEUtils.RandomBool(10, 100)) { focusBandHappened = true; victim.HP = 1; } else if (oldHP == victim.MaxHP && victim.Item == PBEItem.FocusSash) { focusSashHappened = true; victim.HP = 1; } } victim.HPPercentage = (double)victim.HP / victim.MaxHP; BroadcastPkmnHPChanged(victim, oldHP, oldPercentage); if (sturdyHappened) { BroadcastAbility(victim, culprit, PBEAbility.Sturdy, PBEAbilityAction.Damage); BroadcastEndure(victim); } else if (focusBandHappened) { BroadcastItem(victim, culprit, PBEItem.FocusBand, PBEItemAction.Damage); } else if (focusSashHappened) { BroadcastItem(victim, culprit, PBEItem.FocusSash, PBEItemAction.Consumed); } return((ushort)(oldHP - victim.HP)); } }
public bool CanBecomeParalyzedBy(PBEPokemon other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } return(Status1 == PBEStatus1.None && !(Ability == PBEAbility.Limber && !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 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())); }
private ushort CalculateDamage(PBEPokemon user, PBEPokemon target, PBEMove move, PBEType moveType, PBEMoveCategory moveCategory, double basePower, bool criticalHit) { ushort damage; double a = 0, d = 0; bool unawareA = user != target && target.Ability == PBEAbility.Unaware && !user.HasCancellingAbility(); bool unawareD = user != target && user.Ability == PBEAbility.Unaware && !target.HasCancellingAbility(); // Verified: A target with Mold Breaker will accept more damage from a user with Unaware switch (move) { case PBEMove.FoulPlay: { double aMod = unawareA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, target.AttackChange) : target.AttackChange, false); a = CalculateAttack(user, target, moveType, target.Attack * aMod); double dMod = unawareD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false); d = CalculateDefense(user, target, target.Defense * dMod); break; } case PBEMove.Psyshock: case PBEMove.Psystrike: case PBEMove.SecretSword: { double aMod = unawareA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.SpAttackChange) : user.SpAttackChange, false); a = CalculateSpAttack(user, target, moveType, user.SpAttack * aMod); double dMod = unawareD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false); d = CalculateDefense(user, target, target.Defense * dMod); break; } default: { if (moveCategory == PBEMoveCategory.Physical) { double aMod = unawareA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.AttackChange) : user.AttackChange, false); a = CalculateAttack(user, target, moveType, user.Attack * aMod); double dMod = unawareD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false); d = CalculateDefense(user, target, target.Defense * dMod); } else if (moveCategory == PBEMoveCategory.Special) { double aMod = unawareA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.SpAttackChange) : user.SpAttackChange, false); a = CalculateSpAttack(user, target, moveType, user.SpAttack * aMod); double dMod = unawareD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.SpDefenseChange) : target.SpDefenseChange, false); d = CalculateSpDefense(user, target, target.SpDefense * dMod); } break; } } damage = (ushort)((2 * user.Level / 5) + 2); damage = (ushort)(damage * a * basePower / d); damage /= 50; damage += 2; return((ushort)(damage * (100 - PBEUtils.RandomInt(0, 15)) / 100)); }
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())); }
/// <summary>Returns True if the Pokémon can become <see cref="PBEStatus2.Infatuated"/> with <paramref name="other"/>.</summary> 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())); }
// 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 CanBecomePoisonedBy(PBEPokemon other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } return(Status1 == PBEStatus1.None && !HasType(PBEType.Poison) && !HasType(PBEType.Steel) && !(Ability == PBEAbility.Immunity && !other.HasCancellingAbility())); }
/// <summary> /// Deals damage to <paramref name="victim"/> and broadcasts the HP changing and substitute damage. /// </summary> /// <param name="culprit">The Pokémon responsible for the damage.</param> /// <param name="victim">The Pokémon receiving the damage.</param> /// <param name="hp">The amount of HP <paramref name="victim"/> will try to lose.</param> /// <param name="ignoreSubstitute">Whether the damage should ignore <paramref name="victim"/>'s <see cref="PBEStatus2.Substitute"/>.</param> /// <param name="ignoreSturdy">Whether the damage should ignore <paramref name="victim"/>'s <see cref="PBEAbility.Sturdy"/>, <see cref="PBEItem.FocusBand"/>, or <see cref="PBEItem.FocusSash"/>.</param> /// <returns>The amount of damage dealt.</returns> ushort DealDamage(PBEPokemon culprit, PBEPokemon victim, ushort hp, bool ignoreSubstitute, bool ignoreSturdy = false) { if (!ignoreSubstitute && victim.Status2.HasFlag(PBEStatus2.Substitute)) { ushort oldHP = victim.SubstituteHP; victim.SubstituteHP = (ushort)Math.Max(0, victim.SubstituteHP - Math.Max((ushort)1, hp)); // Always lose at least 1 HP ushort damageAmt = (ushort)(oldHP - victim.SubstituteHP); BroadcastStatus2(victim, culprit, PBEStatus2.Substitute, PBEStatusAction.Damage); return(damageAmt); } else { ushort oldHP = victim.HP; double oldPercentage = victim.HPPercentage; victim.HP = (ushort)Math.Max(0, victim.HP - Math.Max((ushort)1, hp)); // Always lose at least 1 HP bool sturdyHappened = false, focusBandHappened = false, focusSashHappened = false; if (!ignoreSturdy && victim.HP == 0) { // TODO: Endure if (oldHP == victim.MaxHP && victim.Ability == PBEAbility.Sturdy) // TODO: Mold Breaker { sturdyHappened = true; victim.HP = 1; } else if (victim.Item == PBEItem.FocusBand && PBEUtils.RNG.ApplyChance(10, 100)) { focusBandHappened = true; victim.HP = 1; } else if (oldHP == victim.MaxHP && victim.Item == PBEItem.FocusSash) { focusSashHappened = true; victim.HP = 1; } } victim.HPPercentage = (double)victim.HP / victim.MaxHP; BroadcastPkmnHPChanged(victim, oldHP, oldPercentage); if (sturdyHappened) { BroadcastAbility(victim, culprit, PBEAbility.Sturdy, PBEAbilityAction.Damage); BroadcastEndure(victim); } else if (focusBandHappened) { BroadcastItem(victim, culprit, PBEItem.FocusBand, PBEItemAction.Damage); } else if (focusSashHappened) { victim.Item = PBEItem.None; BroadcastItem(victim, culprit, PBEItem.FocusSash, PBEItemAction.Consumed); } return((ushort)(oldHP - victim.HP)); } }
private double CalculateSpAttack(PBEPokemon user, PBEPokemon target, PBEType moveType, double initialSpAttack) { double spAttack = initialSpAttack; if (user.Item == PBEItem.DeepSeaTooth && user.OriginalSpecies == PBESpecies.Clamperl) { spAttack *= 2.0; } if (user.Item == PBEItem.LightBall && user.OriginalSpecies == PBESpecies.Pikachu) { spAttack *= 2.0; } if (moveType == PBEType.Fire && user.Ability == PBEAbility.Blaze && user.HP <= user.MaxHP / 3) { spAttack *= 1.5; } if (moveType == PBEType.Grass && user.Ability == PBEAbility.Overgrow && user.HP <= user.MaxHP / 3) { spAttack *= 1.5; } if (moveType == PBEType.Water && user.Ability == PBEAbility.Torrent && user.HP <= user.MaxHP / 3) { spAttack *= 1.5; } if (ShouldDoWeatherEffects() && Weather == PBEWeather.HarshSunlight && user.Ability == PBEAbility.SolarPower) { spAttack *= 1.5; } if (user.Item == PBEItem.SoulDew && (user.OriginalSpecies == PBESpecies.Latias || user.OriginalSpecies == PBESpecies.Latios)) { spAttack *= 1.5; } if (user.Item == PBEItem.ChoiceSpecs) { spAttack *= 1.5; } if ((user.Ability == PBEAbility.Minus || user.Ability == PBEAbility.Plus) && Array.Exists(user.Team.ActiveBattlers, p => p != user && (p.Ability == PBEAbility.Minus || p.Ability == PBEAbility.Plus))) { spAttack *= 1.5; } if ((moveType == PBEType.Fire || moveType == PBEType.Ice) && target.Ability == PBEAbility.ThickFat && !user.HasCancellingAbility()) { spAttack *= 0.5; } if (user.Ability == PBEAbility.Defeatist && user.HP <= user.MaxHP / 2) { spAttack *= 0.5; } return(spAttack); }
double CalculateAttack(PBEPokemon user, PBEPokemon target, PBEType moveType, double initialAttack) { double attack = initialAttack; if (user.Ability == PBEAbility.HugePower || user.Ability == PBEAbility.PurePower) { attack *= 2.0; } if (user.Item == PBEItem.ThickClub && (user.Shell.Species == PBESpecies.Cubone || user.Shell.Species == PBESpecies.Marowak)) { attack *= 2.0; } if (user.Item == PBEItem.LightBall && user.Shell.Species == PBESpecies.Pikachu) { attack *= 2.0; } if (moveType == PBEType.Fire && user.Ability == PBEAbility.Blaze && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (moveType == PBEType.Grass && user.Ability == PBEAbility.Overgrow && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (moveType == PBEType.Water && user.Ability == PBEAbility.Torrent && user.HP <= user.MaxHP / 3) { attack *= 1.5; } if (user.Ability == PBEAbility.Hustle) { attack *= 1.5; } if (user.Ability == PBEAbility.Guts && user.Status1 != PBEStatus1.None) { attack *= 1.5; } if (user.Item == PBEItem.ChoiceBand) { attack *= 1.5; } if ((moveType == PBEType.Fire || moveType == PBEType.Ice) && target.Ability == PBEAbility.ThickFat) { attack *= 0.5; } if (user.Ability == PBEAbility.Defeatist && user.HP <= user.MaxHP / 2) { attack *= 0.5; } return(attack); }
private bool TypeCheck(PBEPokemon user, PBEPokemon target, PBEType moveType, out PBEResult result, out double damageMultiplier, bool statusMove = false) { result = target.IsAffectedByMove(user, moveType, out damageMultiplier, statusMove: statusMove); if (result == PBEResult.Ineffective_Ability) { BroadcastAbility(target, target, target.Ability, PBEAbilityAction.Damage); } if (result != PBEResult.NotVeryEffective_Type && result != PBEResult.Success && result != PBEResult.SuperEffective_Type) { BroadcastMoveResult(user, target, result); return(false); } return(true); }
/// <summary> /// Restores HP to <paramref name="pkmn"/> and broadcasts the HP changing if it changes. /// </summary> /// <param name="pkmn">The Pokémon receiving the HP.</param> /// <param name="hp">The amount of HP <paramref name="pkmn"/> will try to gain.</param> /// <returns>The amount of HP restored.</returns> private ushort HealDamage(PBEPokemon pkmn, ushort hp) { ushort oldHP = pkmn.HP; double oldPercentage = pkmn.HPPercentage; pkmn.HP = (ushort)Math.Min(pkmn.MaxHP, pkmn.HP + Math.Max((ushort)1, hp)); // Always try to heal at least 1 HP ushort healAmt = (ushort)(pkmn.HP - oldHP); if (healAmt > 0) { pkmn.HPPercentage = (double)pkmn.HP / pkmn.MaxHP; BroadcastPkmnHPChanged(pkmn, oldHP, oldPercentage); } return(healAmt); }
/// <summary>Transforms into <paramref name="target"/> and sets <see cref="PBEStatus2.Transformed"/>.</summary> /// <param name="target">The Pokémon to transform into.</param> /// <remarks>Frees the Pokémon of its <see cref="ChoiceLockedMove"/>.</remarks> public void Transform(PBEPokemon target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (Team != target.Team) { KnownAbility = target.KnownAbility = Ability = target.Ability; KnownType1 = target.KnownType1 = Type1 = target.Type1; KnownType2 = target.KnownType2 = Type2 = target.Type2; KnownWeight = target.KnownWeight = Weight = target.Weight; } else { Ability = target.Ability; KnownAbility = target.KnownAbility; Type1 = target.Type1; KnownType1 = target.KnownType1; Type2 = target.Type2; KnownType2 = target.KnownType2; Weight = target.Weight; KnownWeight = target.KnownWeight; } KnownGender = target.KnownGender = target.Gender; KnownShiny = target.KnownShiny = target.Shiny; KnownSpecies = target.KnownSpecies = Species = target.Species; Attack = target.Attack; Defense = target.Defense; SpAttack = target.SpAttack; SpDefense = target.SpDefense; Speed = target.Speed; AttackChange = target.AttackChange; DefenseChange = target.DefenseChange; SpAttackChange = target.SpAttackChange; SpDefenseChange = target.SpDefenseChange; SpeedChange = target.SpeedChange; AccuracyChange = target.AccuracyChange; EvasionChange = target.EvasionChange; TransformBackupMoves.Reset(Moves); PBEBattleMoveset.DoTransform(this, target); if (!Moves.Contains(ChoiceLockedMove)) { ChoiceLockedMove = PBEMove.None; } Status2 |= PBEStatus2.Transformed; }
ushort CalculateDamage(PBEPokemon user, PBEPokemon target, PBEMove move, PBEType moveType, PBEMoveCategory moveCategory, double basePower, bool criticalHit) { ushort damage; double a = 0, d = 0; switch (move) { case PBEMove.FoulPlay: { a = CalculateAttack(user, target, moveType, target.Attack * GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, target.AttackChange) : target.AttackChange, false)); d = CalculateDefense(user, target, target.Defense * GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false)); break; } case PBEMove.Psyshock: case PBEMove.Psystrike: case PBEMove.SecretSword: { a = CalculateSpAttack(user, target, moveType, user.SpAttack * GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.SpAttackChange) : user.SpAttackChange, false)); d = CalculateDefense(user, target, target.Defense * GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false)); break; } default: { if (moveCategory == PBEMoveCategory.Physical) { a = CalculateAttack(user, target, moveType, user.Attack * GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.AttackChange) : user.AttackChange, false)); d = CalculateDefense(user, target, target.Defense * GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false)); } else if (moveCategory == PBEMoveCategory.Special) { a = CalculateSpAttack(user, target, moveType, user.SpAttack * GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, user.SpAttackChange) : user.SpAttackChange, false)); d = CalculateSpDefense(user, target, target.SpDefense * GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.SpDefenseChange) : target.SpDefenseChange, false)); } break; } } damage = (ushort)(2 * user.Shell.Level / 5 + 2); damage = (ushort)(damage * a * basePower / d); damage /= 50; damage += 2; return((ushort)(damage * PBEUtils.RNG.Next(85, 101) / 100)); }
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); }
private double CalculateDefense(PBEPokemon user, PBEPokemon target, double initialDefense) { double defense = initialDefense; if (target.Item == PBEItem.MetalPowder && target.OriginalSpecies == PBESpecies.Ditto && !target.Status2.HasFlag(PBEStatus2.Transformed)) { defense *= 2.0; } if (target.Ability == PBEAbility.MarvelScale && target.Status1 != PBEStatus1.None && !user.HasCancellingAbility()) { defense *= 1.5; } if (target.Item == PBEItem.Eviolite && PBEPokemonData.GetData(target.OriginalSpecies).Evolutions.Count > 0) { defense *= 1.5; } return(defense); }
/// <summary> /// Determines whether chosen switches are valid. /// </summary> /// <param name="team">The team the inputted switches belong to.</param> /// <param name="switches">The switches the team wishes to execute.</param> /// <returns>False if the team already chose switches or the switches are illegal, True otherwise.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForSwitchIns"/>.</exception> public static bool AreSwitchesValid(PBETeam team, IEnumerable <Tuple <byte, PBEFieldPosition> > switches) { 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) { return(false); } foreach (Tuple <byte, PBEFieldPosition> s in switches) { PBEPokemon pkmn = team.Battle.TryGetPokemon(s.Item1); if (pkmn == null || pkmn.Team != team || pkmn.HP == 0 || pkmn.FieldPosition != PBEFieldPosition.None) { return(false); } } return(true); }
/// <summary>Selects switches if they are valid. Changes the battle state if both teams have selected valid switches.</summary> /// <param name="team">The team the inputted switches belong to.</param> /// <param name="switches">The switches the team wishes to execute.</param> /// <returns>True if the switches are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForSwitchIns"/>.</exception> public static bool SelectSwitchesIfValid(PBETeam team, IList <PBESwitchIn> 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)) { lock (team.Battle._disposeLockObj) { 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(false); }