// Verified: Sturdy and Substitute only activate on damaging attacks (so draining HP or liquid ooze etc can bypass sturdy) private ushort DealDamage(PBEBattlePokemon culprit, PBEBattlePokemon victim, int hp, bool ignoreSubstitute = true, bool ignoreSturdy = true) { if (hp < 1) { hp = 1; } if (!ignoreSubstitute && victim.Status2.HasFlag(PBEStatus2.Substitute)) { ushort oldSubHP = victim.SubstituteHP; victim.SubstituteHP = (ushort)Math.Max(0, victim.SubstituteHP - hp); ushort damageAmt = (ushort)(oldSubHP - victim.SubstituteHP); BroadcastStatus2(victim, culprit, PBEStatus2.Substitute, PBEStatusAction.Damage); return(damageAmt); } 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 && PBERandom.RandomBool(10, 100)) { focusBandHappened = true; victim.HP = 1; } else if (oldHP == victim.MaxHP && victim.Item == PBEItem.FocusSash) { focusSashHappened = true; victim.HP = 1; } } victim.UpdateHPPercentage(); 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)); }
/// <summary>Selects actions if they are valid. Changes the battle state if both teams have selected valid actions.</summary> /// <param name="trainer">The trainer 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(PBETrainer trainer, IReadOnlyList <PBETurnAction> actions) { if (AreActionsValid(trainer, actions)) { trainer.ActionsRequired.Clear(); foreach (PBETurnAction action in actions) { PBEBattlePokemon pkmn = trainer.TryGetPokemon(action.PokemonId); if (action.Decision == PBETurnDecision.Fight && pkmn.GetMoveTargets(action.FightMove) == PBEMoveTarget.RandomFoeSurrounding) { switch (trainer.Battle.BattleFormat) { case PBEBattleFormat.Single: case PBEBattleFormat.Rotation: { action.FightTargets = PBETurnTarget.FoeCenter; break; } case PBEBattleFormat.Double: { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeRight; break; } case PBEBattleFormat.Triple: { if (pkmn.FieldPosition == PBEFieldPosition.Left) { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeCenter : PBETurnTarget.FoeRight; } else if (pkmn.FieldPosition == PBEFieldPosition.Center) { PBETeam oppTeam = trainer.Team.OpposingTeam; int r; // Keep randomly picking until a non-fainted foe is selected roll: r = PBERandom.RandomInt(0, 2); if (r == 0) { if (oppTeam.TryGetPokemon(PBEFieldPosition.Left) != null) { action.FightTargets = PBETurnTarget.FoeLeft; } else { goto roll; } } else if (r == 1) { if (oppTeam.TryGetPokemon(PBEFieldPosition.Center) != null) { action.FightTargets = PBETurnTarget.FoeCenter; } else { goto roll; } } else { if (oppTeam.TryGetPokemon(PBEFieldPosition.Right) != null) { action.FightTargets = PBETurnTarget.FoeRight; } else { goto roll; } } } else { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeCenter; } break; } default: throw new ArgumentOutOfRangeException(nameof(trainer.Battle.BattleFormat)); } } pkmn.TurnAction = action; } if (trainer.Battle.Trainers.All(t => t.ActionsRequired.Count == 0)) { trainer.Battle.BattleState = PBEBattleState.ReadyToRunTurn; trainer.Battle.OnStateChanged?.Invoke(trainer.Battle); } return(true); } return(false); }
private IEnumerable <PBEBattlePokemon> GetActingOrder(IEnumerable <PBEBattlePokemon> pokemon, bool ignoreItemsThatActivate) { var evaluated = new List <(PBEBattlePokemon Pokemon, double Speed)>(); // TODO: Full Incense, Lagging Tail, Stall, Quick Claw foreach (PBEBattlePokemon pkmn in pokemon) { double speed = pkmn.Speed * GetStatChangeModifier(pkmn.SpeedChange, false); switch (pkmn.Item) { case PBEItem.ChoiceScarf: { speed *= 1.5; break; } case PBEItem.MachoBrace: case PBEItem.PowerAnklet: case PBEItem.PowerBand: case PBEItem.PowerBelt: case PBEItem.PowerBracer: case PBEItem.PowerLens: case PBEItem.PowerWeight: { speed *= 0.5; break; } case PBEItem.QuickPowder: { if (pkmn.OriginalSpecies == PBESpecies.Ditto && !pkmn.Status2.HasFlag(PBEStatus2.Transformed)) { speed *= 2.0; } break; } } if (ShouldDoWeatherEffects()) { if (Weather == PBEWeather.HarshSunlight && pkmn.Ability == PBEAbility.Chlorophyll) { speed *= 2.0; } else if (Weather == PBEWeather.Rain && pkmn.Ability == PBEAbility.SwiftSwim) { speed *= 2.0; } else if (Weather == PBEWeather.Sandstorm && pkmn.Ability == PBEAbility.SandRush) { speed *= 2.0; } } switch (pkmn.Ability) { case PBEAbility.QuickFeet: { if (pkmn.Status1 != PBEStatus1.None) { speed *= 1.5; } break; } case PBEAbility.SlowStart: { if (pkmn.SlowStart_HinderTurnsLeft > 0) { speed *= 0.5; } break; } } if (pkmn.Ability != PBEAbility.QuickFeet && pkmn.Status1 == PBEStatus1.Paralyzed) { speed *= 0.25; } if (pkmn.Team.TeamStatus.HasFlag(PBETeamStatus.Tailwind)) { speed *= 2.0; } Debug.WriteLine("Team {0}'s {1}'s evaluated speed: {2}", pkmn.Team.Id, pkmn.Nickname, speed); (PBEBattlePokemon Pokemon, double Speed)tup = (pkmn, speed); if (evaluated.Count == 0) { evaluated.Add(tup); } else { int pkmnTiedWith = evaluated.FindIndex(t => t.Speed == speed); if (pkmnTiedWith != -1) // Speed tie - randomly go before or after the Pokémon it tied with { if (PBERandom.RandomBool()) { if (pkmnTiedWith == evaluated.Count - 1) { evaluated.Add(tup); } else { evaluated.Insert(pkmnTiedWith + 1, tup); } } else { evaluated.Insert(pkmnTiedWith, tup); } } else { int pkmnToGoBefore = evaluated.FindIndex(t => BattleStatus.HasFlag(PBEBattleStatus.TrickRoom) ? t.Speed > speed : t.Speed < speed); if (pkmnToGoBefore == -1) { evaluated.Add(tup); } else { evaluated.Insert(pkmnToGoBefore, tup); } } } Debug.WriteLine(evaluated.Select(t => $"{t.Pokemon.Team.Id} {t.Pokemon.Nickname} {t.Speed}").Print()); } return(evaluated.Select(t => t.Pokemon)); }