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)); }
private double CalculateBasePower(PBEPokemon user, PBEPokemon[] targets, PBEMove move, PBEType moveType) { PBEMoveData mData = PBEMoveData.Data[move]; // Get move's base power double basePower; switch (move) { case PBEMove.CrushGrip: case PBEMove.WringOut: { PBEPokemon target = targets[0]; basePower = Math.Max(1, 120 * target.HP / target.MaxHP); break; } case PBEMove.Eruption: case PBEMove.WaterSpout: { basePower = Math.Max(1, mData.Power * user.HP / user.MaxHP); break; } case PBEMove.Flail: case PBEMove.Reversal: { int val = 48 * user.HP / user.MaxHP; if (val < 2) { basePower = 200; } else if (val < 4) { basePower = 150; } else if (val < 8) { basePower = 100; } else if (val < 16) { basePower = 80; } else if (val < 32) { basePower = 40; } else { basePower = 20; } break; } case PBEMove.Frustration: { basePower = Math.Max(1, (byte.MaxValue - user.Friendship) / 2.5); break; } case PBEMove.GrassKnot: case PBEMove.LowKick: { PBEPokemon target = targets[0]; if (target.Weight >= 200.0) { basePower = 120; } else if (target.Weight >= 100.0) { basePower = 100; } else if (target.Weight >= 50.0) { basePower = 80; } else if (target.Weight >= 25.0) { basePower = 60; } else if (target.Weight >= 10.0) { basePower = 40; } else { basePower = 20; } break; } case PBEMove.HeatCrash: case PBEMove.HeavySlam: { PBEPokemon target = targets[0]; double relative = user.Weight / target.Weight; if (relative < 2) { basePower = 40; } else if (relative < 3) { basePower = 60; } else if (relative < 4) { basePower = 80; } else if (relative < 5) { basePower = 100; } else { basePower = 120; } break; } case PBEMove.HiddenPower: { basePower = user.IndividualValues.HiddenPowerBasePower; break; } case PBEMove.Magnitude: { int val = PBEUtils.RandomInt(0, 99); byte magnitude; if (val < 5) // Magnitude 4 - 5% { magnitude = 4; basePower = 10; } else if (val < 15) // Magnitude 5 - 10% { magnitude = 5; basePower = 30; } else if (val < 35) // Magnitude 6 - 20% { magnitude = 6; basePower = 50; } else if (val < 65) // Magnitude 7 - 30% { magnitude = 7; basePower = 70; } else if (val < 85) // Magnitude 8 - 20% { magnitude = 8; basePower = 90; } else if (val < 95) // Magnitude 9 - 10% { magnitude = 9; basePower = 110; } else // Magnitude 10 - 5% { magnitude = 10; basePower = 150; } BroadcastMagnitude(magnitude); break; } case PBEMove.Punishment: { PBEPokemon target = targets[0]; basePower = Math.Max(1, Math.Min(200, 60 + (20 * target.GetPositiveStatTotal()))); break; } case PBEMove.Return: { basePower = Math.Max(1, user.Friendship / 2.5); break; } case PBEMove.StoredPower: { basePower = mData.Power + (20 * user.GetPositiveStatTotal()); break; } default: { basePower = mData.Power; break; } } // Ability/Item-specific power boosts bool canUseGems = !mData.Flags.HasFlag(PBEMoveFlag.UnaffectedByGems); switch (moveType) { case PBEType.Bug: { if (user.Ability == PBEAbility.Swarm && user.HP <= user.MaxHP / 3) { basePower *= 1.5; } switch (user.Item) { case PBEItem.InsectPlate: case PBEItem.SilverPowder: { basePower *= 1.2; break; } case PBEItem.BugGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.BugGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Dark: { switch (user.Item) { case PBEItem.BlackGlasses: case PBEItem.DreadPlate: { basePower *= 1.2; break; } case PBEItem.DarkGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.DarkGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Dragon: { switch (user.Item) { case PBEItem.AdamantOrb: { if (user.OriginalSpecies == PBESpecies.Dialga) { basePower *= 1.2; } break; } case PBEItem.DracoPlate: case PBEItem.DragonFang: { basePower *= 1.2; break; } case PBEItem.GriseousOrb: { if (user.OriginalSpecies == PBESpecies.Giratina_Origin) { basePower *= 1.2; } break; } case PBEItem.LustrousOrb: { if (user.OriginalSpecies == PBESpecies.Palkia) { basePower *= 1.2; } break; } case PBEItem.DragonGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.DragonGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Electric: { switch (user.Item) { case PBEItem.Magnet: case PBEItem.ZapPlate: { basePower *= 1.2; break; } case PBEItem.ElectricGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.ElectricGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Fighting: { switch (user.Item) { case PBEItem.BlackBelt: case PBEItem.FistPlate: { basePower *= 1.2; break; } case PBEItem.FightingGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.FightingGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Fire: { switch (user.Item) { case PBEItem.Charcoal: case PBEItem.FlamePlate: { basePower *= 1.2; break; } case PBEItem.FireGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.FireGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Flying: { switch (user.Item) { case PBEItem.SharpBeak: case PBEItem.SkyPlate: { basePower *= 1.2; break; } case PBEItem.FlyingGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.FlyingGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Ghost: { switch (user.Item) { case PBEItem.GriseousOrb: { if (user.OriginalSpecies == PBESpecies.Giratina_Origin) { basePower *= 1.2; } break; } case PBEItem.SpellTag: case PBEItem.SpookyPlate: { basePower *= 1.2; break; } case PBEItem.GhostGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.GhostGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Grass: { switch (user.Item) { case PBEItem.MeadowPlate: case PBEItem.MiracleSeed: case PBEItem.RoseIncense: { basePower *= 1.2; break; } case PBEItem.GrassGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.GrassGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Ground: { switch (user.Item) { case PBEItem.EarthPlate: case PBEItem.SoftSand: { basePower *= 1.2; break; } case PBEItem.GroundGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.GroundGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Ice: { switch (user.Item) { case PBEItem.IciclePlate: case PBEItem.NeverMeltIce: { basePower *= 1.2; break; } case PBEItem.IceGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.IceGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.None: { break; } case PBEType.Normal: { switch (user.Item) { case PBEItem.SilkScarf: { basePower *= 1.2; break; } case PBEItem.NormalGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.NormalGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Poison: { switch (user.Item) { case PBEItem.PoisonBarb: case PBEItem.ToxicPlate: { basePower *= 1.2; break; } case PBEItem.PoisonGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.PoisonGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Psychic: { switch (user.Item) { case PBEItem.MindPlate: case PBEItem.OddIncense: case PBEItem.TwistedSpoon: { basePower *= 1.2; break; } case PBEItem.PsychicGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.PsychicGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Rock: { switch (user.Item) { case PBEItem.HardStone: case PBEItem.RockIncense: case PBEItem.StonePlate: { basePower *= 1.2; break; } case PBEItem.RockGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.RockGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Steel: { switch (user.Item) { case PBEItem.AdamantOrb: { if (user.OriginalSpecies == PBESpecies.Dialga) { basePower *= 1.2; } break; } case PBEItem.IronPlate: case PBEItem.MetalCoat: { basePower *= 1.2; break; } case PBEItem.SteelGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.SteelGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } case PBEType.Water: { switch (user.Item) { case PBEItem.LustrousOrb: { if (user.OriginalSpecies == PBESpecies.Palkia) { basePower *= 1.2; } break; } case PBEItem.MysticWater: case PBEItem.SeaIncense: case PBEItem.SplashPlate: case PBEItem.WaveIncense: { basePower *= 1.2; break; } case PBEItem.WaterGem: { if (canUseGems) { BroadcastItem(user, user, PBEItem.WaterGem, PBEItemAction.Consumed); basePower *= 1.5; } break; } } break; } default: throw new ArgumentOutOfRangeException(nameof(moveType)); } // Move-specific power boosts switch (move) { case PBEMove.Acrobatics: { if (user.Item == PBEItem.None) { basePower *= 2.0; } break; } case PBEMove.Brine: { PBEPokemon target = targets[0]; if (target.HP <= target.HP / 2) { basePower *= 2.0; } break; } case PBEMove.Facade: { if (user.Status1 == PBEStatus1.Burned || user.Status1 == PBEStatus1.Paralyzed || user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned) { basePower *= 2.0; } break; } case PBEMove.Hex: { PBEPokemon target = targets[0]; if (target.Status1 != PBEStatus1.None) { basePower *= 2.0; } break; } case PBEMove.Retaliate: { if (user.Team.MonFaintedLastTurn) { basePower *= 2.0; } break; } case PBEMove.Venoshock: { PBEPokemon target = targets[0]; if (target.Status1 == PBEStatus1.Poisoned || target.Status1 == PBEStatus1.BadlyPoisoned) { basePower *= 2.0; } break; } case PBEMove.WeatherBall: { if (ShouldDoWeatherEffects() && Weather != PBEWeather.None) { basePower *= 2.0; } break; } } // Weather-specific power boosts if (ShouldDoWeatherEffects()) { switch (Weather) { case PBEWeather.HarshSunlight: { if (moveType == PBEType.Fire) { basePower *= 1.5; } else if (moveType == PBEType.Water) { basePower *= 0.5; } break; } case PBEWeather.Rain: { if (moveType == PBEType.Water) { basePower *= 1.5; } else if (moveType == PBEType.Fire) { basePower *= 0.5; } break; } case PBEWeather.Sandstorm: { if (user.Ability == PBEAbility.SandForce && (user.HasType(PBEType.Rock) || user.HasType(PBEType.Ground) || user.HasType(PBEType.Steel))) { basePower *= 1.3; } break; } } } // Other power boosts if (user.Status2.HasFlag(PBEStatus2.HelpingHand)) { basePower *= 1.5; } if (user.Ability == PBEAbility.ToxicBoost && mData.Category == PBEMoveCategory.Physical && (user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned)) { basePower *= 1.5; } if (user.Item == PBEItem.LifeOrb) { basePower *= 1.3; } if (user.Ability == PBEAbility.IronFist && mData.Flags.HasFlag(PBEMoveFlag.AffectedByIronFist)) { basePower *= 1.2; } if (user.Ability == PBEAbility.Reckless && mData.Flags.HasFlag(PBEMoveFlag.AffectedByReckless)) { basePower *= 1.2; } if (mData.Category == PBEMoveCategory.Physical && user.Item == PBEItem.MuscleBand) { basePower *= 1.1; } if (mData.Category == PBEMoveCategory.Special && user.Item == PBEItem.WiseGlasses) { basePower *= 1.1; } return(basePower); }
/// <summary>Selects actions if they are valid. Changes the battle state if both teams have selected valid actions.</summary> /// <param name="team">The team the inputted actions belong to.</param> /// <param name="actions">The actions the team wishes to execute.</param> /// <returns>True if the actions are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForActions"/>.</exception> public static bool SelectActionsIfValid(PBETeam team, IList <PBETurnAction> 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)) { lock (team.Battle._disposeLockObj) { if (!team.Battle.IsDisposed) { team.ActionsRequired.Clear(); foreach (PBETurnAction action in actions) { PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId); if (action.Decision == PBETurnDecision.Fight && pkmn.GetMoveTargets(action.FightMove) == PBEMoveTarget.RandomFoeSurrounding) { switch (team.Battle.BattleFormat) { case PBEBattleFormat.Single: case PBEBattleFormat.Rotation: { 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) { action.FightTargets = PBEUtils.RandomBool() ? PBETurnTarget.FoeCenter : PBETurnTarget.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) { if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left) != null) { action.FightTargets = PBETurnTarget.FoeLeft; } else { goto roll; } } else if (r == 1) { if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center) != null) { action.FightTargets = PBETurnTarget.FoeCenter; } else { goto roll; } } else { if (team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right) != null) { action.FightTargets = PBETurnTarget.FoeRight; } else { goto roll; } } } else { action.FightTargets = PBEUtils.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeCenter; } break; } default: throw new ArgumentOutOfRangeException(nameof(team.Battle.BattleFormat)); } } 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); } } } return(false); }
/// <summary> /// Selects actions if they are valid. Changes the battle state if both teams have selected valid actions. /// </summary> /// <param name="team">The team the inputted actions belong to.</param> /// <param name="actions">The actions the team wishes to execute.</param> /// <returns>True if the actions are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForActions"/>.</exception> public static bool SelectActionsIfValid(PBETeam team, IEnumerable <PBEAction> actions) { 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) { PBEPokemon pkmn = team.TryGetPokemon(action.PokemonId); pkmn.SelectedAction = action; switch (pkmn.SelectedAction.Decision) { case PBEDecision.Fight: { switch (pkmn.GetMoveTargets(pkmn.SelectedAction.FightMove)) { case PBEMoveTarget.RandomFoeSurrounding: { switch (team.Battle.BattleFormat) { case PBEBattleFormat.Single: case PBEBattleFormat.Rotation: { pkmn.SelectedAction.FightTargets = PBETarget.FoeCenter; break; } case PBEBattleFormat.Double: { pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeLeft : PBETarget.FoeRight; break; } case PBEBattleFormat.Triple: { if (pkmn.FieldPosition == PBEFieldPosition.Left) { pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeCenter : PBETarget.FoeRight; } else if (pkmn.FieldPosition == PBEFieldPosition.Center) { 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 (opposingTeam.TryGetPokemon(PBEFieldPosition.Left) != null) { pkmn.SelectedAction.FightTargets = PBETarget.FoeLeft; } else { goto roll; } } else if (r == 1) { if (opposingTeam.TryGetPokemon(PBEFieldPosition.Center) != null) { pkmn.SelectedAction.FightTargets = PBETarget.FoeCenter; } else { goto roll; } } else { if (opposingTeam.TryGetPokemon(PBEFieldPosition.Right) != null) { pkmn.SelectedAction.FightTargets = PBETarget.FoeRight; } else { goto roll; } } } else { pkmn.SelectedAction.FightTargets = PBEUtils.RandomBool() ? PBETarget.FoeLeft : PBETarget.FoeCenter; } break; } } break; } } break; } } } 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); }