public static async Task Recover(Pokemon Attacker, int Amount, BattleViewModel Battle) { var maxHp = Attacker.Stats.MaxHP; if (Attacker.Stats.CurrentHP < maxHp) { Attacker.Stats.Heal(Amount); await Battle.WriteStatus($"{Battle.GetStatusName(Attacker)}'s HP was restored"); } else { await Battle.WriteStatus($"{Battle.GetStatusName(Attacker)}'s already has full HP"); } }
public static async Task ConfusedSelfAttack(Pokemon Pokemon, BattleViewModel Battle) { // Typeless 40 base power Physical attack var damage = GetBaseDamage(Pokemon.Level, 40, GetAttack(Pokemon, MoveKind.Physical, Battle, false), GetDefense(Pokemon, MoveKind.Physical, Battle, false)); await Pokemon.Stats.Damage((int)damage, Battle); await Battle.WriteStatus($"{Battle.GetStatusName(Pokemon)} hurt itself in confusion"); }
static async Task Confuse(Pokemon Target, double Probability, BattleViewModel Battle) { if (Target.ConfusionCounter > 0) { return; } if (BattleViewModel.Random.NextDouble() > Probability) { return; } // Own Tempo if (Target.Ability == Ability.OwnTempo) { return; } Target.ConfusionCounter = BattleViewModel.Random.Next(1, 5); await Battle.WriteStatus($"{Battle.GetStatusName(Target)} is confused."); }
static async Task GiveNonVolatileStatus(NonVolatileStatusDefinition Definition, Pokemon Target, Pokemon Attacker, BattleViewModel Battle, Func <Task> AbilityCallback = null) { if (Definition == null) { return; } if (Target.Stats.CurrentHP == 0) { return; } if (Target.NonVolatileStatus != NonVolatileStatus.None) { return; } // Leaf Guard if (Battle.SuppressWeather == 0 && Target.Ability == Ability.LeafGuard && Battle.Weather.Is(Weather.HarshSunlight, Weather.ExtremelyHarshSunlight)) { return; } if (Definition.Probability < 1 && BattleViewModel.Random.NextDouble() > Definition.Probability) { return; } switch (Definition.Status) { case NonVolatileStatus.Burn: if (Target.Is(Types.Fire)) { return; } // Water Veil, Water Bubble if (Target.Ability.Is(Ability.WaterVeil, Ability.WaterBubble)) { return; } if (AbilityCallback != null) { await AbilityCallback.Invoke(); } Target.NonVolatileStatus = NonVolatileStatus.Burn; await Battle.WriteStatus($"{Battle.GetStatusName(Target)} is burned"); break; case NonVolatileStatus.Freeze: if (Battle.SuppressWeather == 0 && Battle.Weather.Is(Weather.HarshSunlight, Weather.ExtremelyHarshSunlight)) { return; } if (Target.Is(Types.Ice)) { return; } // Magma Armor if (Target.Ability == Ability.MagmaArmor) { return; } if (AbilityCallback != null) { await AbilityCallback.Invoke(); } Target.NonVolatileStatus = NonVolatileStatus.Freeze; await Battle.WriteStatus($"{Battle.GetStatusName(Target)} is frozen"); break; case NonVolatileStatus.Paralysis: if (Target.Is(Types.Electric)) { return; } // Limber if (Target.Ability == Ability.Limber) { return; } if (AbilityCallback != null) { await AbilityCallback.Invoke(); } Target.NonVolatileStatus = NonVolatileStatus.Paralysis; await Battle.WriteStatus($"{Battle.GetStatusName(Target)} is paralysed. It may be unable to move."); break; case NonVolatileStatus.Poison: case NonVolatileStatus.BadPoison: if (Attacker?.Ability != Ability.Corrosion) { if (Target.Is(Types.Poison, Types.Steel)) { return; } } // Immunity if (Target.Ability == Ability.Immunity) { return; } if (AbilityCallback != null) { await AbilityCallback.Invoke(); } Target.NonVolatileStatus = Definition.Status; await Battle.WriteStatus($"{Battle.GetStatusName(Target)} is poisoned."); break; case NonVolatileStatus.Sleep: if (Target.Ability.Is(Ability.Insomnia, Ability.VitalSpirit)) { return; } if (AbilityCallback != null) { await AbilityCallback.Invoke(); } Target.SleepCounter = BattleViewModel.Random.Next(1, 4); Target.NonVolatileStatus = NonVolatileStatus.Sleep; await Battle.WriteStatus($"{Battle.GetStatusName(Target)} fell asleep."); break; } // Synchronize if (Target.Ability == Ability.Synchronize && Attacker != null) { switch (Definition.Status) { case NonVolatileStatus.Burn: case NonVolatileStatus.Paralysis: case NonVolatileStatus.Poison: case NonVolatileStatus.BadPoison: await Battle.ShowAbility(Target); await GiveNonVolatileStatus(new NonVolatileStatusDefinition(Definition.Status), Attacker, Target, Battle); break; } } }
public static async Task StatChanges(Pokemon Attacker, Pokemon Opponent, IEnumerable <StatChange> StatChanges, BattleViewModel Battle) { foreach (var statChange in StatChanges) { if (statChange.Probability < 1) { if (BattleViewModel.Random.NextDouble() > statChange.Probability) { continue; } } var stageChange = statChange.StageChange; var victim = statChange.Self ? Attacker : Opponent; if (victim.Stats.CurrentHP == 0) { continue; } // Clear Body, White Smoke, Full Metal Body if (stageChange < 0 && !statChange.Self && victim.Ability.Is(Ability.ClearBody, Ability.WhiteSmoke, Ability.FullMetalBody)) { await Battle.ShowAbility(victim); await Battle.WriteStatus($"{Battle.GetStatusName(victim)}'s {statChange.Stat.SpaceAtCapitals()} was not lowered."); continue; } // Contrary if (victim.Ability == Ability.Contrary) { stageChange = -stageChange; } if (!statChange.Self && victim.Ability is StatFallPreventionAbility statFallPreventionAbility && statFallPreventionAbility.Stat == statChange.Stat && stageChange < 0) { await Battle.ShowAbility(victim); await Battle.WriteStatus($"{Battle.GetStatusName(victim)} {statChange.Stat.SpaceAtCapitals()} was not lowered"); continue; } stageChange = victim.Stats.ChangeStage(statChange.Stat, stageChange); if (stageChange == 0) { await Battle.WriteStatus($"{Battle.GetStatusName(victim)}'s {statChange.Stat.SpaceAtCapitals()} won't go any {(statChange.StageChange > 0 ? "higher" : "lower")}"); } else { var sharply = stageChange > 1 || stageChange < -1; await Battle.WriteStatus($"{Battle.GetStatusName(victim)}'s {statChange.Stat.SpaceAtCapitals()} {(stageChange > 0 ? "rose" : "fell")}{(sharply ? " sharply" : "")}"); } } }
static async Task ContactEffects(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle) { // Long Reach if (Attacker.Ability == Ability.LongReach) { return; } if (Move.Info.Flags.HasFlag(MoveFlags.Contact)) { #region Opponent Abilities // Static if (Opponent.Ability == Ability.Static && Attacker.NonVolatileStatus == NonVolatileStatus.None) { await GiveNonVolatileStatus(new NonVolatileStatusDefinition(NonVolatileStatus.Paralysis, 0.3), Attacker, Opponent, Battle, () => Battle.ShowAbility(Opponent)); } // Flame Body else if (Opponent.Ability == Ability.FlameBody) { await GiveNonVolatileStatus(new NonVolatileStatusDefinition(NonVolatileStatus.Burn, 0.3), Attacker, Opponent, Battle, () => Battle.ShowAbility(Opponent)); } // Gooey, Tangling Hair else if (Opponent.Ability.Is(Ability.Gooey, Ability.TanglingHair)) { await StatChange(Attacker, Stats.Speed, -1, Battle); } // Iron Barbs, Rough Skin else if (Opponent.Ability.Is(Ability.IronBarbs, Ability.RoughSkin)) { await Battle.ShowAbility(Opponent); await Battle.WriteStatus($"{Battle.GetStatusName(Attacker)} took damaged"); await Attacker.Stats.Damage(Attacker.Stats.MaxHP / 8, Battle); } // Poison Point else if (Opponent.Ability == Ability.PoisonPoint) { await GiveNonVolatileStatus(new NonVolatileStatusDefinition(NonVolatileStatus.Poison, 0.3), Attacker, Opponent, Battle, () => Battle.ShowAbility(Opponent)); } // Effect Spore else if (Opponent.Ability == Ability.EffectSpore) { var status = NonVolatileStatus.None; switch (BattleViewModel.Random.Next(3)) { case 0: status = NonVolatileStatus.Poison; break; case 1: status = NonVolatileStatus.Paralysis; break; case 2: status = NonVolatileStatus.Sleep; break; } await GiveNonVolatileStatus(new NonVolatileStatusDefinition(status, 0.3), Attacker, Opponent, Battle, () => Battle.ShowAbility(Opponent)); } #endregion #region Attacker abilities // Poison Touch if (Attacker.Ability == Ability.PoisonTouch) { await GiveNonVolatileStatus(new NonVolatileStatusDefinition(NonVolatileStatus.Poison, 0.3), Opponent, Attacker, Battle, () => Battle.ShowAbility(Attacker)); } #endregion } }
static async Task <bool> Absorb(Move Move, Pokemon Opponent, BattleViewModel Battle) { // Flash Fire if (Opponent.Ability == Ability.FlashFire && Move.Type == Types.Fire) { await Battle.ShowAbility(Opponent); await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)} absorbed the attack"); // Hit by a fire type move Opponent.AbilityData = 1; return(true); } // Motor Drive if (Opponent.Ability == Ability.MotorDrive && Move.Type == Types.Electric && Opponent.GetTypeEffectiveness(Types.Electric) > 0) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Speed, 1, Battle); return(true); } // Sap Sipper if (Opponent.Ability == Ability.SapSipper && Move.Type == Types.Grass) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Attack, 1, Battle); return(true); } // Volt Absorb if (Opponent.Ability == Ability.VoltAbsorb && Move.Type == Types.Electric && Opponent.GetTypeEffectiveness(Types.Electric) > 0) { await Battle.ShowAbility(Opponent); if (Opponent.Stats.Heal(Opponent.Stats.MaxHP / 4)) { await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)} absorbed the attack."); } return(true); } // Water Absorb, Dry Skin if (Opponent.Ability.Is(Ability.WaterAbsorb, Ability.DrySkin) && Move.Type == Types.Water && Opponent.GetTypeEffectiveness(Types.Water) > 0) { await Battle.ShowAbility(Opponent); if (Opponent.Stats.Heal(Opponent.Stats.MaxHP / 4)) { await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)} absorbed the attack."); } return(true); } return(false); }
public static async Task Default(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle) { await StatChanges(Attacker, Opponent, Move.Info.PreStatChanges, Battle); if (!await IsHit(Attacker, Move, Opponent, Battle)) { return; } if (await Absorb(Move, Opponent, Battle)) { return; } var power = GetPower(Attacker, Move, Opponent, Battle); if (power != null) { var criticalHit = IsCriticalHit(Attacker, Move, Opponent); var attack = GetAttack(Attacker, Move.Kind, Battle, criticalHit); var defense = GetDefense(Opponent, Move.Kind, Battle, criticalHit); var damage = GetBaseDamage(Attacker.Level, power.Value, attack, defense); damage *= SameTypeAttackBoost(Attacker, Move.Type); if (Battle.SuppressWeather == 0) { switch (Battle.Weather) { case Weather.HeavyRain when Move.Type == Types.Fire: damage = 0; break; case Weather.ExtremelyHarshSunlight when Move.Type == Types.Water: damage = 0; break; } } // Weather can nullify damage if (damage == 0) { await Battle.WriteStatus("But it failed"); return; } var typeEffectivenessDisplays = new List <string>(); var typeEffect = TypeEffectiveness(Attacker, Move, Opponent, Battle, typeEffectivenessDisplays); if (typeEffect == 0) { await Battle.WriteStatus(typeEffectivenessDisplays[0]); return; } // Burn if (Move.Kind == MoveKind.Physical && Attacker.Ability != Ability.Guts && Attacker.NonVolatileStatus == NonVolatileStatus.Burn) { damage /= 2; } // Heatproof if (Move.Type == Types.Fire && Opponent.Ability == Ability.Heatproof) { damage /= 2; } // Fur Coat else if (Move.Kind == MoveKind.Physical && Opponent.Ability == Ability.FurCoat) { damage /= 2; } // Water Bubble else if (Move.Type == Types.Fire && Opponent.Ability == Ability.WaterBubble) { damage /= 2; } // Hustle if (Move.Kind == MoveKind.Physical && Attacker.Ability == Ability.Hustle) { damage *= 1.5; } // Thick Fat else if (Move.Type.Is(Types.Fire, Types.Ice) && Attacker.Ability == Ability.ThickFat) { damage /= 2; } // Tough Claws else if (Move.Info.Flags.HasFlag(MoveFlags.Contact) && Attacker.Ability == Ability.ToughClaws) { damage *= 1.33; } damage *= typeEffect; if (criticalHit) { damage *= Attacker.Ability == Ability.Sniper ? 2.25 : 1.5; } if (Attacker.Stats.CurrentHP <= Attacker.Stats.MaxHP / 3) { if (Attacker.Ability is PinchAbility pinchAbility && pinchAbility.Type == Move.Type) { damage *= 1.5; } } // Multiscale if (Opponent.Ability == Ability.Multiscale && Opponent.Stats.CurrentHP == Opponent.Stats.MaxHP) { damage /= 2; } else if (Opponent.Ability == Ability.DrySkin) { damage *= 1.25; } // Rivalry if (Attacker.Ability == Ability.Rivalry && Attacker.Gender != Gender.Genderless && Opponent.Gender != Gender.Genderless) { damage *= Attacker.Gender == Opponent.Gender ? 1.25 : 0.75; } // Flash Fire if (Attacker.Ability == Ability.FlashFire && Move.Type == Types.Fire && Attacker.AbilityData == 1) { damage *= 1.5; } // Random damage *= BattleViewModel.Random.Next(85, 101) / 100.0; // Damage is reduced by 25% when there are Multiple Targets if (Move.Multitargets) { damage *= 0.75; } if (damage > Opponent.Stats.CurrentHP) { damage = Opponent.Stats.CurrentHP; } if (damage < 1) { damage = 1; } // Disguise if (Opponent.Ability == Ability.Disguise && Opponent.AbilityData == 0) { await Battle.ShowAbility(Opponent); await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)}'s disguise was busted"); Opponent.AbilityData = 1; return; } await Opponent.Stats.Damage((int)damage, Battle); foreach (var display in typeEffectivenessDisplays) { await Battle.WriteStatus(display); } if (criticalHit) { await Battle.WriteStatus("It's a critical hit"); } if (damage > 0 && Move.Info.Drain > 0) { await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)} had its energy drained"); var drain = (int)(Move.Info.Drain * damage); Attacker.Stats.Heal(drain); } if (damage > 0 && Move.Info.Recoil > 0 && Attacker.Ability != Ability.RockHead) { await Battle.WriteStatus($"{Battle.GetStatusName(Attacker)} is hit by recoil"); var recoil = (int)(Move.Info.Recoil * damage); await Attacker.Stats.Damage(recoil, Battle); } // Cell Battery if (Opponent.HeldItem == HeldItem.CellBattery && Move.Type == Types.Electric) { await StatChange(Opponent, Stats.Attack, 1, Battle); Opponent.HeldItem = null; } // Anger Point if (criticalHit && Opponent.Ability == Ability.AngerPoint) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Attack, 12, Battle); } // Cursed Body if (Opponent.Ability == Ability.CursedBody && BattleViewModel.Random.Next(100) < 30) { await Battle.ShowAbility(Opponent); Move.Disabled = true; await Battle.WriteStatus($"{Battle.GetStatusName(Attacker)}'s {Move} was disabled"); } } // Justified if (Move.Type == Types.Dark && Opponent.Ability == Ability.Justified) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Attack, 1, Battle); } // Stamina else if (Move.Kind != MoveKind.Status && Opponent.Ability == Ability.Stamina) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Defense, 1, Battle); } // Rattled else if (Opponent.Ability == Ability.Rattled) { if (Move.Type.Is(Types.Dark, Types.Ghost, Types.Bug)) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Speed, 1, Battle); } } // Weak Armor else if (Move.Kind == MoveKind.Physical && Opponent.Ability == Ability.WeakArmor) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Speed, 1, Battle); await StatChange(Opponent, Stats.Defense, -1, Battle); } // Water Compaction else if (Move.Type == Types.Water && Opponent.Ability == Ability.WaterCompaction) { await Battle.ShowAbility(Opponent); await StatChange(Opponent, Stats.Defense, 2, Battle); } await ContactEffects(Attacker, Move, Opponent, Battle); await StatChanges(Attacker, Opponent, Move.Info.PostStatChanges, Battle); await GiveNonVolatileStatus(Move.Info.NonVolatileStatus, Opponent, Attacker, Battle); if (!Opponent.IsFainted) { if (Move.Info.Flinch != null) { Flinch(Opponent, Move.Info.Flinch.Value); } else if (Attacker.Ability == Ability.Stench) { Flinch(Opponent, 0.1); } if (Move.Info.ConfuseTarget != null) { await Confuse(Opponent, Move.Info.ConfuseTarget.Value, Battle); } } if (Move.Info.Flags.HasFlag(MoveFlags.Recharge)) { Attacker.Recharging = true; } }
static async Task <bool> IsHit(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle) { var accuracy = Move.Info.AccuracyFunction?.Invoke(Attacker, Move, Opponent, Battle); if (accuracy == null) { return(true); } // Wonder Skin if (Opponent.Ability == Ability.WonderSkin && Move.Kind == MoveKind.Status) { accuracy /= 2; } // No Guard if (Attacker.Ability == Ability.NoGuard || Opponent.Ability == Ability.NoGuard) { return(true); } var accuracyMultiplier = GetAccuracyMultiplier(Attacker.Stats.GetStage(Stats.Accuracy), Opponent.Stats.GetStage(Stats.Evasiveness)); // Compound Eyes if (Attacker.Ability == Ability.CompoundEyes && !Move.Info.OneHitKO) { accuracyMultiplier *= 1.3; } // Tangled Feet if (Attacker.Ability == Ability.TangledFeet && Attacker.ConfusionCounter > 0) { accuracyMultiplier *= 2; } // Bright Powder if (Opponent.HeldItem == HeldItem.BrightPowder) { accuracyMultiplier *= 0.9; } // Hustle if (Move.Kind == MoveKind.Physical && Attacker.Ability == Ability.Hustle) { accuracy *= 0.8; } var probability = accuracy * accuracyMultiplier; // Snow Cloak if (Battle.SuppressWeather == 0 && Opponent.Ability == Ability.SnowCloak && Battle.Weather == Weather.Hail) { probability /= 1.25; } if (BattleViewModel.Random.Next(100) > probability) { await Battle.WriteStatus($"{Battle.GetStatusName(Opponent)} avoided the attack"); return(false); } return(true); }
static double TypeEffectiveness(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle, List <string> TextsToDisplay) { var typeEffect = Poke.TypeEffectiveness.Get(Move.Type, Opponent.PrimaryType, Opponent.SecondaryType); // Levitate if (Move.Type == Types.Ground && Opponent.Ability == Ability.Levitate) { typeEffect = 0; } // Bulletproof if (Move.Info.Flags.HasFlag(MoveFlags.Bullet) && Opponent.Ability == Ability.BulletProof) { typeEffect = 0; } // Powder moves don't affect Grass type Pokemon if (Move.Info.Flags.HasFlag(MoveFlags.Powder) && (Opponent.Is(Types.Grass) || Opponent.Ability == Ability.Overcoat)) { typeEffect = 0; } // Soundproof if (Move.Info.Flags.HasFlag(MoveFlags.Sound) && Opponent.Ability == Ability.Soundproof) { typeEffect = 0; } // Scrappy if (Attacker.Ability == Ability.Scrappy && Move.Type.Is(Types.Normal, Types.Fighting) && Opponent.Is(Types.Ghost)) { typeEffect = 1; } var opposing = Opponent.Side != Battle.PlayerSide ? " opposing" : ""; if (typeEffect == 0) { TextsToDisplay.Add($"It does not affect{opposing} {Opponent}"); } else if (typeEffect < 1) { if (Attacker.Ability == Ability.TintedLens) { typeEffect = 2; } TextsToDisplay.Add($"It is not very effective on{opposing} {Opponent}"); } else if (typeEffect > 1) { // Prism Armor, Filter, Solid Rock if (Opponent.Ability.Is(Ability.PrismArmor, Ability.Filter, Ability.SolidRock)) { typeEffect *= 0.75; } // Damage Reduction Berries if (Opponent.HeldItem is DamageReductionBerry berry && berry.Type == Move.Type) { typeEffect /= 2; TextsToDisplay.Add($"{Battle.GetStatusName(Opponent)} consumed it's {berry}"); Opponent.HeldItem = null; } TextsToDisplay.Add($"It is super effective on{opposing} {Opponent}"); } return(typeEffect); }