基礎知識
戦闘関係
ここでは、戦闘における味方キャラの移動について紹介する。
味方キャラの場合、味方にターンが回った時、プレイヤーがコマンドを開いていない状態で、十字キーを押した方向へ1マス移動、という一見単純な処理ではあるが、味方キャラの場合、状態異常の「酔い」状態だと十字キーを押した方向ではない方向へ移動する場合がある。
酔い状態による移動処理はどうなるのか、移動がミスする確率はどう計算されるのか、などはこれといって資料もない。
ということで、酔い状態の調査も含めた味方キャラの移動について以下でまとめる。
ちなみに、敵の移動・向き変えにも記した通り、敵の酔い状態は敵の移動には影響しないようである。
ここでは味方キャラ1のアドレスとする。2人目以降のキャラはアドレスを+$40*nする。
味方キャラの状態異常の状態は$00:1C39(+$40*n)に入る。
1バイトで8種の状態異常の状態を表す。
Bit7からBit0の順に(上の桁から順に)、
| Bit | 状態異常 |
|---|---|
| 7 | 石化 |
| 6 | 酔い |
| 5 | 眠り |
| 4 | マヒ |
| 3 | 毒 |
| 2 | 腕かため |
| 1 | 足かため |
| 0 | 首かため |
に1ビットずつ対応して、該当の状態異常だと1が立つ。
酔い状態はBit6に対応だから、%0100 0000 = $40である。
[$00:0345][$00:0344]の2バイトが、コントローラ1のボタンを押した判定値。
どの方向キーを押したかの判断に使用する。
何かしらのボタンが押された時、I/Oポートアドレス[$00:4219][$00:4218](コントローラ1のボタンの状態を出力)の2バイトが、[$00:0345][$00:0344]にコピーされる。
[$00:0344]はR・L・X・Aボタンの判定、[$00:0345]はR・L・X・Aボタン以外。
今回は移動なので、[$00:0345]の値が重要になる。
Bit7からBit0の順に(上の桁から順に)、
| Bit | ボタン |
|---|---|
| 7 | B |
| 6 | Y |
| 5 | セレクト |
| 4 | スタート |
| 3 | 上 |
| 2 | 下 |
| 1 | 左 |
| 0 | 右 |
十字キーは斜め押しすることで2種を同時押し可能だが、その場合の判定方法はサブルーチンで紹介する。
| アドレス | 内容 |
|---|---|
$00:1C18 | キャラ位置のマス目のX座標(左から$00~$06) |
$00:1C19 | キャラ位置のマス目のY座標(上から$00~$06) |
$00:1C1A | 途中計算用のマス目のX座標(左から$00~$06) |
$00:1C1B | 途中計算用のマス目のY座標(上から$00~$06) |
マス目の座標は、左上を原点(0,0)として(6,6)まで。
移動時、移動前のX・Y座標は$00:1C18と$00:1C19に入っているが、移動時には、移動したいマスに移動可能かどうかの判定のため、計算用に$00:1C1Aと$00:1C1Bに値を入れて計算、最後に$00:1C18と$00:1C19に決定値を書き戻す。
$00:1C1E, $00:1C1Fの2バイトで、現在キャラが何のモーションかを表す数値、と思われる(が、よくわからない)。
モーション値というかモーション値のようなものであり(仮)なのでご了承いただきたい。
戦闘開始時に全キャラ$80, $84が入り、技を出す時や移動時に別の値が入る。
$00:04C8~$00:04FEに、敵味方及びダメージフィールドの状態が入っている。
移動したい方向に他キャラ(敵味方)がいないかどうか、フィールド隅ではないか、の判定に使う。
以下、$00:04??の??の値部分のみ記している。
| C8 | C9 | CA | CB | CC | CD | CE |
| D0 | D1 | D2 | D3 | D4 | D5 | D6 |
| D8 | D9 | DA | DB | DC | DD | DE |
| E0 | E1 | E2 | E3 | E4 | E5 | E6 |
| E8 | E9 | EA | EB | EC | ED | EE |
| F0 | F1 | F2 | F3 | F4 | F5 | F6 |
| F8 | F9 | FA | FB | FC | FD | FE |
| 数値 | 内容 |
|---|---|
| $01 | 味方キャラ1 |
| $02 | 味方キャラ2 |
| $03 | 味方キャラ3 |
| $04 | 味方キャラ4 |
| $11 | 敵キャラ0 |
| $12 | 敵キャラ1 |
| $13 | 敵キャラ2 |
| $14 | 敵キャラ3 |
| $15 | 敵キャラ4 |
| $16 | 敵キャラ5 |
| $17 | 敵キャラ6 |
| $18 | 敵キャラ7 |
| $19 | 敵キャラ8 |
| $1A | 敵キャラ9 |
| $1B | 敵キャラA |
| $1C | 敵キャラB |
| $1D | 敵キャラC |
| $1E | 敵キャラD |
| $1F | 敵キャラE |
| $80 | 水フィールド |
| $A0 | 毒フィールド |
| $C0 | 火炎フィールド |
| $E0 | 電撃フィールド |
$00:1C1Dにキャラの現在の向きの値が入る。
| 向き | 値 |
|---|---|
| 右下 | $00 |
| 左下 | $01 |
| 右上 | $02 |
| 左上 | $03 |
味方キャラが移動する場合、同時に向きが変わることがあるため、向き判定・変更も行うことになる。
順に説明していく。
味方キャラは基本的に味方キャラ1ということでメモリアドレスを記す。
(戦闘中、[$00:1C00]~[$00:1C3F]に各種の値が入る)
まず、コントローラのボタンが押されたかどうかの判定を行う。
;ループ処理開始 $C1/8AD8 LDA $4212 [$00:4212] ;Aに[$00:4212]をロード Bit0自動ジョイパッド読み取りフラグ $C1/8ADB AND #$01 ;Aと$01で論理積 $C1/8ADD BNE $F9 [$8AD8] ;ゼロフラグが立っていないとき[$8AD8]分岐 ;ループ処理ここまで $C1/8ADD BNE $F9 [$8AD8] ;ゼロフラグが立っていないとき[$8AD8]分岐 $C1/8ADF LDX #$0000 ;Xに$0000をロード $C1/8AE2 LDA $4218 [$00:4218] ;Aに[$00:4218]をロード コントローラ1ボタン判定 $C1/8AE5 BIT #$0F ;Aと$0Fで論理積(ステータスフラグ変更のみ) $C1/8AE7 BNE $03 [$8AEC] ;ゼロフラグが立っていないとき[$8AEC]分岐 $C1/8AE9 LDX $4218 [$00:4218] ;Xに[$00:4219][$00:4218]をロード コントローラ1ボタン判定 $C1/8AEC STX $44 [$00:0344] ;Xを[$00:0345][$00:0344]に書き込み $C1/8AEE LDX #$0000 ;Xに$0000をロード $C1/8AF1 LDA $421A [$00:421A] ;Aに[$00:421A]をロード コントローラ2ボタン判定 $C1/8AF4 BIT #$0F ;Aと$0Fで論理積(ステータスフラグ変更のみ) $C1/8AF6 BNE $03 [$8AFB] ;ゼロフラグが立っていないとき[$8AFB]分岐 $C1/8AF8 LDX $421A [$00:421A] ;Xに[$00:421B][$00:421A]をロード コントローラ2ボタン判定 $C1/8AFB STX $46 [$00:0346] ;Xを[$00:0347][$00:0346]に書き込み $C1/8AFD RTS ;サブルーチン戻り
I/Oポートアドレス[$00:4212]のBit0が自動ジョイパッド読み取りフラグで、Bit0に1を書き込んで有効にすると、コントローラ1のボタンの状態を[$00:4219][$00:4218]に書き出しBit0の値を0に戻す。
上の通り、何かしらのボタンが押された時は、[$00:4219][$00:4218]の2バイトが、[$00:0345][$00:0344]にコピーされる。
押したかどうかだけの判断なので、ボタンを離したかどうかは判定していない。
実際にプレイすればわかるとおり、戦闘ではAボタンを一瞬押しただけでも、Aボタン押しっぱなしでも、コマンドウィンドウが表示される。Aボタンを押す→離すを行わないとコマンドが出ない、ということはない。
他ボタンでも同じで、少しでも何かしらのボタンを押せば、そのボタンに対応した操作が実行されることになる。
何かしらのボタンが押された場合、どのボタンを押したかの判定を行う。
詳細は省略して、ここでは「コマンドウィンドウが開いていない状態で十字キーが押された」の分岐先から紹介する。
つまり、移動処理の最初の処理になるのだが、実は最初に行うのは移動に伴う向き処理である。
$C1/9589 LDX $5A [$00:035A] ;Xに[$00:035A]をロード $C1/958B LDY #$0208 ;Yに$0208をロード 0010 0000 1000 $C1/958E JSR $95A4 [$C1:95A4] ;[$C1:95A4]へジャンプ $C1/95A4 STY $10 [$00:0310] ;Yを[$00:0311][$00:0310]に書き込み $C1/95A6 LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/95A8 BIT $10 [$00:0310] ;Aと[$00:0310]で論理積(ステータスフラグ変更のみ) $C1/95AA BEQ $34 [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグON(上キーのみ、または上キーを含めたボタンを押している) $C1/95AC LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード 向き $C1/95AF BIT $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で論理積(ステータスフラグ変更のみ) $C1/95B1 BNE $2D [$95E0] ;ゼロフラグが立っていないとき[$95E0]分岐 ;ゼロフラグON 右上向きではない $C1/95B3 ORA $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で論理和 $C1/95B5 STA $001D,x[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き $C1/95B8 BRA $16 [$95D0] ;フラグにかかわりなく常に分岐[$95D0] ;ゼロフラグOFF(上キーは押していない) $C1/95E0 RTS ;サブルーチン戻り $C1/9591 LDY #$0204 ;Yに$0204をロード 0010 0000 0100 $C1/9594 JSR $95BA [$C1:95BA] ;[$C1:95BA]へジャンプ $C1/95BA STY $10 [$00:0310] ;Yを[$00:0311][$00:0310]に書き込み $C1/95BC LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/95BE BIT $10 [$00:0310] ;Aと[$00:0310]で論理積(ステータスフラグ変更のみ) $C1/95C0 BEQ $1E [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグON(下キーのみ、または下キーを含めたボタンを押している) $C1/95C2 LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード 向き $C1/95C5 BIT $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で論理積(ステータスフラグ変更のみ) $C1/95C7 BEQ $17 [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグOFF 右上向き $C1/95C9 EOR $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で排他的論理和XOR $C1/95CB STA $001D,x[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き $C1/95CE BRA $00 [$95D0] ;フラグにかかわりなく常に分岐[$95D0] ;ゼロフラグOFF(下キーは押していない) $C1/95E0 RTS ;サブルーチン戻り $C1/9597 LDY #$0102 ;Yに$0102をロード 0001 0000 0010 $C1/959A JSR $95A4 [$C1:95A4] ;[$C1:95A4]へジャンプ $C1/95A4 STY $10 [$00:0310] ;Yを[$00:0311][$00:0310]に書き込み $C1/95A6 LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/95A8 BIT $10 [$00:0310] ;Aと[$00:0310]で論理積(ステータスフラグ変更のみ) $C1/95AA BEQ $34 [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグON(左キーのみ、または左キーを含めたボタンを押している) $C1/95AC LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード 向き $C1/95AF BIT $11 [$00:0311] ;Aと[$00:0311]($01 = %0001)で論理積(ステータスフラグ変更のみ) $C1/95B1 BNE $2D [$95E0] ;ゼロフラグが立っていないとき[$95E0]分岐 ;ゼロフラグON 左下向きではない $C1/95B3 ORA $11 [$00:0311] ;Aと[$00:0311]で論理和 $C1/95B5 STA $001D,x[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き $C1/95B8 BRA $16 [$95D0] ;フラグにかかわりなく常に分岐[$95D0] ;ゼロフラグOFF(左キーは押していない) $C1/95E0 RTS ;サブルーチン戻り $C1/959D LDY #$0101 ;Yに$0101をロード 0001 0000 0001 $C1/95A0 JSR $95BA [$C1:95BA] ;[$C1:95BA]へジャンプ $C1/95BA STY $10 [$00:0310] ;Yを[$00:0311][$00:0310]に書き込み $C1/95BC LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/95BE BIT $10 [$00:0310] ;Aと[$00:0310]で論理積(ステータスフラグ変更のみ) $C1/95C0 BEQ $1E [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグON(右キーのみ、または右キーを含めたボタンを押している) $C1/95C2 LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード 向き $C1/95C5 BIT $11 [$00:0311] ;Aと[$00:0311]($01 = %0001)で論理積(ステータスフラグ変更のみ) $C1/95C7 BEQ $17 [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグOFF 左下向き $C1/95C9 EOR $11 [$00:0311] ;Aと[$00:0311]($01 = %0001)で排他的論理和XOR $C1/95CB STA $001D,x[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き $C1/95CE BRA $00 [$95D0] ;フラグにかかわりなく常に分岐[$95D0]
最終的に分岐先がすべて$C1/95D0で合流するが、それは後で説明するとして、上の分岐処理について説明する。
少しややこしいので、まず上キーを押した場合の処理だけ見てみよう。
$C1/9589 LDX $5A [$00:035A] ;Xに[$00:035A]をロード $C1/958B LDY #$0208 ;Yに$0208をロード 0010 0000 1000 $C1/958E JSR $95A4 [$C1:95A4] ;[$C1:95A4]へジャンプ $C1/95A4 STY $10 [$00:0310] ;Y($0208)を[$00:0311][$00:0310]に書き込み $C1/95A6 LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/95A8 BIT $10 [$00:0310] ;Aと[$00:0310]($08)で論理積(ステータスフラグ変更のみ) $C1/95AA BEQ $34 [$95E0] ;ゼロフラグが立っているとき[$95E0]分岐 ;ゼロフラグON(上キーのみ、または上キーを含めたボタンを押している) $C1/95AC LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード 向き $C1/95AF BIT $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で論理積(ステータスフラグ変更のみ) $C1/95B1 BNE $2D [$95E0] ;ゼロフラグが立っていないとき[$95E0]分岐 ;ゼロフラグON 右上向きではない $C1/95B3 ORA $11 [$00:0311] ;Aと[$00:0311]($02 = %0010)で論理和 $C1/95B5 STA $001D,x[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き $C1/95B8 BRA $16 [$95D0] ;フラグにかかわりなく常に分岐[$95D0] ;ゼロフラグOFF(上キーは押していない) $C1/95E0 RTS ;サブルーチン戻り
$C1/95A6~$C1/95AAは、[$00:0345] & $08(0000 1000)のゼロフラグ判定で、[$00:0345]のBit3に1が立つのは上キーを押した場合。
よって、$C1/95ACからは上キーを押した場合の処理である。
ただしここでは「上キーのみ、または、上キーを含めた複数のボタンを押している」の意味になることに注意。
本作は十字キー斜め押しができるから、右上を押しても、左上を押しても、ここでは共通処理になる。
逆に言えば右上または左上を押した場合、上方向処理が優先になる、ということでもある。
$C1/95ACでキャラの向きをロードしている。
向きは、右下$00, 左下$01, 右上$02, 左上$03が入っている。
$C1/95B1での分岐は、
向き & $02
のゼロフラグ判定分岐なので、右上向きならゼロフラグOFFだから、向きについてここでは変更しない。
右上向き以外の処理が$C1/95B3~$C1/95B8である。
その場合、
向き | $02
を、新たな向きとしてキャラの向きのアドレスに書き込んでいる。
向きと$02の論理和を取るとどうなるのか。
簡単に言えば、下向きの場合は左右はそのままで上向きになる、という処理である。
向き変えが行われた場合は$C1/95D0にジャンプだが、元々右上向きで向き変えをしなかった時のみ、$C1/95E0にジャンプしてサブルーチンを抜け、次の方向キー判定(下判定)へ移っている。
右上斜め押しの場合の場合の右キー判定なども行う都合と思われる。
他の向き処理は以下の通り。
下キーを押した時、
左キーを押した時、
右キーを押した時、
結局のところ、移動方向に対して、向きが左右方向が逆なら左右を逆に、上下方向が逆なら上下を逆にする、という、当たり前といえば当たり前な処理をしている。
以上、移動の前に向きの処理が行われることがわかった。
この後に、実際に移動可能かどうかの判定が入るが、フィールド隅で移動できない、移動先のマスに既に他キャラが居る、という時は移動はできない。
だが、移動したい方向へ向き変えだけは可能ということがわかる。
向き処理を終えると$C1/95D0に処理がジャンプすることになる。
;処理合流 $C1/95D0 LDA $001E,x[$00:1C1E] ;Aに[$00:1C1E]をロード $C1/95D3 AND #$0F ;Aと$0Fで論理積 $C1/95D5 STA $001E,x[$00:1C1E] ;Aを[$00:1C1E]に書き込み $C1/95D8 LDA $001F,x[$00:1C1F] ;Aに[$00:1C1F]をロード $C1/95DB AND #$0F ;Aと$0Fで論理積 $C1/95DD STA $001F,x[$00:1C1F] ;Aを[$00:1C1F]に書き込み $C1/95E0 RTS ;サブルーチン戻り $C1/95A3 RTS ;サブルーチン戻り
ここで、モーション値(のようなもの)である、$00:1C1Eと$00:1C1Fの処理がある。
この2バイトは戦闘開始時にどの味方キャラでも必ず、
| アドレス | 値 |
|---|---|
$00:1C1E | $80 |
$00:1C1F | $84 |
と、値が入る。
ボタン判定でキャラが何かしらの行動を取ることが決まると、上の処理が入るから、
[$00:1C1E]($80) & $0F = $00[$00:1C1F]($84) & $0F = $04上処理の直後、$C1/95A3の次の処理から、移動できるかどうかと移動方向判定を行う。
$C1/9536 LDA $0039,x ;Aに[$00:1C39]をロード 味方キャラ状態異常 $C1/9539 BIT #$B2 ;Aと$B2(1011 0010)で論理積(ステータスフラグ変更のみ) 石化/眠り/マヒ/足かため $C1/953B BNE $3E [$957B] ;ゼロフラグが立っていないとき[$957B]分岐 ;ゼロフラグON 石化/眠り/マヒ/足かためではない $C1/953D BIT #$40 ;Aと$40で論理積(ステータスフラグ変更のみ) 酔い $C1/953F BEQ $11 [$9552] ;ゼロフラグが立っているとき[$9552]分岐 ;ゼロフラグON 酔い状態である $C1/9541 LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したボタン $C1/9543 BIT #$0F ;Aと$0Fで論理積(ステータスフラグ変更のみ) 方向キー $C1/9545 BEQ $34 [$957B] ;ゼロフラグが立っているとき[$957B]分岐 ;ゼロフラグOFF 上下左右いずれかのボタンを押した $C1/9547 JSR $6150 [$C1:6150] ;[$C1:6150]へジャンプ ;戦闘乱数生成 $C1/6150 PHX ;Xレジスタをスタックにプッシュ ;(中略) $C1/61A4 RTS ;サブルーチン戻り $C1/954A BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) 乱数 & $80 $C1/954C BNE $04 [$9552] ;ゼロフラグが立っていないとき[$9552]分岐 0~$79ならゼロフラグON(1/2) ;ゼロフラグON $C1/954E AND #$0F ;Aと$0Fで論理積 $C1/9550 STA $45 [$00:0345] ;Aを[$00:0345]に書き込み ;ゼロフラグOFF(酔い状態ではない場合とも処理合流) $C1/9552 LDA $45 [$00:0345] ;Aに[$00:0345]をロード $C1/9554 BIT #$08 ;Aと$08で論理積(ステータスフラグ変更のみ) $C1/9556 BEQ $03 [$955B] ;ゼロフラグが立っているとき[$955B]分岐
ここが移動する味方キャラの状態異常判定及び、酔い状態の時の処理になる。
$C1/9536でロードする$0039,xが、味方キャラの現在の状態異常の値。
まず、$B2(%1011 0010)と論理積を取りゼロフラグ判定だが、状態異常の$B2(%1011 0010)は、石化、眠り、マヒ、足かためのいずれかが1が立っているとゼロフラグが立たない。
石化、眠り、マヒ、足かためのいずれかだと$C1/957Bにジャンプだが、これは移動処理スキップという意味である。
石化、眠り、マヒの時はそもそも該当キャラにターンが回ってこないはずだから、実質、足かため用の分岐処理ということになるのだろう。
$C1/953Dの$40(%0100 0000)との論理積が、酔い状態の判定になる。
ここでゼロフラグが立つ場合は酔い状態で、$C1/9541に進む。
酔い状態でない場合は$C1/9552までジャンプする。
$C1/9541では、[$00:0345]と$0F(%0000 1111)の論理積を取ってゼロフラグ判定である。
[$00:0345]は押したボタンの値が入り、Bit0~3(下4桁)は、上下左右キーに対応している。
つまり移動するため上下左右(十字キー)を押したかどうかの判定である。
十字キーを押したのではないのなら移動判定はスキップで$C1/957Bにジャンプ。
十字キーを押した場合、「酔い状態かつ移動のため十字キーを押した」ということになる。
(ちなみに右と上同時押しなど、つまり十字キー斜め押しの場合も該当する)
$C1/9547に進み、$C1/6150にジャンプなので、戦闘乱数生成のサブルーチンに飛ぶことになる。
戦闘乱数生成サブルーチンの説明は省略。
$C1/61A4の時点で、乱数がAレジスタと$00:033Bに入る。
$C1/954Aからが移動時の乱数判定。
生成した戦闘乱数($00~$FF)と$80で論理積を取り、ゼロフラグ判定をしている。
$00~$FFと$80で論理積を取った場合、乱数が$00~$7Fならゼロになりゼロフラグが立つ。
その場合は分岐処理で$C1/954Eに進み、Aと$0Fの論理積、つまり、
戦闘乱数 & $0F
が[$00:0345]に入る。つまり、新たな「押したボタンの値」になる。
ここで、押したボタンが変更されてしまうということだから、
戦闘乱数が$00~$7Fの時(確率1/2)、戦闘乱数 & $0Fが新たな「押した十字キーの値」になる。
これが酔い状態における、十字キーで押した方向と異なる方向へ移動してしまう、という処理である。
とはいえ、この方法だと、十字キーの値が変更されたとしても、元々押していた十字キーの値と同じになる可能性もある。
例えば、酔い状態で左キーを押して左へ1マス移動しようとした時、[$00:0345]には
%0000 0010 = $02
が入る。
この時に戦闘乱数が$62だったら、戦闘乱数は$00~$7Fに含まれるので、
戦闘乱数 & $0F
が新たな「押した十字キーの値」になる。
$62 & $0F = $02
だから、結局[$00:0345]に入る値は$02で、左に移動する。
乱数が$22でも$32でも、下1桁が2なら同じ結果になるから、戦闘乱数が$00~$7Fの時でも、確率1/16で「プレイヤーが押した十字キーと同一になる」。
更に、乱数が$6Fの場合、戦闘乱数 & $0F = $0Fだから、「押した十字キーの値」は上下左右すべてということになる。
スーパーファミコンの通常のコントローラでは不可能である(バーチャルコンソールでも不可能)。
周辺機器で対応していたものがあったかもしれないが、何にしても任天堂純正コントローラではできないから、サブルーチンできちんと処理をしているはずである。
更なる問題として乱数が$60だったら、戦闘乱数 & $0F = $00で、「何の十字キーも押していない」ことになり、移動すらしないことになってしまう。
ということを考慮すると、酔い状態で押したキーの方向へ移動しない確率は、単純に1/2ではない。
十字キー同時押し判定について、きちんと見てみる必要がある。
その処理が、酔い状態ではなくても移動する場合の処理$C1/9552~である。
;ゼロフラグOFF(酔い状態ではない場合とも処理合流) $C1/9552 LDA $45 [$00:0345] ;Aに[$00:0345]をロード $C1/9554 BIT #$08 ;Aと$08で論理積(ステータスフラグ変更のみ) $C1/9556 BEQ $03 [$955B] ;ゼロフラグが立っているとき[$955B]分岐 ;ゼロフラグOFF 上キー(Bit3)に1が立っている $C1/9558 BRL $00AD [$9608] ;フラグにかかわりなく常に分岐[$9608] ;ゼロフラグON $C1/955B BIT #$04 ;Aと$04で論理積(ステータスフラグ変更のみ) $C1/955D BEQ $03 [$9562] ;ゼロフラグが立っているとき[$9562]分岐→左右 ;ゼロフラグOFF 下キー(Bit2)に1が立っている $C1/955F BRL $00D0 [$9632] ;フラグにかかわりなく常に分岐[$9632] ;ゼロフラグON 左右いずれか $C1/9562 BIT #$02 ;Aと$02で論理積(ステータスフラグ変更のみ) $C1/9564 BEQ $03 [$9569] ;ゼロフラグが立っているとき[$9569]分岐 ;ゼロフラグOFF 左キー(Bit1)に1が立っている $C1/9566 BRL $010F [$9678] ;フラグにかかわりなく常に分岐[$9678] ;ゼロフラグON $C1/9569 BIT #$01 ;Aと$01で論理積(ステータスフラグ変更のみ) $C1/956B BEQ $0E [$957B] ;ゼロフラグが立っているとき[$957B]分岐→移動なし ;ゼロフラグOFF 右キー(Bit0)に1が立っている $C1/956D BRL $0132 [$96A2] ;フラグにかかわりなく常に分岐[$96A2]
$C1/9552~$C1/956Dが[$00:0345]の値、つまり十字キーによる分岐で、十字キー同時押しの場合の分岐処理もここでわかる。
[$00:0345]のBit3, Bit2, Bit1, Bit0の順に1が立っているかどうかで分岐処理をしているので、同時押しなら、
上>下>左>右
という順で、優先されることがわかる。
斜め押しは上か下と左か右を同時に押すから、斜め押しでは上下の方が左右より優先される、ということである。
先のように酔い状態で方向キー判定が変わって、十字キー全部同時押しという状態になった場合は、上が最優先である。
なお、この方向キー判定順序は、コマンド選択やフィールド移動などでも共通している。
それぞれのジャンプ先では、キャラの座標から、上下左右方向1マスに移動できるか(移動したいマスに他キャラがいたり、フィールド隅ではみ出す場合はそもそも移動ができない)を判定して、座標を変更する処理が行われるが、それは後回しにして、酔い状態の移動についてもう一度考えてみよう。
戦闘乱数が$00~$7Fかつ、戦闘乱数の下1桁について考える。
戦闘乱数の下1桁は$0(%0000)~$F(%1111)のいずれかになるが、それぞれどの方向へ移動することになるのか。
| 下1桁 | 十字キー |
|---|---|
| $0(%0000) | キーなし(移動なし) |
| $1(%0001) | 右 |
| $2(%0010) | 左 |
| $3(%0011) | 左 |
| $4(%0100) | 下 |
| $5(%0101) | 下 |
| $6(%0110) | 下 |
| $7(%0111) | 下 |
| $8(%1000) | 上 |
| $9(%1001) | 上 |
| $A(%1010) | 上 |
| $B(%1011) | 上 |
| $C(%1100) | 上 |
| $D(%1101) | 上 |
| $E(%1110) | 上 |
| $F(%1111) | 上 |
このようになるはずだ。
つまり、確率としては、まず乱数$00~$7Fが出た場合(確率1/2)でキーの入力方向が変更になり、
| 確率 | 移動方向 |
|---|---|
| 1/16 | 移動しない |
| 1/16 | 右移動 |
| 2/16 | 左移動 |
| 4/16 | 下移動 |
| 8/16 | 上移動 |
に変更される。
上下左右で確率がまったく異なるのである。
まとめると、酔い状態で移動する時、
| 確率 | 移動方向 |
|---|---|
| 16/32 | 方向キーの通り移動 |
| 1/32 | 移動しない |
| 1/32 | 強制的に右移動 |
| 2/32 | 強制的に左移動 |
| 4/32 | 強制的に下移動 |
| 8/32 | 強制的に上移動 |
と、いうことになる。
ただ、元々の移動方向と被る場合もあり得るため、結果として移動方向が変わらないように見えることがある。
上に移動しようとした場合、
ということで、酔い状態で上に移動しようとした時、
キーを押した通りに移動できる確率は3/4(75%)とそこそこ高い。
ところが、酔い状態で右に移動しようとした時、同じ理屈で考えると、
右だとキーを押した通りに移動できる確率は17/32(53%くらい)と、上方向移動時とはまったく異なる結果になることがわかった。
左や下もまた、確率が異なるということになるが省略。
以上で、酔い状態であってもなくても、上下左右いずれかへの移動が決まる。
先の分岐処理から、それぞれの移動方向へのサブルーチンジャンプ先がわかる。
$C1/9608 LDX $5A [$00:035A] ;Xに[$00:035B][$00:035A]をロード $C1/960A LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード キャラの状態 $C1/960D CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/960F BNE $0A [$961B] ;ゼロフラグが立っていないとき[$961B]分岐 ;ゼロフラグOFF 敵操作状態ではない $C1/961B LDA $0019,x[$00:1C19] ;Aに[$00:1C19]をロード Y座標 $C1/961E BEQ $3E [$965E] ;ゼロフラグが立っているとき[$965E]分岐 ;ゼロフラグOFF 移動したいマスがフィールド外ではない ;Y座標が0なら上移動不可 $C1/9620 DEC A ;Aをデクリメント -1 $C1/9621 STA $001B,x[$00:1C1B] ;Aを[$00:1C1B]に書き込み 途中計算用Y座標 $C1/9624 LDA $0018,x[$00:1C18] ;Aに[$00:1C18]をロード X座標 $C1/9627 STA $001A,x[$00:1C1A] ;Aを[$00:1C1A]に書き込み 途中計算用X座標 $C1/962A JSR $42D4 [$C1:42D4] ;[$C1:42D4]へジャンプ
$C1/9632 LDX $5A [$00:035A] ;Xに[$00:035B][$00:035A]をロード $C1/9634 LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード キャラの状態 $C1/9637 CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/9639 BNE $0A [$9645] ;ゼロフラグが立っていないとき[$9645]分岐 ;ゼロフラグOFF 敵操作状態ではない $C1/9645 LDA $0019,x[$00:1C19] ;Aに[$00:1C19]をロード Y座標 $C1/9648 CMP #$06 ;Aと$06を減算比較(ステータスレジスタ変更のみ) $C1/964A BEQ $12 [$965E] ;ゼロフラグが立っているとき[$965E]分岐 ;ゼロフラグOFF 移動したいマスがフィールド外ではない $C1/964C INC A ;Aをインクリメント +1 $C1/964D STA $001B,x[$00:1C1B] ;Aを[$00:1C1B]に書き込み 途中計算用Y座標 $C1/9650 LDA $0018,x[$00:1C18] ;Aに[$00:1C18]をロード X座標 $C1/9653 STA $001A,x[$00:1C1A] ;Aを[$00:1C1A]に書き込み 途中計算用X座標 $C1/9656 JSR $42D4 [$C1:42D4] ;[$C1:42D4]へジャンプ
$C1/9678 LDX $5A [$00:035A] ;Xに[$00:035B][$00:035A]をロード $C1/967A LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード キャラの状態 $C1/967D CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/967F BNE $0A [$968B] ;ゼロフラグが立っていないとき[$968B]分岐 ;ゼロフラグOFF 敵操作状態ではない $C1/968B LDA $0018,x[$00:1C18] ;Aに[$00:1C18]をロード X座標 $C1/968E BEQ $D7 [$9667] ;ゼロフラグが立っているとき[$9667]分岐 ;ゼロフラグOFF 移動したいマスがフィールド外ではない ;X座標が0なら左移動不可 $C1/9690 DEC A ;Aをデクリメント -1 $C1/9691 STA $001A,x[$00:1C1A] ;Aを[$00:1C1A]に書き込み 途中計算用X座標 $C1/9694 LDA $0019,x[$00:1C19] ;Aに[$00:1C19]をロード Y座標 $C1/9697 STA $001B,x[$00:1C1B] ;Aを[$00:1C1B]に書き込み 途中計算用Y座標 $C1/969A JSR $42D4 [$C1:42D4] ;[$C1:42D4]へジャンプ
$C1/96A2 LDX $5A [$00:035A] ;Xに[$00:035B][$00:035A]をロード $C1/96A4 LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード キャラの状態 $C1/96A7 CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/96A9 BNE $0A [$96B5] ;ゼロフラグが立っていないとき[$96B5]分岐 ;ゼロフラグOFF 敵操作状態ではない $C1/96B5 LDA $0018,x[$00:1C18] ;Aに[$00:1C18]をロード X座標 $C1/96B8 CMP #$06 ;Aと$06を減算比較(ステータスレジスタ変更のみ) $C1/96BA BEQ $AB [$9667] ;ゼロフラグが立っているとき[$9667]分岐 ;ゼロフラグOFF 移動したいマスがフィールド外ではない $C1/96BC INC A ;Aをインクリメント +1 $C1/96BD STA $001A,x[$00:1C1A] ;Aを[$00:1C1A]に書き込み 途中計算用X座標 $C1/96C0 LDA $0019,x[$00:1C19] ;Aに[$00:1C19]をロード Y座標 $C1/96C3 STA $001B,x[$00:1C1B] ;Aを[$00:1C1B]に書き込み 途中計算用Y座標 $C1/96C6 JSR $42D4 [$C1:42D4] ;[$C1:42D4]へジャンプ
それぞれ4パターンの処理を見てみると、共通して、
という順序で処理していることがわかる。
更に、フィールド外移動ではない場合、上処理の後のジャンプ先はすべて$C1/42D4である。
ここから、$C1/42D4以降は、移動先予定のマスに既に他キャラがいるかどうかの判定であろうことが予想できる。
その前に、移動したいマスがフィールド外だった場合の分岐処理を確認しておこう。
上または下へ移動しようとしたがフィールド外、という場合は$C1/965Eに、右または左の場合は$C1/9667にジャンプしている。
このふたつの処理だが、実際には途中で処理が合流する。
;上または下へ移動しようとしたがフィールド外 $C1/965E~ $C1/965E LDA $45 [$00:0345] ;Aに[$00:0345]をロード 押したキー $C1/9660 BIT #$03 ;Aと$03で論理積(ステータスフラグ変更のみ) $C1/9662 BEQ $03 [$9667] ;ゼロフラグが立っているとき[$9667]分岐 ;ゼロフラグOFF 右または左を同時押し $C1/9664 BRL $FEFB [$9562] ;フラグにかかわりなく常に分岐[$9562] ;右または左へ移動しようとしたがフィールド外 $C1/9667~ ;$C1/9662でゼロフラグON $C1/9667 LDA $001E,x[$00:1C1E] ;Aに[$00:1C1E]をロード $C1/966A AND #$0F ;Aと$0Fで論理積 $C1/966C STA $001E,x[$00:1C1E] ;Aを[$00:1C1E]に書き込み $C1/966F LDA $001F,x[$00:1C1F] ;Aに[$00:1C1F]をロード $C1/9672 AND #$0F ;Aと$0Fで論理積 $C1/9674 STA $001F,x[$00:1C1F] ;Aを[$00:1C1F]に書き込み $C1/9677 RTS ;サブルーチン戻り
$C1/965Eからが上または下へ移動しようとしたがフィールド外のパターンで、[$00:0345]と$03(%0011)の論理積を取りゼロフラグ判定である。
つまり、右か左も同時に押しているかどうかの判定で、斜め押ししている場合はゼロフラグが立たないから$C1/9664に進み、$C1/9562にジャンプする。
この$C1/9562は、先に説明した、左右いずれかへの移動処理である。
上下はフィールド外になるため移動できないが、左右なら移動できるかどうか、という判定を行うということである。
もし左右同時押しではないのであればゼロフラグが立ち、$C1/9667に進むが、これは「右または左へ移動しようとしたがフィールド外」の処理との合流である。
実質、上下にも左右にも移動できない、フィールドの4隅であり、移動不可ということになる。
この時はモーション値が更新される。
先の処理で、
| アドレス | 値 |
|---|---|
[$00:1C1E] | $00 |
[$00:1C1F] | $04 |
と、値が入っていたが、上処理でも$0Fとの論理積を取るため、結局値は変わらない。
なお、直後に、移動停止のため、以下処理にて[$00:1C1E]と[$00:1C1F]には$80と$84が入る。
$C1/5F89 STZ $FE [$00:03FE] ;[$00:03FE]に$00を書き込み $C1/5F8B LDY #$D000 ;Yに$D000をロード $C1/5F8E STY $1C [$00:031C] ;Yを[$00:031C]に書き込み $C1/5F90 LDY #$0000 ;Yに$0000をロード $C1/5F93 STY $16 [$00:0316] ;Yを[$00:0316]に書き込み $C1/5F95 LDX #$1C00 ;Xに$1C00をロード $C1/5F98 STX $1E [$00:031E] ;Xを[$00:031E]に書き込み $C1/5F9A LDA $0000,x[$00:1C00] ;Aに[$00:1C00]をロード $C1/5F9D BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/5F9F BNE $2D [$5FCE] ;ゼロフラグが立っていないとき[$5FCE]分岐 $C1/5FA1 LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード $C1/5FA4 BIT #$20 ;Aと$20で論理積(ステータスフラグ変更のみ) $C1/5FA6 BEQ $26 [$5FCE] ;ゼロフラグが立っているとき[$5FCE]分岐 $C1/5FA8 LDA $001E,x[$00:1C1E] ;Aに[$00:1C1E]をロード $C1/5FAB BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/5FAD BNE $0D [$5FBC] ;ゼロフラグが立っていないとき[$5FBC]分岐 $C1/5FAF ORA #$80 ;Aと$80で論理和 $C1/5FB1 STA $001E,x[$00:1C1E] ;Aを[$00:1C1E]に書き込み $C1/5FB4 LDY $000C,x[$00:1C0C] ;Yに[$00:1C0C]をロード $C1/5FB7 JSR $5FE0 [$C1:5FE0] ;[$C1:5FE0]へジャンプ ; ;(中略、キャラのグラフィックデータ転送処理) ; $C1/5FBA LDX $1E [$00:031E] ;Xに[$00:031E]をロード $C1/5FBC LDA $001F,x[$00:1C1F] ;Aに[$00:1C1F]をロード $C1/5FBF BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/5FC1 BNE $0B [$5FCE] ;ゼロフラグが立っていないとき[$5FCE]分岐 $C1/5FC3 ORA #$80 ;Aと$80で論理和 $C1/5FC5 STA $001F,x[$00:1C1F] ;Aを[$00:1C1F]に書き込み $C1/5FC8 LDY $000E,x[$00:1C0E] ;Yに[$00:1C0E]をロード $C1/5FCB JSR $5FE0 [$C1:5FE0] ;[$C1:5FE0]へジャンプ
ここまでが、「移動時の向き変え及び、1マス移動、かつ、移動先はフィールド外ではない」という処理にあたる。
次に、移動先のマスに他キャラがいるかどうかの判定を行う。
つまり、$C1/42D4~の処理である。
$C1/42D4 LDA $001B,x[$00:1C1B] ;Aに[$00:1C1B]をロード 計算用Y座標 $C1/42D7 ASL A ;Aを算術左シフト *2 $C1/42D8 ASL A ;Aを算術左シフト *2 $C1/42D9 ASL A ;Aを算術左シフト *2 $C1/42DA ADC $001A,x[$00:1C1A] ;A + [$00:1C1A] 計算用X座標 $C1/42DD STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/42DF STZ $11 [$00:0311] ;[$00:0311]に$00を書き込み $C1/42E1 LDY $10 [$00:0310] ;Yに[$00:0310]をロード $C1/42E3 LDA $04C8,y[$00:04E5] ;Aに[$04C8,y]をロード $C1/42E6 BIT #$0F ;Aと$0Fで論理積(ステータスフラグ変更のみ) $C1/42E8 BEQ $01 [$42EB] ;ゼロフラグが立っているとき[$42EB]分岐 ;ゼロフラグOFF 移動先のマスに他キャラがいる $C1/42EA RTS ;サブルーチン戻り ;ゼロフラグON 移動先のマスに他キャラはいない $C1/42EB TXA ;Xレジスタの値をAレジスタに転送 $C1/42EC CLC ;キャリーフラグクリア $C1/42ED ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/42EE ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/42EF ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/42F0 INC A ;Aをインクリメント +1 $C1/42F1 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/42F3 LDA $04C8,y[$00:04E5] ;Aに[$04C8,y]をロード $C1/42F6 AND #$E0 ;Aと$E0で論理積 フィールドの状態 $C1/42F8 ORA $10 [$00:0310] ;Aと[$00:0310]で論理和 $C1/42FA STA $04C8,y[$00:04E5] ;Aを[$04C8,y]に書き込み $C1/42FD LDA $0019,x[$00:1C19] ;Aに[$00:1C19]をロード Y座標 $C1/4300 ASL A ;Aを算術左シフト *2 $C1/4301 ASL A ;Aを算術左シフト *2 $C1/4302 ASL A ;Aを算術左シフト *2 $C1/4303 ADC $0018,x[$00:1C18] ;A + [$00:1C18] X座標 $C1/4306 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/4308 STZ $11 [$00:0311] ;[$00:0311]に$00を書き込み $C1/430A LDY $10 [$00:0310] ;Yに[$00:0310]をロード $C1/430C LDA $04C8,y[$00:04E4] ;Aに[$04C8,y]をロード 元いた場所の座標 $C1/430F AND #$E0 ;Aと$E0で論理積 フィールドの状態 $C1/4311 STA $04C8,y[$00:04E4] ;Aを[$04C8,y]に書き込み $C1/4314 AND #$00 ;Aと$00で論理積 $C1/4316 RTS ;サブルーチン戻り
$C1/42D4~$C1/42E8で、先に移動先として書き込んだ計算用Y座標と計算用X座標から、
計算用Y座標 * 8 + 計算用X座標
を計算する。
X座標もY座標も$0~$6(%000~%110)なので、Y座標を×8(3ビット左にずらす)してX座標を足すと、Bit0~2がX座標、Bit3~5がY座標という1バイト値になり、$00:04C8~$00:04FEのフィールドマス状態の位置(アドレス)が、
$00:04C8 + (計算用Y座標 * 8 + 計算用X座標)
で計算できる。
その加算処理で算出したアドレスが、$C1/42E3 LDA $04C8,yである。
後は、$04C8,yに敵キャラや味方キャラがいなければ移動可能ということになる。
味方キャラや敵キャラだと、1バイト値の下1桁に0以外が入る。
なので、$C1/42E6で$0Fと論理積を取りゼロフラグ判定をしているのである。
ここでゼロフラグが立たないのなら、移動先のマスに他キャラがいるということなので、$C1/42EAでサブルーチンを抜ける。
つまり、移動処理は行わない。
ゼロフラグが立つ場合は$C1/42EB~で移動処理に移る。
$C1/42EB~$C1/42FAは、移動先のマスに移動するキャラの値を書き込む処理。
$C1/42FD~$C1/4316は、元いた場所のマスの座標[$00:1C18][$00:1C19]の値から、フィールドマスの該当マスのキャラの値を削除する処理である。
どちらでも$E0との論理積を取っているのは、ダメージフィールドの状態を保存しているからである。
これで、フィールドの状態は1マス移動後の状態に更新された。
ただし、キャラのグラフィックはまだ移動していない。
[$00:1C18][$00:1C19]の値が更新されるのは、1マス移動が終了してから。
上下1マスでも、左右1マスでも、1マス移動に12フレーム使っている。
ただ、本作の1マスは、縦24ピクセル×横32ピクセルなので、左右へ1マス移動する時は1フレームあたり3ピクセル、上下に1マス移動する時は1フレームあたり2ピクセル移動、ということになる。
1フレームの移動毎にグラフィックデータの更新や、時間経過によるタメ時間・状態異常継続時間などの計算も行われることになるが、ここでは省略する。
$C1/4450 LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード $C1/4453 CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/4455 BNE $14 [$446B] ;ゼロフラグが立っていないとき[$446B]分岐 $C1/446B INC $0006,x[$00:1C06] ;[$00:1C06]をインクリメント +1 タメ時間ありの技発動までの時間 $C1/446E LDA $0006,x[$00:1C06] ;Aに[$00:1C06]をロード $C1/4471 CMP $0007,x[$00:1C07] ;Aと[$00:1C07]を減算比較(ステータスレジスタ変更のみ)実行しようとしている技のタメ時間 $C1/4474 BNE $24 [$449A] ;ゼロフラグが立っていないとき[$449A]分岐 $C1/4476 LDA $001A,x[$00:1C1A] ;Aに[$00:1C1A]をロード 計算用X座標 $C1/4479 STA $0018,x[$00:1C18] ;Aを[$00:1C18]に書き込み X座標 $C1/447C LDA $001B,x[$00:1C1B] ;Aに[$00:1C1B]をロード 計算用Y座標 $C1/447F STA $0019,x[$00:1C19] ;Aを[$00:1C19]に書き込み Y座標 $C1/4482 JSR $45D5 [$C1:45D5] ;[$C1:45D5]へジャンプ
以上が味方キャラの戦闘における移動処理である。