ますたーです。こんにちは。
2つ目の記事は、剣盾DLC第2段カンムリ雪原で新たに増えたワットショップ、雪中渓谷の「目玉商品」交換に主眼をおいたArduino Leonardo自動化の記事です。
ソースコードの紹介と、使い方を載せていきます。
なお、Arduino Leonardo自動化の導入・機材構成については前記事を参考にしてください。
前記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
本記事では、日付バグの併用による目玉商品の無限回収を実装します。
2020/12/5追記:NintendoSwitch Ver.11.0アップデート対応済です。
2021/9/20追記:NintendoSwitch Ver.13.0アップデート対応済です。
2022/10/23追記:NintendoSwitch Ver.15.0アップデート対応済です。
今回のもくじです。
作ったきっかけ
色々なWebサイトを見て調べた限りでは、DLC対応のArduino自動化プログラムで、この「目玉商品の自動回収」は見当たらなかったので作りました。ぼんぐりの回収とレアアイテムの回収が同時にできてハッピーです。
ちなみに、私はまだ見たことありませんが、ここのワットショップでは「ドリームボール」もラインナップにあるそうです。ほしい…
使い方
ソースコードは本ページ最下部(コチラ)にございます。「使い方はいいから早くソースコードを寄越せ!」という方は本項を読み飛ばしてください。
一応、日付更新・ワット回収部分・話しかけ部分に工夫しており、任意の日付から始めてもうまく稼働すると思います(つまり予め1/1に戻したり31日まである月に変更したりしなくてOK)。
事前準備
正しく動作するための条件は下記の通りです。
- ポケモン剣盾側の設定
- しっかりと充分なワットをあらかじめ貯めておく
- 一度はワットショップのおじさんに話しかけておく
(初回の専用ルート云々のくだりを回収しておく)
- ワットショップおじさんの隣にある巣穴に手動で柱を立てておく
- 自転車からは降りておく
- Xでメニューを開いた際のカーソルを「タウンマップ」にあわせておく
- NintendoSwitch側の設定
- Switch設定「時刻と時刻」の「インターネットで時間をあわせる」をOFF
- インターネットには接続しない
基本的に難しい操作はないのですが、雪中渓谷に「空飛ぶタクシー」で移動し、ワットショップの隣(奥)にある巣穴に「ねがいのかたまり」を投げ入れてください。
あとは、「自転車に乗っていないこと」「Xボタンでメニューを開いたときに『タウンマップ』がカーソルされていること」さえクリアしていれば多分動きます。
レイドバトルの「みんなで 挑戦!」を押す前の状態で、スケッチを書き込んだArduino LeonardoをSwitchに差し込むと、自動化が始まります。
挿して3~5秒ほど、何度かHomeボタンが押されるような挙動をするかもしれませんが、これは仕様です(SwitchがArduinoをコントローラーとして認識するまでにかかる時間を調整しています)。
挿してから数秒後、5秒ほどなんの操作もしていない時間がありますが、このタイミングでHome画面(Switchでゲームを選ぶ画面)になっていればOKです。もし、なっていなければ手動で1度Homeボタンを押しておいてください。
書いたプログラム
処理フロー
setup()関数内の PushHome()連打は、挿してから認識されるまでの時間合わせです。
処理フローとしては、下記になります。
- 「みんなで挑戦」を押して、日付を進める
- 雪中渓谷に空を飛ぶ
- ワットショップに向かい、目玉商品を購入
- 雪中渓谷に空を飛ぶ
- 巣穴に向かい、ワット回収。
- 1に戻る(ループ)
空を飛ぶのは、天候や出現ポケモンによる負荷の違い?によって移動する向きが微妙にずれたり、オフセット誤差が溜まったりするのでこれらを是正するために入れています。効率ではなく安定性重視です。筆者環境では、このソースコードで3日間放置できました。
ワット回収ならびにワットショップ購入部分に工夫してあり、日付更新は、任意の月・日付から始められます。「1月1日から始めないと動かない」「31日ある月じゃないと動かない」みたいな煩雑な前準備は不要なはずです。
2020/12/5追記:NintendoSwitch Ver.11.0アップデート対応済(変更点はこちら)
2021/9/20追記:NintendoSwitch Ver.13.0アップデート対応済(詳細はこちら)
2022/10/23追記:NintendoSwitch Ver.15.0アップデート対応済
※Switch Liteをご利用の方は「#define SWITCH_VER (13)」を「#define SWITCH_VER (12)」に書き換えてご利用ください。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define INTERVAL (105)
#define SWITCH_VER (15)
void PushHome(int delay_time_ms);
void PushA(int delay_time_ms);
void PushB(int delay_time_ms);
void PushX(int delay_time_ms);
void PushL(int delay_time_ms);
void move_for(char* Direction);
void move_for(char* Direction, int delay_time_ms);
void move_for(char* Direction, int hold_time_ms, int delay_time_ms);
void setup() {
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(3000);
PushHome(1000);
delay(100);
}
void loop() {
PushA(4000);
PushHome(1000);
move_for("down");
move_for("right");
move_for("right");
move_for("right");
move_for("right");
move_for("right");
PushA(1500);
move_for("down", 1500, (int)INTERVAL/4);
move_for("right");
#if (SWITCH_VER >= 15)
move_for("down", (int)750, (int)INTERVAL);
#elif (SWITCH_VER >= 13)
move_for("down", (int)780, (int)INTERVAL/2);
#else
for(int i=0; i<4; i++){
move_for("down", (int)HOLDTIME/2, (int)INTERVAL/2);
}
#endif
PushA(500);
move_for("down");
move_for("down");
PushA(500);
move_for("right");
move_for("right");
move_for("up");
PushA(INTERVAL);
PushA(INTERVAL);
PushA(INTERVAL);
PushA(INTERVAL);
PushHome(2000);
PushA(1000);
PushB(500);
PushA(1000);
PushA(4500);
PushX(700);
PushA(2700);
SwitchControlLibrary().MoveLeftStick((128+70), (128+100));
delay(170);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushA(650);
PushA(1700);
SwitchControlLibrary().MoveLeftStick((128+100), (128-115));
delay(230);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushL(400);
SwitchControlLibrary().MoveLeftStick((128), (128-128));
delay(1800);
SwitchControlLibrary().MoveLeftStick((128), (128));
PushA(650);
PushA(650);
move_for("d");
PushA(650);
PushA(650);
PushA(650);
PushA(2600);
PushB(2600);
PushB(750);
PushB(550);
PushB(550);
PushB(550);
PushB(550);
PushB(550);
PushX(700);
PushA(2700);
SwitchControlLibrary().MoveLeftStick((128+70), (128+100));
delay(170);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushA(650);
PushA(1700);
SwitchControlLibrary().MoveLeftStick((128+28), (128-115));
delay(230);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushL(400);
SwitchControlLibrary().MoveLeftStick((128), (128-128));
delay(4300);
SwitchControlLibrary().MoveLeftStick((128), (128));
PushA(500);
PushB(2500);
PushA(1500);
}
void PushHome(int delay_time_ms){
SwitchControlLibrary().PressButtonHome();
delay(210);
SwitchControlLibrary().ReleaseButtonHome();
delay(delay_time_ms);
return;
}
void PushA(int delay_time_ms){
SwitchControlLibrary().PressButtonA();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonA();
delay(delay_time_ms);
return;
}
void PushB(int delay_time_ms){
SwitchControlLibrary().PressButtonB();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonB();
delay(delay_time_ms);
return;
}
void PushX(int delay_time_ms){
SwitchControlLibrary().PressButtonX();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonX();
delay(delay_time_ms);
return;
}
void PushL(int delay_time_ms){
SwitchControlLibrary().PressButtonL();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonL();
delay(delay_time_ms);
return;
}
void move_for(char* Direction){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
default:
break;
}
}
void move_for(char* Direction, int delay_time){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
default:
break;
}
}
void move_for(char* Direction, int hold_time_ms, int delay_time_ms){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
default:
break;
}
}
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
#define INTERVAL (105)
#define SWITCH_VER (13)
void PushHome(int delay_time_ms);
void PushA(int delay_time_ms);
void PushB(int delay_time_ms);
void PushX(int delay_time_ms);
void PushL(int delay_time_ms);
void move_for(char* Direction);
void move_for(char* Direction, int delay_time_ms);
void move_for(char* Direction, int hold_time_ms, int delay_time_ms);
void setup() {
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(1000);
PushHome(3000);
PushHome(1000);
delay(100);
}
void loop() {
PushA(4000);
PushHome(1000);
move_for("down");
move_for("right");
move_for("right");
move_for("right");
move_for("right");
move_for("right");
PushA(1500);
move_for("down", 1500, (int)INTERVAL/4);
move_for("right");
#if (SWITCH_VER >= 13)
move_for("down", (int)780, (int)INTERVAL/2);
#else
for(int i=0; i<4; i++){
move_for("down", (int)HOLDTIME/2, (int)INTERVAL/2);
}
#endif
PushA(500);
move_for("down");
move_for("down");
PushA(500);
move_for("right");
move_for("right");
move_for("up");
PushA(INTERVAL);
PushA(INTERVAL);
PushA(INTERVAL);
PushA(INTERVAL);
PushHome(2000);
PushA(1000);
PushB(500);
PushA(1000);
PushA(4500);
PushX(700);
PushA(2700);
SwitchControlLibrary().MoveLeftStick((128+70), (128+100));
delay(170);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushA(650);
PushA(1700);
SwitchControlLibrary().MoveLeftStick((128+100), (128-115));
delay(230);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushL(400);
SwitchControlLibrary().MoveLeftStick((128), (128-128));
delay(1800);
SwitchControlLibrary().MoveLeftStick((128), (128));
PushA(650);
PushA(650);
move_for("d");
PushA(650);
PushA(650);
PushA(650);
PushA(2600);
PushB(2600);
PushB(750);
PushB(550);
PushB(550);
PushB(550);
PushB(550);
PushB(550);
PushX(700);
PushA(2700);
SwitchControlLibrary().MoveLeftStick((128+70), (128+100));
delay(170);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushA(650);
PushA(1700);
SwitchControlLibrary().MoveLeftStick((128+28), (128-115));
delay(230);
SwitchControlLibrary().MoveLeftStick((128), (128));
delay(100);
PushL(400);
SwitchControlLibrary().MoveLeftStick((128), (128-128));
delay(4300);
SwitchControlLibrary().MoveLeftStick((128), (128));
PushA(500);
PushB(2500);
PushA(1500);
}
void PushHome(int delay_time_ms){
SwitchControlLibrary().PressButtonHome();
delay(210);
SwitchControlLibrary().ReleaseButtonHome();
delay(delay_time_ms);
return;
}
void PushA(int delay_time_ms){
SwitchControlLibrary().PressButtonA();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonA();
delay(delay_time_ms);
return;
}
void PushB(int delay_time_ms){
SwitchControlLibrary().PressButtonB();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonB();
delay(delay_time_ms);
return;
}
void PushX(int delay_time_ms){
SwitchControlLibrary().PressButtonX();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonX();
delay(delay_time_ms);
return;
}
void PushL(int delay_time_ms){
SwitchControlLibrary().PressButtonL();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonL();
delay(delay_time_ms);
return;
}
void move_for(char* Direction){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(INTERVAL);
break;
default:
break;
}
}
void move_for(char* Direction, int delay_time){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(HOLDTIME);
SwitchControlLibrary().MoveHat(8);
delay(delay_time);
break;
default:
break;
}
}
void move_for(char* Direction, int hold_time_ms, int delay_time_ms){
switch(Direction[0]){
case 'r':
case 'R':
SwitchControlLibrary().MoveHat(2);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'l':
case 'L':
SwitchControlLibrary().MoveHat(6);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'u':
case 'U':
SwitchControlLibrary().MoveHat(0);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
case 'd':
case 'D':
SwitchControlLibrary().MoveHat(4);
delay(hold_time_ms);
SwitchControlLibrary().MoveHat(8);
delay(delay_time_ms);
break;
default:
break;
}
}
あとがき
剣盾の自動化は、今年の2/2にArduino Leonardoを買って以来、色々と試してきましたが、こんなふうにDLCが出てから更に魅力的になったと思います。
入手方法や個数が実質的に限られていたアイテム・ポケモンがちゃんと後からでも手に入れられるようになることは、改造やセーブデータ改竄など非合法な方法を撲滅する上でも必要だと思います。一方で、きのみ集めやレイド周回、ウッウロボガチャなど、通常プレイでは苦行と言わざるを得ないパートが残っているのもまた事実で、やりこみ要素としてのバランスは難しいのでしょう。
ここは、ゲームに対するプレイスタイルに依るところが大きいので、それぞれがポリシーを持って向き合っていただければと思います。
なお、本記事を始めとするArduinoを使った自動化は、コントローラー操作の代替手段の一つです。いわば、連射コントローラーや、マクロ対応コントローラーと同じカテゴリのものです。すなわち、ポケモン剣盾の仕様の範囲内で完結でき、前述の非合法的な手段(改造やセーブデータ改竄など)を使わない、プレイスタイルの一つとして位置づけられると考えます。
本記事を読んでいただいた皆様にはそれぞれに考え方があるかと存じますが、それぞれのプレイスタイルに応じた使い方を演出いただければ幸いです。
ではではc⌒っ.ω.)っ
前の記事:
【Arduino自動化01】Arduino開発環境の導入
次の記事:
【Arduino自動化03】ワット稼ぎ・自動回収