Esempio n. 1
0
        // 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);
        }
Esempio n. 2
0
        /// <summary>Checks if <see cref="PBEMoveEffect.ThunderWave"/>'s type affects the target, taking into account <see cref="PBEAbility.Normalize"/>.</summary>
        public static PBEResult ThunderWaveTypeCheck(PBEBattlePokemon user, PBEBattlePokemon target, PBEMove move, bool useKnownInfo = false)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            PBEType moveType = user.GetMoveType(move);
            double  d        = GetEffectiveness(moveType, target, useKnownInfo);

            if (d <= 0)
            {
                return(PBEResult.Ineffective_Type);
            }
            return(PBEResult.Success);
        }
Esempio n. 3
0
        private PBETurnAction DecideAction(PBEBattlePokemon user, List <PBETurnAction> actions, List <PBEBattlePokemon> standBy)
        {
            // Gather all options of switching and moves
            PBEMove[] usableMoves     = user.GetUsableMoves();
            var       possibleActions = new List <(PBETurnAction Action, float 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))
                    {
                        Trainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Left, targets);
                    }
                    if (possibleTarget.HasFlag(PBETurnTarget.AllyCenter))
                    {
                        Trainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Center, targets);
                    }
                    if (possibleTarget.HasFlag(PBETurnTarget.AllyRight))
                    {
                        Trainer.Team.TryAddPokemonToCollection(PBEFieldPosition.Right, targets);
                    }
                    if (possibleTarget.HasFlag(PBETurnTarget.FoeLeft))
                    {
                        Trainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Left, targets);
                    }
                    if (possibleTarget.HasFlag(PBETurnTarget.FoeCenter))
                    {
                        Trainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Center, targets);
                    }
                    if (possibleTarget.HasFlag(PBETurnTarget.FoeRight))
                    {
                        Trainer.Team.OpposingTeam.TryAddPokemonToCollection(PBEFieldPosition.Right, targets);
                    }
                    float score = ScoreMove(targets, user, move, moveType, actions);
                    possibleActions.Add((new PBETurnAction(user, move, possibleTarget), score));
                }
            }
            if (user.CanSwitchOut())
            {
                PBEBattlePokemon[] availableForSwitch = Trainer.Party.Except(standBy).Where(p => p.FieldPosition == PBEFieldPosition.None && p.CanBattle).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
                    float score = -10;
                    possibleActions.Add((new PBETurnAction(user, switchPkmn), score));
                }
            }

            IOrderedEnumerable <(PBETurnAction Action, float Score)> byScore = possibleActions.OrderByDescending(t => t.Score);

            Debug_LogGeneratedActions(user, byScore);
            float 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
        }
Esempio n. 4
0
        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
        }
Esempio n. 5
0
        // 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);
        }
Esempio n. 6
0
        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);
        }
        private string CreateKnownPokemonEmbed(PBEBattlePokemon pkmn)
        {
            IPBEPokemonData pData   = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);
            var             sb      = new StringBuilder();
            string          formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).English})" : string.Empty;

            sb.AppendLine($"{GetTrainerName(pkmn.Trainer)}'s {pkmn.KnownNickname}/{pkmn.KnownSpecies}{formStr} {(pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.Gender.ToSymbol() : pkmn.KnownGender.ToSymbol())} Lv.{pkmn.Level}{(pkmn.KnownShiny ? $" {_shinyEmoji}" : string.Empty)}");
            sb.AppendLine($"**HP:** {pkmn.HPPercentage:P2}");
            sb.Append($"**Known types:** {Utils.TypeEmotes[pkmn.KnownType1]}");
            if (pkmn.KnownType2 != PBEType.None)
            {
                sb.Append($" {Utils.TypeEmotes[pkmn.KnownType2]}");
            }
            sb.AppendLine();
            if (pkmn.Status1 != PBEStatus1.None)
            {
                sb.AppendLine($"**Main status:** {Utils.Status1Emotes[pkmn.Status1]}");
                if (pkmn.Status1 == PBEStatus1.Asleep)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.Asleep]} turns:** {pkmn.Status1Counter}");
                }
                else if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.BadlyPoisoned]} counter:** {pkmn.Status1Counter}");
                }
            }
            if (pkmn.KnownStatus2 != PBEStatus2.None)
            {
                sb.AppendLine($"**Volatile status:** {pkmn.KnownStatus2}");
                if (pkmn.KnownStatus2.HasFlag(PBEStatus2.Confused))
                {
                    sb.AppendLine($"**Confusion turns:** {pkmn.ConfusionCounter}");
                }
            }
            PBEDataUtils.GetStatRange(pData, PBEStat.HP, pkmn.Level, PBESettings.DefaultSettings, out ushort lowHP, out ushort highHP);
            PBEDataUtils.GetStatRange(pData, PBEStat.Attack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowAttack, out ushort highAttack);
            PBEDataUtils.GetStatRange(pData, PBEStat.Defense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowDefense, out ushort highDefense);
            PBEDataUtils.GetStatRange(pData, PBEStat.SpAttack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpAttack, out ushort highSpAttack);
            PBEDataUtils.GetStatRange(pData, PBEStat.SpDefense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpDefense, out ushort highSpDefense);
            PBEDataUtils.GetStatRange(pData, PBEStat.Speed, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpeed, out ushort highSpeed);
            sb.AppendLine($"**Stat range:** [HP] {lowHP}-{highHP}, [A] {lowAttack}-{highAttack}, [D] {lowDefense}-{highDefense}, [SA] {lowSpAttack}-{highSpAttack}, [SD] {lowSpDefense}-{highSpDefense}, [S] {lowSpeed}-{highSpeed}, [W] {pkmn.KnownWeight:0.0}");
            AddStatChanges(pkmn, sb);
            if (pkmn.KnownAbility == PBEAbility.MAX)
            {
                sb.AppendLine($"**Possible abilities:** {string.Join(", ", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).English))}");
            }
            else
            {
                sb.AppendLine($"**Known ability:** {PBEDataProvider.Instance.GetAbilityName(pkmn.KnownAbility).English}");
            }
            sb.AppendLine($"**Known item:** {(pkmn.KnownItem == (PBEItem)ushort.MaxValue ? "???" : PBEDataProvider.Instance.GetItemName(pkmn.KnownItem).English)}");
            sb.Append("**Known moves:** ");
            for (int i = 0; i < PBESettings.DefaultNumMoves; i++)
            {
                PBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.KnownMoves[i];
                PBEMove move = slot.Move;
                if (move != PBEMove.None)
                {
                    int pp    = slot.PP;
                    int maxPP = slot.MaxPP;
                    if (i > 0)
                    {
                        sb.Append(", ");
                    }
                    if (move == PBEMove.MAX)
                    {
                        sb.Append("???");
                    }
                    else
                    {
                        sb.Append($"{Utils.TypeEmotes[pkmn.GetMoveType(move, useKnownInfo: true)]} {PBEDataProvider.Instance.GetMoveName(move).English} ({pp}{(maxPP == 0 ? ")" : $"/{maxPP})")}");
                    }
                }
            }
        private static void CreatePokemonEmbed(PBEBattlePokemon pkmn, bool addReactionChars, out string outStr, out EmbedFieldBuilder[] outFields)
        {
            var    sb      = new StringBuilder();
            string formStr = PBEDataUtils.HasForms(pkmn.Species, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn).English})" : string.Empty;

            sb.AppendLine($"{pkmn.Nickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.Species).English}{formStr} {pkmn.Gender.ToSymbol()} Lv.{pkmn.Level}{(pkmn.Shiny ? $" {_shinyEmoji}" : string.Empty)}");
            sb.AppendLine($"**HP:** {pkmn.HP}/{pkmn.MaxHP} ({pkmn.HPPercentage:P2})");
            sb.Append($"**Types:** {Utils.TypeEmotes[pkmn.Type1]}");
            if (pkmn.Type2 != PBEType.None)
            {
                sb.Append($" {Utils.TypeEmotes[pkmn.Type2]}");
            }
            sb.AppendLine();
            if (pkmn.Status1 != PBEStatus1.None)
            {
                sb.AppendLine($"**Main status:** {Utils.Status1Emotes[pkmn.Status1]}");
            }
            if (pkmn.FieldPosition != PBEFieldPosition.None)
            {
                if (pkmn.Status1 == PBEStatus1.Asleep)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.Asleep]} turns:** {pkmn.Status1Counter}");
                }
                else if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.BadlyPoisoned]} counter:** {pkmn.Status1Counter}");
                }
                if (pkmn.Status2 != PBEStatus2.None)
                {
                    sb.AppendLine($"**Volatile status:** {pkmn.Status2}");
                    if (pkmn.Status2.HasFlag(PBEStatus2.Confused))
                    {
                        sb.AppendLine($"**Confusion turns:** {pkmn.ConfusionCounter}");
                    }
                    if (pkmn.Status2.HasFlag(PBEStatus2.Disguised))
                    {
                        formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).English})" : string.Empty;
                        sb.AppendLine($"**Disguised as:** {pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).English}{formStr} {pkmn.KnownGender.ToSymbol()}");
                    }
                    if (pkmn.Status2.HasFlag(PBEStatus2.Substitute))
                    {
                        sb.AppendLine($"**Substitute HP:** {pkmn.SubstituteHP}");
                    }
                }
            }
            sb.AppendLine($"**Stats:** [A] {pkmn.Attack}, [D] {pkmn.Defense}, [SA] {pkmn.SpAttack}, [SD] {pkmn.SpDefense}, [S] {pkmn.Speed}, [W] {pkmn.Weight:0.0}");
            if (pkmn.FieldPosition != PBEFieldPosition.None)
            {
                AddStatChanges(pkmn, sb);
            }
            sb.AppendLine($"**Ability:** {PBEDataProvider.Instance.GetAbilityName(pkmn.Ability).English}");
            sb.AppendLine($"**Item:** {PBEDataProvider.Instance.GetItemName(pkmn.Item).English}");
            if (pkmn.Moves.Contains(PBEMoveEffect.Frustration) || pkmn.Moves.Contains(PBEMoveEffect.Return))
            {
                sb.AppendLine($"**Friendship:** {pkmn.Friendship} ({pkmn.Friendship / (double)byte.MaxValue:P2})");
            }
            if (pkmn.Moves.Contains(PBEMoveEffect.HiddenPower))
            {
                sb.AppendLine($"**{PBEDataProvider.Instance.GetMoveName(PBEMove.HiddenPower).English}:** {Utils.TypeEmotes[pkmn.IndividualValues.GetHiddenPowerType()]}|{pkmn.IndividualValues.GetHiddenPowerBasePower(PBESettings.DefaultSettings)}");
            }
            outStr = sb.ToString();
            sb.Clear();
            outFields = new EmbedFieldBuilder[2];
            EmbedFieldBuilder field = new EmbedFieldBuilder()
                                      .WithName("**Moves:**")
                                      .WithIsInline(true);

            outFields[0] = field;
            for (int i = 0; i < PBESettings.DefaultNumMoves; i++)
            {
                PBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.Moves[i];
                PBEMove move = slot.Move;
                if (move != PBEMove.None)
                {
                    PBEType moveType = pkmn.GetMoveType(move);
                    sb.Append($"{Utils.TypeEmotes[moveType]} {PBEDataProvider.Instance.GetMoveName(move).English} ({slot.PP}/{slot.MaxPP})");
                    if (i < PBESettings.DefaultNumMoves - 1)
                    {
                        sb.AppendLine();
                    }
                }
            }
            field.WithValue(sb.ToString());
            sb.Clear();
            field = new EmbedFieldBuilder()
                    .WithName("**Usable moves:**")
                    .WithIsInline(true);
            outFields[1] = field;
            PBEMove[] usableMoves = pkmn.GetUsableMoves();
            for (int i = 0; i < usableMoves.Length; i++)
            {
                PBEMove move     = usableMoves[i];
                PBEType moveType = pkmn.GetMoveType(move);
                if (addReactionChars)
                {
                    sb.Append($"{_moveEmotes[i][moveType]} ");
                }
                else
                {
                    sb.Append($"{Utils.TypeEmotes[moveType]} ");
                }
                sb.Append(PBEDataProvider.Instance.GetMoveName(move).English);
                if (i < usableMoves.Length - 1)
                {
                    sb.AppendLine();
                }
            }
            field.WithValue(sb.ToString());
        }