2022/12/15

【ポケモンSV×arduino02】最序盤ストーリー自動化

 ※本記事は、ポケモンSVのストーリー最序盤、宝探しが始まってテラレイドバトルに参加可能になるまでを自動化するプログラムについて解説した記事です。ポケモンSVの操作をArduino Leonardoで自動化しております。「Arduinoって何?」って方は剣盾の00回目の記事を、Arduinoの環境構築(ライブラリ含む)は01回目の記事をご覧ください。


2024/06/06:Ver3.0を公開しました。2データ以上を順にストーリー自動化しやすくなり、言語、容姿、主人公名を自動で入力できるようになりました。また、不思議な贈り物を受け取った後に手持ち2匹目以降をボックスに預ける動作の追加、スケッチ記述の整理、その他動作不安定箇所の修正などをおこないました。

 

本記事では、このプログラム(スケッチ)でできることや原理を解説したうえで、スケッチを配布しています。とにかくスケッチが欲しいという方は下へと読み飛ばしてください。


 

きっかけ、動機

 これまで、ポケモン剣盾、BDSP にてそれぞれ殿堂入り付近までのストーリーを自動化してきました。今作ポケモンSVについても、時間がたってマンネリ化してきたころに作ってみよう...と思っておりましたが、期間限定のさいきょうリザードンのテラレイドが開始、ストーリー最序盤だけ進めれば1データにつき1匹だけ捕まえられる「さいきょうのあかし」付のリザードンの回収が可能であると知り、テラレイドバトルに参加できる最序盤のみ勢いで作ってみました。 

これで何ができるの?

ポケモンスカーレット・バイオレットの新規データにて、言語、主人公の名前、容姿を決定した直後から、アカデミーで宝探しが始まりコライドン/ミライドンに乗れるまでを自動で、およそ80分程度で進めます。このタイミングからテラレイドバトルへの参加、その他一通りのことができるようになるため、データごとに1匹しか捕まえられない特殊なテラレイド産ポケモン、不思議な贈り物受け取りなどにご利用ください。

いちおう正式版公開しましたが、おそらくどこかで止まります。ご了承ください。

原理、おおまかな流れ

概要

本プログラムでは、新規データにて言語、主人公の名前、容姿を決定した直後から、アカデミーで宝探しが始まりコライドン/ミライドンに乗れるまでを自動で進めます。

主人公決定でマイコンを挿す→オープニング→設定変更→最初のポケモンをもらう→一致技連打で野生ポケモンと戦う→コライドン/ミライドン餌付け→ポケセン経由でテーブルシティへ→2回目のネモ戦で敗北→テーブルシティ入国して学校生活→宝探し開始

ここまでを約80分で勝手に進めます。

実際の動きは下へ進み動画をご覧ください。

最初のポケモンについて

本自動化では、最初のポケモンの選択を自動で行います。

ニャオハ、ホゲータ、クワッスのうち、どれを選択するかをスケッチ書き込み前に設定することができます。

スケッチ書き込み前に、上段にある

int gosanke_choice=2;

の数字を変更してください。

0:クワッス(選択時に中央)、1:ニャオハ(左)、2:ホゲータ(右)となります。


なお、道中の野生ポケモンに対してはタイプ一致技(水鉄砲、木の葉、火の粉)のみを使用します。最初の御三家によって成功率が変わるため、ホゲータをおすすめします。成功率はホゲータ≧クワッス>>>>>ニャオハ。

0:クワッス...概ね安定。ただしテーブルシティ付近の道中のLv6以上のハネッコに吸い取られると負けがち。コサジの灯台でのイベント後にハネッコに出待ちされるとレッツゴーで倒せずに動作停止する。2回目のネモ戦ではパモにスムーズに倒されて次に進めるので良し。

1:ニャオハ...圧倒的事故率。道中の虫ポケモンに1/2、ハネッコに至っては1/4。弱点を受けることは少ないが、戦闘時間がかかる。レッツゴーも負けがち。テーブルシティのスター団1戦目のシュシュルーも厳しい。野生ポケモンに負ける割に、2回目のネモ戦では負けるまでに時間がかかる。

2:ホゲータ...最も安定。上2匹が道中至る所に漂うハネッコに弱いのに対して、火の粉で焼き払える。コダックやブイゼルなどの水ポケモンに負ける可能性はあるが、水ポケモンと戦闘する可能性があるのはコサジの灯台~最初のポケセンの途中にある橋付近の一瞬のみ。なお、他より素早さが遅いため、個体値や性格と運が悪いとスター団2戦目のヤング―スに上から噛みつく連打されて負けることあり。


準備、必要なもの

本スケッチでは、マイコンを挿す前に以下の準備が必要となります。

①スケッチの編集

スケッチ上段にある設定項目より、最初にもらうポケモンを設定してください。

 int gosanke_choice=2;

のように、0:クワッス、1:ニャオハ、2:ホゲータのいずれかを選択すると、ストーリー自動化にてその御三家を選択します。

上記のようにホゲータ≧クワッス>ニャオハの順に安定します。タイプ一致技を連打する関係で、道中の野生ポケモンに対する通りの良さで安定性が変わります。

 

②ゲーム側の準備

マイコンを挿す前に、新規データの言語、主人公の名前、容姿を決定してください。入学届?が表示されて以降でマイコンを挿せばストーリーを自動で進行します。


作成したスケッチ

作成した最序盤ストーリー自動化のスケッチ最新版は2024.06.06_Ver3.0はこちら

 

旧Ver、2024.04.06_Ver2.201はこちら。2023.08.15_Ver2.1はこちら。2023.07.17_Ver2.04はこちら。2023.03.19_Ver2.03はこちら。2023.02.19_Ver2.02はこちら。2023.01.02_Ver1.021はこちら。2022.12.15_Ver1.01はこちら

2024/06/06:Ver3.0を公開しました。2データ以上を順にストーリー自動化しやすくなり、言語、容姿、主人公名を自動で入力できるようになりました。また、不思議な贈り物を受け取った後に手持ち2匹目以降をボックスに預ける動作の追加、スケッチ記述の整理、その他動作不安定箇所の修正などをおこないました。

 

本スケッチを実際に使用した動画はこちら↓

Ver1.01、初期版(旧解説動画はこちら

/**
* ポケモンSVのストーリー最序盤、宝探しが開始されてコライドン/ミライドンに乗れるようになるまでの自動化
* 開始直後、言語や名前、容姿を決定したところからマイコンを挿す
* 最初のポケモンにはホゲータを推奨。変更可能だが、御三家によって走破できる確率が変わります。
*
* 初期条件は以下の通り
* 0. 本スケッチ内の設定項目(下)より最初のポケモンを設定しておく。ホゲータ推奨。
* 1. ポケモンSVの新規データを作成する。言語、主人公の名前、(主人公の容姿)を決定する
* 2. マイコンを挿してスタート。うまくいけばテーブルシティでコライドン/ミライドンに乗れるようになるまでを自動で進行。
*/

// --------------------↓以下、設定項目↓--------------------

// 最初に選ぶ御三家ポケモン...0,中央クワッス、1,左ニャオハ、2,右ホゲータ
// タイプ一致技(水鉄砲、木の葉、火の粉)のみを使用するため、相性によってストーリー進行失敗率が変わる、本スケッチはクワッス想定で作成。
// 最序盤自動化での安定性はホゲータ≧クワッス>>>ニャオハ、特にニャオハを選ぶ場合は道中で負けることを覚悟すべき。
/*
 * クワッス...比較的安定。テーブルシティ手前でハネッコLv6以上のすいとるに負けることあり、またレッツゴーで負ける恐れ
 * ニャオハ...色々と事故る。タマンチュラ半減、ハネッコ1/4と厳しめでレッツゴーも負けがち。スター団(女)のシュシュル―もきつい。バトルの勝敗の前に戦闘時間が長引く。また、テーブルシティ手前のネモ戦で時間がかかる可能性(電気ショック半減)
 * ホゲータ...比較的安定。コダック等水タイプが怖い。天候が雨だと常時半減(ストーリー最序盤は天候固定?)なので、今後ストーリー全体を自動化する場合は候補から外れる可能性あり。
 */
int gosanke_choice=2;

// --------------------↑以上、設定項目↑--------------------

#include <auto_command_util.h>

// 言語、主人公名前、容姿を決定した後~OP~移動可能になるまで
void startStory(){
    // 容姿決定、学生証が表示されたあたりからOP、移動可能になるまで約3分40秒A連打
    pushButton(Button::A, 100, 1100);
    // 移動可能になったらスティック下に倒して立ち上がる
    tiltJoystick(0, 100, 0, 0, 3000);
    delay(5000);

    // 左の壁まで進む(立ち上がった直後はメニュー開けない)
    tiltJoystick(-100, 0, 0, 0, 6000);
}


// 自室の椅子から立ち上がった直後、設定変更
// 設定終了後にセーブ
void openOptions(){
    // Xボタンメニュー、オプション
    pushButton(Button::X, 1000);
    pushButton(Button::A, 1000);

    // 上から順に、話はやい、技覚えスキップ、ボックス自動、ニックネームしない、カメラ上下そのまま、左右そのまま、
    for(int i=0; i<4; i++){
      pushHatButton(Hat::RIGHT, 200);
      pushHatButton(Hat::DOWN, 200);
    }
    pushHatButton(Hat::DOWN, 200, 2);

    // お任せレポートしない、ニックネーム表示みない、ムービースキップする、BGM、SE、鳴き声、振動しない、お助け機能しない
    for(int i=0; i<3; i++){
      pushHatButton(Hat::RIGHT, 200);
      pushHatButton(Hat::DOWN, 200);
    }
    pushHatButton(Hat::DOWN, 200, 3);
    pushHatButton(Hat::RIGHT, 200);
    pushHatButton(Hat::DOWN, 200);
    pushHatButton(Hat::RIGHT, 300);

    // 設定変更、ムービースキップOFFの警告あり
    pushButton(Button::A, 800, 9);
    delay(500);

    // ここでRボタンセーブ
    pushButton(Button::R, 1000);
    pushButton(Button::A, 4000);

    // セーブ完了、メニューを閉じる
    pushButton(Button::B, 800, 2);

}


// 設定変更を終え、移動して階段を下り、最初のポケモンをもらうまで
// 最初のポケモンにはホゲータを推奨、タイプ一致技のみを使用
// あらかじめ上の設定項目より御三家を設定しておくこと
void firstPokemon(){
    // 上、左、下で階段を下り、ホシガリス
    tiltJoystick(0, -100, 0, 0, 2000);
    tiltJoystick(-100, 0, 0, 0, 3500);
    tiltJoystick(0, 100, 0, 0, 2500);
    pushButton(Button::A, 100, 30);

    // 下、左で母会話20秒
    tiltJoystick(0, 100, 0, 0, 3500);
    tiltJoystick(-100, 0, 0, 0, 7500);
    pushButton(Button::A, 100, 100);

    // 右下、右でクラベル訪問40秒
    tiltJoystick(80, 80, 0, 0, 1500);
    tiltJoystick(100, 0, 0, 0, 7500);
    pushButton(Button::A, 100, 180);

    // 右1.5秒、右上、上、右4.5秒、下2.3秒、右で身支度20秒
    tiltJoystick(100, 0, 0, 0, 1500);
    tiltJoystick(60, -80, 0, 0, 2000);
    tiltJoystick(0, -100, 0, 0, 4000);
    tiltJoystick(100, 0, 0, 0, 4500);
    tiltJoystick(0, 100, 0, 0, 2300);
    tiltJoystick(100, 0, 0, 0, 2000);
    pushButton(Button::A, 100, 100);

    // 左上、左、下、左で母たちと会話25秒
    tiltJoystick(-80, -80, 0, 0, 4000);
    tiltJoystick(-100, 0, 0, 0, 3500);
    tiltJoystick(0, 100, 0, 0, 4000);
    tiltJoystick(-100, 0, 0, 0, 2000);
    pushButton(Button::A, 100, 130);

    // 右下へ進み家を出て、ポケモンと対面、スマホロトム入手
    tiltJoystick(90, 60, 0, 0, 5000);
    pushButton(Button::A, 100, 450);

    // ひたすらまっすぐ36秒、そこから左下の脇道へそれるとルートを戻される
    tiltJoystick(0, -100, 0, 0, 20000);
    tiltJoystick(0, -100, 0, 0, 16000);
    tiltJoystick(-90, 60, 0, 0, 1000);
    pushButton(Button::A, 6500);

    // Yボタンマップを開き、チュートリアル
    pushButton(Button::Y, 3000);
    pushButton(Button::A, 500, 4);

    // Rスティック押し込み、地図の向きを北固定にする
    pushButton(Button::RCLICK, 500);
    // ZLボタンで地図を1段階縮小
    // 東南東にカーソルを動かし、海上を目的地にする
    tiltJoystick(100, 40, 0, 0, 800);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    // 前進、ネモたちと会話45秒
    tiltJoystick(0, -100, 0, 0, 15000);
    tiltJoystick(-80, -80, 0, 0, 1000);
    pushButton(Button::A, 100, 230);

    // 最初のポケモン、前進して中央のクワッスを選ぶ
    // 他のポケモンを選びたい場合、スケッチ最上段の設定項目より変更可能、スティック角度を調整、ニャオハ左、ホゲータ右
    tiltJoystick(0, -100, 0, 0, 700);
    switch(gosanke_choice){
      case 1:
        tiltJoystick(-80, -30, 0, 0, 300);
        break;
      case 2:
        tiltJoystick(80, -30, 0, 0, 300);
        break;
    }

    // 話しかける
    pushButton(Button::A, 100, 5);
    delay(4000);

    // 鳴き声
    pushButton(Button::A, 100, 5);
    delay(6000);

    // ポケモン決定
    pushButton(Button::A, 1000, 7);
    //ニックネーム不要、会話70秒
    pushButton(Button::B, 100, 100);
    pushButton(Button::A, 100, 250);
}


// もらったポケモンで初戦闘~コラミラとの出会い~ペパー戦~コサジの灯台に昇り降りまで
void firstBattle(){
    // ここでメニュー、お任せ回復のチュートリアル
    pushButton(Button::X, 1000);
    pushButton(Button::A, 300, 2);

    delay(500);
    // 手持ちのクワッスのつよさをみる
    pushHatButton(Hat::LEFT, 200);
    pushButton(Button::A, 300, 2);
    delay(1000);

    // 技の入れ替え、タイプ一致技を1番上に
    pushHatButton(Hat::RIGHT, 500);
    pushButton(Button::Y, 500);
    pushButton(Button::A, 500);
    pushHatButton(Hat::DOWN, 200, 2);
    pushButton(Button::A, 500);

    // メニューを閉じる
    pushButton(Button::B, 1000, 3);
    // Yボタンでマップを開き、南南東方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(70, 90, 0, 0, 400);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    // 途中で一瞬左に動きつつ16秒進み、白い柵にぶつかる
    tiltJoystick(0, -100, 0, 0, 16000);
    delay(500);

    // Yボタンでマップを開き、北東方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(80, -80, 0, 0, 1000);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // 柵をつたいながら5秒進み、壁にぶつかる
    tiltJoystick(10, -100, 0, 0, 5000);
    // そこから壁沿いに進み、ネモに話しかける
    SwitchController().setStickTiltRatio(80, -80, 0, 0);
    delay(500);
    SwitchController().setStickTiltRatio(10, -100, 0, 0);
    pushButton(Button::A, 100, 35);

    // ネモの左側を通り過ぎることがあるので、その場合はUターン
    SwitchController().setStickTiltRatio(20, 90, 0, 0);
    delay(300);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(200);
    pushButton(Button::L, 300);
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 25);
    SwitchController().setStickTiltRatio(-20, 90, 0, 0);
    delay(300);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(200);
    pushButton(Button::L, 300);
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 25);
    SwitchController().setStickTiltRatio(-20, 90, 0, 0);
    delay(300);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(200);
    pushButton(Button::L, 300);
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 25);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);

    // 以後A連打、戦闘および会話終了まで3分を想定
    pushButton(Button::A, 100, 900);
    // 前進、その後右を向いて前進、左上を向いて前進
    tiltJoystick(0, -100, 0, 0, 12000);
    tiltJoystick(100, 0, 0, 0, 300);
    pushButton(Button::L, 300);
    tiltJoystick(0, -100, 0, 0, 4600);
    tiltJoystick(-80, -80, 0, 0, 100);
    pushButton(Button::L, 300);
    tiltJoystick(-50, -90, 0, 0, 3000);
    tiltJoystick(-30, -90, 0, 0, 2000);

    // 母会話60秒
    pushButton(Button::A, 100, 300);
    // 前進6秒、右上を向き前進8秒
    tiltJoystick(0, -100, 0, 0, 6000);
    tiltJoystick(80, 60, 0, 0, 100);
    pushButton(Button::L, 300);
    tiltJoystick(0, -100, 0, 0, 8000);

    // 左スティックを前に倒した状態で捕獲チュートリアル
    // 捕獲せずみずでっぽう連打、2分を想定、そのまま前進して壁にぶつかる
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 600);

    // ここから、「一瞬左右に動き障害物をよける→前に進みながらA連打」を繰り返す、A連打17秒×10セット
    // 野生と戦闘しながら前へと進み、柵にぶつかる
    for(int i=0; i<10; i++){
      tiltJoystick(-80, -80, 0, 0, 200);
      tiltJoystick(80, -80, 0, 0, 200);
      SwitchController().setStickTiltRatio(0, -100, 0, 0);
      delay(1000);
      pushButton(Button::A, 100, 80);
    }
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(500);

    // Yボタンでマップを開き、北東方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(70, -90, 0, 0, 1500);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 1000);
    // 咆哮が聞こえ、崖から落ち、コラミラに餌付けを開始するまで2分程度+野生戦闘を想定
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    delay(1500);
    pushButton(Button::A, 100, 850);

    // Bキャンセルorサンドイッチ以外を選ぶと、テキスト送り3回で再びアイテム選択画面
    pushButton(Button::B, 100, 4);
    // AB交互入力によりアイテム選択で1番上のアイテムを選ぶ→キャンセルを繰り返す
    for(int i=0; i<8; i++){
      pushButton(Button::A, 1000);
      pushButton(Button::B, 1000);
    }

    // 大切なものポケット1番下のサンドイッチを選択
    pushHatButton(Hat::UP, 200);
    // 餌付けから洞窟へ80秒
    pushButton(Button::A, 100, 400);
    // 前進25秒程度でネモの声かけ20秒
    tiltJoystick(0, -100, 0, 0, 27000);
    pushButton(Button::A, 100, 100);

    // 前進2秒、方向転換、少し進んで待機、10秒あたりでコラミラ壁破壊1
    tiltJoystick(0, -100, 0, 0, 2000);
    delay(500);
    tiltJoystick(80, -80, 0, 0, 100);
    delay(500);
    pushButton(Button::L, 500);
    tiltJoystick(0, -100, 0, 0, 1000);
    delay(5000);

    // 1秒進んでからカメラを少し右にずらし、さらに8秒
    tiltJoystick(0, -100, 0, 0, 1000);
    delay(500);
    tiltJoystick(30, -90, 0, 0, 100);
    pushButton(Button::L, 100);
    tiltJoystick(0, -100, 0, 0, 12000);
    delay(5000);

    // 右方向に進むコラミラについていくため、右上に方向転換、前進15秒、右上に方向転換、前進10秒
    tiltJoystick(80, -80, 0, 0, 100);
    pushButton(Button::L, 50);
    tiltJoystick(0, -100, 0, 0, 13800);
    delay(2000);
    tiltJoystick(80, -80, 0, 0, 100);
    pushButton(Button::L, 50);
    tiltJoystick(0, -100, 0, 0, 9500);

    // デルビル追い払い18秒、前進5.5秒、右を向き前進30秒ほどでヘルガー遭遇
    delay(18000);
    tiltJoystick(0, -100, 0, 0, 5500);
    tiltJoystick(90, 50, 0, 0, 100);
    pushButton(Button::L, 50);

    // ヘルガー遭遇付近から前進しながらA連打、脱出まで3分ほど
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    delay(28000);
    SwitchController().setStickTiltRatio(80, -80, 0, 0);
    pushButton(Button::A, 100, 15);
    SwitchController().setStickTiltRatio(-80, -80, 0, 0);
    pushButton(Button::A, 100, 20);
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 350);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    pushButton(Button::A, 100, 500);
    delay(5000);

    // 13秒ほど前進するとペパーと会話開始、途中に戦闘、終了まで3分半程度を想定
    tiltJoystick(0, -100, 0, 0, 15000);
    // スティックを倒したままにして、戦闘後はコサジの灯台はしご近くまで進む
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 1100);

    // 前進し、右にあるはしごを上る
    tiltJoystick(80, -80, 0, 0, 4000);
    delay(4000);

    // 突き当りまで進み、右を向いて進むと柵に沿ってネモまですすむ、会話55秒
    tiltJoystick(0, -100, 0, 0, 4000);
    tiltJoystick(100, 0, 0, 0, 100);
    pushButton(Button::L, 50);
    tiltJoystick(0, -100, 0, 0, 7000);
    pushButton(Button::A, 100, 280);

    // はしごを下りる
    tiltJoystick(0, -100, 0, 0, 1000);
    tiltJoystick(-40, -100, 0, 0, 1500);
    tiltJoystick(-100, 0, 0, 0, 1000);
    delay(5000);
}


// コサジの灯台を下りたところ~テーブルシティ南のポケセンに到着するまで
void goToTable(){
    // レッツゴーRボタンで前方の安全を確認(ポケモンによって負ける可能性あり?)
    pushButton(Button::R, 5000);
    // 前進2.5秒
    tiltJoystick(0, -100, 0, 0, 2500);
    // Yボタンでマップを開き、プラトタウンポケセンに目的地設定
    pushButton(Button::Y, 2000);
    // ZLボタンでマップを1段階拡大
    pushButton(Button::ZR, 1000);
    tiltJoystick(-40, -100, 0, 0, 700);
    delay(500);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // ポケモンを戻すZR
    pushButton(Button::ZR, 500);
    // A連打しながら前進し続ける、野生ポケモンがいればタイプ一致技連打
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    pushButton(Button::A, 100, 150);

    // ここから、「一瞬左右に動き障害物をよける→Yボタンでマップを開き向きを調整→前に進みながらA連打」を繰り返す、A連打17秒×9セット
    // 野生と戦闘しながら前へと進み、柵にぶつかる
    for(int i=0; i<9; i++){
      // 左右に動き障害物を避ける
      tiltJoystick(-80, -80, 0, 0, 200);
      tiltJoystick(80, -80, 0, 0, 200);
      tiltJoystick(0, -100, 0, 0, 300);
      delay(200);

      // Yボタンでマップを開き、Bでキャンセル
      // 戦闘時は、強さをみてBで戻る
      pushButton(Button::Y, 2000);
      pushButton(Button::B, 1000);

      // 主人公の向きが目的地に向くのでカメラリセット
      pushButton(Button::L, 500, 2);
      // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
      pushButton(Button::ZR, 1000);
      // 目的地に向かって前進17秒
      SwitchController().setStickTiltRatio(0, -100, 0, 0);
      delay(1500);
      pushButton(Button::A, 100, 80);
    }
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(500);

    // ポケセンの回復、技マシンマシン、ショップに話しかけている状態をキャンセルのためB連打
    pushButton(Button::B, 100, 70);
    // ポケセンでの豆知識はBでテキスト送りできないため、A入力を挟む
    pushButton(Button::A, 500, 2);
    pushButton(Button::B, 100, 30);

    // Yボタンでマップを開き、今いるポケセンに空を飛ぶ
    pushButton(Button::Y, 2000);
    // ZRボタンでマップを1段階拡大
    pushButton(Button::ZR, 1000);
    tiltJoystick(-100, 0, 0, 0, 30);
    delay(500);
    pushButton(Button::A, 300, 2);
    delay(500);
    pushButton(Button::A, 300);

    // うまく飛べなかった場合はマップを閉じる
    pushButton(Button::B, 500, 2);
    delay(5000);

    // ポケセンお姉さんの方を向いた状態から後ろへ3.2秒
    tiltJoystick(0, 100, 0, 0, 3200);
    // Yボタンでマップを開き、北方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(10, -100, 0, 0, 3000);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 1000);
    // A連打しながら前進し続ける、野生ポケモンがいればタイプ一致技連打
    // トレーナーを避けるため、道の右側を進み、最後にテーブルシティ境の塀に直角にぶつかることを想定
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    delay(1500);

    pushButton(Button::A, 100, 80);
    // Yボタンでマップを開き、もう一度北方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(10, -100, 0, 0, 2500);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 1000);
    // A連打しながら前進し続ける、野生ポケモンがいればタイプ一致技連打
    // トレーナーを避けるため、道の右側を進み、最後にテーブルシティ境の塀に直角にぶつかることを想定
    SwitchController().setStickTiltRatio(0, -100, 0, 0);
    delay(1500);
    pushButton(Button::A, 100, 50);
    for(int i=0; i<15; i++){

      // 左右に動き障害物を避ける
      tiltJoystick(-80, -80, 0, 0, 200);
      tiltJoystick(80, -80, 0, 0, 200);
      tiltJoystick(0, -100, 0, 0, 300);
      delay(200);

      // Yボタンでマップを開き、Bでキャンセル
      // 戦闘時は、強さをみてBで戻る
      pushButton(Button::Y, 2000);
      pushButton(Button::B, 1000);

      // 主人公の向きが目的地に向くのでカメラリセット
      pushButton(Button::L, 500, 2);
      // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
      pushButton(Button::ZR, 1000);
      SwitchController().setStickTiltRatio(0, -100, 0, 0);
      delay(1500);
      pushButton(Button::A, 100, 50);
    }
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    delay(500);

    // Yボタンでマップを開き、西方向に目的地設定
    pushButton(Button::Y, 2000);
    tiltJoystick(-100, 0, 0, 0, 3000);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200, 2);

    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 2000);
    // レッツゴーRボタンで前方の安全を確認(ポケモンによって負ける可能性あり?)
    pushButton(Button::R, 5000);
    // 前に進み続け、塀をたどって進む
    // 最後はポケセンで誰かに話しかけるか、そのままテーブルシティ入り口でネモ戦に突入する
    SwitchController().setStickTiltRatio(8, -100, 0, 0);
    // ポケモンを戻すZR
    pushButton(Button::ZR, 500);
    // 10秒ほど前進
    pushButton(Button::A, 100, 50);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);

    // ポケセンからの距離によっては主人公が南寄りに進むことがあるため、少しあるいたところで軌道修正
    // Yボタンでマップを開き、Bでキャンセル
    // 戦闘時は、強さをみてBで戻る
    pushButton(Button::Y, 2000);
    pushButton(Button::B, 1000);

    // 主人公の向きが目的地に向くのでカメラリセット
    pushButton(Button::L, 500, 2);
    // さらに70秒ほど前進
    SwitchController().setStickTiltRatio(8, -100, 0, 0);
    pushButton(Button::A, 100, 350);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);

    // ポケセンの北東に位置するために少しだけ左(南東方向)に進む
    tiltJoystick(-100, 0, 0, 0, 1000);
    // ポケセンの回復、技マシンマシン、ショップに話しかけている状態をキャンセルのためB連打
    pushButton(Button::B, 100, 70);
    // ポケセンでの豆知識はBでテキスト送りできないため、A入力を挟む
    pushButton(Button::A, 500, 2);
    pushButton(Button::B, 100, 30);

    // ポケセンで立ち止まっている場合、Yボタンでマップを開き、すぐ南のポケセンに空を飛ぶ
    pushButton(Button::Y, 2000);
    // ZRボタンでマップを1段階拡大
    pushButton(Button::ZR, 1000);
    tiltJoystick(-80, 80, 0, 0, 50);
    delay(500);
    pushButton(Button::A, 300, 2);
    delay(500);
    pushButton(Button::A, 300);

    // うまく飛べなかった場合はマップを閉じる
    pushButton(Button::B, 500, 2);
    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 100);
    delay(5000);
}

// テーブルシティ南のポケセンからネモ戦(敗北)~入学、宝さがし開始でコライドン、ミライドンに乗れてテラレイド可能になるまで
void firstTable(){
    // テーブルシティ南のポケセンで空を飛んだところから西を向く
    pushButton(Button::Y, 2000);
    tiltJoystick(-100, 0, 0, 0, 2000);
    pushButton(Button::A, 300, 2);
    delay(500);

    // Bボタンで戻ると、目的地方向を向く
    pushButton(Button::B, 1500);
    pushButton(Button::B, 200);

    // Bでしゃがんだ時の対策として、ポケモンを投げて出す動作
    pushButton(Button::ZR, 1000);
    // 前進、右上でテーブルシティ門の前、ネモに呼び止められる
    tiltJoystick(30, -90, 0, 0, 1300);
    tiltJoystick(0, -100, 0, 0, 2000);
    tiltJoystick(30, -90, 0, 0, 5000);

    // ネモと戦闘開始、ネモ2匹目のパモの電気ショックに敗れ、そのままストーリー進行してテーブルシティで動けるまで約5分
    pushButton(Button::A, 100, 1500);
    // 入国したら、人を避けつつ中央の階段を40秒ほど進む
    tiltJoystick(0, -100, -70, 0, 500);
    tiltJoystick(0, -100, 70, 0, 500);
    tiltJoystick(0, -100, 0, 0, 13000);
    tiltJoystick(0, -100, 70, 0, 550);
    tiltJoystick(0, -100, -70, 0, 500);
    tiltJoystick(0, -100, 0, 0, 31000);

    // スター団(女)と戦闘2分程度
    pushButton(Button::A, 100, 75);
    // 引き続きスター団(男)、テラスタル使用のためAとR交互に入力、さらに3分程度
    for(int i=0; i<420; i++){
      pushButton(Button::A, 100);
      pushButton(Button::R, 100);
    }

    // 戦闘終了後、まっすぐ進み校舎内へ30秒程度
    tiltJoystick(0, -100, 0, 0, 30000);
    // 校舎の扉の左で止まったり、通行人にさえぎられたりするので少し右に移動
    tiltJoystick(0, -100, 100, 0, 500);
    tiltJoystick(0, -100, -100, 0, 500);
    tiltJoystick(0, -100, 0, 0, 5000);
    tiltJoystick(80, -80, 0, 0, 5000);

    // エントランス、教室順にで会話2分程度
    pushButton(Button::A, 100, 550);
    // 左、下で教室を出る
    tiltJoystick(-100, 20, 0, 0, 3000);
    tiltJoystick(0, 100, 0, 0, 2000);

    // 行き先として、1番上の食堂へ
    pushButton(Button::A, 100, 100);
    // 正面にいるペパーと会話、1分半
    tiltJoystick(0, -100, 0, 0, 3000);
    tiltJoystick(-100, 0, 0, 0, 300);
    pushButton(Button::A, 100, 400);

    // 食堂を出ようとするとカシオペア電話~クラベル会話1分程度
    tiltJoystick(0, 100, 0, 0, 3000);
    pushButton(Button::A, 100, 325);

    // エントランスに出るので、授業受付の横の端末へ
    tiltJoystick(0, -100, 0, 0, 3500);
    tiltJoystick(100, 0, 0, 0, 800);

    // 職員室へ
    pushButton(Button::A, 1000, 2);
    pushHatButton(Hat::DOWN, 200);

    // そのままネモたちと会話2分ほど
    pushButton(Button::A, 100, 600);
    // 部屋を出て校長室へ
    SwitchController().setStickTiltRatio(-60, 80, 0, 0);
    pushButton(Button::A, 500, 8);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);

    // そのままクラベルと会話2分半ほど
    pushButton(Button::A, 100, 750);
    delay(300);

    // 部屋を出ようとするとネモと遭遇、自室へ、会話50秒
    tiltJoystick(0, 100, 0, 0, 200);
    pushButton(Button::A, 500);
    tiltJoystick(50, 90, 0, 0, 1500);
    tiltJoystick(-30, 100, 0, 0, 3000);
    pushButton(Button::A, 100, 230);

    // ベッドで寝る、宝さがし開催まで1分ほど
    SwitchController().setStickTiltRatio(-50, -90, 0, 0);
    delay(1000);
    pushButton(Button::A, 100, 50);
    SwitchController().setStickTiltRatio(0, 0, 0, 0);
    pushButton(Button::A, 100, 300);

    // 右のドアから外へ、校長ムービーはスキップして1分程度
    tiltJoystick(100, 0, 0, 0, 2000);
    pushButton(Button::A, 100, 320);

    // 前進、ネモ等と会話2分半程度
    tiltJoystick(0, -100, 0, 0, 12000);
    pushButton(Button::A, 100, 750);

    // コラミラに乗る
    pushButton(Button::PLUS, 500, 2);
    // 会話終了まで35秒程度
    pushButton(Button::A, 100, 200);
}


// 上記のストーリー自動化を主人公設定直後から順に実行する
void storyAll(){
    startStory();
    openOptions();
    firstPokemon();
    firstBattle();
    goToTable();
    firstTable();
}

// ストーリーのうち、OPを終え動けるようになり、自室の左壁にぶつかった状態で設定変更を終えたところから開始
void storyStartFromOptions(){
    firstPokemon();
    firstBattle();
    goToTable();
    firstTable();
}

// 設定変更まで終えたデータを複数用意して置き、並んでいる順に指定回数だけ繰り返し実行
// あらかじめ指定した数のユーザーを用意し、それぞれ主人公の容姿設定、設定変更を終え、自室左の壁に接した状態でセーブをしておくこと。
// ユーザー1人目のデータを開き、自室左壁からスタート
void changeUsers(int user_num){
    // 1つ目のデータは主人公が動ける状態、設定変更を終えたところからストーリーを進める
    storyStartFromOptions();
    // 誤作動している場合もB連打でメニューなど閉じる
    pushButton(Button::B, 500, 20);
    // Xボタンメニュー
    pushButton(Button::X, 1000);
    // Rボタンセーブ
    pushButton(Button::R, 1000);
    pushButton(Button::A, 4000);

    // セーブ完了、メニューを閉じる
    pushButton(Button::B, 800, 2);
    // ここからユーザーを切り替えて次のデータを進行
    // あらかじめユーザー切り替えの回数(進めたいデータ数-1)を下の「s<〇」に設定
    // Switch本体のユーザーを右に続くように並べ替え、それぞれ設定変更までを終えておくこと
    for(int s=0; s<user_num -1; s++){
      // HOMEボタンからユーザー切り替え
      pushButton(Button::HOME, 1000);
      pushButton(Button::Y, 800);
      pushButton(Button::A, 5000);

      // 1つ右隣のユーザーを選択、動けるところまで待機
      // 「本体更新」が出る場合は更新せずに始める
      pushHatButton(Hat::UP, 300);
      pushHatButton(Hat::RIGHT, 300);
      pushButton(Button::A, 500);
      pushHatButton(Hat::RIGHT, 300);
      pushButton(Button::A, 15000);
      pushButton(Button::A, 1000, 30);

      // 御三家を変えたい場合は、gosanke_choiceの値を変更する
      //gosanke_choice=(gosanke_choice+1)%3;
      // 設定変更を終えたところからストーリーを進める
      storyStartFromOptions();
      // Xボタンメニュー
      pushButton(Button::X, 1000);
      // Rボタンセーブ
      pushButton(Button::R, 1000);
      pushButton(Button::A, 4000);

      // セーブ完了、メニューを閉じる
      pushButton(Button::B, 800, 2);
    }

    
}


void setup(){
    // 最初の数回の入力はswitchが認識しない場合があるので、無駄打ちをしておく
    pushButton(Button::A, 500, 13);
    // 主人公の容姿を決定したところからスタート
    storyAll();
    
    //複数データを進行させたい場合、あらかじめ設定変更までを完了させておいたデータを用意し、Switchのユーザー順を連続するようにしておく
    //changeUsers(5);

}

void loop(){
    delay(10000);

}

 

 

最後に

本スケッチを使用することで、ポケモンSVの最序盤のストーリーを自動でクリアできます。テラレイドバトルに参加できるまでを自動化しているため、80分の動作を終えればイベントレイドに参加したり、不思議な贈り物を受け取ったりと1データにつき1回限りの要素の集会が楽になるかと思います。

オープンワールドのポケモン、分かってはいたが自動化のハードルが上がったように思います。さすがに全ストーリー自動化は実現しないかと思います。

 

プログラムの不備、改善点などありましたらコメントやお問い合わせからお願いいたします。

Arduino Arduino Leonardo (ピンソケット・ピンヘッダ実装済) 【A000057】
by カエレバ

9 件のコメント:

  1. グーグルドライブからダウンロードしたv1.02で序盤から安定せず、下記の部分が色々おかしいのでは、と思いました(関数の前に//が入っていたり、setupにfirstTableが入っていたり)。現在v1.01で進めさせて貰っていますが、大方流れ通りに進行しています。

    void setup(){
    // 最初の数回の入力はswitchが認識しない場合があるので、無駄打ちをしておく
    pushButton(Button::A, 500, 13);
    // 主人公の容姿を決定したところからスタート
    //storyAll();
    //storyStartFromOptions();
    //複数データを進行させたい場合、あらかじめ設定変更までを完了させておいたデータを用意し、Switchのユーザー順を連続するようにしておく
    //changeUsers(3);
    firstTable();
    }

    返信削除
    返信
    1. コメントありがとうございます。Ver1.01から修正する際の動作テスト時にコメントアウトした箇所がそのままになっており、プログラムが後半部分のみ動くようになっていました。正しく動くようにVer1.021を公開しましたのでご確認ください。

      削除
  2. コメント失礼します
    最序盤の主人公の家から出て御三家ポケモンを選ぶ道中なのですが、一度おじさんにぶつかって、それから方向転換して校長の待つ家に入っていくのが正しいと思うのですが、おじさんにぶつかる事なく毎回素通りしてしまうため御三家ポケモンを選ぶ家に入れずストーリーの自動化ができないのですが、何か改善方法はあるでしょうか?
    環境はスカーレット(パッケージ)、バージョンは最新、DLC未購入です
    もし何か改善方法があれば教えていただきたいです

    返信削除
    返信
    1. 科学の力おじさん付近の動作にてぶつかる場合とぶつからない場合があったため、先ほど公開したVer2.04にて修正しました。
      最新版Ver2.04では、おじさんにぶつかる手前で左脇に進路を変え、呼び止められて進路を戻された後にネモ宅へ向かうようになったかと思います。

      削除
  3. コメント失礼いたします。
    SVのVer.2.0.1アップデートによりゲーム内の設定画面にも変更があり、従来のコマンドではストーリー進行が不可能となりましたのでご報告させていただきます。
    openOptionsのコマンドを変更することで従来通りのポケセンまでの進行と不思議な贈り物の受け取りが確認できましたので併せてご確認ください。
    以下、更新後用のopenOptionsです。「」部分が変更部分です。

    void openOptions(){
    // Xボタンメニュー、オプション
    pushButton(Button::X, 1000);
    pushButton(Button::A, 1000);
    // 上から順に、話はやい、技覚えスキップ、ボックス自動、ニックネームしない、カメラ上下そのまま、左右そのまま、
    for(int i=0; i<4; i++){
    pushHatButton(Hat::RIGHT, 200);
    pushHatButton(Hat::DOWN, 200);
    }
    pushHatButton(Hat::DOWN, 200, 「3→5」);
    // お任せレポートしない、ニックネーム表示みない、ムービースキップする、BGM、SE、鳴き声、振動しない、お助け機能しない
    for(int i=0; i<3; i++){
    pushHatButton(Hat::RIGHT, 200);
    pushHatButton(Hat::DOWN, 200);
    }
    pushHatButton(Hat::DOWN, 200, 3);
    pushHatButton(Hat::RIGHT, 200);
    pushHatButton(Hat::DOWN, 200);
    pushHatButton(Hat::RIGHT, 300);
    // 設定変更、ムービースキップOFFの警告あり
    pushButton(Button::A, 「800→1000」, 「9→10」);
    delay(500);
    // 設定完了、メニューを閉じる
    pushButton(Button::B, 800, 2);

    }

    返信削除
    返信
    1. コメント、修正箇所の報告ありがとうございます。返信遅くなりました。
      DLCアプデに伴い、カメラに関する設定項目の追加を確認しました。おそらく「」部分の変更でうまく動作するかと思います。
      こちらで動作確認次第、公開しているプログラムも修正させていただきます。

      削除
  4. このコメントは投稿者によって削除されました。

    返信削除
    返信
    1. このコメントは投稿者によって削除されました。

      削除
    2. このコメントは投稿者によって削除されました。

      削除

【ポケモンSV×arduino06】道具プリンター乱数自動化(藍の円盤)

 ※本記事は、ポケモンSVの藍の円盤(DLC)にて、 道具プリンター を決まった日時に起動することで 特定のアイテム、ボールを狙って量産する操作を自動化 するプログラムについて解説した記事です。ポケモンSVの操作をArduino Leonardoで自動化しております。 本記事の内...