オリジナルベストマッチをつくる 〜トランスチームガン・カスタム〜

前回のハザードトリガーに引き続き、今回もビルド関連の工作です。トランスチームガンをベースに、『ぼくのかんがえたベストマッチ』を実現するオモチャを作ってみました。

正確に言うと、任意のフルボトルを組み合わせることで、元々存在しているベストマッチ以外の組み合わせを、新たなベストマッチとして定義することができます。わかりやすい例でいうと、『魔法使い』x『ライオン』で、『仮面ライダービースト』!とかですね。また、フルボトルの組み合わせも、別に二つの組み合わせに限定する必要もないだろうということで、とりあえず三連ベストマッチまで定義できるようにしてみました。

さらに、「こういうベストマッチもありかと思うけど、それにうまく合うようなフルボトルが東都にも北都にも西都にもレジェンド関連にも存在しない!」という事態にも対応できるように、自分で新たなフルボトルを定義する仕組みも用意しておきました。これにより、ベストマッチの組み合わせはアイデア次第でほぼ無限大に用意できることになりました。

工作のアイデアとしては昨年(2017年)11月頃、特撮玩具好きの部屋のクレーンの丈様によるビルドドライバーの音声解析結果を見たときに思いついていました。ただ、その頃はフルボトル専用発光台座の制作で大忙しだったのと、丁度その制作がひと段落した頃にハザードトリガーが発売されて、そちらの工作を優先した結果、ようやく今(2018年4月)になって完成させることができました。このアイデアの特性上、ある程度フルボトルが出揃ってからでないと面白くないなあとは思っていたので、結果としてはベストな時期に作成することができたと思います。

 

th_transteam_20

で、こちらが改造を施したトランスチームガンです。一応、トランスチームガン・カスタムと名付けてみました。。。が、こうしてみると外見はほとんど変わっていません。

th_transteam_22

裏面も然り。

th_transteam_23

銃口を見ると、奥の方にフルカラーLEDが仕込んであるのがわかります。私の工作では毎度お馴染み、Neo Pixelです。

th_transteam_21

そして見た目に一番変わっているのは、実はこの後部分です。本来ならスチームブレードが接続されるべきところに、インタフェースが二つ追加されています。

th_transteam_27

向かって右側がMP3プレイヤー用のmicroSDカードスロットで、

th_transteam_25

左側が制御基板へのプログラム書き込み用のUSB-シリアル変換アダプタを接続するための口になっています。

ガシャット制作のときは基板もMP3プレイヤーも完全に中に閉じ込める形にしていたのですが、今回は作品としての性質上、後からプログラムの書き換えやMP3音声ファイルの追加が頻発することがわかっていたので、それぞれアクセスしやすいように内部レイアウトに気を遣ってみました。このあたりの考え方については、以前私がマリオガシャットを作成した際に、私のやり方を取り入れつつも独自にオリジナルガシャットを作り上げられたのり軍曹様のやり方を参考にさせて頂いています。

 

さて、中をもう少し詳しく見てみます。

 

th_transteam_18

基本的には元々のトランスチームの部品をそのまま流用していますが、スピーカーはあまりに音質が悪かったので、以下のスピーカーの中身を取り出して置き換えました(さすがにケースごとはちょっと入りませんでした)。

th_transteam_19

元々の音声基板は除いてしまって、代わりにArduino Pro Mini (5V, 16MHz)を埋め込んでいます。

 th_transteam_15

MP3プレイヤーはここに入っています。私の工作では毎度お馴染み、DFPlayer miniを使用しています。

th_transteam_7

それから、フルボトルのイジェクトボタンのところは本来電気的なスイッチは入っていないのですが、ここに一つスイッチを埋め込んでいます。

 

さて、中身についてサラッと見ましたが、今回の工作では、ハード側の方で語ることはあまりありません。今回の肝になるのはソフト(プログラム)側、具体的には『どうやってフルボトルの個別認識機能を実現するか?』に尽きます。要するに、例えばラビットフルボトルは「1」、タンクフルボトルは「2」であるとプログラムで認識することさえできれば、あとはプログラム上で「1と2はベストマッチだよ」という定義を書いてあげれば良い、ということになります。

各フルボトルにはそれぞれ固有のナンバーが割り振られていて、それは認識ピンの下に明記されています。ラビットなら「001」、タンクなら「002」といった具合です。認識ピンの配列とこれらの固有ナンバーの関係についてはクレーンの丈様がこちらで解説してくださっていますが、計算に必要な情報をまとめると以下のようになります。

th_transteam_41

わかってしまえば単純ですが、自分は二進数と思い込んでしまっていたせいで、最初は結構混乱してしまいました。二進数ではなく三進数であることに気づくのがポイントです。

 

さて、「ピンの並びがわかれば固有ナンバーの計算はできる」というところまで来ました。あとは、「ピンの並びをプログラムでどう認識するか」です。

th_transteam_42

これはトランスチームガンの認識ピン読み取り部分の部品を取り出してきたものです。6本の線が出ている内、真ん中の2本は白色LEDの制御用の線です。残りの4本がフルボトルの認識用の線で、一番左端の線をGNDに繋いで、残りのABCの3本の線をそれぞれ制御基板(Arduino Pro mini)のGPIOにINPUT_PULLUP指定で接続すると、各読み取りスイッチは押されていないときに1、押されているときに0を出力するようになります。

これを踏まえて、フルボトルが装填されると0と1がどのように変化していくかを考えてみます。基本的な考え方は以下のようになります。

th_transteam_43

 A列先行型の場合は、A列が読み取り開始の合図を出す役目を持つピンだと考えるとわかりやすいです。C列先行型の場合は、当然ですがC列がその役目になります。

この考え方は素直でわかりやすいので、私も最初はこの考え方で認識ロジックを書いてみたのですが、いつもビルドドライバーで遊んでいるときのようにボトルを素早く装填してしまうと、うまく認識できないという現象が発生してしまいました。

調べてみたところ、ボトルを素早く装填してしまうと、どうも先述の「読み取り開始の合図を出すピン」を読み飛ばしてしまうことがあるようだということがわかりました。

th_transteam_44

ということで、少し考え方を変えてみました。「途中にある読み取り開始の合図を出すピン」は読み飛ばすことは多いものの、「一番最初の読み取り開始の合図を出すピン」と「読み取り終了」の数字の並び(111)を読み飛ばすことはあまりないようでしたので、「読み取り終了」をベースにピン配列を認識する方法に変えてみました。これで、認識不良や誤認識はだいぶ減らすことができた気がします(←発生しない、ということはないです)。

 

あとは、上記のロジックをプログラムとしてコーディングして、それに肉付けしていく形でプログラムを完成させていきます。この記事の最後にソースコードの全文を載せていますので、興味のある方はご覧ください。

 

さて、ここまでくれば、オリジナルのフルボトルとベストマッチを定義することはそんなに難しくありません。作成したソースコードの中から、関連するところだけを抜き出して説明します。まずはフルボトルの方から。

const struct fullbottle fullbottles[NUMBER_OF_FULLBOTTLES] PROGMEM = {
  {  1,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ラビット
  {  2,  0,  0,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タンク
  {  3,102, 51,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ゴリラ
  {  4,  0,153,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タイヤモンド
  ...
  { 29,  0,  0,255,READY_CLOSE_DRAGON,CHANGE_CLOSE_DRAGON,CRITICAL_DRAGONIC_FINISH},  // ドラゴン
  ...
  {121,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 平成ライダー
  {122,204,  0,204,READY_GENM, CHANGE_GENM,   CRITICAL_GENM},  // 未定義 → ゲンム
  {123,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  ...
  {162,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK}   // 未定義
};

ソースコードの中では、こんな感じでフルボトルについての情報を定義しています。各フルボトルにつき、

  • フルボトルID(フルボトル名音声IDを兼ねる)
  • 発光色(R),
  • 発光色(G)
  • 発光色(B)
  • ボトル挿入時変身待機音声ID
  • トリガー押下時変身音声ID
  • トリガー長押し時必殺技音声ID

の情報を自分で好きなように与えることで、フルボトルごとの挙動を自由に定義することができます。特に、フルボトルIDの122番目以降は元々ビルドドライバーでも利用が想定されていない領域なので、この部分を使えば、元々用意されたフルボトルは全て認識可能な上に、自分で新たなフルボトルを追加してあげることが可能です。私の場合は122番目に新たに『ゲンム』のフルボトルを定義しています。

続いて、オリジナルベストマッチの作り方です。こちらも、上記のオリジナルフルボトル追加の手順と似たようなものです。こちらは

struct bestmatch bestmatches[] = {
  { 1,  1,  2, MATCH_BEST, READY_BEST_MATCH, 163, CRITICAL_VORTEX_FINISH}, // ラビットタンク
  { 2,  3,  4, MATCH_BEST, READY_BEST_MATCH, 164, CRITICAL_VORTEX_FINISH}, // ゴリラモンド
  { 3,  5,  6, MATCH_BEST, READY_BEST_MATCH, 165, CRITICAL_VORTEX_FINISH}, // ホークガトリング
  ...
  {48,115,116, MATCH_BEST, READY_BEST_MATCH, 210, CRITICAL_VORTEX_FINISH}, // 鎧武 x ドライブ
  {49,117,118, MATCH_BEST, READY_BEST_MATCH, 211, CRITICAL_VORTEX_FINISH},  // ゴースト x エグゼイド 

  // 以下は独自またはビルドドライバー以外で定義されたベストマッチ
  {50, 42, 58, MATCH_FUNKY, READY_GEAR, 212, CRITICAL_FUNKY_FINISH},   // ヘルブロス
  {51,  1,  1, MATCH_SUPER_BEST, READY_SUPBER_BESTMATCH, 213, CRITICAL_FUNKY_FINISH},  // ラビットラビット
  {52,  2,  2, MATCH_SUPER_BEST, READY_SUPBER_BESTMATCH, 214, CRITICAL_FUNKY_FINISH},  // タンクタンク
  {53,122, 35, MATCH_BEST, READY_BEST_MATCH, 215, CRITICAL_GENM_LEVEL_X},   // ゲンム レベルX(ゲンム x オバケ)
  {54, 68, 85, MATCH_BEST, READY_BEST_MATCH, 216, CRITICAL_VORTEX_FINISH},  // バース(ケーキ x メダル)
  {55, 84, 62, MATCH_BEST, READY_BEST_MATCH, 217, CRITICAL_VORTEX_FINISH},  // ビースト(魔法使い x ライオン)
  {56,104, 22, MATCH_BEST, READY_BEST_MATCH, 218, CRITICAL_VORTEX_FINISH},  // アクセルフォーム(ファイズ x ウォッチ)
  {57, 29, 74, MATCH_BEST, READY_BEST_MATCH, 219, CRITICAL_VORTEX_FINISH},  // 龍騎(ドラゴン x カード)
  {58, 41, 74, MATCH_BEST, READY_BEST_MATCH, 220, CRITICAL_VORTEX_FINISH},  // ナイト(バット x カード)
  {59, 65, 74, MATCH_BEST, READY_BEST_MATCH, 221, CRITICAL_VORTEX_FINISH},  // 王蛇(コブラ x カード)
  {60, 45, 74, MATCH_BEST, READY_BEST_MATCH, 222, CRITICAL_VORTEX_FINISH},  // ガイ(サイ x カード)
  {61, 25, 74, MATCH_BEST, READY_BEST_MATCH, 223, CRITICAL_VORTEX_FINISH},  // タイガ(トラ x カード)
  {62, 27, 74, MATCH_BEST, READY_BEST_MATCH, 224, CRITICAL_VORTEX_FINISH},  // オーディン(フェニックス x カード)
  {63, 23, 74, MATCH_BEST, READY_BEST_MATCH, 225, CRITICAL_VORTEX_FINISH},  // ブレイド(カブトムシ x カード)
  {64, 94, 74, MATCH_BEST, READY_BEST_MATCH, 226, CRITICAL_VORTEX_FINISH},  // ギャレン(クワガタ x カード)
  {65, 19, 74, MATCH_BEST, READY_BEST_MATCH, 227, CRITICAL_VORTEX_FINISH},  // レンゲル(スパイダー x カード)
  {66, 16, 24, MATCH_BEST, READY_BEST_MATCH, 228, CRITICAL_VORTEX_FINISH}   // マッハ(バイク x カメラ)
};

こんな感じで、

  • ベストマッチID
  • ベストマッチになるフルボトルのID1
  • ベストマッチになるフルボトルのID2
  • ベストマッチ判定時音声ID
  • 変身待機音声ID
  • 変身音声ID
  • 必殺技音声ID

を指定してやればOKです。一応、ビルドドライバー組み込みで49個程度(?)ベストマッチが存在するようなので、50番目以降のIDを使えば、新しいベストマッチの追加ということになります。

最後に、三連ベストマッチの定義は以下のようになります。

struct bestmatch_triple bestmatch_triples[] = {
  {101, 87, 92, 94, MATCH_SUPER_BEST, READY_SCLASH, 251, CRITICAL_SCRASH}, // グリス(キャッスル x フクロウ x クワガタ)
  {102,  7, 85, 94, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 252, CRITICAL_OOO}, // ガタキリバ(タカ x メダル x クワガタ)
  {103,  7, 85, 62, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 253, CRITICAL_OOO}, // ラトラータ(タカ x メダル x ライオン)
  {104,  7, 85,  3, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 254, CRITICAL_OOO}, // ゴリラ (タカ x メダル x ゴリラ)
  {105,  7, 85, 11, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 255, CRITICAL_OOO}, // シャウタ (タカ x メダル x オクトパス)
  {106,  7, 85, 65, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 256, CRITICAL_OOO}  // ブラカワニ (タカ x メダル x コブラ)
};

各項目の内容は、二連ベストマッチのときとほぼ同様です。

あとは、それぞれ自分が定義した音声IDに対応するMP3音声ファイルを用意する必要があります。自分の場合は、基本的に玩具の音声をiPhoneのボイスメモで録音して、それをPCのiTunes上でMP3に変換するようにしていますが、どうにも手に入らない音声については、YouTube上で音声素材を公開されている方にお願いして、音声を使用させて頂きました。ご快諾頂いた冬樹因幡様に、この場を借りてお礼申し上げます。ありがとうございました。

 

というわけで、随分長くなってしまいましたが、以上がこのトランスチームガン・カスタムの仕組みです。今回はガシャットのときと違って見た目はほとんど変わっていないので、ある程度中身のことをちゃんと明らかにしておかないと、動画を公開しても「音声合成w」みたいな扱いを受けてしまいそうな気がしましたので、ポイントだけはちょっと詳しめに説明してみました。

現時点で実装済みの、私が自分で定義したオリジナルのベストマッチは以下になります。

フルボトル1 フルボトル2 仮面ライダー
ドラゴン カード 龍騎
バット カード ナイト
コブラ カード 王蛇
サイ カード ガイ
トラ カード タイガ
フェニックス カード オーディン
ウルフ スマホ ファイズ(乾巧)
ファイズ ウォッチ ファイズ アクセルフォーム
カブト虫 カード ブレイド
クワガタ カード ギャレン
スパイダー カード レンゲル
ケーキ メダル バース
魔法使い ライオン ビースト
バイク カメラ マッハ
ゲンム オバケ ゲンム ゾンビゲーマー レベルX

。。。「カード」が重要過ぎますね。続いて三連ベストマッチです。

フルボトル1 フルボトル2 フルボトル3 仮面ライダー
キャッスル フクロウ クワガタ グリス
タカ メダル クワガタ オーズ ガタキリバ
タカ メダル ライオン オーズ ラトラータ
タカ メダル ゴリラ オーズ サゴーゾ
タカ メダル オクトパス オーズ シャウタ
タカ メダル コブラ オーズ ブラカワニ

。。。オーズがほとんどですね。ラトラータについては、ライオンの代わりにトラにしても良いと思いますし、サゴーゾについてはゴリラの代わりにサイを使っても良いと思います。ブラカワニについてはなんと三匹ともフルボトルで揃っている(コブラ、タートル、クロコダイル)のですが、これにメダルを加えると四連ベストマッチにしなくてはいけないので、今回はやめておきました(やろうと思えばできます)。

 

th_transteam_45

以上、ベストマッチをカスタマイズ可能にした『トランスチームガン・カスタム』のご紹介でした。ベストマッチのカスタマイズが第一目的で作ったものですが、結果的に元々のトランスチームガンから以下の8つの機能が追加されることになりました。

  1. 銃口発光機能
  2. 変身後の通常攻撃と必殺攻撃の切り替え機能(トリガー長押し)
  3. フルボトル個別認識機能
  4. 新たなフルボトルの認識追加機能
  5. 既存ベストマッチ機能
  6. 既存ベストマッチ上書き機能
  7. 新たなベストマッチの認識追加機能
  8. 三連ベストマッチ機能

それでも見た目の派手さはないし、また音声再生のディレイのせいで全体的に動作がもっさりした感じにはなってしまっていますが、フルボトルを使った遊びの幅をだいぶ広げるものにはなっただろうと思います。「コレとコレはベストマッチだろう」「いやいやコッチとアッチの方がベストマッチだろう」という、各自の思い思いのアイデアを実現することができますので、『さぁ、実験を始めようか。』がキャッチコピーのビルドにふさわしいオモチャにできたのではないかなあと思います。

例えば、『仮面ライダー』という枠を超えても良いのであれば、こういうことを考えても良いわけです。

  • スーパー戦隊 x 忍者 = ニンニンジャー、ハリケンジャー、カクレンジャー
  • スーパー戦隊 x ウォッチ = タイムレンジャー
  • スーパー戦隊 x 海賊 = ゴーカイジャー
  • スーパー戦隊 x 電車 = トッキュウジャー
  • スーパー戦隊 x ピラミッド = オーレンジャー
  • スーパー戦隊 x 魔法使い = マジレンジャー
  • スーパー戦隊 x ゲーム = メガレンジャー
  • スーパー戦隊 x ジェット = ジェットマン

さらに、『特撮』という枠まで超えて良いなら、

  • コミック x 海賊 = ワンピース

というようなことまでやってしまったって良いのです。これはもう本当に、自由です。是非、思い思いのベストマッチを考えてみて、楽しんでもらえればと思います。

 

以下、「自分でも作ってみたい」という方向けに、回路図とソースコードを載せておきます。ご興味のある方は、ご参照ください。今回の作品では、

  • ソースコードの作り込みが甘く、フルボトルの認識に失敗することがある
  • 使っているMP3モジュール(DFPlayer mini)に再生開始の信号を送ってから実際の再生開始までの若干のディレイがあり、全体的にワンテンポ遅れた感じになってしまう(Class 10のSDカードを使用していても)

というところが改善すべき点として残っていますので、「ここはこうすればもっと良くなるよ」というアイデアがあれば、是非教えてください。

回路図

th_transteam_47

今までの自分の工作にないポイントとしては、DFPlayerのBUSYピンを使用していることです。このピンの状態を監視することで、「今再生している音声が終了するまで待つ」というような処理が可能になりました。

ソースコード

あんまり綺麗じゃないと思いますが、ご了承ください。

#include <string.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Adafruit_NeoPixel.h>

#define ON  LOW
#define OFF HIGH

#define NUM_OF_LED  1

#define READER_A_PIN    4
#define READER_B_PIN    5
#define READER_C_PIN    6
#define TRIGGER_PIN     7
#define EJECT_PIN       8
#define WHITE_LED_PIN   9
#define COLOR_LED_PIN  10
#define SOUND_BUSY_PIN 11

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_OF_LED, COLOR_LED_PIN, NEO_GRB);
SoftwareSerial mySerial(2, 3); // RX, TX
DFRobotDFPlayerMini myDFPlayer;

//--------------------- 効果音定義 ---------------------//

#define NONE          0

#define MATCH_BEST       300  
#define MATCH_FUNKY      301
#define MATCH_SUPER_BEST 302

#define READY_BEST_MATCH       310
#define READY_CLOSE_DRAGON     311
#define READY_TRANSTREAM_GUN   312
#define READY_GEAR             313
#define READY_CROCODILE        314
#define READY_SUPER_BESTMATCH  315
#define READY_SCLASH           316
#define READY_GENM             317

#define CHANGE_START            320
#define CHANGE_TRIAL            321
#define CHANGE_BESTMATCH        322
#define CHANGE_CLOSE_DRAGON     323
#define CHANGE_BAT              324
#define CHANGE_COBRA            325
#define CHANGE_ENGINE           326
#define CHANGE_REMOCON          327
#define CHANGE_CROCODILE        328
#define CHANGE_SUPER_BEST_MATCH 329
#define CHANGE_GENM             330

#define ATTACK_NORMAL 340

#define CRITICAL_VORTEX_ATTACK        350
#define CRITICAL_VORTEX_FINISH        351
#define CRITICAL_DRAGONIC_FINISH      352
#define CRITICAL_STEAM_ATTACK         353
#define CRITICAL_STEAM_BREAK_BAT      354
#define CRITICAL_STEAM_BREAK_COBRA    355
#define CRITICAL_FUNKY_DRIVE_ENGINE   356
#define CRITICAL_FUNKY_DRIVE_REMOCON  357
#define CRITICAL_FUNKY_FINISH         358
#define CRITICAL_FUNKY_BREAK          359
#define CRITICAL_RABBIT_RABBIT_FINISH 360
#define CRITICAL_TANK_TANK_FINISH     361
#define CRITICAL_SCRASH               362
#define CRITICAL_GENM                 363
#define CRITICAL_GENM_LEVEL_X         364
#define CRITICAL_OOO                  365

#define SYSTEM_POWER_ON     400
#define SYSTEM_BOTTLE_IN    401
#define SYSTEM_BOTTLE_OUT   402
#define SYSTEM_BOTTLE_EMPTY 403

//--------------------- フルボトル定義 ---------------------//

struct fullbottle{
 uint8_t  id;
 uint8_t  color_r;
 uint8_t  color_g;
 uint8_t  color_b;
 uint16_t sound_ready_id;
 uint16_t sound_change_id;
 uint16_t sound_critical_id;
};

const int NUMBER_OF_FULLBOTTLES = 162;
const struct fullbottle fullbottles[NUMBER_OF_FULLBOTTLES] PROGMEM = {
  {  1,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ラビット
  {  2,  0,  0,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タンク
  {  3,102, 51,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ゴリラ
  {  4,  0,153,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タイヤモンド
  {  5,102,  0,102,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 忍者
  {  6,255,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // コミック
  {  7,255,102,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タカ
  {  8,153,153,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ガトリング
  {  9,255,255,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // パンダ
  { 10, 51,153,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ロケット
  { 11,204,  0,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // オクトパス
  { 12,255,255, 51,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ライト
  { 13,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 恐竜
  { 14,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // F1
  { 15,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // サメ
  { 16,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // バイク
  { 17,  0, 51,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 海賊
  { 18,  0,153,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 電車
  { 19,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スパイダー
  { 20,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 冷蔵庫
  { 21,  0,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // タートル
  { 22,153,153,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ウォッチ
  { 23,153, 51,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カブト虫
  { 24,  0,153,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カメラ
  { 25,255,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // トラ
  { 26,255, 51,102,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // UFO
  { 27,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // フェニックス
  { 28,153,153,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ロボット
  { 29,  0,  0,255,READY_CLOSE_DRAGON,CHANGE_CLOSE_DRAGON,CRITICAL_DRAGONIC_FINISH},  // ドラゴン
  { 30,204,153,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ロック
  { 31,  0, 51,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // クジラ
  { 32,  0,102,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ジェット
  { 33,  0,153,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ユニコーン
  { 34,255,255,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 消しゴム
  { 35,  0,153,102,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // オバケ
  { 36,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // マグネット
  { 37,  0, 51,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // シカ
  { 38,255,153,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ピラミッド
  { 39,255,  0, 51,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ローズ
  { 40, 51,153,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ヘリコプター
  { 41,102,  0,102,READY_TRANSTREAM_GUN,CHANGE_BAT,CRITICAL_STEAM_BREAK_BAT},  // バット
  { 42,255,255,255,READY_GEAR,CHANGE_ENGINE,CRITICAL_FUNKY_DRIVE_ENGINE},  // エンジン
  { 43,255,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ハチ
  { 44,  0, 51,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 潜水艦
  { 45,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // サイ
  { 46,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ドライヤー
  { 47,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スコーピオン
  { 48,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ゴールド
  { 49,128,128,128,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ウルフ
  { 50,  0, 51,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スマホ
  { 51,255,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // クマ
  { 52,255,255,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // テレビ
  { 53,153,102, 51,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ドッグ
  { 54,128,128,128,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // マイク
  { 55,255,204,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // キリン
  { 56,  0, 51,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 扇風機
  { 57,204,  0,204,READY_CROCODILE,CHANGE_CROCODILE,CRITICAL_FUNKY_BREAK},  // クロコダイル
  { 58,  0,153,  0,READY_GEAR,CHANGE_REMOCON,CRITICAL_FUNKY_DRIVE_REMOCON},  // リモコン
  { 59,128,128,128,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ペンギン
  { 60,  0,204,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スケボー
  { 61, 51,153,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 掃除機
  { 62,255,204,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ライオン
  { 63,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 消防車
  { 64,128,128,128,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ハリネズミ
  { 65,102,  0,102,READY_TRANSTREAM_GUN,CHANGE_COBRA,CRITICAL_STEAM_BREAK_COBRA},  // コブラ
  { 66,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ブットバソウル
  { 67,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ガンバライジング
  { 68,255,204,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ケーキ
  { 69,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // サンタクロース
  { 70,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カップ麺
  { 71,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // エナジードリンク
  { 72,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スペシャル
  { 73,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // プレミアム
  { 74,  0, 51,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カード
  { 75,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // モモタロス
  { 76,102,  0,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 探偵
  { 77,  0,204, 51,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // USBメモリ
  { 78,255,102,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // オレンジ
  { 79,255,255,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ドクター
  { 80,255,  0,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ゲーム
  { 81,255,102, 51,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 友情
  { 82,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 警察官
  { 83,255,102,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // パーカー
  { 84,204,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 魔法使い
  { 85,255,204,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // メダル
  { 86,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スーパー戦隊
  { 87,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // キャッスル 
  { 88,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // シマウマ
  { 89,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ハンマー
  { 90,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カンガルー
  { 91,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ハサミ
  { 92,255,255,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // フクロウ
  { 93,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // スパナ
  { 94,  0,  0,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // クワガタ
  { 95,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // CD
  { 96,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ラビットタンクスパークリング
  { 97,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 拡張アイテム用?
  { 98,  0,  0,  0,READY_CLOSE_DRAGON,CHANGE_CLOSE_DRAGON,CRITICAL_DRAGONIC_FINISH},  // クローズドラゴン・フルフル
  { 99,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 拡張アイテム用?
  {100,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 1号
  {101,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // クウガ
  {102,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // アギト
  {103,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 龍騎
  {104,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ファイズ
  {105,  0,  0,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ブレイド
  {106,102,  0,153,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 響鬼
  {107,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // カブト
  {108,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 電脳
  {109,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // キバ
  {110,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ディケイド
  {111,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // W
  {112,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // オーズ
  {113,255,255,255,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // フォーゼ
  {114,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ウィザード
  {115,255,102,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 鎧武
  {116,255,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ドライブ
  {117,255,102,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // ゴースト
  {118,255,  0,204,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // エグゼイド
  {119,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 仮面ライダー
  {120,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 昭和ライダー
  {121,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 平成ライダー
  {122,204,  0,204,READY_GENM, CHANGE_GENM,   CRITICAL_GENM},  // 未定義 → ゲンム
  {123,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {124,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {125,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {126,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {127,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {128,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {129,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {130,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {131,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {132,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {133,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {134,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {135,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {136,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {137,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {138,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {139,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {140,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {141,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {142,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {143,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {144,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {145,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {146,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {147,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {148,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {149,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {150,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {151,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {152,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {153,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {154,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {155,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {156,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {157,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {158,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {159,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {160,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {161,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK},  // 未定義
  {162,  0,  0,  0,NONE,CHANGE_TRIAL,CRITICAL_VORTEX_ATTACK}   // 未定義
};

//--------------------- ベストマッチ定義 ---------------------//

struct bestmatch{
 uint8_t  bestmatch_id;
 uint8_t  fullbottle_id_1;
 uint8_t  fullbottle_id_2;
 uint16_t sound_match_id;
 uint16_t sound_ready_id;
 uint16_t sound_change_id;
 uint16_t sound_critical_id;
};

struct bestmatch bestmatches[] = {
  { 1,  1,  2, MATCH_BEST, READY_BEST_MATCH, 163, CRITICAL_VORTEX_FINISH}, // ラビットタンク
  { 2,  3,  4, MATCH_BEST, READY_BEST_MATCH, 164, CRITICAL_VORTEX_FINISH}, // ゴリラモンド
  { 3,  5,  6, MATCH_BEST, READY_BEST_MATCH, 165, CRITICAL_VORTEX_FINISH}, // ホークガトリング
  { 4,  7,  8, MATCH_BEST, READY_BEST_MATCH, 166, CRITICAL_VORTEX_FINISH}, // ニンニンコミック
  { 5,  9, 10, MATCH_BEST, READY_BEST_MATCH, 167, CRITICAL_VORTEX_FINISH}, // ロケットパンダ
  { 6, 11, 12, MATCH_BEST, READY_BEST_MATCH, 168, CRITICAL_VORTEX_FINISH}, // オクトパスライト
  { 7, 13, 14, MATCH_BEST, READY_BEST_MATCH, 169, CRITICAL_VORTEX_FINISH}, // F1ザウルス
  { 8, 15, 16, MATCH_BEST, READY_BEST_MATCH, 170, CRITICAL_VORTEX_FINISH}, // サメバイク
  { 9, 17, 18, MATCH_BEST, READY_BEST_MATCH, 171, CRITICAL_VORTEX_FINISH}, // 海賊レッシャー
  {10, 19, 20, MATCH_BEST, READY_BEST_MATCH, 172, CRITICAL_VORTEX_FINISH}, // スパイダークーラー
  {11, 21, 22, MATCH_BEST, READY_BEST_MATCH, 173, CRITICAL_VORTEX_FINISH}, // タートルウォッチ
  {12, 23, 24, MATCH_BEST, READY_BEST_MATCH, 174, CRITICAL_VORTEX_FINISH}, // ビートルカメラ
  {13, 25, 26, MATCH_BEST, READY_BEST_MATCH, 175, CRITICAL_VORTEX_FINISH}, // トラUFO
  {14, 27, 28, MATCH_BEST, READY_BEST_MATCH, 176, CRITICAL_VORTEX_FINISH}, // フェニックスロボ
  {15, 29, 30, MATCH_BEST, READY_BEST_MATCH, 177, CRITICAL_VORTEX_FINISH}, // キードラゴン
  {16, 31, 32, MATCH_BEST, READY_BEST_MATCH, 178, CRITICAL_VORTEX_FINISH}, // クジラジェット
  {17, 33, 34, MATCH_BEST, READY_BEST_MATCH, 179, CRITICAL_VORTEX_FINISH}, // ユニレイサー
  {18, 35, 36, MATCH_BEST, READY_BEST_MATCH, 180, CRITICAL_VORTEX_FINISH}, // マグゴースト
  {19, 37, 38, MATCH_BEST, READY_BEST_MATCH, 181, CRITICAL_VORTEX_FINISH}, // シカミッド
  {20, 39, 40, MATCH_BEST, READY_BEST_MATCH, 182, CRITICAL_VORTEX_FINISH}, // ローズコプター
  {21, 41, 42, MATCH_BEST, READY_BEST_MATCH, 183, CRITICAL_VORTEX_FINISH}, // バットエンジン
  {22, 43, 44, MATCH_BEST, READY_BEST_MATCH, 184, CRITICAL_VORTEX_FINISH}, // ハチマリン
  {23, 45, 46, MATCH_BEST, READY_BEST_MATCH, 185, CRITICAL_VORTEX_FINISH}, // サイドライヤー
  {24, 47, 48, MATCH_BEST, READY_BEST_MATCH, 186, CRITICAL_VORTEX_FINISH}, // ゴールドスコーピオン
  {25, 49, 50, MATCH_BEST, READY_BEST_MATCH, 187, CRITICAL_VORTEX_FINISH}, // スマホウルフ
  {26, 51, 52, MATCH_BEST, READY_BEST_MATCH, 188, CRITICAL_VORTEX_FINISH}, // クマテレビ
  {27, 53, 54, MATCH_BEST, READY_BEST_MATCH, 189, CRITICAL_VORTEX_FINISH}, // ドッグマイク
  {28, 55, 56, MATCH_BEST, READY_BEST_MATCH, 190, CRITICAL_VORTEX_FINISH}, // キリンサイクロン
  {29, 57, 58, MATCH_BEST, READY_BEST_MATCH, 191, CRITICAL_VORTEX_FINISH}, // クロコダイコン
  {30, 59, 60, MATCH_BEST, READY_BEST_MATCH, 192, CRITICAL_VORTEX_FINISH}, // ペンギンスケーター
  {31, 61, 62, MATCH_BEST, READY_BEST_MATCH, 193, CRITICAL_VORTEX_FINISH}, // ライオンクリーナー
  {32, 63, 64, MATCH_BEST, READY_BEST_MATCH, 194, CRITICAL_VORTEX_FINISH}, // ファイヤーヘッジホッグ
  {33, 68, 69, MATCH_BEST, READY_BEST_MATCH, 195, CRITICAL_VORTEX_FINISH}, // メリークリスマス
  {34, 70, 71, MATCH_BEST, READY_BEST_MATCH, 196, CRITICAL_VORTEX_FINISH}, // カップエナジー

  {35, 66, 67, MATCH_BEST, READY_BEST_MATCH, 197, CRITICAL_VORTEX_FINISH}, // ブットバガンバ
  {36,119, 86, MATCH_BEST, READY_BEST_MATCH, 198, CRITICAL_VORTEX_FINISH}, // スーパーヒーロータイム

  {37, 75, 18, MATCH_BEST, READY_BEST_MATCH, 199, CRITICAL_VORTEX_FINISH}, // 電王
  {38, 74, 24, MATCH_BEST, READY_BEST_MATCH, 200, CRITICAL_VORTEX_FINISH}, // ディケイド
  {39, 76, 77, MATCH_BEST, READY_BEST_MATCH, 201, CRITICAL_VORTEX_FINISH}, // W
  {40,  7, 85, MATCH_BEST, READY_BEST_MATCH, 202, CRITICAL_VORTEX_FINISH}, // オーズ
  {41, 81, 10, MATCH_BEST, READY_BEST_MATCH, 203, CRITICAL_VORTEX_FINISH}, // フォーゼ
  {42, 84,  4, MATCH_BEST, READY_BEST_MATCH, 204, CRITICAL_VORTEX_FINISH}, // ウィザード
  {43, 78, 30, MATCH_BEST, READY_BEST_MATCH, 205, CRITICAL_VORTEX_FINISH}, // 鎧武
  {44, 82, 14, MATCH_BEST, READY_BEST_MATCH, 206, CRITICAL_VORTEX_FINISH}, // ドライブ
  {45, 35, 83, MATCH_BEST, READY_BEST_MATCH, 207, CRITICAL_VORTEX_FINISH}, // ゴースト
  {46, 79, 80, MATCH_BEST, READY_BEST_MATCH, 208, CRITICAL_VORTEX_FINISH}, // エグゼイド

  {47,113,114, MATCH_BEST, READY_BEST_MATCH, 209, CRITICAL_VORTEX_FINISH}, // フォーゼ x ウィザード
  {48,115,116, MATCH_BEST, READY_BEST_MATCH, 210, CRITICAL_VORTEX_FINISH}, // 鎧武 x ドライブ
  {49,117,118, MATCH_BEST, READY_BEST_MATCH, 211, CRITICAL_VORTEX_FINISH},  // ゴースト x エグゼイド 

  // 以下は独自またはビルドドライバー以外で定義されたベストマッチ
  {50, 42, 58, MATCH_FUNKY, READY_GEAR, 212, CRITICAL_FUNKY_FINISH},   // ヘルブロス
  {51,  1,  1, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 213, CRITICAL_RABBIT_RABBIT_FINISH},  // ラビットラビット
  {52,  2,  2, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 214, CRITICAL_TANK_TANK_FINISH},  // タンクタンク
  {53,122, 35, MATCH_BEST, READY_BEST_MATCH, 215, CRITICAL_GENM_LEVEL_X},   // ゲンム レベルX(ゲンム x オバケ)
  {54, 68, 85, MATCH_BEST, READY_BEST_MATCH, 216, CRITICAL_VORTEX_FINISH},  // バース(ケーキ x メダル)
  {55, 84, 62, MATCH_BEST, READY_BEST_MATCH, 217, CRITICAL_VORTEX_FINISH},  // ビースト(魔法使い x ライオン)
  {56,104, 22, MATCH_BEST, READY_BEST_MATCH, 218, CRITICAL_VORTEX_FINISH},  // アクセルフォーム(ファイズ x ウォッチ)
  {57, 29, 74, MATCH_BEST, READY_BEST_MATCH, 219, CRITICAL_VORTEX_FINISH},  // 龍騎(ドラゴン x カード)
  {58, 41, 74, MATCH_BEST, READY_BEST_MATCH, 220, CRITICAL_VORTEX_FINISH},  // ナイト(バット x カード)
  {59, 65, 74, MATCH_BEST, READY_BEST_MATCH, 221, CRITICAL_VORTEX_FINISH},  // 王蛇(コブラ x カード)
  {60, 45, 74, MATCH_BEST, READY_BEST_MATCH, 222, CRITICAL_VORTEX_FINISH},  // ガイ(サイ x カード)
  {61, 25, 74, MATCH_BEST, READY_BEST_MATCH, 223, CRITICAL_VORTEX_FINISH},  // タイガ(トラ x カード)
  {62, 27, 74, MATCH_BEST, READY_BEST_MATCH, 224, CRITICAL_VORTEX_FINISH},  // オーディン(フェニックス x カード)
  {63, 23, 74, MATCH_BEST, READY_BEST_MATCH, 225, CRITICAL_VORTEX_FINISH},  // ブレイド(カブトムシ x カード)
  {64, 94, 74, MATCH_BEST, READY_BEST_MATCH, 226, CRITICAL_VORTEX_FINISH},  // ギャレン(クワガタ x カード)
  {65, 19, 74, MATCH_BEST, READY_BEST_MATCH, 227, CRITICAL_VORTEX_FINISH},  // レンゲル(スパイダー x カード)
  {66, 16, 24, MATCH_BEST, READY_BEST_MATCH, 228, CRITICAL_VORTEX_FINISH}   // マッハ(バイク x カメラ)
};

uint8_t NUM_OF_BEST_MATCHES = sizeof(bestmatches) / sizeof(bestmatch);

//--------------------- トリプルベストマッチ定義 ---------------------//

struct bestmatch_triple{
 uint8_t  bestmatch_id;
 uint8_t  fullbottle_id_1;
 uint8_t  fullbottle_id_2;
 uint8_t  fullbottle_id_3;
 uint16_t sound_match_id;
 uint16_t sound_ready_id;
 uint16_t sound_change_id;
 uint16_t sound_critical_id;
};

struct bestmatch_triple bestmatch_triples[] = {
  {101, 87, 92, 94, MATCH_SUPER_BEST, READY_SCLASH, 251, CRITICAL_SCRASH}, // グリス(キャッスル x フクロウ x クワガタ)
  {102,  7, 85, 94, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 252, CRITICAL_OOO}, // ガタキリバ(タカ x メダル x クワガタ)
  {103,  7, 85, 62, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 253, CRITICAL_OOO}, // ラトラータ(タカ x メダル x ライオン)
  {104,  7, 85,  3, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 254, CRITICAL_OOO}, // ゴリラ (タカ x メダル x ゴリラ)
  {105,  7, 85, 11, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 255, CRITICAL_OOO}, // シャウタ (タカ x メダル x オクトパス)
  {106,  7, 85, 65, MATCH_SUPER_BEST, READY_SUPER_BESTMATCH, 256, CRITICAL_OOO}  // ブラカワニ (タカ x メダル x コブラ)
};

uint8_t NUM_OF_BEST_MATCH_TRIPLES = sizeof(bestmatch_triples) / sizeof(bestmatch_triple);

//------------------------------------------------------------------//

#define FULLBOTTLE_MAX 3
#define N_READER_PINS 3
#define FIX_COUNTER_MAX 100
#define RESET_COUNTER_MAX 100
#define TRIGGER_COUNTER_MAX 300
#define COLOR_COUNTER_MAX 80
#define COLOR_LEVELS 10

uint8_t before_reader_state[3]  = {OFF, OFF, OFF};
uint8_t current_reader_state[3] = {OFF, OFF, OFF};
uint8_t same_reader_state_count = 0;
uint8_t read_stage = 0;
boolean is_reading_1st_half = false;
boolean is_reading_2nd_half = false;
uint8_t read_values[4] = {0,0,0,0};
boolean is_read_finished = false;
boolean is_identified = false;
uint8_t reset_counter = 0;
uint8_t fullbottle_index = 0;
uint8_t fullbottle_ids[FULLBOTTLE_MAX] = {0,0,0};
uint8_t bestmatch_id = 0;

uint8_t before_eject_button_state = OFF;
uint8_t eject_button_state = OFF;
uint8_t before_trigger_button_state = OFF;
uint8_t trigger_button_state = OFF;
int trigger_counter = 0;
boolean is_long_trigger_on = false;
boolean is_change_ready = false;
uint8_t color_counter = 0;
uint8_t color_index = 0;

// Reader State
const uint8_t OFF_OFF_OFF[] = {OFF, OFF, OFF}; // 読み取り終了
const uint8_t ON_OFF_OFF[]  = {ON,  OFF, OFF}; // 前半グループの認識開始を兼ねる
const uint8_t OFF_ON_OFF[]  = {OFF,  ON, OFF}; 
const uint8_t OFF_OFF_ON[]  = {OFF, OFF,  ON}; // 後半グループの認識開始を兼ねる
const uint8_t ON_ON_OFF[]   = {ON,   ON, OFF};
const uint8_t ON_OFF_ON[]   = {ON,  OFF,  ON}; // このケースは発生し得ない
const uint8_t OFF_ON_ON[]   = {OFF,  ON,  ON};
const uint8_t ON_ON_ON[]    = {ON,   ON,  ON}; // このケースは無視する

void read_pins(uint8_t* reader_state){
  if(memcmp(reader_state, OFF_OFF_OFF, N_READER_PINS) == 0){
    ;
  }else if(memcmp(reader_state, ON_OFF_OFF, N_READER_PINS) == 0){
    if(is_reading_2nd_half){
      // 後半グループの値読み取り
      read_values[read_stage] = 0; // ピン配列[A,B]=[1,0]は0とみなす
      read_stage++;
    }
  }else if(memcmp(reader_state, OFF_ON_OFF, N_READER_PINS) == 0){
    if(is_reading_1st_half){
      read_values[read_stage] = 0; // ピン配列[B,C]=[1,0]は0とみなす
    }else if(is_reading_2nd_half){
      read_values[read_stage] = 1; // ピン配列[A,B]=[0,1]は1とみなす
    }
    read_stage++;
  }else if(memcmp(reader_state, OFF_OFF_ON, N_READER_PINS) == 0){
    if(is_reading_1st_half){
      read_values[read_stage] = 1; // ピン配列[B,C]=[0,1]は1とみなす
    }
    read_stage++;
  }else if(memcmp(reader_state, ON_ON_OFF, N_READER_PINS) == 0){
    if(is_reading_2nd_half){
      read_values[read_stage] = 2; // ピン配列[A,B]=[1,1]は2とみなす
    }
    read_stage++;
  }else if(memcmp(reader_state, ON_OFF_ON, N_READER_PINS) == 0){
    ;
  }else if(memcmp(reader_state, OFF_ON_ON, N_READER_PINS) == 0){
    if(is_reading_1st_half){
      read_values[read_stage] = 2; // ピン配列[B,C]=[1,1]は2とみなす
    }
    read_stage++;
  }else if(memcmp(reader_state, ON_ON_ON, N_READER_PINS) == 0){
    ;
  }  
}

uint8_t get_bestmatch_id(){
  uint8_t bestmatch_id = 0;
  for(int i=0;i<NUM_OF_BEST_MATCHES;i++){
    uint8_t first_id  = bestmatches[i].fullbottle_id_1;
    uint8_t second_id = bestmatches[i].fullbottle_id_2;
    if(  (fullbottle_ids[0] == first_id  && fullbottle_ids[1] == second_id)
      || (fullbottle_ids[0] == second_id && fullbottle_ids[1] == first_id) ){
       Serial.println(F("Best Match!"));
       bestmatch_id = i+1;
       break;
    }
  }
  return bestmatch_id;
}

void dim_led(uint8_t r, uint8_t g, uint8_t b){
    uint8_t dim_num = 10;
    uint8_t unit_r = r / dim_num;
    uint8_t unit_g = g / dim_num;
    uint8_t unit_b = b / dim_num;
    for(int i=dim_num;i>=0;i--){
      pixels.setPixelColor(0, pixels.Color(unit_r*i,unit_g*i,unit_b*i));
      pixels.show();
      delay(50);
    }
}

void wait_for_sound_stop(){
  delay(500); // 最低500msは待たせないと、whileループに入らないことがある
  while(digitalRead(SOUND_BUSY_PIN) == LOW){ // LOW ... 再生中
    delay(100);
  }  
}

void wait_for_sound_stop_with_lighting(struct fullbottle* bt1, struct fullbottle* bt2, struct fullbottle* bt3){
  uint8_t i = 0;
  uint8_t i_max = 0;
  if(bt2 != NULL){
    if(bt3 != NULL){
      i_max = 2;  
    }else{
      i_max = 1;
    }  
  }

  delay(1000); // 最低500msは待たせないと、whileループに入らないことがある
  while(digitalRead(SOUND_BUSY_PIN) == LOW){ // LOW ... 再生中
    switch(i){
    case 0: pixels.setPixelColor(0, pixels.Color(bt1->color_r, bt1->color_g, bt1->color_b)); break;
    case 1: pixels.setPixelColor(0, pixels.Color(bt2->color_r, bt2->color_g, bt2->color_b)); break;
    case 2: pixels.setPixelColor(0, pixels.Color(bt3->color_r, bt3->color_g, bt3->color_b)); break;
    }
    pixels.show();
    i++;
    if(i > i_max){
      i = 0;
    }
    delay(400);
  }  
}

void setup() {
  Serial.begin(115200);

  mySerial.begin (9600);
  if(!myDFPlayer.begin(mySerial)) {
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));
  myDFPlayer.setTimeOut(300); //Set serial communictaion time out 300ms
  myDFPlayer.volume(18);  //Set volume value (0~30).

  pinMode(READER_A_PIN, INPUT_PULLUP);
  pinMode(READER_B_PIN, INPUT_PULLUP);
  pinMode(READER_C_PIN, INPUT_PULLUP);
  pinMode(TRIGGER_PIN,  INPUT_PULLUP);
  pinMode(EJECT_PIN,    INPUT_PULLUP);
  pinMode(WHITE_LED_PIN, OUTPUT);
  pinMode(SOUND_BUSY_PIN, INPUT_PULLUP);

  pixels.begin();
  pixels.setPixelColor(0, pixels.Color(0,0,0));
  pixels.show();

  Serial.println(F("Fullbottle Reader Ready."));
  myDFPlayer.playMp3Folder(SYSTEM_POWER_ON); // 音声再生:起動音 
}

void loop() {

  ///////////////////////////// 認識ピン読み取りループ ///////////////////////////// 

  // 認識ピンの状態読み取り
  current_reader_state[0] = digitalRead(READER_A_PIN);
  current_reader_state[1] = digitalRead(READER_B_PIN);
  current_reader_state[2] = digitalRead(READER_C_PIN);

  if(memcmp(current_reader_state, OFF_OFF_OFF, N_READER_PINS) == 0){
    if(is_identified){
      // 認識完了後の読み取りリセット用カウント
      reset_counter++;
    }else{
      read_pins(before_reader_state); // 直前状態読み取り
    }   
  }else if(memcmp(current_reader_state, ON_OFF_OFF, N_READER_PINS) == 0){
    if(!is_reading_1st_half && !is_reading_2nd_half){
      // 前半グループの読み取りReady
      is_reading_1st_half = true;
      myDFPlayer.playMp3Folder(SYSTEM_BOTTLE_IN); // 音声再生:フルボトル装填音(少し早めにここで開始する)  
    }
  }else if(memcmp(current_reader_state, OFF_ON_OFF, N_READER_PINS) == 0){
    ;
  }else if(memcmp(current_reader_state, OFF_OFF_ON, N_READER_PINS) == 0){
    if(!is_reading_1st_half && !is_reading_2nd_half){
      // 後半グループの読み取りReady
      is_reading_2nd_half = true;
      myDFPlayer.playMp3Folder(SYSTEM_BOTTLE_IN); // 音声再生:フルボトル装填音(少し早めにここで開始する)  
    }
  }else if(memcmp(current_reader_state, ON_ON_OFF, N_READER_PINS) == 0){
    ;
  }else if(memcmp(current_reader_state, ON_OFF_ON, N_READER_PINS) == 0){
    ;
  }else if(memcmp(current_reader_state, OFF_ON_ON, N_READER_PINS) == 0){
    ;
  }else if(memcmp(current_reader_state, ON_ON_ON, N_READER_PINS) == 0){
    ;
  }

  // 最後の読み取り段階で、OFF_OFF_OFF以外で同じ読み取り状態がFIX_COUNTER_MAX回続いたら、それを最後の読み取り値とする
  if(!is_read_finished && read_stage == 3
      && (memcmp(before_reader_state, OFF_OFF_OFF, N_READER_PINS) != 0)
      && (memcmp(before_reader_state, current_reader_state, N_READER_PINS) == 0)){
    same_reader_state_count++;
  }
  if(!is_read_finished && same_reader_state_count == FIX_COUNTER_MAX){
    read_pins(current_reader_state); // 現在状態読み取り
    is_read_finished = true;
    same_reader_state_count = 0;
  }

  before_reader_state[0] = current_reader_state[0];
  before_reader_state[1] = current_reader_state[1];
  before_reader_state[2] = current_reader_state[2];

  ///////////////////////////// 認識ピン読み取り後のフルボトル特定 ///////////////////////////// 

  if(is_read_finished && !is_identified){
    digitalWrite(WHITE_LED_PIN, HIGH); 
    wait_for_sound_stop(); // フルボトル装填音の待ち
    //delay(1600); // フルボトル装填音の待ち(ここだけ微調整)

    // 4回分の読み取りが終わっていれば、最終的なフルボトルのIDを特定する
    uint8_t fullbottle_id = read_values[0]*27 + read_values[1]*9 + read_values[2]*3 + read_values[3] + 1;
    if(is_reading_2nd_half){
      fullbottle_id += 81;
    }
    is_identified = true; // 音声再生前に確定させる

    struct fullbottle bt;
    memcpy_P(&bt, &fullbottles[fullbottle_id-1], sizeof(fullbottle));

    Serial.print(F("Fullbottle ID: "));
    Serial.println(fullbottle_id);

    //wait_for_sound_stop(); // フルボトル装填音の待ち
    pixels.setPixelColor(0, pixels.Color(bt.color_r,bt.color_g,bt.color_b));
    pixels.show();
    myDFPlayer.playMp3Folder(bt.id); // 音声再生:各フルボトルの認識音声
    //wait_for_sound_stop_with_lighting(&bt,NULL,NULL);
    delay(1000);
    dim_led(bt.color_r,bt.color_g,bt.color_b);

    if((fullbottle_ids[0] != 0 || fullbottle_ids[1] != 0) && fullbottle_ids[2] == 0){
      fullbottle_index++;
    } 
    fullbottle_ids[fullbottle_index] = fullbottle_id;

    // 現在の認識状態
    Serial.println(fullbottle_ids[0]);
    Serial.println(fullbottle_ids[1]);
    Serial.println(fullbottle_ids[2]);

    wait_for_sound_stop();
    if(fullbottle_ids[0] != 0 && fullbottle_ids[1] != 0 && fullbottle_ids[2] != 0){
      ///////////////// 三連ベストマッチチェック /////////////////
      bestmatch_id = 0;
      for(int i=0;i<NUM_OF_BEST_MATCH_TRIPLES;i++){
        uint8_t first_id  = bestmatch_triples[i].fullbottle_id_1;
        uint8_t second_id = bestmatch_triples[i].fullbottle_id_2;
        uint8_t third_id  = bestmatch_triples[i].fullbottle_id_3;
        if(  (fullbottle_ids[0] == first_id   && fullbottle_ids[1] == second_id && fullbottle_ids[2] == third_id)
          || (fullbottle_ids[0] == first_id   && fullbottle_ids[1] == third_id  && fullbottle_ids[2] == second_id)
          || (fullbottle_ids[0] == second_id  && fullbottle_ids[1] == first_id  && fullbottle_ids[2] == third_id)
          || (fullbottle_ids[0] == second_id  && fullbottle_ids[1] == third_id  && fullbottle_ids[2] == first_id)
          || (fullbottle_ids[0] == third_id   && fullbottle_ids[1] == first_id  && fullbottle_ids[2] == second_id)
          || (fullbottle_ids[0] == third_id   && fullbottle_ids[1] == second_id && fullbottle_ids[2] == first_id)
          ){
           Serial.println(F("Triple Best Match!"));
           bestmatch_id = i+101;
           break;
        }
      }

      if(bestmatch_id > 100){
        myDFPlayer.playMp3Folder(bestmatch_triples[bestmatch_id-101].sound_match_id); // 音声再生:マッチ音声
        struct fullbottle bt1, bt2, bt3;
        memcpy_P(&bt1, &fullbottles[fullbottle_ids[0]-1], sizeof(fullbottle));
        memcpy_P(&bt2, &fullbottles[fullbottle_ids[1]-1], sizeof(fullbottle));
        memcpy_P(&bt3, &fullbottles[fullbottle_ids[2]-1], sizeof(fullbottle));
        pixels.setPixelColor(0, pixels.Color(bt1.color_r,bt1.color_g,bt1.color_b));
        pixels.show();
        delay(600);
        pixels.setPixelColor(0, pixels.Color(bt2.color_r,bt2.color_g,bt2.color_b));
        pixels.show();
        delay(600);
        dim_led(bt3.color_r,bt3.color_g,bt3.color_b);
        wait_for_sound_stop();
        myDFPlayer.playMp3Folder(bestmatch_triples[bestmatch_id-101].sound_ready_id); // 音声再生:変身待機音
      }else{
        myDFPlayer.playMp3Folder(READY_BEST_MATCH); // ベストマッチが成立していない場合は、トライアフォームとして待ち
      }
      is_change_ready = true;
    }else if(fullbottle_ids[0] != 0 && fullbottle_ids[1] != 0 && fullbottle_ids[2] == 0){
      ///////////////// ベストマッチチェック /////////////////
      bestmatch_id = get_bestmatch_id();

      if(bestmatch_id > 0){       
        myDFPlayer.playMp3Folder(bestmatches[bestmatch_id-1].sound_match_id); // 音声再生:マッチ音声
        struct fullbottle bt1, bt2;
        memcpy_P(&bt1, &fullbottles[fullbottle_ids[0]-1], sizeof(fullbottle));
        memcpy_P(&bt2, &fullbottles[fullbottle_ids[1]-1], sizeof(fullbottle));
        pixels.setPixelColor(0, pixels.Color(bt1.color_r,bt1.color_g,bt1.color_b));
        pixels.show();
        delay(600);
        dim_led(bt2.color_r,bt2.color_g,bt2.color_b);
        wait_for_sound_stop();
        myDFPlayer.playMp3Folder(bestmatches[bestmatch_id-1].sound_ready_id); // 音声再生:変身待機音
      }else{
        myDFPlayer.playMp3Folder(READY_BEST_MATCH); // ベストマッチが成立していない場合は、トライアフォームとして待ち
      }
      is_change_ready = true;
    }else if(fullbottle_ids[0] != 0 && fullbottle_ids[1] == 0 && fullbottle_ids[2] == 0){
      ///////////////// 単体成立チェック /////////////////
      if(bt.sound_ready_id != NONE){
        wait_for_sound_stop();
        myDFPlayer.playMp3Folder(bt.sound_ready_id); // 音声再生:待機音
      }

      is_change_ready = true;
    }    
  }

  ///////////////////////////// イジェクトボタンが押されたときの処理 ///////////////////////////// 

  eject_button_state = digitalRead(EJECT_PIN);
  if(before_eject_button_state == OFF && eject_button_state == ON){
    Serial.println(F("Eject ON."));
    digitalWrite(WHITE_LED_PIN, LOW); 
    if(is_identified){ // フルボトルがセットされている状態で押されたとき
      myDFPlayer.playMp3Folder(SYSTEM_BOTTLE_OUT); // 音声再生:フルボトル解除音
      if(fullbottle_ids[2] != 0){ // フルボトルが3個装填されている場合は、2個装填時のベストマッチ状態に戻す
        bestmatch_id  = get_bestmatch_id();
        fullbottle_ids[2] = 0;
        fullbottle_index--;
      }
      Serial.println(F("Fullbottle Eject."));      
    }else{ // フルボトルがセットされていない状態で押されたとき(*フルボトル装填時にも発生する)
      if(read_stage == 0){ // フルボトル装填中に発生させないための条件
        Serial.println(F("Fullbottle Reset."));
        if(fullbottle_ids[fullbottle_index] != 0){
          myDFPlayer.playMp3Folder(SYSTEM_BOTTLE_OUT); // 音声再生:フルボトル解除音
        }else{
          myDFPlayer.playMp3Folder(SYSTEM_BOTTLE_EMPTY); // 音声再生:空音
        }
        bestmatch_id = 0; // ベストマッチ解除
        fullbottle_ids[fullbottle_index] = 0;
        if(fullbottle_index > 0){
          fullbottle_index--;  
        }
      }
    }
  }
  before_eject_button_state = eject_button_state;

  ///////////////////////////// トリガーボタンが押されたときの処理 ///////////////////////////// 

  trigger_button_state = digitalRead(TRIGGER_PIN);
  if(before_trigger_button_state == OFF && trigger_button_state == ON){
    Serial.println(F("Trigger ON."));
    if(is_change_ready){ // 変身待機中のとき
      struct fullbottle bt1;
      memcpy_P(&bt1, &fullbottles[fullbottle_ids[0]-1], sizeof(fullbottle));

      if(bestmatch_id > 100){ // 三連ベストマッチのとき
        pixels.setPixelColor(0, pixels.Color(bt1.color_r,bt1.color_g,bt1.color_b));
        pixels.show();
        myDFPlayer.playMp3Folder(bestmatch_triples[bestmatch_id-101].sound_change_id); // 音声再生:変身音
      }else if(bestmatch_id > 0){ // 二連ベストマッチのとき
        struct fullbottle bt2;
        memcpy_P(&bt2, &fullbottles[fullbottle_ids[1]-1], sizeof(fullbottle));
        if(bestmatch_id < 50){ // ビルドドライバー組み込みのベストマッチのとき
          myDFPlayer.playMp3Folder(CHANGE_START); // 音声再生:変身開始〜Are you ready?        
          wait_for_sound_stop_with_lighting(&bt1,&bt2,NULL);
        }
        myDFPlayer.playMp3Folder(bestmatches[bestmatch_id-1].sound_change_id); // 音声再生:ベストマッチ音声
      }else if(fullbottle_ids[0] != 0 && fullbottle_ids[1] != 0){ // 二連、三連でベストマッチ以外のとき
        struct fullbottle bt2;
        memcpy_P(&bt2, &fullbottles[fullbottle_ids[1]-1], sizeof(fullbottle));
        myDFPlayer.playMp3Folder(CHANGE_START); // 音声再生:変身開始〜Are you ready?
        if(fullbottle_ids[2] != 0){
          struct fullbottle bt3;
          memcpy_P(&bt3, &fullbottles[fullbottle_ids[2]-1], sizeof(fullbottle));
          wait_for_sound_stop_with_lighting(&bt1,&bt2,&bt3);
        }else{        
          wait_for_sound_stop_with_lighting(&bt1,&bt2,NULL);
        }
        myDFPlayer.playMp3Folder(CHANGE_TRIAL); // 音声再生:トライアル変身
      }else{ // 単体のとき
        if(bt1.sound_change_id == CHANGE_TRIAL){
          myDFPlayer.playMp3Folder(CHANGE_START); // 音声再生:変身開始〜Are you ready?
          wait_for_sound_stop_with_lighting(&bt1,NULL,NULL);
        }else{
          pixels.setPixelColor(0, pixels.Color(bt1.color_r,bt1.color_g,bt1.color_b));
          pixels.show();
        }
        myDFPlayer.playMp3Folder(bt1.sound_change_id); // 音声再生:変身音
      }
      wait_for_sound_stop_with_lighting(&bt1,NULL,NULL);
      dim_led(bt1.color_r,bt1.color_g,bt1.color_b);
      is_change_ready = false;
    }else{ // フルボトル装填前、または変身後
      myDFPlayer.playMp3Folder(ATTACK_NORMAL); // 音声再生:銃撃音
      if(fullbottle_ids[fullbottle_index] != 0){
        struct fullbottle bt;
        memcpy_P(&bt, &fullbottles[fullbottle_ids[fullbottle_index]-1], sizeof(fullbottle));
        dim_led(bt.color_r,bt.color_g,bt.color_b);
      }else{
        dim_led(255,255,255);
      }
    }
  }
  before_trigger_button_state = trigger_button_state;

  ///////////////////////////// トリガーボタンが長押しされたときの処理 ///////////////////////////// 

  if(trigger_button_state == ON){
    trigger_counter++;
    if((trigger_counter > TRIGGER_COUNTER_MAX)  && !is_long_trigger_on){
      Serial.println(F("Trigger long ON."));
      if(fullbottle_ids[0] != 0){
        struct fullbottle bt1;
        memcpy_P(&bt1, &fullbottles[fullbottle_ids[0]-1], sizeof(fullbottle));
        pixels.setPixelColor(0, pixels.Color(bt1.color_r,bt1.color_g,bt1.color_b));
        pixels.show();
        if(bestmatch_id > 100){ // 三連ベストマッチのとき
          myDFPlayer.playMp3Folder(bestmatch_triples[bestmatch_id-101].sound_critical_id); // 音声再生:必殺技音
        }else if(bestmatch_id > 0){ // 二連ベストマッチのとき
          myDFPlayer.playMp3Folder(bestmatches[bestmatch_id-1].sound_critical_id); // 音声再生:必殺技音
        }else if(fullbottle_ids[0] != 0 && fullbottle_ids[1] != 0){ // 二連、三連でベストマッチ以外のとき
          myDFPlayer.playMp3Folder(CRITICAL_VORTEX_ATTACK); // 音声再生:必殺技音
        }else{ // 上記以外
          myDFPlayer.playMp3Folder(bt1.sound_critical_id); // 音声再生:必殺技音
        }
        wait_for_sound_stop_with_lighting(&bt1,NULL,NULL);
        dim_led(bt1.color_r,bt1.color_g,bt1.color_b);
      }
      trigger_counter = 0; // カウントリセット
      is_long_trigger_on = true;
    }
  }else{
    trigger_counter = 0; // カウントリセット
    is_long_trigger_on = false;
  }

  ///////////////////////////// フルカラーLED処理 ///////////////////////////// 

  if(is_change_ready){ // 変身待機中のみ発光
    color_counter++;
    if(color_counter == COLOR_COUNTER_MAX){
      color_index++;
      color_counter = 0;
    }

    if(fullbottle_ids[0] != 0 && fullbottle_ids[1] != 0){
      if((fullbottle_ids[2] == 0 && color_index == 2) || // 二個装填状態
         (color_index == 3)){  // 三個装填状態
        color_index = 0;
      }
      struct fullbottle bt;   
      memcpy_P(&bt, &fullbottles[fullbottle_ids[color_index]-1], sizeof(fullbottle)); 
      pixels.setPixelColor(0, pixels.Color(bt.color_r,bt.color_g,bt.color_b));
      pixels.show();
    }else{ // 単体変身のとき
      if(color_index == 2){
        color_index = 0;
      }
      struct fullbottle bt;   
      memcpy_P(&bt, &fullbottles[fullbottle_ids[0]-1], sizeof(fullbottle)); 
      if(bt.sound_ready_id != NONE){ // 単体変身サウンドがあるときのみ発光させる
        if(color_index == 0){
          pixels.setPixelColor(0, pixels.Color(bt.color_r,bt.color_g,bt.color_b));
        }else{
          uint8_t level = color_counter / (COLOR_COUNTER_MAX/COLOR_LEVELS);
          pixels.setPixelColor(0, pixels.
            Color(bt.color_r/COLOR_LEVELS*(COLOR_LEVELS-level),bt.color_g/COLOR_LEVELS*(COLOR_LEVELS-level),bt.color_b/COLOR_LEVELS*(COLOR_LEVELS-level)));
        }
        pixels.show();
      }  
    }
  }

  ///////////////////////////// フルボトル再読み込みのための初期化処理 ///////////////////////////// 

  if(reset_counter >= RESET_COUNTER_MAX){
    // 読み取り値の初期化
    for(int i=0;i<4;i++){
      read_values[i] = 0;
    }
    // 各種フラグ・カウンタのリセット
    reset_counter = 0;
    is_reading_1st_half = false;
    is_reading_2nd_half = false;
    read_stage = 0;
    is_read_finished = false;
    is_identified = false;
    is_change_ready = false;
    color_counter = 0;
    color_index   = 0;
    pixels.setPixelColor(0, pixels.Color(0,0,0));
    pixels.show();
    Serial.println(F("Fullbottle Reader Ready."));
  }

  delay(5);
}

「オリジナルベストマッチをつくる 〜トランスチームガン・カスタム〜」への8件のフィードバック

  1. ( ゚д゚) ・・・
     
    (つд⊂)ゴシゴシ
     
    (;゚д゚) ・・・
     
    (つд⊂)ゴシゴシゴシ
      _, ._
    (;゚ Д゚) …!?

    すげー!!
    以前 3進数の話をされてた時、
    もしやと思いましたが、、
    まさか こういった形にまで昇華されるとは!!!
    (∩´∀`)∩<素晴らしいっ!!
    普段 こう言う事は言わないのですが、
    マジで欲しいですわwww

    1. クレーンの丈様

      お褒めいただきありがとうございます◎ 同業者(?)の方に褒められるとまた一段と嬉しいです(笑)。でもやっぱりそもそもの動機としては、丈様の解析結果があってこそのものです。私は既にあるモノを組み合わせての応用はそこそこ得意な方だと思うのですが、基礎的なところをきちんとやりきるのが苦手なので、あの解析結果は本当にすごいと思います。ありがとうございます。

  2. もしやと思いきや3連ベストマッチまで成されていたとは・・・
    さすがです!
    またもパクらせていただきます(
    ラビラビなどの効果音のみを使えば完全オリジナルベストマッチまで作成可能なのは心が踊りますねー

    (わざとかもれませんがゲンムのところゲームの誤字かと

    1. 帝都の快男児 様

      ありがとうございます◎ ゲンムのところはゲームの誤字じゃなくて、本当にゲンムなんですよー。新たにゲンムのフルボトルを定義しているのでゲンムなのです。YouTubeの方の動画の5:20あたりから登場します。

      1. これは失敬・・・
        動画の方も拝見させてもらいましたが、これ想像以上にめちゃくちゃ楽しいですね!!!!

        1. はい、作った身としてはとても楽しいのですが、ガシャットと違って見た目のインパクトがないのでなかなか楽しさが伝わりません(笑)

  3. すげー
    私が悩んだところが簡単にクリアされてる( Д ) ゜゜ポーン
    DFPlayerのBUSYピンで再生監視は知ってたんですけど音声が短すぎて
    上手く信号拾えなかったのでdelayだけで処理したんですよね。
    それだとどうしても大量の音声を綺麗に繋げられなくて妥協していたんですが。。
    早速パクッ・・いや参考にさせていただきます。

    1. かっぱ次郎様

      DFPlayerのBUSYピン、確かに癖がありますねー。BUSYピン監視でループ待ちをしても、再生命令直後にBUSYピンを見にいくと「まだ再生してなからBUSYじゃないよ」となってループ待ちをスルーしてしまうという。。。この問題を回避するために、wait_for_sound_stopとかwait_for_sound_stop_with_lightingの冒頭でdelay(500)とかdelay(1000)を入れているんですが、やっぱりdelay(1000)とかにしとかないと、ループ待ちをスルーすることがありますね。

      今回の工作で一番悔しいところは、DFPlayerの反応が鈍過ぎて、フルボトルの認識後の音声再生とかトリガー押した後の銃撃音が遅れて聞こえてくるというところです。Class10のSDカードを使ってもダメでした。DFPlayerはMP3が簡単に扱えるだけでも十分にありがたいモジュールなのですが、もう少し値が張ってもよいので、応答速度の早いMP3モジュールが欲しいなあと思う次第です。

帝都の快男児 にコメントする コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>