private int CalculateDamage(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData, PBEType moveType, float basePower, bool criticalHit) { PBEBattlePokemon aPkmn; PBEMoveCategory aCat = mData.Category, dCat; switch (mData.Effect) { case PBEMoveEffect.FoulPlay: { aPkmn = target; dCat = aCat; break; } case PBEMoveEffect.Psyshock: { aPkmn = user; dCat = PBEMoveCategory.Physical; break; } default: { aPkmn = user; dCat = aCat; break; } } bool ignoreA = user != target && target.Ability == PBEAbility.Unaware && !user.HasCancellingAbility(); bool ignoreD = user != target && (mData.Effect == PBEMoveEffect.ChipAway || user.Ability == PBEAbility.Unaware); float a, d; if (aCat == PBEMoveCategory.Physical) { float m = ignoreA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, aPkmn.AttackChange) : aPkmn.AttackChange, false); a = CalculateAttack(user, target, moveType, aPkmn.Attack * m); } else { float m = ignoreA ? 1 : GetStatChangeModifier(criticalHit ? Math.Max((sbyte)0, aPkmn.SpAttackChange) : aPkmn.SpAttackChange, false); a = CalculateSpAttack(user, target, moveType, aPkmn.SpAttack * m); } if (dCat == PBEMoveCategory.Physical) { float m = ignoreD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.DefenseChange) : target.DefenseChange, false); d = CalculateDefense(user, target, target.Defense * m); } else { float m = ignoreD ? 1 : GetStatChangeModifier(criticalHit ? Math.Min((sbyte)0, target.SpDefenseChange) : target.SpDefenseChange, false); d = CalculateSpDefense(user, target, target.SpDefense * m); } return(CalculateDamage(user, a, d, basePower)); }
private static IEnumerable <PBEMove> GetMovesWithoutFlag(PBEMoveFlag flag, PBEMoveEffect?exception = null) { return(AllMoves.Where(m => { IPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(m, cache: false); if (!mData.IsMoveUsable()) { return false; } if (exception.HasValue && mData.Effect == exception.Value) { return true; } return !mData.Flags.HasFlag(flag); })); }
public async Task Info([Remainder] string moveName) { PBEMove?nMove = PBEDataProvider.Instance.GetMoveByName(moveName); if (!nMove.HasValue || nMove.Value == PBEMove.None) { await Context.Channel.SendMessageAsync($"{Context.User.Mention} ― Invalid move!"); } else { PBEMove move = nMove.Value; moveName = PBEDataProvider.Instance.GetMoveName(move).English; IPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move); EmbedBuilder embed = new EmbedBuilder() .WithAuthor(Context.User) .WithColor(Utils.TypeColors[mData.Type]) .WithTitle(moveName) .WithUrl(Utils.URL) .WithDescription(PBEDataProvider.Instance.GetMoveDescription(move).English.Replace('\n', ' ')) .AddField("Type", Utils.TypeEmotes[mData.Type], true) .AddField("Category", mData.Category, true) .AddField("Priority", mData.Priority, true) .AddField("PP", Math.Max(1, mData.PPTier * PBESettings.DefaultPPMultiplier), true) .AddField("Power", mData.Power == 0 ? "―" : mData.Power.ToString(), true) .AddField("Accuracy", mData.Accuracy == 0 ? "―" : mData.Accuracy.ToString(), true) .AddField("Targets", mData.Targets, true) .AddField("Flags", mData.Flags, true); switch (mData.Effect) { case PBEMoveEffect.Recoil: embed.AddField("Recoil", $"1/{mData.EffectParam} damage dealt"); break; case PBEMoveEffect.Recoil__10PercentBurn: embed.AddField("Recoil", $"1/{mData.EffectParam} damage dealt"); break; // TODO: Burn chance case PBEMoveEffect.Recoil__10PercentParalyze: embed.AddField("Recoil", $"1/{mData.EffectParam} damage dealt"); break; // TODO: Paralyze chance case PBEMoveEffect.Struggle: embed.AddField("Recoil", "1/4 user's max HP"); break; case PBEMoveEffect.TODOMOVE: embed.AddField("**ATTENTION**", $"{moveName} is not yet implemented in Pokémon Battle Engine"); break; } await Context.Channel.SendMessageAsync(string.Empty, embed : embed.Build()); } }
private float CalculateBasePower(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, PBEType moveType) { float basePower; #region Get move's base power switch (mData.Effect) { case PBEMoveEffect.CrushGrip: { basePower = Math.Max(1, targets.Select(t => (float)mData.Power * t.HP / t.MaxHP).Average()); break; } case PBEMoveEffect.Eruption: { basePower = Math.Max(1, mData.Power * user.HP / user.MaxHP); break; } case PBEMoveEffect.Flail: { int val = 48 * user.HP / user.MaxHP; if (val < 2) { basePower = 200; } else if (val < 4) { basePower = 150; } else if (val < 8) { basePower = 100; } else if (val < 16) { basePower = 80; } else if (val < 32) { basePower = 40; } else { basePower = 20; } break; } case PBEMoveEffect.Frustration: { basePower = Math.Max(1, (byte.MaxValue - user.Friendship) / 2.5f); break; } case PBEMoveEffect.GrassKnot: { basePower = targets.Select(t => { if (t.Weight >= 200.0f) { return(120f); } else if (t.Weight >= 100.0f) { return(100f); } else if (t.Weight >= 50.0f) { return(80f); } else if (t.Weight >= 25.0f) { return(60f); } else if (t.Weight >= 10.0f) { return(40f); } return(20f); }).Average(); break; } case PBEMoveEffect.HeatCrash: { basePower = targets.Select(t => { float relative = user.Weight / t.Weight; if (relative < 2) { return(40f); } else if (relative < 3) { return(60f); } else if (relative < 4) { return(80f); } else if (relative < 5) { return(100f); } return(120f); }).Average(); break; } case PBEMoveEffect.HiddenPower: { basePower = user.IndividualValues !.GetHiddenPowerBasePower(Settings); break; } case PBEMoveEffect.Magnitude: { int val = _rand.RandomInt(0, 99); byte magnitude; if (val < 5) // Magnitude 4 - 5% { magnitude = 4; basePower = 10; } else if (val < 15) // Magnitude 5 - 10% { magnitude = 5; basePower = 30; } else if (val < 35) // Magnitude 6 - 20% { magnitude = 6; basePower = 50; } else if (val < 65) // Magnitude 7 - 30% { magnitude = 7; basePower = 70; } else if (val < 85) // Magnitude 8 - 20% { magnitude = 8; basePower = 90; } else if (val < 95) // Magnitude 9 - 10% { magnitude = 9; basePower = 110; } else // Magnitude 10 - 5% { magnitude = 10; basePower = 150; } BroadcastMagnitude(magnitude); break; } case PBEMoveEffect.Punishment: { basePower = Math.Max(1, Math.Min(200, targets.Select(t => mData.Power + (20f * t.GetPositiveStatTotal())).Average())); break; } case PBEMoveEffect.Return: { basePower = Math.Max(1, user.Friendship / 2.5f); break; } case PBEMoveEffect.StoredPower: { basePower = mData.Power + (20 * user.GetPositiveStatTotal()); break; } default: { basePower = Math.Max(1, (int)mData.Power); break; } } #endregion // Technician goes before any other power boosts if (user.Ability == PBEAbility.Technician && basePower <= 60) { basePower *= 1.5f; } #region Item-specific power boosts switch (moveType) { case PBEType.Bug: { switch (user.Item) { case PBEItem.InsectPlate: case PBEItem.SilverPowder: { basePower *= 1.2f; break; } case PBEItem.BugGem: { BroadcastItem(user, user, PBEItem.BugGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Dark: { switch (user.Item) { case PBEItem.BlackGlasses: case PBEItem.DreadPlate: { basePower *= 1.2f; break; } case PBEItem.DarkGem: { BroadcastItem(user, user, PBEItem.DarkGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Dragon: { switch (user.Item) { case PBEItem.AdamantOrb: { if (user.OriginalSpecies == PBESpecies.Dialga) { basePower *= 1.2f; } break; } case PBEItem.DracoPlate: case PBEItem.DragonFang: { basePower *= 1.2f; break; } case PBEItem.GriseousOrb: { if (user.OriginalSpecies == PBESpecies.Giratina && user.RevertForm == PBEForm.Giratina_Origin) { basePower *= 1.2f; } break; } case PBEItem.LustrousOrb: { if (user.OriginalSpecies == PBESpecies.Palkia) { basePower *= 1.2f; } break; } case PBEItem.DragonGem: { BroadcastItem(user, user, PBEItem.DragonGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Electric: { switch (user.Item) { case PBEItem.Magnet: case PBEItem.ZapPlate: { basePower *= 1.2f; break; } case PBEItem.ElectricGem: { BroadcastItem(user, user, PBEItem.ElectricGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Fighting: { switch (user.Item) { case PBEItem.BlackBelt: case PBEItem.FistPlate: { basePower *= 1.2f; break; } case PBEItem.FightingGem: { BroadcastItem(user, user, PBEItem.FightingGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Fire: { switch (user.Item) { case PBEItem.Charcoal: case PBEItem.FlamePlate: { basePower *= 1.2f; break; } case PBEItem.FireGem: { BroadcastItem(user, user, PBEItem.FireGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Flying: { switch (user.Item) { case PBEItem.SharpBeak: case PBEItem.SkyPlate: { basePower *= 1.2f; break; } case PBEItem.FlyingGem: { BroadcastItem(user, user, PBEItem.FlyingGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Ghost: { switch (user.Item) { case PBEItem.GriseousOrb: { if (user.OriginalSpecies == PBESpecies.Giratina && user.RevertForm == PBEForm.Giratina_Origin) { basePower *= 1.2f; } break; } case PBEItem.SpellTag: case PBEItem.SpookyPlate: { basePower *= 1.2f; break; } case PBEItem.GhostGem: { BroadcastItem(user, user, PBEItem.GhostGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Grass: { switch (user.Item) { case PBEItem.MeadowPlate: case PBEItem.MiracleSeed: case PBEItem.RoseIncense: { basePower *= 1.2f; break; } case PBEItem.GrassGem: { BroadcastItem(user, user, PBEItem.GrassGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Ground: { switch (user.Item) { case PBEItem.EarthPlate: case PBEItem.SoftSand: { basePower *= 1.2f; break; } case PBEItem.GroundGem: { BroadcastItem(user, user, PBEItem.GroundGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Ice: { switch (user.Item) { case PBEItem.IciclePlate: case PBEItem.NeverMeltIce: { basePower *= 1.2f; break; } case PBEItem.IceGem: { BroadcastItem(user, user, PBEItem.IceGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.None: { break; } case PBEType.Normal: { switch (user.Item) { case PBEItem.SilkScarf: { basePower *= 1.2f; break; } case PBEItem.NormalGem: { BroadcastItem(user, user, PBEItem.NormalGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Poison: { switch (user.Item) { case PBEItem.PoisonBarb: case PBEItem.ToxicPlate: { basePower *= 1.2f; break; } case PBEItem.PoisonGem: { BroadcastItem(user, user, PBEItem.PoisonGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Psychic: { switch (user.Item) { case PBEItem.MindPlate: case PBEItem.OddIncense: case PBEItem.TwistedSpoon: { basePower *= 1.2f; break; } case PBEItem.PsychicGem: { BroadcastItem(user, user, PBEItem.PsychicGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Rock: { switch (user.Item) { case PBEItem.HardStone: case PBEItem.RockIncense: case PBEItem.StonePlate: { basePower *= 1.2f; break; } case PBEItem.RockGem: { BroadcastItem(user, user, PBEItem.RockGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Steel: { switch (user.Item) { case PBEItem.AdamantOrb: { if (user.OriginalSpecies == PBESpecies.Dialga) { basePower *= 1.2f; } break; } case PBEItem.IronPlate: case PBEItem.MetalCoat: { basePower *= 1.2f; break; } case PBEItem.SteelGem: { BroadcastItem(user, user, PBEItem.SteelGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } case PBEType.Water: { switch (user.Item) { case PBEItem.LustrousOrb: { if (user.OriginalSpecies == PBESpecies.Palkia) { basePower *= 1.2f; } break; } case PBEItem.MysticWater: case PBEItem.SeaIncense: case PBEItem.SplashPlate: case PBEItem.WaveIncense: { basePower *= 1.2f; break; } case PBEItem.WaterGem: { BroadcastItem(user, user, PBEItem.WaterGem, PBEItemAction.Consumed); basePower *= 1.5f; break; } } break; } default: throw new ArgumentOutOfRangeException(nameof(moveType)); } #endregion #region Move-specific power boosts switch (mData.Effect) { case PBEMoveEffect.Acrobatics: { if (user.Item == PBEItem.None) { basePower *= 2.0f; } break; } case PBEMoveEffect.Brine: { if (Array.FindIndex(targets, t => t.HP <= t.HP / 2) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.Facade: { if (user.Status1 == PBEStatus1.Burned || user.Status1 == PBEStatus1.Paralyzed || user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned) { basePower *= 2.0f; } break; } case PBEMoveEffect.Hex: { if (Array.FindIndex(targets, t => t.Status1 != PBEStatus1.None) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.Payback: { if (Array.FindIndex(targets, t => t.HasUsedMoveThisTurn) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.Retaliate: { if (user.Team.MonFaintedLastTurn) { basePower *= 2.0f; } break; } case PBEMoveEffect.SmellingSalt: { if (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Paralyzed) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.Venoshock: { if (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Poisoned || t.Status1 == PBEStatus1.BadlyPoisoned) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.WakeUpSlap: { if (Array.FindIndex(targets, t => t.Status1 == PBEStatus1.Asleep) != -1) { basePower *= 2.0f; } break; } case PBEMoveEffect.WeatherBall: { if (ShouldDoWeatherEffects() && Weather != PBEWeather.None) { basePower *= 2.0f; } break; } } #endregion #region Weather-specific power boosts if (ShouldDoWeatherEffects()) { switch (Weather) { case PBEWeather.HarshSunlight: { if (moveType == PBEType.Fire) { basePower *= 1.5f; } else if (moveType == PBEType.Water) { basePower *= 0.5f; } break; } case PBEWeather.Rain: { if (moveType == PBEType.Water) { basePower *= 1.5f; } else if (moveType == PBEType.Fire) { basePower *= 0.5f; } break; } case PBEWeather.Sandstorm: { if (user.Ability == PBEAbility.SandForce && (moveType == PBEType.Rock || moveType == PBEType.Ground || moveType == PBEType.Steel)) { basePower *= 1.3f; } break; } } } #endregion #region Other power boosts if (user.Status2.HasFlag(PBEStatus2.HelpingHand)) { basePower *= 1.5f; } if (user.Ability == PBEAbility.FlareBoost && mData.Category == PBEMoveCategory.Special && user.Status1 == PBEStatus1.Burned) { basePower *= 1.5f; } if (user.Ability == PBEAbility.ToxicBoost && mData.Category == PBEMoveCategory.Physical && (user.Status1 == PBEStatus1.Poisoned || user.Status1 == PBEStatus1.BadlyPoisoned)) { basePower *= 1.5f; } if (user.Item == PBEItem.LifeOrb) { basePower *= 1.3f; } if (user.Ability == PBEAbility.IronFist && mData.Flags.HasFlag(PBEMoveFlag.AffectedByIronFist)) { basePower *= 1.2f; } if (user.Ability == PBEAbility.Reckless && mData.Flags.HasFlag(PBEMoveFlag.AffectedByReckless)) { basePower *= 1.2f; } if (user.Item == PBEItem.MuscleBand && mData.Category == PBEMoveCategory.Physical) { basePower *= 1.1f; } if (user.Item == PBEItem.WiseGlasses && mData.Category == PBEMoveCategory.Special) { basePower *= 1.1f; } #endregion return(basePower); }
private static PBETurnAction DecideAction(PBETrainer trainer, PBEBattlePokemon user, IEnumerable <PBETurnAction> actions, IEnumerable <PBEBattlePokemon> standBy) { // Gather all options of switching and moves PBEMove[] usableMoves = user.GetUsableMoves(); var possibleActions = new List <(PBETurnAction Action, double Score)>(); for (int m = 0; m < usableMoves.Length; m++) // Score moves { PBEMove move = usableMoves[m]; PBEType moveType = user.GetMoveType(move); PBEMoveTarget moveTargets = user.GetMoveTargets(move); PBETurnTarget[] possibleTargets = PBEDataUtils.IsSpreadMove(moveTargets) ? new PBETurnTarget[] { PBEBattleUtils.GetSpreadMoveTargets(user, moveTargets) } : PBEBattleUtils.GetPossibleTargets(user, moveTargets); foreach (PBETurnTarget possibleTarget in possibleTargets) { // TODO: RandomFoeSurrounding (probably just account for the specific effects that use this target type) // TODO: Don't queue up to do the same thing (two trying to afflict the same target when there are multiple targets) var targets = new List <PBEBattlePokemon>(); if (possibleTarget.HasFlag(PBETurnTarget.AllyLeft)) { targets.Add(trainer.TryGetPokemon(PBEFieldPosition.Left)); } if (possibleTarget.HasFlag(PBETurnTarget.AllyCenter)) { targets.Add(trainer.TryGetPokemon(PBEFieldPosition.Center)); } if (possibleTarget.HasFlag(PBETurnTarget.AllyRight)) { targets.Add(trainer.TryGetPokemon(PBEFieldPosition.Right)); } if (possibleTarget.HasFlag(PBETurnTarget.FoeLeft)) { targets.Add(trainer.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Left)); } if (possibleTarget.HasFlag(PBETurnTarget.FoeCenter)) { targets.Add(trainer.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Center)); } if (possibleTarget.HasFlag(PBETurnTarget.FoeRight)) { targets.Add(trainer.Team.OpposingTeam.TryGetPokemon(PBEFieldPosition.Right)); } double score; if (targets.All(p => p == null)) { score = -100; } else { score = 0; targets.RemoveAll(p => p == null); IPBEMoveData mData = PBEDataProvider.Instance.GetMoveData(move); if (!mData.IsMoveUsable()) { throw new ArgumentOutOfRangeException(nameof(trainer), $"{move} is not yet implemented in Pokémon Battle Engine."); } switch (mData.Effect) { case PBEMoveEffect.Acrobatics: case PBEMoveEffect.Bounce: case PBEMoveEffect.BrickBreak: case PBEMoveEffect.Brine: case PBEMoveEffect.ChipAway: case PBEMoveEffect.CrushGrip: case PBEMoveEffect.Dig: case PBEMoveEffect.Dive: case PBEMoveEffect.Eruption: case PBEMoveEffect.Facade: case PBEMoveEffect.Feint: case PBEMoveEffect.Flail: case PBEMoveEffect.Fly: case PBEMoveEffect.FoulPlay: case PBEMoveEffect.Frustration: case PBEMoveEffect.GrassKnot: case PBEMoveEffect.HeatCrash: case PBEMoveEffect.Hex: case PBEMoveEffect.HiddenPower: case PBEMoveEffect.Hit: case PBEMoveEffect.Hit__2Times: case PBEMoveEffect.Hit__2Times__MaybePoison: case PBEMoveEffect.Hit__2To5Times: case PBEMoveEffect.Hit__MaybeBurn: case PBEMoveEffect.Hit__MaybeBurn__10PercentFlinch: case PBEMoveEffect.Hit__MaybeBurnFreezeParalyze: case PBEMoveEffect.Hit__MaybeConfuse: case PBEMoveEffect.Hit__MaybeFlinch: case PBEMoveEffect.Hit__MaybeFreeze: case PBEMoveEffect.Hit__MaybeFreeze__10PercentFlinch: case PBEMoveEffect.Hit__MaybeLowerTarget_ACC_By1: case PBEMoveEffect.Hit__MaybeLowerTarget_ATK_By1: case PBEMoveEffect.Hit__MaybeLowerTarget_DEF_By1: case PBEMoveEffect.Hit__MaybeLowerTarget_SPATK_By1: case PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By1: case PBEMoveEffect.Hit__MaybeLowerTarget_SPDEF_By2: case PBEMoveEffect.Hit__MaybeLowerTarget_SPE_By1: case PBEMoveEffect.Hit__MaybeLowerUser_ATK_DEF_By1: case PBEMoveEffect.Hit__MaybeLowerUser_DEF_SPDEF_By1: case PBEMoveEffect.Hit__MaybeLowerUser_SPATK_By2: case PBEMoveEffect.Hit__MaybeLowerUser_SPE_By1: case PBEMoveEffect.Hit__MaybeLowerUser_SPE_DEF_SPDEF_By1: case PBEMoveEffect.Hit__MaybeParalyze: case PBEMoveEffect.Hit__MaybeParalyze__10PercentFlinch: case PBEMoveEffect.Hit__MaybePoison: case PBEMoveEffect.Hit__MaybeRaiseUser_ATK_By1: case PBEMoveEffect.Hit__MaybeRaiseUser_ATK_DEF_SPATK_SPDEF_SPE_By1: case PBEMoveEffect.Hit__MaybeRaiseUser_DEF_By1: case PBEMoveEffect.Hit__MaybeRaiseUser_SPATK_By1: case PBEMoveEffect.Hit__MaybeRaiseUser_SPE_By1: case PBEMoveEffect.Hit__MaybeToxic: case PBEMoveEffect.HPDrain: case PBEMoveEffect.Judgment: case PBEMoveEffect.Magnitude: case PBEMoveEffect.Payback: case PBEMoveEffect.PayDay: case PBEMoveEffect.Psyshock: case PBEMoveEffect.Punishment: case PBEMoveEffect.Recoil: case PBEMoveEffect.Recoil__10PercentBurn: case PBEMoveEffect.Recoil__10PercentParalyze: case PBEMoveEffect.Retaliate: case PBEMoveEffect.Return: case PBEMoveEffect.SecretPower: case PBEMoveEffect.ShadowForce: case PBEMoveEffect.SmellingSalt: case PBEMoveEffect.StoredPower: case PBEMoveEffect.TechnoBlast: case PBEMoveEffect.Venoshock: case PBEMoveEffect.WakeUpSlap: case PBEMoveEffect.WeatherBall: { foreach (PBEBattlePokemon target in targets) { // TODO: Favor hitting ally with move if waterabsorb/voltabsorb etc // TODO: Liquid ooze // TODO: Check items // TODO: Stat changes and accuracy (even thunder/guillotine accuracy) // TODO: Check base power specifically against hp remaining (include spread move damage reduction) PBETypeEffectiveness.IsAffectedByAttack(user, target, moveType, out double damageMultiplier, useKnownInfo: true); if (damageMultiplier <= 0) // (-infinity, 0.0] Ineffective { score += target.Team == trainer.Team ? 0 : -60; } else if (damageMultiplier <= 0.25) // (0.0, 0.25] NotVeryEffective { score += target.Team == trainer.Team ? -5 : -30; } else if (damageMultiplier < 1) // (0.25, 1.0) NotVeryEffective { score += target.Team == trainer.Team ? -10 : -10; } else if (damageMultiplier == 1) // [1.0, 1.0] Normal { score += target.Team == trainer.Team ? -15 : +10; } else if (damageMultiplier < 4) // (1.0, 4.0) SuperEffective { score += target.Team == trainer.Team ? -20 : +25; } else // [4.0, infinity) SuperEffective { score += target.Team == trainer.Team ? -30 : +40; } if (user.ReceivesSTAB(moveType) && damageMultiplier > 0) { score += (user.Ability == PBEAbility.Adaptability ? 7 : 5) * (target.Team == trainer.Team ? -1 : +1); } } break; } case PBEMoveEffect.Attract: { foreach (PBEBattlePokemon target in targets) { // TODO: Destiny knot if (target.IsAttractionPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.Burn: { foreach (PBEBattlePokemon target in targets) { // TODO: Heatproof, physical attacker if (target.IsBurnPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.ChangeTarget_ACC: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Accuracy, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_ATK: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_DEF: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Defense, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_EVA: case PBEMoveEffect.Minimize: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Evasion, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_SPATK: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.SpAttack, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_SPDEF: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.SpDefense, mData.EffectParam, ref score); } break; } case PBEMoveEffect.ChangeTarget_SPE: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Speed, mData.EffectParam, ref score); } break; } case PBEMoveEffect.Confuse: case PBEMoveEffect.Flatter: case PBEMoveEffect.Swagger: { foreach (PBEBattlePokemon target in targets) { // TODO: Only swagger/flatter if the opponent most likely won't use it against you if (target.IsConfusionPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.Growth: { int change = trainer.Battle.WillLeafGuardActivate() ? +2 : +1; foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, change, ref score); ScoreStatChange(user, target, PBEStat.SpAttack, change, ref score); } break; } case PBEMoveEffect.LeechSeed: { foreach (PBEBattlePokemon target in targets) { if (target.IsLeechSeedPossible(useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.LightScreen: { score += trainer.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen) || IsTeammateUsingEffect(actions, PBEMoveEffect.LightScreen) ? -100 : +40; break; } case PBEMoveEffect.LowerTarget_ATK_DEF_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, -1, ref score); ScoreStatChange(user, target, PBEStat.Defense, -1, ref score); } break; } case PBEMoveEffect.LowerTarget_DEF_SPDEF_By1_Raise_ATK_SPATK_SPE_By2: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Defense, -1, ref score); ScoreStatChange(user, target, PBEStat.SpDefense, -1, ref score); ScoreStatChange(user, target, PBEStat.Attack, +2, ref score); ScoreStatChange(user, target, PBEStat.SpAttack, +2, ref score); ScoreStatChange(user, target, PBEStat.Speed, +2, ref score); } break; } case PBEMoveEffect.LuckyChant: { score += trainer.Team.TeamStatus.HasFlag(PBETeamStatus.LuckyChant) || IsTeammateUsingEffect(actions, PBEMoveEffect.LuckyChant) ? -100 : +40; break; } case PBEMoveEffect.Moonlight: case PBEMoveEffect.Rest: case PBEMoveEffect.RestoreTargetHP: case PBEMoveEffect.Roost: { foreach (PBEBattlePokemon target in targets) { if (target.Team == trainer.Team) { score += HPAware(target.HPPercentage, +45, -15); } else { score -= 100; } } break; } case PBEMoveEffect.Nothing: case PBEMoveEffect.Teleport: { score -= 100; break; } case PBEMoveEffect.Paralyze: case PBEMoveEffect.ThunderWave: { foreach (PBEBattlePokemon target in targets) { bool tw = mData.Effect != PBEMoveEffect.ThunderWave || PBETypeEffectiveness.ThunderWaveTypeCheck(user, target, move, useKnownInfo: true) == PBEResult.Success; if (tw && target.IsParalysisPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.Poison: case PBEMoveEffect.Toxic: { foreach (PBEBattlePokemon target in targets) { // TODO: Poison Heal if (target.IsPoisonPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.RaiseTarget_ATK_ACC_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); ScoreStatChange(user, target, PBEStat.Accuracy, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_ATK_DEF_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); ScoreStatChange(user, target, PBEStat.Defense, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_ATK_DEF_ACC_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); ScoreStatChange(user, target, PBEStat.Defense, +1, ref score); ScoreStatChange(user, target, PBEStat.Accuracy, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_ATK_SPATK_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); ScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_ATK_SPE_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); ScoreStatChange(user, target, PBEStat.Speed, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_DEF_SPDEF_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Defense, +1, ref score); ScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_SPATK_SPDEF_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score); ScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_SPATK_SPDEF_SPE_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.SpAttack, +1, ref score); ScoreStatChange(user, target, PBEStat.SpDefense, +1, ref score); ScoreStatChange(user, target, PBEStat.Speed, +1, ref score); } break; } case PBEMoveEffect.RaiseTarget_SPE_By2_ATK_By1: { foreach (PBEBattlePokemon target in targets) { ScoreStatChange(user, target, PBEStat.Speed, +2, ref score); ScoreStatChange(user, target, PBEStat.Attack, +1, ref score); } break; } case PBEMoveEffect.Reflect: { score += trainer.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect) || IsTeammateUsingEffect(actions, PBEMoveEffect.Reflect) ? -100 : +40; break; } case PBEMoveEffect.Safeguard: { score += trainer.Team.TeamStatus.HasFlag(PBETeamStatus.Safeguard) || IsTeammateUsingEffect(actions, PBEMoveEffect.Safeguard) ? -100 : +40; break; } case PBEMoveEffect.Sleep: { foreach (PBEBattlePokemon target in targets) { // TODO: Bad Dreams if (target.IsSleepPossible(user, useKnownInfo: true) == PBEResult.Success) { score += target.Team == trainer.Team ? -20 : +40; } else { score += target.Team == trainer.Team ? 0 : -60; } } break; } case PBEMoveEffect.Substitute: { foreach (PBEBattlePokemon target in targets) { if (target.IsSubstitutePossible() == PBEResult.Success) { score += target.Team == trainer.Team ? HPAware(target.HPPercentage, -30, +50) : -60; } else { score += target.Team == trainer.Team ? 0 : -20; } } break; } case PBEMoveEffect.BellyDrum: case PBEMoveEffect.Camouflage: case PBEMoveEffect.ChangeTarget_SPATK__IfAttractionPossible: case PBEMoveEffect.Conversion: case PBEMoveEffect.Curse: case PBEMoveEffect.Endeavor: case PBEMoveEffect.Entrainment: case PBEMoveEffect.FinalGambit: case PBEMoveEffect.FocusEnergy: case PBEMoveEffect.Foresight: case PBEMoveEffect.GastroAcid: case PBEMoveEffect.Hail: case PBEMoveEffect.Haze: case PBEMoveEffect.HelpingHand: case PBEMoveEffect.HPDrain__RequireSleep: case PBEMoveEffect.LockOn: case PBEMoveEffect.MagnetRise: case PBEMoveEffect.Metronome: case PBEMoveEffect.MiracleEye: case PBEMoveEffect.Nightmare: case PBEMoveEffect.OneHitKnockout: case PBEMoveEffect.PainSplit: case PBEMoveEffect.PowerTrick: case PBEMoveEffect.Protect: case PBEMoveEffect.PsychUp: case PBEMoveEffect.Psywave: case PBEMoveEffect.QuickGuard: case PBEMoveEffect.RainDance: case PBEMoveEffect.ReflectType: case PBEMoveEffect.Refresh: case PBEMoveEffect.RolePlay: case PBEMoveEffect.Sandstorm: case PBEMoveEffect.SeismicToss: case PBEMoveEffect.Selfdestruct: case PBEMoveEffect.SetDamage: case PBEMoveEffect.SimpleBeam: case PBEMoveEffect.Sketch: case PBEMoveEffect.Snore: case PBEMoveEffect.Soak: case PBEMoveEffect.Spikes: case PBEMoveEffect.StealthRock: case PBEMoveEffect.SuckerPunch: case PBEMoveEffect.SunnyDay: case PBEMoveEffect.SuperFang: case PBEMoveEffect.Tailwind: case PBEMoveEffect.ToxicSpikes: case PBEMoveEffect.Transform: case PBEMoveEffect.TrickRoom: case PBEMoveEffect.Whirlwind: case PBEMoveEffect.WideGuard: case PBEMoveEffect.WorrySeed: { // TODO break; } default: throw new ArgumentOutOfRangeException(nameof(IPBEMoveData.Effect)); } } possibleActions.Add((new PBETurnAction(user, move, possibleTarget), score)); } } if (user.CanSwitchOut()) { PBEBattlePokemon[] availableForSwitch = trainer.Party.Except(standBy).Where(p => p.FieldPosition == PBEFieldPosition.None && p.HP > 0).ToArray(); for (int s = 0; s < availableForSwitch.Length; s++) // Score switches { PBEBattlePokemon switchPkmn = availableForSwitch[s]; // TODO: Entry hazards // TODO: Known moves of active battlers // TODO: Type effectiveness double score = -10d; possibleActions.Add((new PBETurnAction(user, switchPkmn), score)); } } string ToDebugString((PBETurnAction Action, double Score) t) { string str = "{"; if (t.Action.Decision == PBETurnDecision.Fight) { str += string.Format("Fight {0} {1}", t.Action.FightMove, t.Action.FightTargets); } else { str += string.Format("Switch {0}", trainer.TryGetPokemon(t.Action.SwitchPokemonId).Nickname); } str += " [" + t.Score + "]}"; return(str); } IOrderedEnumerable <(PBETurnAction Action, double Score)> byScore = possibleActions.OrderByDescending(t => t.Score); Debug.WriteLine("{0}'s possible actions: {1}", user.Nickname, byScore.Select(t => ToDebugString(t)).Print()); double bestScore = byScore.First().Score; return(PBEDataProvider.GlobalRandom.RandomElement(byScore.Where(t => t.Score == bestScore).ToArray()).Action); // Pick random action of the ones that tied for best score }
/// <summary>Temporary check to see if a move is usable, can be removed once all moves are added</summary> public static bool IsMoveUsable(this IPBEMoveData mData) { return(PBEDataUtils.IsMoveUsable(mData.Effect)); }
public static bool IsWeatherMove(this IPBEMoveData mData) { return(PBEDataUtils.IsWeatherMove(mData.Effect)); }
public static bool IsSpreadMove(this IPBEMoveData mData) { return(PBEDataUtils.IsSpreadMove(mData.Targets)); }
private void BasicHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, Func <PBEBattlePokemon, PBEResult>?failFunc = null, Action <PBEBattlePokemon>?beforeDoingDamage = null, Action <PBEBattlePokemon, ushort>?beforePostHit = null, Action <PBEBattlePokemon>?afterPostHit = null, Func <int, int?>?recoilFunc = null) { // Targets array is [FoeLeft, FoeCenter, FoeRight, AllyLeft, AllyCenter, AllyRight] // User can faint or heal with a berry at LiquidOoze, IronBarbs/RockyHelmet, and also at Recoil/LifeOrb // -------------Official order------------- // Setup - [effectiveness/fail checks foes], [effectiveness/fail checks allies], [miss/protection checks foes] [miss/protection checks allies], gem, // Allies - [sub damage allies, sub effectiveness allies, sub crit allies, sub break allies], [hit allies], [effectiveness allies], [crit allies], [posthit allies], [faint allies], // Foes - [sub damage foes, sub effectiveness foes, sub crit foes, sub break foes], [hit foes], [effectiveness foes], [crit foes], [posthit foes], [faint foes], // Cleanup - recoil, lifeorb, [colorchange foes], [colorchange allies], [berry allies], [berry foes], [antistatusability allies], [antistatusability foes], exp PBEType moveType = user.GetMoveType(mData); // DreamEater checks for sleep before gem activates // SuckerPunch fails Hit_GetVictims(user, targets, mData, moveType, out List <PBEAttackVictim> victims, failFunc: failFunc); if (victims.Count == 0) { return; } float basePower = CalculateBasePower(user, targets, mData, moveType); // Gem activates here float initDamageMultiplier = victims.Count > 1 ? 0.75f : 1; int totalDamageDealt = 0; void CalcDamage(PBEAttackVictim victim) { PBEBattlePokemon target = victim.Pkmn; PBEResult result = victim.Result; float damageMultiplier = initDamageMultiplier * victim.TypeEffectiveness; // Brick Break destroys Light Screen and Reflect before doing damage (after gem) // Feint destroys protection // Pay Day scatters coins beforeDoingDamage?.Invoke(target); bool crit = CritCheck(user, target, mData); damageMultiplier *= CalculateDamageMultiplier(user, target, mData, moveType, result, crit); int damage = (int)(damageMultiplier * CalculateDamage(user, target, mData, moveType, basePower, crit)); victim.Damage = DealDamage(user, target, damage, ignoreSubstitute: false, ignoreSturdy: false); totalDamageDealt += victim.Damage; victim.Crit = crit; } void DoSub(List <PBEAttackVictim> subs) { foreach (PBEAttackVictim victim in subs) { CalcDamage(victim); PBEBattlePokemon target = victim.Pkmn; PBEResult result = victim.Result; if (result != PBEResult.Success) { BroadcastMoveResult(user, target, result); } if (victim.Crit) { BroadcastMoveCrit(target); } if (target.SubstituteHP == 0) { BroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended); } } } void DoNormal(List <PBEAttackVictim> normals) { foreach (PBEAttackVictim victim in normals) { CalcDamage(victim); } Hit_DoMoveResult(user, normals); Hit_DoCrit(normals); foreach (PBEAttackVictim victim in normals) { PBEBattlePokemon target = victim.Pkmn; // Stats/statuses are changed before post-hit effects // HP-draining moves restore HP beforePostHit?.Invoke(target, victim.Damage); // TODO: LiquidOoze fainting/healing DoPostHitEffects(user, target, mData, moveType); // ShadowForce destroys protection // SmellingSalt cures paralysis // WakeUpSlap cures sleep afterPostHit?.Invoke(target); // Verified: These happen before Recoil/LifeOrb } Hit_FaintCheck(normals); } Hit_HitTargets(user.Team, DoSub, DoNormal, victims, out List <PBEAttackVictim> allies, out List <PBEAttackVictim> foes); DoPostAttackedEffects(user, allies, foes, true, recoilDamage: recoilFunc?.Invoke(totalDamageDealt), colorChangeType: moveType); }
public static bool IsMultiHitMove(this IPBEMoveData mData) { return(PBEDataUtils.IsMultiHitMove(mData.Effect)); }
public static bool IsHPRestoreMove(this IPBEMoveData mData) { return(PBEDataUtils.IsHPRestoreMove(mData.Effect)); }
public static bool HasSecondaryEffects(this IPBEMoveData mData, PBESettings settings) { return(PBEDataUtils.HasSecondaryEffects(mData.Effect, settings)); }
// TODO: TripleKick miss logic private void Hit_GetVictims(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, PBEType moveType, out List <PBEAttackVictim> victims, Func <PBEBattlePokemon, PBEResult>?failFunc = null) { victims = new List <PBEAttackVictim>(targets.Length); foreach (PBEBattlePokemon target in targets) { if (!AttackTypeCheck(user, target, moveType, out PBEResult result, out float typeEffectiveness)) { continue; } // Verified: These fails are after type effectiveness (So SuckerPunch will not affect Ghost types due to Normalize before it fails due to invalid conditions) if (failFunc is not null && failFunc.Invoke(target) != PBEResult.Success) { continue; } victims.Add(new PBEAttackVictim(target, result, typeEffectiveness)); } if (victims.Count == 0) { return; } victims.RemoveAll(t => MissCheck(user, t.Pkmn, mData)); return; }
// None of these moves are multi-target private void MultiHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, byte numHits, bool subsequentMissChecks = false, Action <PBEBattlePokemon>?beforePostHit = null) { PBEType moveType = user.GetMoveType(mData); Hit_GetVictims(user, targets, mData, moveType, out List <PBEAttackVictim> victims); if (victims.Count == 0) { return; } float basePower = CalculateBasePower(user, targets, mData, moveType); // Verified: Gem boost applies to all hits float initDamageMultiplier = victims.Count > 1 ? 0.75f : 1; void CalcDamage(PBEAttackVictim victim) { PBEBattlePokemon target = victim.Pkmn; PBEResult result = victim.Result; float damageMultiplier = initDamageMultiplier * victim.TypeEffectiveness; bool crit = CritCheck(user, target, mData); damageMultiplier *= CalculateDamageMultiplier(user, target, mData, moveType, result, crit); int damage = (int)(damageMultiplier * CalculateDamage(user, target, mData, moveType, basePower, crit)); victim.Damage = DealDamage(user, target, damage, ignoreSubstitute: false, ignoreSturdy: false); victim.Crit = crit; } void DoSub(List <PBEAttackVictim> subs) { foreach (PBEAttackVictim victim in subs) { CalcDamage(victim); PBEBattlePokemon target = victim.Pkmn; if (victim.Crit) { BroadcastMoveCrit(target); } if (target.SubstituteHP == 0) { BroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended); } } } void DoNormal(List <PBEAttackVictim> normals) { normals.RemoveAll(v => v.Pkmn.HP == 0); // Remove ones that fainted from previous hits foreach (PBEAttackVictim victim in normals) { CalcDamage(victim); } Hit_DoCrit(normals); foreach (PBEAttackVictim victim in normals) { PBEBattlePokemon target = victim.Pkmn; // Twineedle has a chance to poison on each strike beforePostHit?.Invoke(target); DoPostHitEffects(user, target, mData, moveType); } } byte hit = 0; List <PBEAttackVictim> allies, foes; do { Hit_HitTargets(user.Team, DoSub, DoNormal, victims, out allies, out foes); hit++; } while (hit < numHits && user.HP > 0 && user.Status1 != PBEStatus1.Asleep && victims.FindIndex(v => v.Pkmn.HP > 0) != -1); Hit_DoMoveResult(user, allies); Hit_DoMoveResult(user, foes); BroadcastMultiHit(hit); Hit_FaintCheck(allies); Hit_FaintCheck(foes); DoPostAttackedEffects(user, allies, foes, true, colorChangeType: moveType); }
// None of these moves are multi-target private void FixedDamageHit(PBEBattlePokemon user, PBEBattlePokemon[] targets, IPBEMoveData mData, Func <PBEBattlePokemon, int> damageFunc, Func <PBEBattlePokemon, PBEResult>?failFunc = null, Action?beforePostHit = null) { PBEType moveType = user.GetMoveType(mData); // Endeavor fails if the target's HP is <= the user's HP // One hit knockout moves fail if the target's level is > the user's level Hit_GetVictims(user, targets, mData, moveType, out List <PBEAttackVictim> victims, failFunc: failFunc); if (victims.Count == 0) { return; } // BUG: Gems activate for these moves despite base power not being involved if (!Settings.BugFix) { _ = CalculateBasePower(user, targets, mData, moveType); } void CalcDamage(PBEAttackVictim victim) { PBEBattlePokemon target = victim.Pkmn; // FinalGambit user faints here victim.Damage = DealDamage(user, target, damageFunc.Invoke(target)); } void DoSub(List <PBEAttackVictim> subs) { foreach (PBEAttackVictim victim in subs) { CalcDamage(victim); PBEBattlePokemon target = victim.Pkmn; if (target.SubstituteHP == 0) { BroadcastStatus2(target, user, PBEStatus2.Substitute, PBEStatusAction.Ended); } } } void DoNormal(List <PBEAttackVictim> normals) { foreach (PBEAttackVictim victim in normals) { CalcDamage(victim); } foreach (PBEAttackVictim victim in normals) { PBEBattlePokemon target = victim.Pkmn; // "It's a one-hit KO!" beforePostHit?.Invoke(); DoPostHitEffects(user, target, mData, moveType); } Hit_FaintCheck(normals); } Hit_HitTargets(user.Team, DoSub, DoNormal, victims, out List <PBEAttackVictim> allies, out List <PBEAttackVictim> foes); DoPostAttackedEffects(user, allies, foes, false, colorChangeType: moveType); }
private float CalculateDamageMultiplier(PBEBattlePokemon user, PBEBattlePokemon target, IPBEMoveData mData, PBEType moveType, PBEResult moveResult, bool criticalHit) { float damageMultiplier = 1; if (target.Status2.HasFlag(PBEStatus2.Airborne) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageAirborne)) { damageMultiplier *= 2.0f; } if (target.Minimize_Used && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageMinimized)) { damageMultiplier *= 2.0f; } if (target.Status2.HasFlag(PBEStatus2.Underground) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderground)) { damageMultiplier *= 2.0f; } if (target.Status2.HasFlag(PBEStatus2.Underwater) && mData.Flags.HasFlag(PBEMoveFlag.DoubleDamageUnderwater)) { damageMultiplier *= 2.0f; } if (criticalHit) { damageMultiplier *= Settings.CritMultiplier; if (user.Ability == PBEAbility.Sniper) { damageMultiplier *= 1.5f; } } else if (user.Ability != PBEAbility.Infiltrator) { if ((target.Team.TeamStatus.HasFlag(PBETeamStatus.Reflect) && mData.Category == PBEMoveCategory.Physical) || (target.Team.TeamStatus.HasFlag(PBETeamStatus.LightScreen) && mData.Category == PBEMoveCategory.Special)) { if (target.Team.NumPkmnOnField == 1) { damageMultiplier *= 0.5f; } else { damageMultiplier *= 0.66f; } } } switch (moveResult) { case PBEResult.NotVeryEffective_Type: { if (user.Ability == PBEAbility.TintedLens) { damageMultiplier *= 2.0f; } break; } case PBEResult.SuperEffective_Type: { if ((target.Ability == PBEAbility.Filter || target.Ability == PBEAbility.SolidRock) && !user.HasCancellingAbility()) { damageMultiplier *= 0.75f; } if (user.Item == PBEItem.ExpertBelt) { damageMultiplier *= 1.2f; } break; } } if (user.ReceivesSTAB(moveType)) { if (user.Ability == PBEAbility.Adaptability) { damageMultiplier *= 2.0f; } else { damageMultiplier *= 1.5f; } } if (mData.Category == PBEMoveCategory.Physical && user.Status1 == PBEStatus1.Burned && user.Ability != PBEAbility.Guts) { damageMultiplier *= 0.5f; } if (moveType == PBEType.Fire && target.Ability == PBEAbility.Heatproof && !user.HasCancellingAbility()) { damageMultiplier *= 0.5f; } return(damageMultiplier); }
public static bool IsSetDamageMove(this IPBEMoveData mData) { return(PBEDataUtils.IsSetDamageMove(mData.Effect)); }