基礎知識
戦闘関係
わかりやすく「セレクトバグ」というタイトルにしたが、このページでは、戦闘において「Xボタンを押すとひとつ前の技を出す」機能によるバグっぽい挙動は、プログラム上どんなことが起きているのか? という話を紹介する。
「Xボタンを押すとひとつ前の技を出す」機能におけるバグは幾つかある。
一番有名なのは、功夫編のラストバトルで、一度しか出せないはずの「旋牙連山拳」が、セレクトボタンとXボタンを同時押しすると何度でも出せてしまう、というネタだろう。
Xボタンではワンタッチで、ひとつ前に出した技を出せるが、本来、Xボタンを押した時にひとつ前の技が出せない状況の時(功夫編ラストバトルのように一度しか使えないフラグがあったり、酔い・毒・腕かため・足かためのように一部技が出せない状態異常になった場合など)、エラー音が鳴ってひとつ前の技が出せない。
ところがセレクトボタンを同時押しすると出せてしまう。
ここから推測できるのは、セレクトボタンを押すことで、本来なら該当の技を出せないフラグが立っていても、それを無視することができる何かしらの処理が行われている、ということである。
他に「Xボタンを押すとひとつ前の技を出す」機能のバグとして、本来ならそのキャラが出せない技が出せてしまうというバグもある。
ブリキ大王を操作する際、セレクトボタンとXボタンを同時押しすると、ブリキ大王が使えないはずの技が出せてしまう。
また、最終編オルステッド主人公時は各シナリオのラストボスを操作するが、他のボスが出した技を、セレクトボタンとXボタンを同時押しで次のボスでも出すことができてしまう。
当ページではこれらの現象の理由を説明しているが、はっきり言って長いので、理由をざっくりとまとめておく。
(まとめも長くなってしまったのは申し訳ない)
以上が大雑把な説明だが、興味があればこれ以降の長ったらしい説明もお読みいただきたい。
また、説明のために必要な、戦闘などで使用される敵味方キャラのデータについて、最初に説明している。
戦闘などで使用される各種データについて、改めて説明する。
味方キャラのデータ、敵キャラのデータ、技データなどは、メモリのアドレスの$D5:00C0以降にまとめて入っており、戦闘では必要なデータをここから読みこんでいる。
$D5:00C0~のデータは、味方キャラの初期レベルなどの初期値、習得可能な技、レベルアップ時の各ステータスの上昇値、無効状態異常など、ゲーム内で変動することがない基礎値が入っているため、読み取り専用のデータということになる。
実際のアドレスは、
| データ | アドレス |
|---|---|
| 味方キャラデータ | $D5:00C0~$D5:04CA |
| 敵キャラデータ | $D5:04CB~$D5:24CA |
| 味方技データ | $D5:24CB~$D5:3DB1 |
| 敵技データ | $D5:3DB2~$D5:56CA |
である(この後も続くが省略)。
味方キャラのデータと敵キャラのデータが分かれているように、技のデータも味方と敵とで分かれている、という点に注意。
なお、ブリキ大王は敵キャラとしてデータが登録されているため、ブリキ大王のステータスは敵キャラデータ内に、ブリキ大王の技データは敵技データ内に入っている。
なぜ敵データなのかだが、一番の理由はブリキ大王のサイズが縦4マス、横2マスだからであろう。
味方キャラのデータには、キャラのサイズを保管するためのアドレスがない。ブリキ大王以外は全キャラ1×1マスで統一されているため、サイズを記録する必要がないのである。
そこでブリキ大王の場合、敵データに登録するしかなかったのではないか、と思う(あくまでも推測)。
味方キャラはレベルアップなどで成長し、レベルや各ステータスが変動していく。武器や防具を装備することでもステータスが変動する。
レベルアップしないキューブやタロイモも、強化パーツでHPが増えるので変動する扱いだし、心山拳老師も装備を変更可能なのでやはり変動する扱いになる。
習得した技も記録しておく必要がある。現代編など、レベルアップではない方法で技を習得できるシナリオがあるためである。
(ブリキ大王は成長せず、装備もできないのでステータス変動もなく、敵キャラデータに登録していても問題ないことになる)
こういった変動する値は、セーブの時に保存しておかなければ、セーブデータをロードして続きをプレイできない(初期値に戻されてしまう)。
これらセーブ時に保存されるデータは、経験値計算で紹介した、「シナリオ別キャラデータ」として、ゲームプレイ中は$00:0D00~$00:0FBFに入っている。
「シナリオ別キャラデータ」のデータがロードされるアドレス一覧表をもう一度掲載しておく。
| メモリ | 原始 | SF | 功夫 | 西部 | 現代 | 近未来 | 幕末 | 中世 | 最終 |
|---|---|---|---|---|---|---|---|---|---|
$00:0D00~ | - | - | - | - | - | - | - | オルステッド | オルステッド |
$00:0D40~ | ポゴ | - | - | - | - | - | - | - | ポゴ |
$00:0D80~ | - | キャプテン スクウェア /キューブ | - | - | - | - | - | - | キューブ |
$00:0DC0~ | - | - | 心山拳老師 | - | - | - | - | - | 心山拳師範 |
$00:0E00~ | - | - | - | サンダウン | - | - | - | - | サンダウン |
$00:0E40~ | - | - | - | - | 高原 日勝 | - | - | - | 高原 日勝 |
$00:0E80~ | - | - | - | - | - | アキラ | - | - | アキラ |
$00:0EC0~ | - | - | - | - | - | - | おぼろ丸 | - | おぼろ丸 |
$00:0F00~ | ゴリ | - | ユン・ジョウ | マッドドッグ | - | タロイモ | とらわれの男 | ストレイボウ | - |
$00:0F40~ | べる | - | レイ・クウゴ | - | - | 無法松 | カラクリ丸 | ウラヌス | - |
$00:0F80~ | ざき | - | サモ・ハッカ | - | - | ブリキ大王 | - | ハッシュ | ブリキ大王 |
各シナリオの主人公8キャラ+各シナリオのみ登場の3キャラの合計11キャラ分のアドレスが、「シナリオ別キャラデータ」の領域になっている。
SF編はラストバトル直前までキャプテンスクウェアのデータが入っているが、ラストバトル前にキューブのデータに上書きされて、そのまま最終編にも使用される。
$00:0DC0~は、功夫編は心山拳老師のデータ、最終編は心山拳師範のデータになる。
各シナリオキャラ用の$00:0F00~、$00:0F40~、$00:0F80~だが、ゲームを開始した時点では、とらわれの男、カラクリ丸、ブリキ大王の初期値が入っていて、各シナリオを開始するかセーブデータをロードした時点で、各シナリオに対応したデータに変更される仕組みである。
「シナリオ別キャラデータ」には、Xボタンで出せる「ひとつ前に出した技」の技IDも収納されている、というのがポイントである。
戦闘中のみ、味方キャラ(最大4キャラ分)のデータの計算に使われるアドレスもある。
(敵キャラ用のアドレスもあるが、ここでは割愛する)
なお、筆者はどれが何なのか全て把握はしていないので、わかる範囲のみ記す。
| 味方キャラ | 各種ステータス | 技データ00~34 | 技ID& 関連ステータス | 状態異常関係 |
|---|---|---|---|---|
| 1 | $00:1C00~$00:1C3F | $00:9010~$00:9028 | $00:E900~ | $00:EA00~ |
| 2 | $00:1C40~$00:1C7F | $00:9050~$00:9068 | $00:E940~ | $00:EA40~ |
| 3 | $00:1C80~$00:1CBF | $00:9090~$00:90A8 | $00:E980~ | $00:EA80~ |
| 4 | $00:1CC0~$00:1CFF | $00:90D0~$00:90E8 | $00:E9C0~ | $00:EAC0~ |
この中にも「ひとつ前に出した技」のIDを収納するアドレスがあるが、「シナリオ別キャラデータ」からロードして使用している。
また、シナリオ別キャラデータもそうだが、$00:????~のアドレスのデータは、$7E:????~でミラーリングされているため、どちらでも同じ値である。
以上、戦闘で使うキャラクターたちの各種データは、
だいたい上の3種類に分かれていて、ゲーム内で必要な時に読み書きされる。
あくまでも敵味方キャラに関するデータだけで、実際にはフィールドダメージやマスの移動、行動順など、戦闘に必要なパラメータは他にもたくさんあるが、「ひとつ前の技を出す機能」の話には関係がないので省く。
以上のステータス関連の知識を前提として、「ひとつ前の技を出す機能」の説明に移る。
「ひとつ前の技を出す機能」は文字通りに、該当キャラがひとつ前に使った技をXボタンで続けて使うことができる機能である。
セーブデータにも「ひとつ前に使った技」が記録されているので、セーブデータをロードして開始した時にもXボタンでひとつ前の技を出すことが可能である。
Xボタンを押すと、技一覧画面を飛ばし、該当の技の範囲が表示されて、技を出す対象を選ぶことができるようになっている。
「ひとつ前に使った技」は、上でも述べた通り、「シナリオ別キャラデータ」の中に記録されていて、戦闘を行う前にロードされ、戦闘終了時には最後に使った技が上書きされることになる。
「ひとつ前に使った技」が記録されるアドレスは、「シナリオ別キャラデータ」の$0C番目に当たる。
例えば近未来編なら、アキラの「ひとつ前に使った技」は[$00:0E8C]、タロイモの「ひとつ前に使った技」は[$00:0F0C]、無法松の「ひとつ前に使った技」は[$00:0F4C]、ブリキ大王の「ひとつ前に使った技」は[$00:0F8C]に入ることになる。
「ひとつ前に使った技」に入る値は$00~$FFまでの16進数2桁の数値だが、これは「技ID」に当たる。
技ID一覧は世界の合言葉は森部様の技一覧ページにあるので見ていただきたいのだが、技IDには「味方技ID」と「敵技ID」の2種類があり、それぞれ$00~$FFの256種類、合計で512種類の技がある。
また、上で説明した通り、ゲームプレイ中は、
$D5:24CB~$D5:3DB1$D5:3DB2~$D5:56CAに、それぞれの技の基礎値データが入っている。
実際にはどちらのID$00にも、何の技も入っておらず、データが$00で埋まっているが、後述のとおりにこれにも意味はある。
味方技IDなのに、実際のゲームでは敵しか使わない技も含まれているが、そこは開発中の色々な都合だろう。
ブリキ大王は敵キャラに登録されているため、ブリキ大王の技4種類は、すべて敵技データの中に入っている。
味方キャラの場合は「味方技ID」が「ひとつ前に使った技」のアドレスに入ることになり、次に使う時に、「味方技ID」一覧から該当のIDを探して使用する、という処理が行われる。
ブリキ大王や、最終編オルステッド主人公時だと、敵キャラを操作しているという判定が入り、「ひとつ前に使った技」に入る技IDを「敵技ID」と判断し、次に使う時に「敵技ID」一覧から該当のIDを探して使用する、という処理が行われる。
戦闘に入ると、「シナリオ別キャラデータ」の「ひとつ前に使った技」のアドレスを読み込むことになるが、「ひとつ前の技」がない状況、つまり、シナリオを新たに開始した時には、「シナリオ別キャラデータ」の中の「ひとつ前に使った技」のデータはどうなっているのか。
答えを言ってしまうと、シナリオ開始時には$00が入っている。
技IDは上の通りに$00~$FFの256種類なのだが、技$00には何も登録されておらず空っぽである($00でデータが埋まっている)
このため、戦闘突入時に、「ひとつ前に使った技」のアドレス内データが$00だと、「ひとつ前に使った技」はない、と判断されて、該当キャラが最初から覚えている技、つまり技一覧の左上にある最初の技のIDを入れる処理を行う。
よって、一番最初の戦闘であっても、Xボタンを押した時に変なエラーが出たりすることなく、一番最初の技を出すことが可能である。
実際に試してみればわかるだろう。
「ひとつ前に使った技」に何が入っているか、プログラム上での処理は以下のようになっている。
戦闘開始時に処理されるサブルーチンである。
重要な部分だけ抜粋した。
$C1/E94E LDY $0002,x ;Yに[$0002,x]をロード $C1/E951 LDX $0004,y ;Xに[$0004,y]をロード (中略) $C1/E965 LDA $D50005,x ;Aに[$D50005,x]をロード $C1/E969 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み (中略) $C1/E9A6 LDA $12 [$00:0312] ;Aに[$00:0312]をロード $C1/E9A8 STA $7ECD00,x ;Aを[$7ECD00,x]に書き込み (中略) $C1/E9F0 LDA $000C,y ;Aに[$000C,y]をロード $C1/E9F3 BNE $07 [$E9FC] ;ゼロフラグが立っていないとき[$E9FC]分岐 $C1/E9F5 LDA $7ECD00,x ;Aに[$7ECD00,x]をロード $C1/E9F9 STA $000C,y ;Aを[$000C,y]に書き込み $C1/E9FC RTS ;サブルーチン戻り
[$0002,x]:「シナリオ別キャラデータ」のメモリ開始位置[$0004,y]:$D5:00C0~以降の味方キャラの基礎データを呼び出すための数値[$D50005,x]:$D50005 +「$D5:00C0~以降の味方キャラの基礎データを呼び出すための数値」(該当キャラの最初の技ID)[$000C,y]:「シナリオ別キャラデータ」の「ひとつ前の技」最後のあたりの$C1/E9F0で、「シナリオ別キャラデータ」の「ひとつ前の技」をロードし、「ひとつ前の技」に$00が入っているかどうか、つまりゼロフラグが立っているかどうかで分岐している。
「ひとつ前の技」がない、つまり$00が入っていたら、これ以前で算出した[$7ECD00,x]を書き込んでいる。
[$7ECD00,x]は、「シナリオ別キャラデータ」に入っている「$D5:00C0~以降の味方キャラの基礎データを呼び出すための数値」(詳細は後述)を使い、味方キャラデータの中から読み出した、該当キャラの技の1番目のIDである。
長くてややこしいので、ざっくりまとめると、
「ひとつ前の技」がない場合は、味方キャラデータの技一覧から、最初の技のIDを取り出して、「ひとつ前の技」に登録した。
こういうことになる。
以上の処理で、通常は何の問題も起こらない。
戦闘中に味方にコマンドが回ってきた時、Xボタンを押すと、「ひとつ前に使った技」を使うことが可能かどうかをまず判定することになる。
判定内容は以下の通り。
以上の3つすべてに合致していない時は、Xボタンを押してもエラー音がして、技一覧が開き、「ひとつ前に使った技」は出ない。
この処理のサブルーチンは以下のようになっている。
$C1/9D38 LDA $4D [$00:034D] ;Aに[$00:034D]をロード $C1/9D3A BIT #$20 ;Aと$20で論理積(ステータスフラグの変更のみ) $C1/9D3C BEQ $08 [$9D46] ;ゼロフラグが立っているとき[$9D46]分岐 ; $C1/9D46 LDX $5A [$00:035A] ;Xに[$00:035A](キャラの順序)をロード $C1/9D48 LDY $0002,x ;Yに[$0002,x]をロード $C1/9D4B LDA $000C,y ;Aに[$000C,y]をロード $C1/9D4E STA $0014,x ;Aを[$0014,x]に書き込み $C1/9D51 BEQ $13 [$9D66] ;ゼロフラグが立っているとき[$9D66]分岐 ; $C1/9D53 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/9D55 LDA #$10 ;Aに$10をロード $C1/9D57 STA $08 [$00:0308] ;A($10)を[$00:0308]に書き込み ; ;技1番目から16番目までループ処理判定 $C1/9D59 LDA $7ECD00,x ;Aに[$7ECD00,x](戦闘時の技n番目の技ID)をロード $C1/9D5D CMP $10 [$00:0310] ;Aと[$00:0310](「前に出した技」の技ID)を減算比較 $C1/9D5F BEQ $08 [$9D69] ;ゼロフラグが立っているとき[$9D69]分岐 $C1/9D61 INX ;Xをインクリメント(+1)、次の技の判定のため番号を+1 $C1/9D62 DEC $08 [$00:0308] ;[$00:0308] - 1 $C1/9D64 BNE $F3 [$9D59] ;ゼロフラグが立っていないとき[$9D59]分岐 ;ループ処理判定ここまで ; $C1/9D66 LDA #$FF ;Aに$FFを書き込み $C1/9D68 RTS ;サブルーチン戻り ; ;「前に出した技」が習得技と一致時の飛び先 $C1/9D69 LDA $7ECD00,x ;Aに[$7ECD00,x]をロード $C1/9D6D BEQ $F7 [$9D66] ;ゼロフラグが立っているとき[$9D66]分岐 $C1/9D6F LDA $7ECD30,x ;Aに[$7ECD30,x]をロード $C1/9D73 STA $11 [$00:0311] ;Aを[$00:0311]に書き込み $C1/9D75 LDA $7ECD10,x ;Aに[$7ECD10,x](技の使用不能状態異常)をロード $C1/9D79 LDX $5A [$00:035A] ;Xに[$00:035A](キャラの順序)をロード $C1/9D7B AND $0039,x ;Aと[$0039,x]で論理積 $C1/9D7E BNE $E6 [$9D66] ;ゼロフラグが立っていないとき[$9D66]分岐 $C1/9D80 LDA $11 [$00:0311] ;Aに[$00:0311](=[$7ECD30,x])をロード $C1/9D82 AND #$01 ;Aと$01で論理積 $C1/9D84 BNE $E0 [$9D66] ;ゼロフラグが立っていないとき[$9D66]分岐 $C1/9D86 RTS ;サブルーチン戻り ; $C1/9D13 BNE $11 [$9D26] ;ゼロフラグが立っていないとき[$9D26]分岐 ;「前に出した技」が出る処理 $C1/9D15 LDA #$00 ;Aに$00をロード $C1/9D17 STA $0001,y ;Aを[$0001,y]に書き込み $C1/9D1A LDA $000C,y ;Aに[$000C,y]をロード $C1/9D1D STA $0014,x ;Aを[$0014,x]に書き込み $C1/9D20 JSR $11E4 [$C1:11E4] ;[$C1:11E4]へ ;「前に出した技」が出ない処理 $C1/9D26 LDA #$00 ;Aに$00をロード $C1/9D28 STA $0001,y ;Aを[$0001,y]に書き込み $C1/9D2B JSR $8A60 [$C1:8A60] ;[$C1:8A60]へ
[$0002,x]:シナリオ別キャラデータ開始アドレス[$000C,y]:シナリオ別キャラデータ内の「前に出した技」[$0014,x]:戦闘中の味方キャラデータ内の「前に出した技」[$7ECD00,x]:戦闘時の技n番目の技ID[$7ECD30,x]:反撃専用だと$01[$7ECD10,x]:技の使用不能状態異常[$0039,x]:キャラの状態異常の状態ざっと説明していく。
$C1/9D38 LDA $4D [$00:034D] ;Aに[$00:034D]をロード $C1/9D3A BIT #$20 ;Aと$20で論理積(ステータスフラグの変更のみ) $C1/9D3C BEQ $08 [$9D46] ;ゼロフラグが立っているとき[$9D46]分岐
この3行の処理は、後々のセレクトバグの説明に関わってくるので、ここで紹介しておく。
最初に[$00:034D]をロードし、この値と$20で論理積を取っている。
通常、Xボタンを押しただけなら、[$00:034D]には$00が入っている。
だが、セレクトボタンを同時に押していると$20が入る。
つまりここで、「Xボタンを押した時に同時にセレクトボタンを押していたかどうか」を論理積を取ることで判定しているのである。
(なお、本作は1フレームに1回、コントローラーの何のボタンが押されているかを判定するサブルーチンが動いている。戦闘中は[$00:034?]あたりにボタン判定が記録されていくようだが、今回は詳しい調査はしていないため省く)
ひとまずは、$00が入っている時、つまり通常の処理の続きを見てみる。
$C1/9D46 LDX $5A [$00:035A] ;Xに[$00:035A](キャラの順序)をロード $C1/9D48 LDY $0002,x ;Yに[$0002,x]をロード $C1/9D4B LDA $000C,y ;Aに[$000C,y]をロード $C1/9D4E STA $0014,x ;Aを[$0014,x]に書き込み $C1/9D51 BEQ $13 [$9D66] ;ゼロフラグが立っているとき[$9D66]分岐
$C1/9D46~$C1/9D48は、「シナリオ別キャラデータ」を呼び出すためのアドレスの場所をロードしている。
$C1/9D4Bで、シナリオ別キャラデータ内の「ひとつ前に出した技」の値、つまり技IDをロードしている。
この時点で戦闘開始時の、『「ひとつ前の技」がない場合は、味方キャラデータの技一覧から、最初の技のIDを取り出して、「ひとつ前の技」に登録』は終了しているので、技IDは$01以上の何かの数値が入っているはずである。
シナリオ別キャラデータ内の「ひとつ前に出した技」は、戦闘中の味方キャラデータ内の「ひとつ前に出した技」のメモリに書き込みされているが、その後に「ゼロフラグが立っているとき[$9D66]分岐」という分岐があるので、念の為に$00が入っていた場合の分岐を行っているようである。
また、飛び先の[$9D66]は、「ひとつ前に出した技」が出ない処理に繋がる。
$C1/9D53 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/9D55 LDA #$10 ;Aに$10をロード $C1/9D57 STA $08 [$00:0308] ;A($10)を[$00:0308]に書き込み ; ;技1番目から16番目までループ処理判定 $C1/9D59 LDA $7ECD00,x ;Aに[$7ECD00,x]をロード $C1/9D5D CMP $10 [$00:0310] ;Aと[$00:0310]を減算比較 $C1/9D5F BEQ $08 [$9D69] ;ゼロフラグが立っているとき[$9D69]分岐(「前に出した技」が習得技と一致) $C1/9D61 INX ;Xをインクリメント(+1)、次の技の判定のため番号を+1 $C1/9D62 DEC $08 [$00:0308] ;[$00:0308] - 1 $C1/9D64 BNE $F3 [$9D59] ;ゼロフラグが立っていないとき[$9D59]分岐 ;ループ処理判定ここまで ; $C1/9D66 LDA #$FF ;Aに$FFを書き込み $C1/9D68 RTS ;サブルーチン戻り
ここは途中でループ処理が入っている。
「ひとつ前に出した技」の技IDが、習得済みの技IDと一致しているかどうか、習得済みの技を1つ1つチェックするためのループ処理である。
「ひとつ前に出した技」の技IDは[$00:0310]に書き込まれ、[$7E:CD00]に入っている習得済みの技の1番目、[$7E:CD01]に入っている習得済みの技の2番目、……、[$7E:CD0F]に入っている習得済みの技の16番目、と、$C1/9D57で[$00:0308]に書き込まれた$10回(10進数16回)分、[$00:0310]との差をチェックしている。味方キャラが習得できる技は最大16種類だから16回分である。
[$7E:CD??](習得済み技ID)から[$00:0310]を引いた値がゼロなら、技が一致したということになるので、ゼロフラグが立ちループを抜けて[$9D69]へジャンプする。
16回ループしても一致しない場合、「ひとつ前に出した技」に入っている技IDは、該当キャラが習得していない技であるから、Xボタンを押しても技が出ないことになり、この場合は$C1/9D66でAに$FFを書き込む。
;「前に出した技」が習得技と一致時の飛び先 $C1/9D69 LDA $7ECD00,x ;Aに[$7ECD00,x]をロード $C1/9D6D BEQ $F7 [$9D66] ;ゼロフラグが立っているとき[$9D66]分岐 $C1/9D6F LDA $7ECD30,x ;Aに[$7ECD30,x]をロード $C1/9D73 STA $11 [$00:0311] ;Aを[$00:0311]に書き込み $C1/9D75 LDA $7ECD10,x ;Aに[$7ECD10,x]をロード $C1/9D79 LDX $5A [$00:035A] ;Xに[$00:035A]をロード $C1/9D7B AND $0039,x ;Aと[$0039,x]で論理積 $C1/9D7E BNE $E6 [$9D66] ;ゼロフラグが立っていないとき[$9D66]分岐 $C1/9D80 LDA $11 [$00:0311] ;Aに[$00:0311]をロード $C1/9D82 AND #$01 ;Aと$01で論理積 $C1/9D84 BNE $E0 [$9D66] ;ゼロフラグが立っていないとき[$9D66]分岐 $C1/9D86 RTS ;サブルーチン戻り
一方、習得済み技IDと「ひとつ前に出した技」IDが一致した時の飛び先[$9D69]の処理は上の通り。
一致した習得済み技IDが$00の時は[$9D66]に飛び、Xボタンを押しても技が出ないことになる。習得済み技IDが$00ということは、おそらく未習得技の枠の値である。
続いて、該当の技が反撃専用技かどうか、$C1/9D6F以降でチェックしている。
技IDが入る[$7ECD00,x]に+$30したアドレス[$7ECD30,x]に、反撃専用技だと$01が入るようになっている。
[$7ECD30,x]の値は一時的に[$00:0311]に入り、実際に反撃専用かの判定が入るのは$C1/9D80~$C1/9D82で、ゼロフラグが立つ、つまり反撃専用なら[$9D66]に飛び、Xボタンを押しても技が出ないことになる。
$C1/9D75からは、状態異常関係の判定である。
技IDが入る[$7ECD00,x]に+$10したアドレス[$7ECD10,x]に、技の使用不能状態異常を記した値が入っている。
16進数2桁(2進数8桁)で、以下のように技の使用不能状態異常が記されており、1が立っている位は使用不能状態異常になる。
| 2進法の位 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |
|---|---|---|---|---|---|---|---|---|
| 状態異常 | 石化 | 酔い | 眠り | マヒ | 毒 | 腕かため | 足かため | 首かため |
| 2進法 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 |
すべての技で、石化、眠り、マヒ状態では技が出せないから、該当部分は必ず1が立っている。
他にも技によっては、酔い、毒、腕かため、足かため、首かためでは出せない設定がされていることもあり、上なら足かためでは出せないので1が立っている状態。
2進数だと%1011 0010だから、16進数に直して$B2が入っている、ということになる。
この値と、該当キャラの現在の状態異常の状況が入っている[$0039,x]とで論理積を取ると、2進数では「技が出せない状態異常になっている位は1、それ以外は0になる。
よって、ゼロフラグが立っていない時は、技を出せない状態異常になっていると判定されて、[$9D66]に飛び、Xボタンを押しても技が出ないことになる。
ここまでの判定を抜けてようやく、「Xボタンを押したらひとつ前の技が出せる」ことになる。
$C1/9D13 BNE $11 [$9D26] ;ゼロフラグが立っていないとき[$9D26]分岐 ;「前に出した技」が出る処理 $C1/9D15 LDA #$00 ;Aに$00をロード $C1/9D17 STA $0001,y ;Aを[$0001,y]に書き込み $C1/9D1A LDA $000C,y ;Aに[$000C,y]をロード $C1/9D1D STA $0014,x ;Aを[$0014,x]に書き込み $C1/9D20 JSR $11E4 [$C1:11E4] ;[$C1:11E4]へ ;「前に出した技」が出ない処理 $C1/9D26 LDA #$00 ;Aに$00をロード $C1/9D28 STA $0001,y ;Aを[$0001,y]に書き込み $C1/9D2B JSR $8A60 [$C1:8A60] ;[$C1:8A60]へ
最後に上の処理が入る。
最初の$C1/9D13で、ゼロフラグが立っているかどうかを調べているが、これはひとつ前の技が出ない判定になった場合、$C1/9D66でAに$FFが書き込まれている一方で、ひとつ前の技が出る判定の場合は、$C1/9D82でゼロフラグが立っていることから分岐になる。
つまり、ひとつ前の技が出ない場合は$C1/9D26にジャンプし、出る場合はそのまま処理を続ける。
なお、どちらに進んでも[$0001,y](シナリオ別キャラデータの2番目の値)に$00を入れる処理が入るが、これが何かは筆者は調べていないのでわからない。
ひとつ前の技が出る場合はそのまま$C1/9D15に進む。
行っている作業は、シナリオ別キャラデータ内の「ひとつ前に出した技」IDを、戦闘中の味方キャラデータ内の「ひとつ前に出した技」IDに書き込む、というものである。つまりここで、「ひとつ前に出した技」IDを更新したことになる。
一方で、ひとつ前の技が出ない場合の$C1/9D26以降は、「ひとつ前に出した技」IDの更新を行わないまま処理終了となる。
以上が、Xボタンを押した時に「ひとつ前に使った技」を出すかどうかの判定用サブルーチンである。
説明の最初に、「Xボタンを押した時に同時にセレクトボタンを押していたかどうか」の判定を行っている、と記した。
では、「Xボタンを押した時に同時にセレクトボタンを押していた」場合はどうなるのか。
$C1/9D38 LDA $4D [$00:034D] ;Aに[$00:034D]をロード $C1/9D3A BIT #$20 ;Aと$20で論理積(ステータスフラグの変更のみ) $C1/9D3C BEQ $08 [$9D46] ;ゼロフラグが立っているとき[$9D46]分岐 $C1/9D3E LDX $5A [$00:035A] ;Xに[$00:035A]をロード $C1/9D40 LDY $0002,x[$00:1C02] ;Yに[$0002,x]をロード $C1/9D43 LDA #$00 ;Aに$00をロード $C1/9D45 RTS ;サブルーチン戻り ; $C1/9D13 BNE $11 [$9D26] ;ゼロフラグが立っていないとき[$9D26]分岐 ;「前に出した技」が出る処理 $C1/9D15 LDA #$00 ;Aに$00をロード $C1/9D17 STA $0001,y ;Aを[$0001,y]に書き込み $C1/9D1A LDA $000C,y ;Aに[$000C,y]をロード $C1/9D1D STA $0014,x ;Aを[$0014,x]に書き込み $C1/9D20 JSR $11E4 [$C1:11E4] ;[$C1:11E4]へ
$C1/9D3Cで「Xボタンを押した時に同時にセレクトボタンを押していた」判定に進んだ結果、先程長々と説明をした判定処理がすっぽりと抜けて、「ひとつ前に出した技」が出せる処理へと飛んでしまった。
2つのファイルを比較表示できるツールWinMerge(WinMerge公式サイト)で比較した結果は以下の通り。
左側がXボタンを押しただけの通常の判定、右側がXボタンと同時にセレクトボタンを押していた時の判定。
セレクトボタン同時押しの右側は、灰色背景部分(通常処理の部分)が抜けている。
セレクトボタンを同時に押していただけで、
これらの条件を満たすかどうかを一切判定せず、「ひとつ前に出した技」を出せるというのが、セレクトバグの真相なのである。
本作の取扱説明書に、セレクトボタンはゲーム中使用しないとある。
実際、通常のプレイの範囲で、セレクトボタンを使う場面は一切ない。
だが実際には、上のとおり、プログラムの中にはセレクトボタンを押しているかどうかを判定するサブルーチンが残されており、そのためにセレクトバグが発生する。
あくまでも筆者の推測だが、セレクトボタンはゲーム開発中のデバッグのために特別な機能を割り振っていたのだろう。
何かの理由でこの機能は製品版にもそのまま残り、プレイヤーに偶然発見された、ということではないだろうか(筆者には詳しい経緯はわからない)。
Xボタンとセレクトボタンによって発生する現象はプログラムの処理に起因している、ということがはっきりしたが、では、セレクトバグを使うと、ブリキ大王が本来出せない技を出せてしまう理由は何だろうか。
理由のひとつは、ブリキ大王のステータスなどが敵キャラデータに登録されていることにある。
敵キャラ扱いであるため、戦闘中に使う技も、味方技IDの技ではなく、敵技IDの技を使う、という点も重要である。
先に、「シナリオ別キャラデータ」についてざっと説明した。
ブリキ大王のステータスも仲間キャラとして「シナリオ別キャラデータ」に入るようになっている。
また、「シナリオ別キャラデータ」には、各キャラ64バイト分のデータが入っている。
最初の値はキャラID、$06番目はキャラのレベル、$0C番目はXボタンで出る「ひとつ前に使った技」などである。
この中の$04~$05番目に当たる2バイト分(16進数4桁分)の数値が少し特殊で、「$D5:00C0~以降の味方キャラの基礎データを呼び出すための数値」が入っている。
例えばアキラだと、$04~$05には$03BDが入っていて、$D50000に$03BDを加えた$D5:03BD~から、アキラの味方キャラデータが入っている。
サモなら$02AFが入っていて、$D50000に$02AFを加えた$D5:02AF~から、サモの味方キャラデータが入っている。
この最初の値に+5すると、各キャラの1番目の味方技IDが入っていて、戦闘に必要になった時に呼び出せる。
といったように使う。
「味方キャラの基礎データが書かれた本の目次ページ」のような値だと思っていただければ良い。
$D50000が0ページ目の本の、$03BDページから、アキラのデータが書かれているというようなイメージである。
ここで問題が生じる。
ブリキ大王は敵キャラとして登録されているので、味方キャラデータ内にはブリキ大王のデータがない。
「シナリオ別キャラデータ」の$04~$05に入れる値が存在しないのである。
ではカラのままなのかというと、そうではない。
実はブリキ大王の場合、同じく$00:0F80~にデータが読み込まれる、サモと同じ$02AFが初期値として入っている。
厳密には、ゲーム開始時に一度サモの「シナリオ別キャラデータ」を呼び出してから、ブリキ大王の「シナリオ別キャラデータ」で上書きしているのだろう、と思われる。
というのは、原始編をプレイすると「シナリオ別キャラデータ」の$00:0F80~にはざきの値が入り、$04~$05は$01FBである。
そして原始編をクリアし、近未来編を開始すると、本来なら$00:0F80~はブリキ大王の「シナリオ別キャラデータ」で上書きされるはずなのに、$04~$05だけ、ざきの$01FBが残ったままになる。
同じことが中世編から最終編にかけても発生する。
中世編だと$00:0F80~にはハッシュのデータが入り、$04~$05は$0147なのだが、最終編を開始しブリキ大王の「シナリオ別キャラデータ」で上書きされても、$04~$05だけハッシュの$0147が残ったままである。
要するに、$00:0F80~にブリキ大王のデータが入っている最中、ブリキ大王には$04~$05に入れる値が存在していないため、それ以前に入っていた値(サモ、ざき、ハッシュの中の誰かのデータ)が残された状態になっている。
このため、近未来編前にプレイしていたシナリオが何なのかによって、$04~$05の値が決まってしまう。
このことによって何が起きるのか。
もう一度、「ひとつ前に使った技」に何が入っているのかについて戦闘開始時に処理するサブルーチンを見返してみよう。
$C1/E94E LDY $0002,x ;Yに[$0002,x]をロード $C1/E951 LDX $0004,y ;Xに[$0004,y]をロード (中略) $C1/E965 LDA $D50005,x ;Aに[$D50005,x]をロード $C1/E969 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み (中略) $C1/E9A6 LDA $12 [$00:0312] ;Aに[$00:0312]をロード $C1/E9A8 STA $7ECD00,x ;Aを[$7ECD00,x]に書き込み (中略) $C1/E9F0 LDA $000C,y ;Aに[$000C,y]をロード $C1/E9F3 BNE $07 [$E9FC] ;ゼロフラグが立っていないとき[$E9FC]分岐 $C1/E9F5 LDA $7ECD00,x ;Aに[$7ECD00,x]をロード $C1/E9F9 STA $000C,y ;Aを[$000C,y]に書き込み $C1/E9FC RTS ;サブルーチン戻り
2行目の$C1/E951で、「$D5:00C0~以降の味方キャラの基礎データを呼び出すための数値」をロードしている。
ブリキ大王の場合、ここに入る値は、シナリオ進行により、サモ、ざき、ハッシュの中の誰かの数値ということになる。
では、仮に、「シナリオ別キャラデータ」$04~$05に、サモのデータ$02AFが入った状態のままだとして、サブルーチンがどう解釈されるかを見てみる。
$C1/E94E LDY $0002,x ;Yに[$0002,x]をロード ;([$0002,x]:ブリキ大王やサモは「$0F80」) $C1/E951 LDX $0004,y ;Xに[$0004,y](=サモのデータ$02AF)をロード (中略) $C1/E965 LDA $D50005,x ;Aに[$D50005,x](=サモの最初の技「クマの手」のID = $A5)をロード $C1/E969 STA $12 [$00:0312] ;A(= $A5)を[$00:0312]に書き込み (中略) $C1/E9A6 LDA $12 [$00:0312] ;Aに[$00:0312](= $A5)をロード $C1/E9A8 STA $7ECD00,x ;A(= $A5)を[$7ECD00,x]に書き込み (中略) $C1/E9F0 LDA $000C,y ;Aに[$000C,y](ひとつ前の技=$00)をロード $C1/E9F3 BNE $07 [$E9FC] ;ゼロフラグが立っていないとき[$E9FC]分岐($00なので[$E9FC]へ) $C1/E9F5 LDA $7ECD00,x ;Aに[$7ECD00,x](= $A5)をロード $C1/E9F9 STA $000C,y ;Aを[$000C,y]に書き込み ;([$000C,y]「ひとつ前の技」に「$A5」を書き込み) $C1/E9FC RTS ;サブルーチン戻り
ブリキ大王のデータを処理しているはずなのに、サモの技のデータばかり処理した結果、「ひとつ前の技」に登録された技IDは「$A5」である。
味方技IDなら「$A5」は「クマの手」なのだが、ブリキ大王は敵キャラとして登録されているので、「ひとつ前の技」を読み込む時、「$A5」を敵技IDとして扱う。
敵技IDの「$A5」は、「電影結界」である。
ということで、ブリキ大王の「ひとつ前の技」に「電影結界」が登録されてしまった。
ブリキ大王は「電影結界」を使用できない。
Xボタンを押した時に、「ひとつ前に使った技」に入っている技を、操作キャラが覚えていない場合はエラー音で「使えない」ことが表現され、技一覧を開く処理が入るようになっているのである。
処理方法のサブルーチンは既に紹介した通りである。
よって本来は、「味方技ID」と「敵技ID」被りを原因とするブリキ大王戦のバグは起こらないはずなのである。
サブルーチンの処理方法からしても、この手のバグが起きないように対策をしていたことはうかがえる。
ところが、セレクトボタンとXボタンを同時押しすると、本来なら該当の技を出せないフラグが立っていても、それを無視することができる、と先ほど説明した通りである。
「ひとつ前の技」に入っている技が何であろうが、おかまいなしなのである。
このためブリキ大王は「ひとつ前に使った技」に入っている「電影結界」を出せてしまう。
というのが、ブリキ大王がセレクトバグで「電影結界」を使えてしまうという行動の真相である。
以上は、ブリキ大王の「シナリオ別キャラデータ」$04~$05に、サモのデータ$02AFが入っていた場合の処理である。
ざきのデータ$01FBが入っていると、ざきの最初の技「ががんご」のID「$38」から、敵技ID「$38」の技「まどわしのひとみ」が「ひとつ前に使った技」に登録される。
よって、原始編プレイ後に近未来編をプレイして、ブリキ大王の$04~$05にざきのデータ$01FBが入ったままだと、セレクトバグで出るのは「まどわしのひとみ」になる。
そして最終編では、ブリキ大王の$04~$05に、中世編のハッシュのデータが残ったままになっている。
ハッシュのデータ$0147が入っていると、ハッシュの最初の技「カットワンウェイ」のID「$76」から、敵技ID「$76」の技「鳥形拳」が「ひとつ前に使った技」に登録される。
このため、オルステッド以外を最終編主人公にした時の最後のブリキ大王戦でセレクトバグを使うと、必ず「鳥形拳」が出ることになる。
余談になるが、このバグで出る技の中で実用的なのは「電影結界」くらいである。
「電影結界」はタメ時間なしの範囲攻撃なので、翔電×3をうまく範囲内に収めることができれば素早く撃破ができる。
「まどわしのひとみ」は単体への攻撃である上にダメージ量が0なので、翔電を眠らせることはできても撃破できない。飛行機である翔電が眠り状態になるというレアな場面が見られる程度である。
「鳥形拳」はダメージ量が少なく、隠呼大仏へのダメージ量も大したことがないので、技のモーションが面白い程度。
最終編オルステッド主人公時では、各シナリオのラストボスを操作するが、この時の戦闘ではセレクトバグを使うと、他のボスが使った技を次の戦闘でも使えてしまう。
この時はどのような理屈で、他のボスの技が使えるようになっているのだろうか。
「シナリオ別キャラデータ」にブリキ大王のデータがないのと同様に、各シナリオのラストボスのデータも入っていない。
最終編オルステッド主人公時は、$00:0D00~に元から入っているオルステッドのデータが、戦闘に入ると戦闘に必要なデータ部分だけラストボスのデータに上書きされる。
「ひとつ前に使った技」も、$00:0D00~のオルステッドのデータ([$00:0D0C])に記録され続ける。
最終編オルステッド主人公時にオルステッド自身が戦闘を行うことはないので、これだけでエラーが出ることはない。
それどころか、$00:0D00~が各シナリオのラストボスのデータに上書きされる影響で、メニュー画面の装備画面でオルステッドのデータを見ると、レベルとHPがひとつ前のラストボスのデータのままになっている。
この現象もエラーが出たりすることはないが、次の戦闘に入ったら他のラストボスのステータスに上書きされるので、戦闘には問題が生じないのだろう。
(このことからもわかるとおり、最終編オルステッド主人公時に、オルステッドのステータスがラストボスのデータに影響を及ぼすことはない。ラストボスのデータ+オルステッドのデータという処理ではなく、オルステッドのデータにラストボスのデータを上書きして消してしまうからである)
「ひとつ前に使った技」には、最終編開始直後なら、オルステッドが中世編で最後に使った技の技IDが入ったままである。
この状態で最終編オルステッド主人公で開始し、戦闘に突入すると、オルステッドの「ひとつ前に使った技」が入ったままだから、各シナリオのラストボスでは使えない。
よって、ただXボタンを押すだけでは、ブリキ大王と同様、エラーが出て使えず、各シナリオのラストボスの技一覧が開く。
そしてセレクトボタン同時押しだとエラーが出ることなく、「ひとつ前に使った技」が使えるが、この時使えるのは、オルステッドが中世編で最後に使った技の技IDを敵技IDにした技である。
| 技ID | 味方技 | 敵技 |
|---|---|---|
| $76 | カットワンウェイ | 鳥形拳 |
| $02 | Vシャイン | 斬り下ろし |
| $03 | プラスリンク | 突き |
| $06 | ハンマーパワー | 体当たり |
| $07 | ソードビュー | 角突進 |
| $05 | インケイジ | 捨身 |
| $08 | ムーンダウン | 角 |
| $04 | ミラードライブ | 不動のかまえ |
| $1B | スピンドル | 毒のキバ |
| $0A | ドラゴンソウル | しっぽ |
| $19 | レイザーソニック | かみ砕き |
| $1A | ジャンプショット | するどいキバ |
| $09 | ヘキサフランジ | 触手 |
| $1C | デストレイル | 毒手 |
上の敵の技の中で、「触手」だけ、ゲーム内で実際に使う敵がいない。いわゆる未使用技である。
よって、中世編でオルステッドが最後に使った技が「ヘキサフランジ」だと、技ID$09が引き継がれて、最終編オルステッド主人公の最初の戦闘でセレクトバグを使った時に未使用技となった「触手」を見ることができる。
といっても、モーションやダメージも普通に表示され、エラーが出たりバグったりすることはないようだ。
「火竜の舞」などで見られる、敵がくるくる回転しながら接近してくるモーションが使われている。
どのラストボスでも回転モーションが見られるという意味では、なかなか面白いかもしれない。
特にマザーCOMだとかなりシュールで、ワイヤーフレームの面とモンスターの面が切り替わりつつスライドしていくという、回転には見えないモーションになる。
戦闘中は「ひとつ前の技」が記録されていて、Xボタンで「ひとつ前の技」を呼び出しすぐに使うことができる。
ただし、Xボタンを押した時に、「ひとつ前の技」が使えない状態だと出せず、エラー音の後に技一覧画面が出る。
「ひとつ前の技」が使えない状態とは、特定の状態異常や(腕かため状態だと腕を使う系統の技が出せなくなるなど)、「ひとつ前の技」が反撃専用技だった、何かのバグで使えないはずの技が「ひとつ前の技」に登録されてしまった、など。
ただし、Xボタンと同時にセレクトボタンを押すことで、本来なら「ひとつ前の技」を出せない状態だったとしても無視して「ひとつ前の技」が出せるという仕様がある。
「ひとつ前の技」が登録されていない場合(シナリオ開始直後など)は、味方キャラデータの技一覧から、最初の技のIDを取り出して、「ひとつ前の技」に登録し、Xボタンで使えるようになる。
ただし、敵キャラとしてデータ登録されているブリキ大王は、味方キャラデータの中にデータがない。
そこで、「ひとつ前の技」を初回登録する時のみ、特定の味方キャラのキャラデータの技一覧から最初の技のIDを取り出して「ひとつ前の技」に登録している。
特定の味方キャラは、基本はサモ、原始編クリアの直後だとざき、中世編クリアの直後だとハッシュである(各シナリオの4人目の仲間キャラに該当する)
以下表のようにIDが読み込まれるのだが、敵キャラとして登録されているブリキ大王が使う時は敵技として使用するため、敵技ID一覧からそれぞれ「電影結界」「まどわしのひとみ」「鳥形拳」を使う仕組みである。
本来ならこれらはブリキ大王が使えない技なので、セレクトボタンでのバグを使わない限りは「ひとつ前の技」扱いされず、出すことは不可能。
一致する技
最終編オルステッド主人公時は、オルステッドの「ひとつ前の技」のデータに操作する各シナリオのラストボスの技IDが記録され続けるという処理が行われているため、各戦闘開始時、ひとつ前の戦闘のボスの技IDが入ったままである。
このため、セレクトボタンでのバグを使うと、他のボスの技が「ひとつ前の技」に入っていても強制的に使える。
また、各ボスの技を使う前なら、オルステッドの「ひとつ前の技」のデータは、オルステッドが中世編に最後に使った味方技IDが入っているため、IDに対応した敵技がセレクトボタンのバグを利用して出せるようになっている。
「ヘキサフランジ」に対応した「触手」は、ゲーム内で使う敵がいない、本来なら未使用技であるが、セレクトボタンのバグを利用すると出せてしまう。