Exemplo n.º 1
0
    private void ForceTurnInternal(RbyTurn turn)
    {
        int random          = SYM["Random"] + 0x10;
        int playerTurnDone1 = SYM["MainInBattleLoop.playerMovesFirst"] + 0x3;
        int playerTurnDone2 = SYM["MainInBattleLoop.AIActionUsedEnemyFirst"] + 0xc;
        int playerTurnDone3 = SYM["HandlePlayerMonFainted"];
        int enemyTurnDone1  = SYM["MainInBattleLoop.enemyMovesFirst"] + 0x11;
        int enemyTurnDone2  = SYM["MainInBattleLoop.playerMovesFirst"] + 0x27;
        int enemyTurnDone3  = SYM["HandleEnemyMonFainted"];

        int    ret;
        Joypad holdButton = Joypad.None;

        if ((CpuRead("wOptions") & 0x7) != 1)
        {
            holdButton = Joypad.A;
        }

        string[] sideEffects =
        {
            "FreezeBurnParalyzeEffect",
            "StatModifierDownEffect",
            "PoisonEffect",
            "ConfusionSideEffect",
            "FlinchSideEffect"
        };

        while ((ret = ClearTextUntil(holdButton, random, playerTurnDone1, playerTurnDone2, playerTurnDone3, enemyTurnDone1, enemyTurnDone2, enemyTurnDone3)) == random)
        {
            int addr = CpuReadLE <ushort>(SP);
            if (addr > 0x4000)
            {
                addr |= CpuRead("hLoadedROMBank") << 16;
            }
            string address = SYM[addr];
            RunUntil(addr);
            if (!address.StartsWith("VBlank"))
            {
                if (address.StartsWith("MoveHitTest"))  // hit/miss
                {
                    A = (turn.Flags & Miss) > 0 ? 0xff : 0x00;
                }
                else if (address.StartsWith("CriticalHitTest"))    // crit
                {
                    A = (turn.Flags & Crit) > 0 ? 0x00 : 0xff;
                }
                else if (address.StartsWith("RandomizeDamage"))    // damage roll
                {
                    int roll = turn.Flags & 0x3f;
                    if (roll < 1)
                    {
                        roll = 1;
                    }
                    if (roll > 39)
                    {
                        roll = 39;
                    }
                    roll += 216;
                    A     = (byte)((roll << 1) | (roll >> 7));     // rotate left to counter a rrca instruction
                }
                else if (address == "StatModifierDownEffect+0021") // AI's 25% chance to miss
                {
                    A = (turn.Flags & Miss) > 0 ? 0x00 : 0xff;
                }
                else if (sideEffects.Any(effect => address.StartsWith(effect)))    // various side effects
                {
                    A = (turn.Flags & SideEffect) > 0 ? 0x00 : 0xff;
                }
                else if (address.StartsWith("TrainerAI"))     // trainer ai
                {
                    if ((turn.Flags & AiItem) > 0)
                    {
                        A = 0x00; break;
                    }
                    else
                    {
                        A = 0xff;
                    }
                }
                else if (address.StartsWith("ThrashPetalDanceEffect"))     // thresh/petal dance length
                {
                    A = (turn.Flags & ThreeTurn) > 0 ? 0 : 1;
                }
                else if (address.StartsWith("CheckPlayerStatusConditions.IsConfused") || address.StartsWith("CheckEnemyStatusConditions.IsConfused"))     // confusion hit through
                {
                    A = (turn.Flags & Hitself) > 0 ? 0xff : 0x00;
                }
                else if (address.StartsWith("CheckPlayerStatusConditions.ThrashingAboutCheck"))    // confused for 2-5 turns after thrash/petal dance ends
                {
                    A = 0;
                }
                else if (address.StartsWith("ApplyAttackToPlayerPokemon.loop"))    // psywave damage
                {
                    A = turn.Flags & 0x3f;
                }
                else if (address.StartsWith("DisableEffect.pickMoveToDisable"))    // disable move
                {
                    A = FindBattleMove(turn.Target) & 0x3;
                }
                else if (address.StartsWith("DisableEffect.playerTurnNotLinkBattle"))    // disable number of turns (1-8)
                {
                    int turns = turn.Flags / Turns;
                    A = ((turns >= 1 ? turns : 8) - 1) & 0x7;
                }
                else if (address.StartsWith("BideEffect.bideEffect"))    // bide number of turns (2-3)
                {
                    int turns = turn.Flags / Turns;
                    A = ((turns >= 2 ? turns : 3) - 2) & 0x7;
                }
                else if (address == "TrappingEffect.trappingEffect+000b" || address == "TwoToFiveAttacksEffect.setNumberOfHits+000e")    // not needed
                {
                    A = 0x3;
                }
                else if (address == "TrappingEffect.trappingEffect+0014" || address == "TwoToFiveAttacksEffect.setNumberOfHits+0017")    // multi-attack number of hits (2-5)
                {
                    int turns = turn.Flags / Turns;
                    A = ((turns >= 2 ? turns : 5) - 2) & 0x3;
                }
                else if (address.StartsWith("MetronomePickMove"))
                {
                    RbyMove move = Moves[turn.Target];
                    Debug.Assert(move != null, "Unable to find the move: " + turn.Target);
                    A = move.Id;
                }
                else
                {
                    Console.WriteLine("Unhandled Random call coming from " + address);
                }
            }
        }
        RunFor(1);
    }
Exemplo n.º 2
0
 public void BattleSwitch(string pokemon, RbyTurn enemyTurn)
 {
     ForceTurn(new RbyTurn(pokemon, Switch), enemyTurn);
 }
Exemplo n.º 3
0
    public void ForceTurn(RbyTurn playerTurn, RbyTurn enemyTurn = null, bool speedTieWin = true)
    {
        bool useItem = Items[playerTurn.Move] != null;

        if (useItem)
        {
            if (playerTurn.Target != null)
            {
                UseItem(playerTurn.Move, playerTurn.Target, playerTurn.Target2);
            }
            else
            {
                UseItem(playerTurn.Move, playerTurn.Flags);
            }
        }
        else if ((playerTurn.Flags & Switch) != 0)
        {
            BattleMenu(1, 0);
            ChooseMenuItem(FindPokemon(playerTurn.Move));
            ChooseMenuItem(0);
        }
        else if (EnemyMon.UsingTrappingMove)
        {
            if (CpuRead("wTopMenuItemX") != 0x9 || CpuRead("wCurrentMenuItem") != 0)
            {
                MenuPress(Joypad.Left | Joypad.Up);
            }
        }
        else if (!BattleMon.ThrashingAbout && !BattleMon.Invulnerable)
        {
            if (CurrentMenuType != MenuType.Fight)
            {
                BattleMenu(0, 0);
            }
            int moveIndex = FindBattleMove(playerTurn.Move);

            // Reusing 'ChooseMenuItem' code, because the final AdvanceFrame advances past 'SelectEnemyMove.done',
            // and I don't have a good solution for this problem right now.
            RunUntil("_Joypad", "HandleMenuInput_.getJoypadState");
            var scroll = CalcScroll(moveIndex, CpuRead("wCurrentMenuItem"), CpuRead("wNumMovesMinusOne"), true);
            for (int i = 0; i < scroll.Amount; i++)
            {
                MenuPress(scroll.Input);
            }
            if ((CpuRead("hJoyLast") & (byte)Joypad.A) != 0)
            {
                Press(Joypad.None);
            }
            Inject(Joypad.A);
        }

        if (!(EnemyMon.StoringEnergy | EnemyMon.ChargingUp | EnemyMon.UsingRage | EnemyMon.Frozen | EnemyMon.UsingTrappingMove))
        {
            Hold(Joypad.A, SYM["SelectEnemyMove.done"]);
            A = enemyTurn != null && enemyTurn.Move != "" ? Moves[enemyTurn.Move].Id : 0;
        }

        bool playerFirst;
        int  speedtie = Hold(Joypad.A, SYM["MainInBattleLoop.speedEqual"] + 9, SYM["MainInBattleLoop.enemyMovesFirst"], SYM["MainInBattleLoop.playerMovesFirst"]);

        if (speedtie == SYM["MainInBattleLoop.enemyMovesFirst"])
        {
            playerFirst = false;
        }
        else if (speedtie == SYM["MainInBattleLoop.playerMovesFirst"])
        {
            playerFirst = true;
        }
        else
        {
            A           = speedTieWin ? 0x00 : 0xff;
            playerFirst = speedTieWin;
        }

        if (playerFirst)
        {
            if (!useItem)
            {
                ForceTurnInternal(playerTurn);
            }
            else
            {
                RunUntil(SYM["MainInBattleLoop.playerMovesFirst"] + 6);
            }
            if (enemyTurn != null)
            {
                ForceTurnInternal(enemyTurn);
            }
        }
        else
        {
            Debug.Assert(enemyTurn != null, "No enemy turn was specified even though the opponent moved first!");
            ForceTurnInternal(enemyTurn);
            if (!useItem)
            {
                ForceTurnInternal(playerTurn);
            }
        }

        CurrentMenuType = MenuType.None;

        // Semi-terrible code to get around thrash. TODO: fix
        if (BattleMon.ThrashingAbout)
        {
            if (EnemyMon.HP == 0)
            {
                if (EnemyParty.Where(mon => mon.HP > 0).Count() > 1)
                {
                    ClearTextUntil(Joypad.None, SYM["PlayCry"]);
                }
                else
                {
                    ClearText();
                }
            }
        }
        else if (!BattleMon.Invulnerable)
        {
            ClearText();
        }
    }