static double GetDefense(Pokemon Opponent, MoveKind MoveKind, BattleViewModel Battle, bool CriticalHit) { if (MoveKind == MoveKind.Special) { double special = Opponent.Stats.GetValue(Stats.SpecialDefense, CriticalHit); if (Battle.SuppressWeather == 0 && Battle.Weather == Weather.Sandstorm && Opponent.Is(Types.Rock)) { special *= 1.5; } return(special); } double physical = Opponent.Stats.GetValue(Stats.Defense, CriticalHit); // Marvel Scale if (Opponent.Ability == Ability.MarvelScale && Opponent.NonVolatileStatus != NonVolatileStatus.None) { physical *= 1.5; } return(physical); }
public static async Task AlterWeather(Pokemon Pokemon, Weather Weather, BattleViewModel Battle) { if (Battle.Weather.Is(Weather.ExtremelyHarshSunlight, Weather.HeavyRain, Weather.MysteriousAirCurrent)) { await Battle.WriteStatus("But it failed"); return; } Battle.Weather = Weather; Battle.WeatherDuration = 5; switch (Weather) { case Weather.Rain: if (Pokemon.HeldItem == HeldItem.DampRock) { Battle.WeatherDuration = 8; } await Battle.WriteStatus("It started to rain"); break; case Weather.HarshSunlight: if (Pokemon.HeldItem == HeldItem.HeatRock) { Battle.WeatherDuration = 8; } await Battle.WriteStatus("The sunlight turned harsh"); break; case Weather.Hail: if (Pokemon.HeldItem == HeldItem.IcyRock) { Battle.WeatherDuration = 8; } await Battle.WriteStatus("It started to hail"); break; case Weather.Sandstorm: if (Pokemon.HeldItem == HeldItem.SmoothRock) { Battle.WeatherDuration = 8; } await Battle.WriteStatus("A sandstorm kicked in"); break; } }
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"); }
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"); } }
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 double GetAttack(Pokemon Attacker, MoveKind MoveKind, BattleViewModel Battle, bool CriticalHit) { if (MoveKind == MoveKind.Special) { double special = Attacker.Stats.GetValue(Stats.SpecialAttack, CriticalHit); if (Attacker.Ability is AttackMultiplierAbility attackMultiplierAbility && attackMultiplierAbility.Special && attackMultiplierAbility.Check(Attacker, null, null, Battle)) { special *= attackMultiplierAbility.Multiplier; } // Light Ball if (Attacker.HeldItem == HeldItem.LightBall && Attacker.Species == PokemonSpecies.Pikachu) { special *= 2; } return(special); } double physical = Attacker.Stats.GetValue(Stats.Attack, CriticalHit); if (Attacker.Ability is AttackMultiplierAbility attackMultAbility && attackMultAbility.Physical && attackMultAbility.Check(Attacker, null, null, Battle)) { physical *= attackMultAbility.Multiplier; } // Light Ball if (Attacker.HeldItem == HeldItem.LightBall && Attacker.Species == PokemonSpecies.Pikachu) { physical *= 2; } return(physical); }
public static int GetEffectiveSpeed(this Pokemon Pokemon, BattleViewModel Battle) { double speed = Pokemon.Stats.GetValue(Stats.Speed); if (Battle.SuppressWeather == 0) { // Chlorophyll if (Pokemon.Ability == Ability.Chlorophyll && Battle.Weather.Is(Weather.HarshSunlight, Weather.ExtremelyHarshSunlight)) { speed *= 2; } // Swift Swim if (Pokemon.Ability == Ability.SwiftSwim && Battle.Weather.Is(Weather.Rain, Weather.HeavyRain)) { speed *= 2; } // Sand Rush if (Pokemon.Ability == Ability.SandRush && Battle.Weather == Weather.Sandstorm) { speed *= 2; } } // Quick Feet if (Pokemon.Ability == Ability.QuickFeet && Pokemon.NonVolatileStatus != NonVolatileStatus.None) { speed *= 1.5; } // Paralysis else if (Pokemon.NonVolatileStatus == NonVolatileStatus.Paralysis) { speed /= 2; } return((int)speed); }
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 double?GetPower(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle) { var power = Move.Info.PowerFunction?.Invoke(Attacker, Move, Opponent, Battle); if (power == null) { return(null); } // Technician if (power <= 60 && Attacker.Ability == Ability.Technician) { power *= 1.5; } else if (Attacker.Ability is NormalizingAbility && Move.Info.Type == Types.Normal) { power *= 1.2; } // Strong Jaws else if (Move.Info.Flags.HasFlag(MoveFlags.Bite) && Attacker.Ability == Ability.StrongJaws) { power *= 1.5; } // Mega Launcher else if (Move.Info.Flags.HasFlag(MoveFlags.Pulse) && Attacker.Ability == Ability.MegaLauncher) { power *= 1.5; } // Iron Fist else if (Move.Info.Flags.HasFlag(MoveFlags.Punch) && Attacker.Ability == Ability.IronFist) { power *= 1.2; } // Reckless else if (Move.Info.Recoil > 0 && Attacker.Ability == Ability.Reckless) { power *= 1.2; } // Sand Force else if (Battle.SuppressWeather == 0 && Attacker.Ability == Ability.SandForce && Battle.Weather == Weather.Sandstorm) { if (Move.Type == Types.Rock || Move.Type == Types.Ground || Move.Type == Types.Steel) { power *= 1.3; } } // Water Bubble else if (Attacker.Ability == Ability.WaterBubble && Move.Type == Types.Water) { power *= 2; } // Steelworker else if (Attacker.Ability == Ability.Steelworker && Move.Type == Types.Steel) { power *= 1.5; } if (Battle.SuppressWeather == 0) { power *= WeatherPowerMultiplier(Move, Battle.Weather); } // Type Enhancement switch (Attacker.HeldItem) { case TypeEnhancement enhancement when enhancement.Type == Move.Type: power *= enhancement.Multiplier; break; case Gem gem when Move.Type == gem.Type: power *= 1.3; Attacker.HeldItem = null; break; } return(power); }
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); }
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); }
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); }
public static async Task StatChange(Pokemon Pokemon, Stats Stat, int StageChange, BattleViewModel Battle) { await StatChanges(Pokemon, null, new[] { new StatChange(Stat, StageChange: StageChange) }, Battle); }
async Task BattleOnDoAnimation(Pokemon Attacker, Move Move, Pokemon Opponent, BattleViewModel Battle) { if (Move.Info.Flags.HasFlag(MoveFlags.Contact) && Move.Info.Target == MoveTarget.Normal) { var playerAttacking = Battle.PlayerSide.IndexOf(Attacker) != -1; var playerIndex = Battle.PlayerSide.IndexOf(playerAttacking ? Attacker : Opponent); var opponentIndex = Battle.OpponentSide.IndexOf(playerAttacking ? Opponent : Attacker); var playerItem = PlayerSideControl.ItemContainerGenerator.ContainerFromIndex(playerIndex) as UIElement; var opponentItem = OpponentSideControl.ItemContainerGenerator.ContainerFromIndex(opponentIndex) as UIElement; var translater = new TranslateTransform(); if (playerAttacking) { playerItem.RenderTransform = translater; } else { opponentItem.RenderTransform = translater; } var pokemonWidth = Width / Battle.Format / 2; double targetX, targetY; if (playerAttacking) { targetX = (Battle.Format - playerIndex) * pokemonWidth + opponentIndex * pokemonWidth; targetY = -220; } else { targetX = -playerIndex * pokemonWidth - (Battle.Format - opponentIndex) * pokemonWidth; targetY = 220; } var xEvent = new ManualResetEvent(false); var yEvent = new ManualResetEvent(false); var xAnimation = new DoubleAnimation(0, targetX, new Duration(TimeSpan.FromMilliseconds(200))) { AutoReverse = true }; xAnimation.Completed += (S, E) => xEvent.Set(); var yAnimation = new DoubleAnimation(0, targetY, new Duration(TimeSpan.FromMilliseconds(200))) { AutoReverse = true }; yAnimation.Completed += (S, E) => yEvent.Set(); translater.BeginAnimation(TranslateTransform.XProperty, xAnimation); translater.BeginAnimation(TranslateTransform.YProperty, yAnimation); await Task.Run(() => WaitHandle.WaitAll(new WaitHandle[] { xEvent, yEvent })); xEvent.Dispose(); yEvent.Dispose(); } }
public PokemonMovePairComparer(BattleViewModel Battle) { _battle = Battle; }
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; } }