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 <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 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 } }
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; } }