async Task Attack(Pokemon Attacker, Move Move, Pokemon Opponent) { Attacker.AlreadyMoved = true; if (Attacker.Flinched) { await WriteStatus($"{GetStatusName(Attacker)} flinched and could not move"); Attacker.Flinched = false; if (Attacker.Ability == Ability.Steadfast) { await ShowAbility(Attacker); await DamageFunctionFactory.StatChange(Attacker, Stats.Speed, 1, this); } return; } switch (Attacker.NonVolatileStatus) { case NonVolatileStatus.Freeze: if (Move.Info.Flags.HasFlag(MoveFlags.Defrost) || Random.NextDouble() < 0.2) { Attacker.NonVolatileStatus = NonVolatileStatus.None; await WriteStatus($"{GetStatusName(Attacker)} thawed"); } else { Attacker.RaiseShowNonVolativeStatusAnimation(); await WriteStatus($"{GetStatusName(Attacker)} is frozen. It is unable to move."); return; } break; case NonVolatileStatus.Paralysis: if (Random.NextDouble() < 0.25) { Attacker.RaiseShowNonVolativeStatusAnimation(); await WriteStatus($"{GetStatusName(Attacker)} is paralysed. It is unable to move."); return; } break; // TODO: Snore and Sleep Talk can be used while sleeping case NonVolatileStatus.Sleep: --Attacker.SleepCounter; if (Attacker.Ability == Ability.EarlyBird) { --Attacker.SleepCounter; } if (Attacker.SleepCounter < 0) { Attacker.SleepCounter = 0; Attacker.NonVolatileStatus = NonVolatileStatus.None; await WriteStatus($"{GetStatusName(Attacker)} woke up"); } else { await WriteStatus($"{GetStatusName(Attacker)} is asleep"); return; } break; } if (Attacker.ConfusionCounter > 0) { --Attacker.ConfusionCounter; await WriteStatus($"{GetStatusName(Attacker)} is confused"); if (Random.Next(100) < 33) { await DamageFunctionFactory.ConfusedSelfAttack(Attacker, this); return; } } if (Move.Info.IsZ) { Attacker.Transforming = true; await WriteStatus($"{GetStatusName(Attacker)} surrounded itself with its Z-Power"); await WriteStatus($"{GetStatusName(Attacker)} unleashes its full force Z-Move"); await WriteStatus(Move.Name.ToUpper()); Attacker.Transforming = false; } else { await WriteStatus($"{GetStatusName(Attacker)} used {Move}{(Format != 1 && Opponent != null ? $" on {Opponent}" : "")}"); if (DoAnimation != null) { await DoAnimation.Invoke(Attacker, Move, Opponent, this); } } Move.Multitargets = false; async Task OnFainted(Pokemon Pokemon) { if (Pokemon?.Stats.CurrentHP == 0) { SwitchOutEffects(Pokemon); await WriteStatus($"{GetStatusName(Pokemon)} fainted"); } } if (Format == 1) { await Move.Info.DamageFunction(Attacker, Move, Opponent, this); await OnFainted(Opponent); } else { switch (Move.Info.Target) { case MoveTarget.Normal: case MoveTarget.Self: await Move.Info.DamageFunction(Attacker, Move, Opponent, this); await OnFainted(Opponent); break; case MoveTarget.AllAdjacentFoes: var adjacentFoes = Attacker.GetAdjacentFoes().ToArray(); Move.Multitargets = adjacentFoes.Length > 1; foreach (var adjacentFoe in adjacentFoes) { await Move.Info.DamageFunction(Attacker, Move, adjacentFoe, this); await OnFainted(adjacentFoe); } break; case MoveTarget.AllAdjacent: var adjacents = Attacker.GetAdjacent(true).ToArray(); Move.Multitargets = adjacents.Length > 1; foreach (var adjacent in adjacents) { await Move.Info.DamageFunction(Attacker, Move, adjacent, this); await OnFainted(adjacent); } break; } } // Recoil if (Attacker.Stats.CurrentHP == 0) { SwitchOutEffects(Attacker); await WriteStatus($"{GetStatusName(Attacker)} fainted"); } // Pressure if (Opponent?.Ability == Ability.Pressure) { --Move.PPLeft; } --Move.PPLeft; }
async void BattleLoop() { while (true) { await Reset(); var sides = new[] { PlayerSide, OpponentSide }; do { await SwitchFainted(sides); // Move Queue var moveQueue = new List <PokemonMovePair>(); // Mega Evolution Queue var megaQueue = new List <PokemonMovePair>(); var switchedIn = new List <PokemonMovePair>(); // TODO: Check Move Target type foreach (var side in sides) { if (side.Computer) { await ComputerMoves(side, megaQueue, moveQueue); } else { await PlayerMoves(side, side.OpposingSide, switchedIn, megaQueue, moveQueue); } } var switchOrder = MoveOrder(switchedIn).ToArray(); // Switch In Effects foreach (var data in switchOrder) { await SwitchInEffects(data.Attacker); } foreach (var data in switchOrder) { await PostTurnStatusEffects(data.Attacker); await PostTurnWeatherEffects(data.Attacker); } // Mega Evolve foreach (var data in megaQueue) { await MegaEvolve(data.Attacker); } // Move foreach (var data in MoveOrder(moveQueue)) { // If Attacker and Target are alive if (!data.Attacker.IsFainted && (data.Target == null || !data.Target.IsFainted)) { await Attack(data.Attacker, data.Move, data.Target); } } var ordered = MoveOrder(AllBattling()).ToArray(); foreach (var data in ordered) { if (!data.Attacker.IsFainted) { await PostTurnWeatherEffects(data.Attacker); } } foreach (var data in ordered) { if (!data.Attacker.IsFainted) { // Speed Boost if (data.Attacker.Ability == Ability.SpeedBoost) { await ShowAbility(data.Attacker); await DamageFunctionFactory.StatChange(data.Attacker, Stats.Speed, 1, this); } await PostTurnStatusEffects(data.Attacker); } } await EndWeather(); } while (!PlayerSide.AllFainted && !OpponentSide.AllFainted); // Result var playerFaint = PlayerSide.AllFainted; var opponentFaint = OpponentSide.AllFainted; PlayerSide = OpponentSide = null; if (playerFaint && opponentFaint) { await WriteStatus("DRAW"); } else if (playerFaint) { await WriteStatus("YOU LOST"); } else { await WriteStatus("YOU WON"); } await WriteStatus("START NEW GAME"); } }
async Task SwitchInEffects(Pokemon Pokemon) { if (Pokemon.Ability is NormalizingAbility normalizingAbility) { foreach (var move in Pokemon.Moves) { if (move.Type == Types.Normal) { move.Type = normalizingAbility.Type; } } } // TODO: Does not work on Substitute // TODO: Only for adjacent Pokemon // Intimidate if (Pokemon.Ability == Ability.Intimidate) { await ShowAbility(Pokemon); foreach (var pokemon in Pokemon.GetAdjacentFoes()) { await DamageFunctionFactory.StatChange(pokemon, Stats.Attack, -1, this); } } // Download // TODO: Verify for Triple Battles // TODO: Consider Power Trick else if (Pokemon.Ability == Ability.Download) { await ShowAbility(Pokemon); var defense = 0; var specialDefense = 0; foreach (var pokemon in Pokemon.Side.OpposingSide.Battling) { if (pokemon != null) { defense += pokemon.Stats.GetValue(Stats.Defense); specialDefense += pokemon.Stats.GetValue(Stats.SpecialDefense); } } await DamageFunctionFactory.StatChange(Pokemon, defense <= specialDefense?Stats.SpecialAttack : Stats.Attack, 1, this); } // Pressure else if (Pokemon.Ability == Ability.Pressure) { await ShowAbility(Pokemon); await WriteStatus($"{GetStatusName(Pokemon)} is exerting its Pressure!"); } // Air Lock, Cloud Nine else if (Pokemon.Ability is WeatherEffectNegatorAbility) { await ShowAbility(Pokemon); ++SuppressWeather; await WriteStatus("The effects of the weather disappeared."); } // Frisk else if (Pokemon.Ability == Ability.Frisk) { await ShowAbility(Pokemon); foreach (var pokemon in Pokemon.Side.OpposingSide.Battling) { if (pokemon?.HeldItem != null) { await WriteStatus($"{GetStatusName(Pokemon)} frisked and found {pokemon}'s {pokemon.HeldItem}"); } } } await WeatherAbility(Pokemon); }