TOP > プログラミング関係解説&調査 > 敵ターンにおける行動選択 > 敵の移動・向き変え(1)

敵ターンにおける行動選択 > 敵の移動・向き変え(1)

当ページの内容は、ある程度のプログラミングの知識を必要とする。
まず、プログラミング関係解説&調査をひととおり読んでいただきたい。

敵ターンにおける行動選択で、移動または向き変えが選択された場合、どのように移動または向き変えするかを紹介する。

移動または向き変えの手順は、以下の手順で進む。

敵に一番近い味方キャラを判定する

敵サイズが大型の場合、敵に一番近い位置のマスと味方キャラとの距離(X方向+Y方向)を味方キャラ全員分計算し、一番距離が小さいキャラを決める。
同じ距離の味方キャラがいる場合、味方キャラ番号が小さいキャラが優先される。
味方キャラが戦闘不能の場合は判定除外。

該当の敵の行動異常または、移動パターンと味方キャラとの相対距離と位置から、行動パターンを求める

敵に設定された移動パターン(敵キャラデータ00)は、データマップ>敵移動データのID$00$28のいずれかなのだが、敵が行動異常$8$Eの場合、移動パターンがそれぞれ$07$0Dで固定になる。
移動パターン9×9マスの中心位置が敵が右下向きの位置として、一番近い味方キャラの位置を探す(もし味方キャラが敵から5マス以上離れていて9×9マスからはみ出す場合、4マスとして計算)。
敵の向きにより上下左右反転で考えること。
味方キャラの位置に書かれている数値が行動パターンになる。

行動パターンに沿ったサブルーチンで移動・向き変えを行う
行動パターン値移動・向き変え
$00味方キャラに近づく方向へ横方向に1マス移動
$01味方キャラに近づく方向へ縦方向に1マス移動
$02味方キャラから遠ざかる方向へ横方向に1マス移動
$03味方キャラから遠ざかる方向へ縦方向に1マス移動?(※表外注記)
$04右に1マス移動
$05下に1マス移動
$06左に1マス移動
$07上に1マス移動
$08向いている横方向に1マス移動
$09向いている縦方向に1マス移動
$0A向いている反対の横方向に1マス移動
$0B向いている反対の縦方向に1マス移動
$0C上下左右それぞれ1/4の確率で、ランダムに1マス移動
1回目の判定で移動不可だった場合、もう一度判定してランダム方向1マス移動
$0D,$0E,$0F,$14以上移動・向き変えしない
$10反時計回りに向き変え
$11時計回りに向き変え
$12一番近くの味方キャラの方向へ向き変え
$13時計回り・反時計回りにそれぞれ1/2の確率で向き変え

$00$0Bで、移動先に他のキャラがいて移動ができなかった場合、$0Cのランダム方向1マス移動の判定を1回行う。
それでも移動できなかったら、$13のランダム向き変えをして行動終了する。

行動パターン値$03は、本来なら敵と味方の座標を比較するべきはずが、敵と次の敵番号の仮座標とで比較をしているため、想定していただろう挙動を行わない。

移動パターンが40種、行動パターンが20種あるので、説明が長くなる。
(移動パターンも行動パターンも被りがあるので、実際にはこれより少ないが)
世界の合言葉は森部様の移動データページが一番まとまっているので、合わせて参考にしていただきたい。

メモリアドレス

計算に必要なメモリアドレスは以下のとおり。

座標と向き

敵の移動・向き変えは、敵に一番近い位置の味方キャラとの座標関係(相対距離と位置)が決定要素のひとつのため、敵の座標、幅、向きと味方の座標のアドレスが必要になる。
敵が大きいサイズの場合、一番近い味方位置に近い座標で距離を決めるので、敵の縦幅と横幅も必要である。
味方側で必要なのは座標のみで、向きは関係ない。
後の処理で紹介するが、敵からの相対距離が同じ味方キャラがいる場合、味方1>味方2>味方3>味方4と優先順位がある。

座標はXが0~6で右方向が正、Yが0~6で下方向が正。原点(0,0)は左上マスになる。
敵にも味方にも、途中計算用のX座標とY座標を記録するアドレスがあるが、今回は敵の移動なので敵の途中計算用座標が使われる。
今回だと、途中計算用座標に移動先の座標を書き込んでおいて、その移動先に他の敵や味方がいないか、という判定に用いる。
向きは、右下$00, 左下$40, 右上$80, 左上$C0

敵種類n (n=0~3)アドレス
横幅$00:1An4
縦幅$00:1An5
敵番号n (n=0~E)アドレス
向き$00:1Bn4
途中計算用X座標$00:1B08
途中計算用Y座標$00:1B09
X座標$00:1BnA
Y座標$00:1BnB
味方キャラ1~4 (n=0~3)アドレス
X座標$00:1C18 + 40n
Y座標$00:1C19 + 40n

移動パターン

敵種類nの$00:1An0が移動パターンで、敵キャラデータ00からコピーされている。
$00$28まであるが、実際の敵データを見ると未使用となったパターンや被っているパターンも含まれている。
各種データアドレス>敵移動データに掲載してある。

行動パターン

移動パターンは1種類につき、9×9マスに対応した$51バイト(81バイト)の値が入っているが、ここに入っている値が行動パターン。
ただ、行動パターンが決まっているのは$00$13のみで、$14以上は設定がない($14以上だと、サブルーチンで$0Fが採用されるようになっている。後で説明する)
それぞれに応じたサブルーチンがあり、例えば行動パターンが$08だったら、敵が向いている方向の横方向に1マス移動、もし移動したい方向に行けないようならランダム移動、といったようにサブルーチンが変わってくる。

行動異常

行動異常データ00の上から6ビット目、%0000 01001が立っている位だが、ここに0が立っている行動異常の時、移動パターンは「行動異常-1」が使われる。
該当するのは行動異常$8$Eで、これらの行動異常は、敵に設定された移動パターンではなく、各行動異常用の移動パターンが使用される、ということである。
「マザーイメージ」で行動異常$9になると、移動パターンがID$08で固定になり、敵が一番近い位置の味方キャラに近寄ってきたり、「忍法コマまわし」で行動異常$Aになると、移動パターンがID$09になって、反時計回りに回転し始める、といった挙動はこの仕様により生じている。

座標と向きの判定前、$C1/1D0E$C1/1D1Aで、敵の現在の行動異常のデータが[$00:0320][$00:0327]にコピーされている。
つまり行動異常データ00の値が[$00:0320]に入っており、サブルーチンで計算に用いる。

戦闘フィールド

移動の際には、移動先に既に他のキャラがいないか、といった判定を行う必要がある。
戦闘フィールド7×7マスの状態は$00:04C8$00:04FEに入っている。
下に、$00:04????の値部分のみ記している。

C8C9CACBCCCDCE
D0D1D2D3D4D5D6
D8D9DADBDCDDDE
E0E1E2E3E4E5E6
E8E9EAEBECEDEE
F0F1F2F3F4F5F6
F8F9FAFBFCFDFE

各アドレスに入る数値は、何もなければ$00、それ以外はキャラクターの数値+ダメージフィールド状態の合計値になる。
敵が複数マスにまたがっている場合は該当マスに同じ数値が入る。

数値内容
$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電撃フィールド

サブルーチン

ここまでの説明の通り、敵の移動・向き変え手順はなかなかややこしいので、順に説明をしていく。

敵ターンにおける行動選択で、移動・向き変えが確定となるのは$C1/8F5Aからであるから、その後のサブルーチンから紹介していく。
以下でn, mは敵種類m(1~4)、敵番号n($0$F)となる。

$C1/8F5A LDA #$00                ;Aに$00をロード
$C1/8F5C STA $7E9106,x[$7E:AC06] ;Aを[$7E:ACn6]に書き込み
$C1/8F60 LDA $7E8F07,x[$7E:AA07] ;Aに[$7E:AAn7](状態異常)をロード
$C1/8F64 BIT #$02                ;Aと$02で論理積(ステータスフラグ変更のみ)
$C1/8F66 BNE $0F    [$8F77]      ;ゼロフラグが立っていないとき[$8F77]分岐
;ゼロフラグON 足かため状態ではない
$C1/8F68 JSR $913A  [$C1:913A]   ;[$C1:913A]へジャンプ

$C1/913A JSR $91F0  [$C1:91F0]   ;[$C1:91F0]へジャンプ

$C1/91F0 LDY $0002,x[$00:1B02]   ;Yに[$00:1Bn2]をロード
$C1/91F3 LDA $000A,x[$00:1B0A]   ;Aに[$00:1BnA](敵X座標)をロード
$C1/91F6 STA $14    [$00:0314]   ;Aを[$00:0314]に書き込み
$C1/91F8 CLC                     ;キャリーフラグクリア
$C1/91F9 ADC $0004,y[$00:1A04]   ;A + [$00:1A04+40m](敵の横幅)
$C1/91FC DEC A                   ;Aをデクリメント -1
$C1/91FD STA $15    [$00:0315]   ;Aを[$00:0315]に書き込み
$C1/91FF LDA $000B,x[$00:1B0B]   ;Aに[$00:1BnB](敵Y座標)をロード
$C1/9202 STA $16    [$00:0316]   ;Aを[$00:0316]に書き込み
$C1/9204 CLC                     ;キャリーフラグクリア
$C1/9205 ADC $0005,y[$00:1A05]   ;A + [$00:1A05+40m](敵の縦幅)
$C1/9208 DEC A                   ;Aをデクリメント -1
$C1/9209 STA $17    [$00:0317]   ;Aを[$00:0317]に書き込み
$C1/920B RTS                     ;サブルーチン戻り

$C1/913D STZ $12    [$00:0312]   ;[$00:0312]に$00を書き込み
$C1/913F LDA #$FF                ;Aに$FFをロード
$C1/9141 STA $13    [$00:0313]   ;Aを[$00:0313]に書き込み
$C1/9143 STZ $1E    [$00:031E]   ;[$00:031E]に$00を書き込み
$C1/9145 STZ $1F    [$00:031F]   ;[$00:031F]に$00を書き込み
$C1/9147 LDY #$1C00              ;Yに$1C00をロード
$C1/914A JSR $9166  [$C1:9166]   ;[$C1:9166]へジャンプ

最初の判定は$C1/8F60で、敵の状態異常[$7E:AAn7]$02で論理積を取ってゼロフラグとなるが、これは敵が足かため状態かどうかの判定である。
足かため状態だと、移動も向き変えもできないので、移動・向き変え処理から抜ける、ということである。

$C1/913Aからは、敵と味方との相対距離と位置を判定するための準備である。
以下のようにメモリに値をコピーしている。

アドレス
[$00:0314]敵X座標
[$00:0315]敵X座標+横幅-1
[$00:0316]敵Y座標
[$00:0317]敵Y座標+縦幅-1
[$00:0312]$00
[$00:0313]$FF
[$00:031E]$00
[$00:031F]$00

敵の座標は左上を基準としているので、「敵X座標+横幅-1」は、敵の一番右マスのX座標、「敵Y座標+縦幅-1」は、敵の一番下マスのY座標、ということである。

敵に一番近い位置の味方を検索

サブルーチンの説明の前に、敵の移動・向き変えのため、敵と味方との相対距離をどう決めるかを図を含めて解説する。
敵と味方の相対距離と位置は、敵を中心とした9×9マス座標で考えた時に、一番近い位置の味方キャラはどこか、ということを決める。
9×9マスだと、敵と味方がX軸またはY軸で5マス以上離れている場合は範囲外にはみ出してしまうが、5マス以上離れていたら4マスという扱いにする。
下の図の★が敵位置で、右下向き(座標の値が正の方向)を基準として考える。

□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□★□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□

敵は複数マスにまたがっていることがほとんどだが、その場合、味方に一番近い位置のマスを基準にする。

例えば、現代編のナムキャット戦の初期位置は以下のようになっている。
7×7マスフィールドの座標は左上を(0,0)として右方向がX軸、下方向がY軸とする。
敵の座標は左上マスを基準にするので、ナムキャットの座標は(0,0)で、高原の座標は(5,4)である。

ナ□□□□□□
ナ□□□□□□
ナ□□□□□□
□□□□□□□
□□□□□高□
□□□□□□□
□□□□□□□

ナムキャットと高原の相対距離だが、ナムキャット側は一番下のマスが高原に一番近いので、座標で言えば(0,2)を基準とする(ナムキャット自身の座標とは異なることに注意)。
(0,2)を基準にすると、高原の位置は、X軸方向に+5、Y軸方向に+2である。
だが、移動・向き変え判定時は、敵を中心とした9×9マス座標で考え、5マス以上離れていたら4マスという扱いにする。
このため、移動・向き変え判定時は、

□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□ナ□□□□
□□□□□□□□□
□□□□□□□□高
□□□□□□□□□
□□□□□□□□□

X軸方向に+4、Y軸方向に+2という位置で判定をする。

以上を踏まえた上で、続きのサブルーチンを見てみる。

;味方の状態判定
$C1/9166 LDA $0000,y             ;Aに[$00:1C00+40y](味方キャラID)をロード
$C1/9169 INC A                   ;Aをインクリメント +1
$C1/916A BEQ $0D    [$9179]      ;ゼロフラグが立っているとき[$9179]分岐
$C1/916C LDA $0001,y             ;Aに[$00:1C01+40y](戦闘状態)をロード
$C1/916F BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C1/9171 BEQ $06    [$9179]      ;ゼロフラグが立っているとき[$9179]分岐
;ゼロフラグOFF ($40:敵操作状態 $FF:通常)
$C1/9173 LDA $0039,y             ;Aに[$00:1C39+40y](状態異常)をロード
$C1/9176 INC A                   ;Aをインクリメント +1
$C1/9177 BNE $01    [$917A]      ;ゼロフラグが立っていないとき[$917A]分岐
;ゼロフラグON ($00:戦闘離脱 $20:味方キャラ敵対)または戦闘不能
$C1/9179 RTS                     ;サブルーチン戻り
;ゼロフラグOFF 戦闘不能・離脱ではない
$C1/917A JSR $91AB  [$C1:91AB]   ;[$C1:91AB]へジャンプ
;
;X座標相対距離判定
$C1/91AB LDA $0018,y             ;Aに[$00:1A18+40y](味方 X座標)をロード
$C1/91AE SEC                     ;キャリーフラグON
$C1/91AF SBC $14    [$00:0314]   ;A - [$00:0314](敵X座標)
$C1/91B1 BCC $0D    [$91C0]      ;キャリーフラグが立っていないとき[$91C0]分岐
;キャリーフラグON(X座標が味方-敵≧0) 減算の結果が0か正の数の時
$C1/91B3 LDA $0018,y             ;Aに[$00:1A18+40y](味方 X座標)をロード
$C1/91B6 SEC                     ;キャリーフラグON
$C1/91B7 SBC $15    [$00:0315]   ;A - [$00:0315](敵X座標+横幅-1)
$C1/91B9 BCS $05    [$91C0]      ;キャリーフラグが立っているとき[$91C0]分岐
;キャリーフラグOFF(X座標が味方-敵の右幅<0) 減算の結果が負の数の時
$C1/91BB LDA #$04                ;Aに$04をロード
$C1/91BD STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/91BF RTS                     ;サブルーチン戻り
;キャリーフラグOFF(X座標が味方-敵<0) 減算の結果が負の数の時
$C1/91C0 JSR $91E1  [$C1:91E1]   ;[$C1:91E1]へジャンプ

$C1/91E1 CLC                     ;キャリーフラグクリア
$C1/91E2 ADC #$04                ;A + $04
$C1/91E4 BPL $03    [$91E9]      ;ネガティブフラグが立っていないとき[$91E9]分岐
;ネガティブフラグOFF
$C1/91E9 CMP #$09                ;Aと$09を減算比較(ステータスレジスタ変更のみ)
$C1/91EB BCC $02    [$91EF]      ;キャリーフラグが立っていないとき[$91EF]分岐
;キャリーフラグON
$C1/91ED LDA #$08                ;Aに$08をロード
;キャリーフラグOFF
$C1/91EF RTS                     ;サブルーチン戻り

$C1/91C3 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/91C5 RTS                     ;サブルーチン戻り

$C1/917D JSR $919C  [$C1:919C]   ;[$C1:919C]へジャンプ

$C1/919C STA $18    [$00:0318]   ;Aを[$00:0318]に書き込み
$C1/919E CMP #$04                ;Aと$04を減算比較(ステータスレジスタ変更のみ)
$C1/91A0 BCS $06    [$91A8]      ;キャリーフラグが立っているとき[$91A8]分岐
;キャリーフラグOFF
$C1/91A2 LDA #$04                ;Aに$04をロード
$C1/91A4 SEC                     ;キャリーフラグON
$C1/91A5 SBC $18    [$00:0318]   ;A - [$00:0318]
$C1/91A7 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/91A8 SBC #$04                ;A - $04
$C1/91AA RTS                     ;サブルーチン戻り

$C1/9180 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/9182 JSR $91C6  [$C1:91C6]   ;[$C1:91C6]へジャンプ

ここから敵と味方との座標・相対位置判定になる。
$C1/9166$C1/917Aは味方の状態の判定で、戦闘不能・離脱の味方は移動判定の基準としない。
$C1/91ABから、X座標についての相対位置判定を行う。
最初に、「味方X座標-敵X座標」の計算を行い、その結果から分岐していく。

上で図で説明したが、X軸にしぼって考えると、

□□□□★□□□□
↓対応した値
432101234

敵位置から±4マス位置の範囲内に味方がいる場合は★からの距離(0~4)、5マス以上離れていたら距離4、として、最終的に$C1/9180にて、X軸相対距離を[$00:0310]に書き込む、という処理になっている。
敵のサイズ(横幅)の考慮もしなければならないので分岐が多い。

続いて、$C1/91C6から、Y座標相対距離判定を行う。

;Y座標相対距離判定
$C1/91C6 LDA $0019,y             ;Aに[$00:1A19](味方Y座標)をロード
$C1/91C9 SEC                     ;キャリーフラグON
$C1/91CA SBC $16    [$00:0316]   ;A - [$00:0316](敵Y座標)
$C1/91CC BCC $0D    [$91DB]      ;キャリーフラグが立っていないとき[$91DB]分岐
;キャリーフラグON(Y座標が味方>敵) 減算の結果が0か正の数の時
$C1/91CE LDA $0019,y             ;Aに[$00:1A19](味方Y座標)をロード
$C1/91D1 SEC                     ;キャリーフラグON
$C1/91D2 SBC $17    [$00:0317]   ;A - [$00:0317](敵Y座標+縦幅-1)
$C1/91D4 BCS $05    [$91DB]      ;キャリーフラグが立っているとき[$91DB]分岐
;キャリーフラグOFF 減算の結果が負の数の時
$C1/91D6 LDA #$04                ;Aに$04をロード
$C1/91D8 STA $11    [$00:0311]   ;Aを[$00:0311]に書き込み
$C1/91DA RTS                     ;サブルーチン戻り
;$C1/91CCでキャリーフラグOFFか、$C1/91D4でキャリーフラグON
$C1/91DB JSR $91E1  [$C1:91E1]   ;[$C1:91E1]へジャンプ

$C1/91E1 CLC                     ;キャリーフラグクリア
$C1/91E2 ADC #$04                ;A + $04
$C1/91E4 BPL $03    [$91E9]      ;ネガティブフラグが立っていないとき[$91E9]分岐
;ネガティブフラグOFF
$C1/91E9 CMP #$09                ;Aと$09を減算比較(ステータスレジスタ変更のみ)
$C1/91EB BCC $02    [$91EF]      ;キャリーフラグが立っていないとき[$91EF]分岐
;キャリーフラグON
$C1/91ED LDA #$08                ;Aに$08をロード
;キャリーフラグOFF
$C1/91EF RTS                     ;サブルーチン戻り

$C1/91DE STA $11    [$00:0311]   ;Aを[$00:0311]に書き込み
$C1/91E0 RTS                     ;サブルーチン戻り

$C1/9185 JSR $919C  [$C1:919C]   ;[$C1:919C]へジャンプ

$C1/919C STA $18    [$00:0318]   ;Aを[$00:0318]に書き込み
$C1/919E CMP #$04                ;Aと$04を減算比較(ステータスレジスタ変更のみ)
$C1/91A0 BCS $06    [$91A8]      ;キャリーフラグが立っているとき[$91A8]分岐
;キャリーフラグOFF
$C1/91A2 LDA #$04                ;Aに$04をロード
$C1/91A4 SEC                     ;キャリーフラグON
$C1/91A5 SBC $18    [$00:0318]   ;A - [$00:0318]
$C1/91A7 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/91A8 SBC #$04                ;A - $04
$C1/91AA RTS                     ;サブルーチン戻り

実質、X座標とやっていることは同じである。
敵位置から±4マス位置の範囲内に味方がいる場合は★からの距離(0~4)、5マス以上離れていたら距離4、としている。
ただ、Y軸相対距離は最終的に、$C1/91A7または$C1/91AAの時点でAに書き込まれて終了になっている。

$C1/9188 CLC                     ;キャリーフラグクリア
$C1/9189 ADC $10    [$00:0310]   ;A + [$00:0310](X軸相対距離)
$C1/918B CMP $13    [$00:0313]   ;Aと[$00:0313]を減算比較(ステータスレジスタ変更のみ)
$C1/918D BCS $0C    [$919B]      ;キャリーフラグが立っているとき[$919B]分岐
;キャリーフラグOFF([$00:0313]の方が小さい) 減算の結果が負の数の時
$C1/918F STA $13    [$00:0313]   ;Aを[$00:0313]に書き込み
$C1/9191 STY $1E    [$00:031E]   ;Yを[$00:031E]に書き込み
$C1/9193 TYA                     ;Yレジスタの値をAレジスタに転送
$C1/9194 CLC                     ;キャリーフラグクリア
$C1/9195 ROL A                   ;Aをキャリーフラグを含めた9bitで左ローテート
$C1/9196 ROL A                   ;Aをキャリーフラグを含めた9bitで左ローテート
$C1/9197 ROL A                   ;Aをキャリーフラグを含めた9bitで左ローテート
$C1/9198 INC A                   ;Aをインクリメント +1
$C1/9199 STA $12    [$00:0312]   ;Aを[$00:0312]に書き込み
;キャリーフラグON([$00:0313]の方が大きいか同値) 減算の結果が0か正の数の時
$C1/919B RTS                     ;サブルーチン戻り

$C1/914D LDY #$1C40              ;Yに$1C40をロード
$C1/9150 JSR $9166  [$C1:9166]   ;[$C1:9166]へジャンプ

$C1/9188$C1/9189で、敵とのX軸相対距離 + Y軸相対距離を計算し、[$00:0313]と減算比較している。
[$00:0313]だが、$C1/9141にて、初期値として$FFが入っているから、最初の味方キャラの判定だと、減算結果は必ず負となり、キャリーフラグが立って$C1/918Fに進む。
$C1/918Fで、「敵とのX軸相対距離 + Y軸相対距離」が新たに[$00:0313]に書き込まれる。
以降の処理だが、$C1/9193$C1/9199の処理で、Yレジスタに入っている味方キャラ番号に対応した$1C00, $1C40, $1C80, $1CC0のいずれかが[$00:031F][$00:031E]に書き出され、キャラに対応した$01, $02, $03, $04が算出されて[$00:0312]に書き込まれる。

$C1/919Bで処理合流となり、ここで味方キャラ1人分の処理が終了して、$C1/914Dで2人目判定へ飛ぶ。

ということで、味方キャラ1人分の判定により、

アドレス内容
[$00:0313]敵とのX軸相対距離 + Y軸相対距離
[$00:0312][$00:0313]に入った味方キャラ番号$01$04
[$00:031F][$00:031E][$00:0313]に入った味方キャラアドレス$1C00, $1C40, $1C80, $1CC0

が、記録された。
この後に味方キャラ2以降を判定して、「敵とのX軸相対距離 + Y軸相対距離」がより短いキャラが[$00:0313][$00:0312]を更新していくことになる。
ただし、「敵とのX軸相対距離 + Y軸相対距離」を比較して同値だったら、$C1/918Dのキャリーフラグ判定で更新スキップだから、味方キャラ番号が小さいキャラの値が残されることになる。

こうして、戦闘フィールドに居る味方キャラ全員分の判定をすると、最終的に、敵との距離が一番近い味方キャラ(相対距離が同値の場合、より味方番号の小さいキャラ)の値が[$00:0313][$00:0312][$00:031F][$00:031E]に残る、ということになる。
判定部分を省略すると、以下のように、$C1/913A$C1/9165で4キャラ分の判定を行っていることがわかる。

$C1/913A JSR $91F0  [$C1:91F0]   ;[$C1:91F0]へジャンプ
;
;(味方キャラ1の判定)
;
$C1/914D LDY #$1C40              ;Yに$1C40をロード
$C1/9150 JSR $9166  [$C1:9166]   ;[$C1:9166]へジャンプ
;
;(味方キャラ2の判定)
;
$C1/9153 LDY #$1C80              ;Yに$1C80をロード
$C1/9156 JSR $9166  [$C1:9166]   ;[$C1:9166]へジャンプ
;
;(味方キャラ3の判定)
;
$C1/9159 LDY #$1CC0              ;Yに$1CC0をロード
$C1/915C JSR $9166  [$C1:9166]   ;[$C1:9166]へジャンプ
;
;(味方キャラ4の判定)
;
$C1/915F LDA $12    [$00:0312]   ;Aに[$00:0312]をロード
$C1/9161 STA $7E9107,x[$7E:AC07] ;Aを[$7E:ACn7]に書き込み
$C1/9165 RTS                     ;サブルーチン戻り

これで敵味方の相対距離判定が終わりになり、最後に、一番近い位置の味方キャラ番号が入った[$00:0312]が、[$7E:ACn7]にもコピーされてサブルーチンが終了する。

敵味方の相対距離と位置から移動パターン判定

敵に一番近い位置の味方キャラの判定を終えたら、該当の味方キャラとの相対距離と位置から移動パターンを判定していく。

$C1/8F6B JSR $90C5  [$C1:90C5]  ;[$C1:90C5]へジャンプ

$C1/90C5 JSR $91F0  [$C1:91F0]  ;[$C1:91F0]へジャンプ

$C1/91F0 LDY $0002,x            ;Yに[$00:1Bn2]をロード
$C1/91F3 LDA $000A,x            ;Aに[$00:1BnA](敵X座標)をロード
$C1/91F6 STA $14    [$00:0314]  ;Aを[$00:0314]に書き込み
$C1/91F8 CLC                    ;キャリーフラグクリア
$C1/91F9 ADC $0004,y[$00:1A04]  ;A + [$00:1A04+40m](敵の横幅)
$C1/91FC DEC A                  ;Aをデクリメント -1
$C1/91FD STA $15    [$00:0315]  ;Aを[$00:0315]に書き込み
$C1/91FF LDA $000B,x            ;Aに[$00:1BnB](敵Y座標)をロード
$C1/9202 STA $16    [$00:0316]  ;Aを[$00:0316]に書き込み
$C1/9204 CLC                    ;キャリーフラグクリア
$C1/9205 ADC $0005,y[$00:1A05]  ;A + [$00:1A05+40m](敵の縦幅)
$C1/9208 DEC A                  ;Aをデクリメント -1
$C1/9209 STA $17    [$00:0317]  ;Aを[$00:0317]に書き込み
$C1/920B RTS                    ;サブルーチン戻り

$C1/90C8 STZ $12    [$00:0312]  ;[$00:0312]に$00を書き込み
$C1/90CA LDA #$FF               ;Aに$FFをロード
$C1/90CC STA $13    [$00:0313]  ;Aを[$00:0313]に書き込み
$C1/90CE LDY $1E    [$00:031E]  ;Yに[$00:031F][$00:031E](一番近い位置の味方キャラアドレス)をロード
$C1/90D0 BNE $03    [$90D5]     ;ゼロフラグが立っていないとき[$90D5]分岐
$C1/90D5 JSR $91AB  [$C1:91AB]  ;[$C1:91AB]へジャンプ

上処理で、以下のように値が入った。

アドレス内容
[$00:0314]敵X座標
[$00:0315]敵X座標+横幅-1
[$00:0316]敵Y座標
[$00:0317]敵Y座標+縦幅-1
[$00:0312]$00
[$00:0313]$FF

また、$C1/90CEY[$00:031F][$00:031E]をロードしている。
先程までに計算した、敵に一番近い位置の味方キャラアドレス$1C00, $1C40, $1C80, $1CC0のいずれかである。

飛び先の$C1/91ABだが、先程、味方キャラ全員に対して行ったX座標のサブルーチンである。
その後にY座標のサブルーチンももう一度行っている。
省略できる部分は省略すると以下のようになる。

;X座標絶対位置判定
$C1/91AB LDA $0018,y             ;Aに[$00:1A18+40y](味方 X座標)をロード
;(省略)
$C1/91C3 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/91C5 RTS                     ;サブルーチン戻り
;
$C1/90D8 JSR $91C6  [$C1:91C6]   ;[$C1:91C6]へジャンプ
;
;Y座標絶対位置判定
;(省略)
$C1/91DE STA $11    [$00:0311]   ;Aを[$00:0311]に書き込み
$C1/91E0 RTS                     ;サブルーチン戻り

ここで注意しなければならないのは、[$00:0310][$00:0311]に入るのは相対距離ではないという点。
X軸でいえば、$C1/91C5でサブルーチンが戻った後、相対距離を求める$C1/919C$C1/9180の処理をしていない。
ということは、X軸で言えば、

□□□□★□□□□
↓対応した値
012345678

一番左の位置を原点とした0~8の値が[$00:0310]に入る。
相対位置ではなく、左端を原点とした絶対位置が入る、ということである。
ただ、今回も、5マス以上離れている時は左側なら0、右側なら8が入ることになる。
Y軸も同じ処理である。

つまり、

  • [$00:0310]:X座標絶対位置(0~8)、5マス以上離れていたら左側なら0、右側なら8
  • [$00:0311]:Y座標絶対位置(0~8)、5マス以上離れていたら上側なら0、下側なら8

を、書き込んだことになる。

いわば、9×9マスで左上原点とした味方位置座標(X,Y)を求めたことと同じである(敵位置は(3,3)固定)。

$C1/90DB LDA $0004,x             ;Aに[$00:1Bn4](向き)をロード
$C1/90DE BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C1/90E0 BEQ $07    [$90E9]      ;ゼロフラグが立っているとき[$90E9]分岐
;ゼロフラグOFF(左下$40、左上$C0
$C1/90E2 LDA #$08                ;Aに$08をロード
$C1/90E4 SEC                     ;キャリーフラグON
$C1/90E5 SBC $10    [$00:0310]   ;A - [$00:0310](X座標相対距離)
$C1/90E7 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
;ゼロフラグON(右下$00、右上$80)
$C1/90E9 LDA $0004,x             ;Aに[$00:1Bn4](向き)をロード
$C1/90EC BIT #$80                ;Aと$80で論理積(ステータスフラグ変更のみ)
$C1/90EE BEQ $07    [$90F7]      ;ゼロフラグが立っているとき[$90F7]分岐
;ゼロフラグOFF(右上$80、左上$C0)
$C1/90F0 LDA #$08                ;Aに$08をロード
$C1/90F2 SEC                     ;キャリーフラグON
$C1/90F3 SBC $11    [$00:0311]   ;A - [$00:0311](Y座標相対距離)
$C1/90F5 STA $11    [$00:0311]   ;Aを[$00:0311]に書き込み
;ゼロフラグON(右下$00、左下$40)
$C1/90F7 LDY $10    [$00:0310]   ;Yに[$00:0310]をロード
$C1/90F7 LDY $10    [$00:0310]   ;Yに[$00:0311][$00:0310]をロード
$C1/90F9 STY $1A    [$00:031A]   ;Yを[$00:031B][$00:031A]に書き込み

ここから敵の向き判定になる。
先に述べた通り、敵と味方の相対距離と位置は、敵を中心とした9×9マス座標で考えるのだが、敵が右下向きであることを基準としている。
敵の向きによっては、この9×9マスの2次元空間を、

敵の向き内容
右下向き(基準)
左下向き左右反転
右上向き上下反転
左上向き左右+上下反転

として考えなければならない。
先ほど算出した、[$00:0310][$00:0311]を味方位置座標と考えた場合、敵位置(3,3)の敵は右下向きという前提での座標ということである。

$C1/90DBで敵の向きをロードしている。
右下$00、左下$40、右上$80、左上$C0である。

$C1/90E0にて、$40との論理積を取ってゼロフラグ分岐があるが、ゼロフラグOFFは左下$40、左上$C0である。
左向きだと、左右反転で考える必要があるため、

$08 - [$00:0310](味方位置X座標)

を新たな[$00:0310]とする。

□□□□★□□□□
876543210

左右反転したら一番左マスを基準に、左方向に値が増える、というような感じである。

$C1/90E9からは、向きと$80との論理積でゼロフラグ分岐だが、今度は右上$80、左上$C0でゼロフラグOFFである。
上向きだと、上下反転で考える必要があるため、

$08 - [$00:0311](味方位置Y座標)

を新たな[$00:0311]とする。

左上向きの場合どちらもゼロフラグOFFになるから、これで左右+上下反転が行われたことになる。
逆に、右下向きはここで何の変更もする必要はなく、[$00:0310][$00:0311]どちらもそのままである。

$C1/90F7$C1/90F9で、[$00:0311][$00:0310][$00:031B][$00:031A]にコピーされる。

$C1/90FB LDA $20    [$00:0320]   ;Aに[$00:0320](行動異常データ00)をロード
$C1/90FD BIT #$04                ;Aと$04(%0000 0100)で論理積(ステータスフラグ変更のみ)
$C1/90FF BNE $06    [$9107]      ;ゼロフラグが立っていないとき[$9107]分岐
;ゼロフラグON(行動異常により移動補正あり)
$C1/9101 LDA $7E8F02,x[$7E:AA02] ;Aに[$7E:AAn2](行動異常)をロード
$C1/9105 BNE $07    [$910E]      ;ゼロフラグが立っていないとき[$910E]分岐
;ゼロフラグOFF(行動異常により移動補正なし)
$C1/9107 LDY $0002,x             ;Yに[$00:1Bn2]をロード
$C1/910A LDA $0020,y             ;Aに[$00:1A20+40m](敵キャラデータ00 移動パターン)をロード
$C1/910D INC A                   ;Aをインクリメント +1
;ゼロフラグOFF(行動異常により移動補正あり)
$C1/910E DEC A                   ;Aをデクリメント -1
$C1/910F STA $4202  [$00:4202]   ;Aを[$00:4202]に書き込み

$C1/90FB[$00:0320]だが、順序ポイント判定の時に、敵の現在の行動異常のデータ8バイトが、[$00:0320][$00:0327]にコピーされたままである。
つまり、[$00:0320]に入っているのは、敵の現在の行動異常の行動異常データ00である。
行動異常データ00の上から5ビット目が0だと、行動異常による移動パターン補正が入る。1なら入らない。
$C1/90FFのゼロフラグ判定はそのための分岐であり、「行動異常データ00の上から5ビット目が0、かつ、行動異常番号0ではない」時は、A[$7E:AAn2](行動異常)をロードし、$C1/910Eにジャンプして、[$7E:AAn2]-1する。つまり行動異常の値が-1される。

一方、行動異常による移動パターン補正が入らないパターンだと、[$00:1A20+40m](敵キャラデータ00 移動パターン)をロードする。
$C1/910D$C1/910Eで、+1して-1することになるから、結局、$C1/910Fの時点のAは移動パターンの値そのままである。

ということから、$C1/910Fの時点のAの値をまとめると、

  • 行動異常$8$Eの状態の敵:$8$Eから-1した値
  • 行動異常$8$E以外の状態の敵:敵に設定された敵キャラデータ00移動パターンの値

このようになる。
行動異常$8$Eの状態の敵は、敵に元から設定された移動パターンの値は関係なく、該当の行動異常に設定された移動パターンに固定されてしまう、ということである。

さて、$C1/910Fでこの移動パターンの値が[$00:4202]に書き込まれて、符号付8bit x 8bitの計算になる。

$C1/910F STA $4202  [$00:4202]   ;Aを[$00:4202]に書き込み
$C1/9112 LDA #$51                ;Aに$51(81)をロード
$C1/9114 STA $4203  [$00:4203]   ;Aを[$00:4203]に書き込み
$C1/9117 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/9119 ASL A                   ;Aを算術左シフト *2
$C1/911A ASL A                   ;Aを算術左シフト *2
$C1/911B ASL A                   ;Aを算術左シフト *2
$C1/911C ADC $11    [$00:0311]   ;A + [$00:0311]
$C1/911E ADC $10    [$00:0310]   ;A + [$00:0310]
$C1/9120 STA $12    [$00:0312]   ;Aを[$00:0312]に書き込み
$C1/9122 STZ $13    [$00:0313]   ;[$00:0313]に$00を書き込み
$C1/9124 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/9126 LDA $D50060[$D5:0060]   ;Aに[$D5:0061][$D5:0060]をロード $644B 
$C1/912A ADC #$0080              ;A + $0080
$C1/912D ADC $12    [$00:0312]   ;A + [$00:0312]
$C1/912F ADC $4216  [$00:4216]   ;A + [$00:4217][$00:4216](移動パターン * $51)
$C1/9132 TAX                     ;Aの値をXレジスタに転送
$C1/9133 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/9135 LDA $D50000,x           ;Aに[$D50000,x](行動パターン)をロード
$C1/9139 RTS                     ;サブルーチン戻り

まとめると、

[$00:0312] = [$00:0311](味方位置Y座標)*8 + [$00:0311](味方位置Y座標) + [$00:0310](味方位置X座標)
[$D5:0061][$D5:0060]($644B) + $0080 + [$00:0312] + 移動パターン * $51

に、$D50000を加算したアドレスが$C1/9135でロードされるのだが、これが該当の敵の行動パターンにあたる。

まず、[$00:0312]は何を表しているのか考えてみよう。
移動パターンは$00$28の19種類あり、1つのパターンが9×9マスに対応した$51バイト(81バイト)の値である。
9バイトずつ区切ると9行の文字列になり、9×9マスと合致する。

□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□★□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□

[$00:0310][$00:0311]には、上図で左上を原点としたX座標とY座標が入っている。
Y座標[$00:0311]を9倍すればちょうど上からの該当の位置の列の値となり、これにX座標[$00:0310]を加算すると、ぴったり目的の味方位置になる、という計算が、

[$00:0312] = [$00:0311](味方位置Y座標)*8 + [$00:0311](味方位置Y座標) + [$00:0310](味方位置X座標)

である。
9倍の計算を、[$00:0311] × 8 + [$00:0311]で行っているのである。65C816には掛け算機能がないが、符号付8bit x 8bitのひと手間かかる計算方法より、ビットシフトと加算を使った方が速く済むということだろう。

ということで、9×9マスの左上基準で[$00:0312]を加算すると味方の座標位置になることがわかった。
あとは、移動パターンの開始アドレスを呼び出して、[$00:0312]を加算すれば行動パターンが算出できる。
その計算が

[$D5:0061][$D5:0060]($644B) + $0080 + [$00:0312] + 移動パターン * $51

である。

[$D5:0061][$D5:0060]($644B) + $0080

が、敵移動データ00の開始位置$D5:64CBのアドレス部分「$64CB」である。
ここに、移動パターン * $51を加算すれば、該当の敵の移動パターン開始アドレスが求められる。
最後に[$00:0312]を加算して、$C1/9135でロードされるのが、行動パターンとなる。

行動パターンから、敵の移動を決定

ここまででようやく、敵移動・向き変え時の行動パターンが求められたが、さらに、この行動パターンから、どういう移動をするかの分岐がある。
ここではまず例として、先程のナムキャット戦について見てみよう。
ナムキャットの移動パターンはID$00である。

移動パターンID$00のデータ$51バイトを、9バイト区切りで並べると以下のようになる。値により色分けした。

ID$00
110B0B0B0B0B0B0B12
0A1212121212121209
0A1212121212090909
0A1212121212121212
0A121212F00B080808
0A1212120809080808
0A1212120809090808
0A120A120909090908
12080A090909090809

高原との相対位置は下のようになっており、ナムキャットは初期配置で右下向きだから、ナムキャットの行動パターンは$08ということになる。

□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□□□□□□
□□□□ナ□□□□
□□□□□□□□□
□□□□□□□□高
□□□□□□□□□
□□□□□□□□□

$C1/9139A$08が入った状態で、続きの処理を見てみよう。
(他パターンについて全部説明すると長くなるので別ページに分けてある)

$C1/8F6E CMP #$14                ;A($08)と$14を減算比較(ステータスレジスタ変更のみ)
$C1/8F70 BCC $02    [$8F74]      ;キャリーフラグが立っていないとき[$8F74]分岐
;キャリーフラグON
$C1/8F72 LDA #$0F                ;Aに$0Fをロード
;キャリーフラグOFF
$C1/8F74 JSR $8F8D  [$C1:8F8D]   ;[$C1:8F8D]へジャンプ

$C1/8F6E$C1/8F70のキャリーフラグ分岐は、

  • 行動パターンが$00$13ならキャリーフラグがOFFで、Aに行動パターンを入れたまま$C1/8F8D
  • 行動パターンが$14以上ならキャリーフラグONがOFFで、A$0Fを入れて$C1/8F8D

と、いうことである。
ということは、行動パターンが$14以上と$0Fは処理がこの先同一という意味になる。
実際、行動パターンは$13までしかない。
また、行動パターン$0Fは後で説明するとおり、「何もしない」である。

$C1/8F8D REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/8F8F AND #$001F              ;Aと$001Fで論理積
$C1/8F92 ASL A                   ;Aを算術左シフト *2
$C1/8F93 ADC #$8E1E              ;A + $8E1E
$C1/8F96 TAX                     ;Aの値をXレジスタに転送(A:8E2E)
$C1/8F97 LDA $C10000,x[$C1:8E2E] ;Aに[$C1:8E2F][$C1:8E2E]をロード
$C1/8F9B STA $1C    [$00:031C]   ;Aを[$00:031D][$00:031C]に書き込み
$C1/8F9D LDX $AC    [$00:03AC]   ;Xに[$00:03AD][$00:03AC]をロード
$C1/8F9F SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/8FA1 JMP ($031C)[$C1:901E]   ;[$C1:901E]へジャンプ(レジスタをスタックに積まない)

$C1/901E LDA $0004,x[$00:1B04]   ;Aに[$00:1B04](敵番号1の向き)をロード
$C1/9021 BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C1/9023 BEQ $AF    [$8FD4]      ;ゼロフラグが立っているとき[$8FD4]分岐
;ゼロフラグOFF(左下$40、左上$C0)
$C1/9025 BRA $CD    [$8FF4]      ;フラグにかかわりなく常に分岐[$8FF4]
;ゼロフラグON(右下$00、右上$80)
$C1/8FD4 LDY $0002,x[$00:1B02]   ;Yに[$00:1B02]をロード
$C1/8FD7 LDA $002F,y[$00:1A2F]   ;Aに[$00:1A2F](敵データ15 タイプ)をロード
$C1/8FDA BIT #$04                ;Aと$04で論理積(ステータスフラグ変更のみ)
$C1/8FDC BEQ $01    [$8FDF]      ;ゼロフラグが立っているとき[$8FDF]分岐
;ゼロフラグON OBJECTではない
$C1/8FDF JSR $E393  [$C1:E393]   ;[$C1:E393]へジャンプ

$C1/8F96の時点のAの値は、

行動パターン($00~$13)*2 + $8E1E

だから、$C1/8F9Bでロードする値は、[$C1:8E1F][$C1:8E1E][$C1:8E45][$C1:8E44]の20パターンになる。
とりあえず、行動パターン$08のまま進めてみると、[$C1:8E2F][$C1:8E2E]$901E)をロードし、[$00:031D][$00:031C]に書き込んでいる。
これが、$C1/8FA1でのジャンプ先、[$C1:901E]の「901E」部分になる。

$C1/901Eでは敵の向きをロードしている。
$40と論理積を取って分岐判定の場合、要するに右向きか左向きかの判定である。
ナムキャットは右向きなのでゼロフラグON、$C1/8FD4にジャンプする。
もし左向きだったら、$C1/8FF4にジャンプである。

実は、$C1/8FD4は、行動パターン$04の処理に合流である。
また、$C1/8FF4は、行動パターン$06の処理に合流である。
後でまとめるが、行動パターン$04の処理は「右に移動」で、行動パターン$06の処理は「左に移動」である。
ここからわかるが、行動パターン$08は、「敵が向いている方向の左または右に移動」という処理なのである。

ジャンプ先の$C1/8FD4では、[$00:1A2F](敵データ15 タイプ)と$04の論理積を取ってゼロフラグ判定なので、敵がOBJECT(しょく台や岩などの障害物)かどうかの判定である。
OBJECTは移動しないので、もしOBJECTならここで移動処理を終了する。
そうでない場合、つまりゼロフラグが立つならOBJECTではないので、$C1/E393にジャンプする。
この先が行動パターン$04「右に移動」の処理である。

$C1/E393 LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/E395 LDA $0004,x[$00:1B04]   ;Aに[$00:1B04](向き)をロード
$C1/E398 AND #$BF                ;Aと$BF(%1011 1111)で論理積
$C1/E39A STA $0004,x[$00:1B04]   ;Aを[$00:1B04]に書き込み
$C1/E39D LDA $000A,x[$00:1B0A]   ;Aに[$00:1B0A](X座標)をロード
$C1/E3A0 CMP #$06                ;Aと$06を減算比較(ステータスレジスタ変更のみ)
$C1/E3A2 BEQ $C7    [$E36B]      ;ゼロフラグが立っているとき[$E36B]分岐
;ゼロフラグOFF 右に移動可能なX座標位置
$C1/E3A4 INC A                   ;Aをインクリメント +1
$C1/E3A5 STA $0008,x[$00:1B08]   ;Aを[$00:1B08](計算用X座標)に書き込み
$C1/E3A8 LDA $000B,x[$00:1B0B]   ;Aに[$00:1B0B](Y座標)をロード
$C1/E3AB STA $0009,x[$00:1B09]   ;Aを[$00:1B09](計算用Y座標)に書き込み
$C1/E3AE JSR $41F2  [$C1:41F2]   ;[$C1:41F2]へジャンプ
;ゼロフラグON X座標が$06なので右移動不可
$C1/E36B JSR $E7BD  [$C1:E7BD]   ;[$C1:E7BD]へジャンプ

$C1/E393~から、移動先の判定を行う。

$C1/E393$C1/E39Aだが、向きの上2ビット目を削除という処理に等しい。つまり左向きだったら左向きではない状態、右向きに強制的に変更という意味である。
とはいえ、行動パターン$08だと、行動パターン$04の処理に入る前に右向き判定済みなので、ここで向きの値が変動することはない。

$C1/E39Dで敵のX座標をロードし、$06と減算比較してゼロフラグ判定だが、ここでゼロフラグが立つのはX座標が$06の場合、つまり戦闘フィールドの右隅にいる場合である。
この時はこれ以上右に移動できないから、別処理に進むのだが、ここではとりあえずナムキャットが原点(0,0)にいる前提で話を続ける。
右に移動可能であれば、X座標を+1した値を、[$00:1B08](計算用X座標)に書き込む。
Y座標はそのまま、[$00:1B09](計算用Y座標)に書き込む。
つまり、X座標のみ+1、右に1マス移動という処理をするということである。
ただ、移動先に移動可能かどうかを判定しなければならない。
その判定が次からである。

$C1/41F2 LDA #$10                ;Aに$10をロード
$C1/41F4 STA $13    [$00:0313]   ;A($10)を[$00:0313]に書き込み
$C1/41F6 LDA $0001,x[$00:1B01]   ;Aに[$00:1B01](戦闘状態)をロード
$C1/41F9 CMP #$20                ;Aと$20を減算比較(ステータスレジスタ変更のみ)
$C1/41FB BNE $02    [$41FF]      ;ゼロフラグが立っていないとき[$41FF]分岐
$C1/41FF TXA                     ;Xレジスタの値(敵番号)をAレジスタに転送
$C1/4200 LSR A                   ;Aを論理右シフト (/2)
$C1/4201 LSR A                   ;Aを論理右シフト (/2)
$C1/4202 LSR A                   ;Aを論理右シフト (/2)
$C1/4203 LSR A                   ;Aを論理右シフト (/2)
$C1/4204 INC A                   ;Aをインクリメント +1
$C1/4205 ORA $13    [$00:0313]   ;Aと[$00:0313]($10)で論理和
$C1/4207 STA $13    [$00:0313]   ;A($11)を[$00:0313]に書き込み
$C1/4209 JSR $4260  [$C1:4260]   ;[$C1:4260]へジャンプ

$C1/4260 LDY $0002,x[$00:1B02]   ;Yに[$00:1B02]をロード
$C1/4263 LDA $0004,y[$00:1A04]   ;Aに[$00:1A04](横幅)をロード
$C1/4266 STA $0A    [$00:030A]   ;Aを[$00:030A]に書き込み
$C1/4268 LDA $0005,y[$00:1A05]   ;Aに[$00:1A05](縦幅)をロード
$C1/426B STA $0B    [$00:030B]   ;Aを[$00:030B]に書き込み
$C1/426D STA $09    [$00:0309]   ;Aを[$00:0309]に書き込み
$C1/426F LDA #$08                ;Aに$08をロード
$C1/4271 SEC                     ;キャリーフラグON
$C1/4272 SBC $0A    [$00:030A]   ;A($08) - [$00:030A](横幅)
$C1/4274 STA $12    [$00:0312]   ;A($08-横幅)を[$00:0312]に書き込み
$C1/4276 RTS                     ;サブルーチン戻り

まず準備として、$C1/41F2$C1/4209の処理で、[$00:0313]に敵番号+1$10の論理和を入れる。
これは後々、戦闘フィールド7×7マスの状態$00:04C8$00:04FEの判定に使う値である。
敵が1人だけの場合は[$00:0313]$11が入る。

$C1/4260からはメモリアドレスに以下の値が入る。

アドレス
[$00:030A]敵の横幅
[$00:030B],[$00:0309]敵の縦幅
[$00:0312]$08-横幅
$C1/420C LDA $0008,x[$00:1B08]   ;Aに[$00:1B08](計算用X座標)をロード
$C1/420F CLC                     ;キャリーフラグクリア
$C1/4210 ADC $0A    [$00:030A]   ;A + [$00:030A](横幅)
$C1/4212 CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C1/4214 BCS $47    [$425D]      ;キャリーフラグが立っているとき[$425D]分岐
;キャリーフラグOFF (1マス移動しても敵横幅がフィールド外に出ない)
$C1/4216 LDA $0009,x[$00:1B09]   ;Aに[$00:1B09](計算用Y座標)をロード
$C1/4219 CLC                     ;キャリーフラグクリア
$C1/421A ADC $0B    [$00:030B]   ;A + [$00:030B](縦幅)
$C1/421C CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C1/421E BCS $3D    [$425D]      ;キャリーフラグが立っているとき[$425D]分岐
;キャリーフラグOFF (移動しても敵縦幅がフィールド外に出ない)
$C1/4220 LDA $0009,x[$00:1B09]   ;Aに[$00:1B09](計算用Y座標)をロード
$C1/4223 ASL A                   ;Aを算術左シフト *2
$C1/4224 ASL A                   ;Aを算術左シフト *2
$C1/4225 ASL A                   ;Aを算術左シフト *2
$C1/4226 ADC $0008,x[$00:1B08]   ;A + [$00:1B08](計算用X座標)
$C1/4229 STA $10    [$00:0310]   ;A(座標1バイト表示)を[$00:0310]に書き込み
$C1/422B STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
$C1/422D LDY $10    [$00:0310]   ;Yに[$00:0310](座標1バイト表示)をロード

移動先座標が入った[$00:1B08][$00:1B09]に、それぞれ敵の横幅と縦幅を加算し、$08を減算することで、負になるかどうか、つまり、移動することで敵の横幅・縦幅分がフィールド外にはみ出ることはないかどうかの判定を$C1/420C$C1/4220で行っている。
はみ出すなら移動はできないので、$C1/425Dにジャンプするが、

;キャリーフラグON(移動でフィールド外に出るので移動できない)
$C1/425D ORA #$FF                ;Aと$FFで論理和
$C1/425F RTS                     ;サブルーチン戻り

ここがジャンプ先でサブルーチンを抜ける。つまりここで移動は行わないことが決まる。
この先も$C1/425Dにジャンプする分岐があるが、いずれも移動不可でサブルーチンを抜けるということである。
ただ、$C1/425Fでサブルーチンを抜けると何もしない、ということではなく、行動パターン$0C(ランダム移動)を判定し、これでも移動できなければ行動パターン$13(ランダム向き変え)を行って処理終了となるのだが、これも後で説明する。

移動しても敵の横幅・縦幅分がフィールド内に収まるのなら、$C1/4220~に進む。
ここでは移動先座標の、X座標を下3ビット、Y座標をその上の3ビットに収めた数値を計算して、[$00:0310]に書き込んでいる。
この先で、$00:04C8$00:04FEの戦闘フィールドを確認していくが、原点にあたる$00:04C8[$00:0310]を加算すると、ちょうど敵の移動先座標となる仕組みである。

;ループ処理
;$00:04C8~$00:04FEの戦闘フィールドの値を確認する
$C1/422F LDA $0A    [$00:030A]   A:9001 X:1B00 Y:0001 P:envMxdIzc;Aに[$00:030A](横幅)をロード
$C1/4231 STA $08    [$00:0308]   A:9001 X:1B00 Y:0001 P:envMxdIzc;A(横幅)を[$00:0308]に書き込み
;横幅ループここから
$C1/4233 LDA $04C8,y             ;Aに[$00:04C8+y](移動先のマス)をロード
$C1/4236 INY                     ;Yをインクリメント +1
$C1/4237 AND #$1F                ;Aと$1Fで論理積
$C1/4239 CMP $13    [$00:0313]   ;Aと[$00:0313]($11)を減算比較(ステータスレジスタ変更のみ)
$C1/423B BEQ $04    [$4241]      ;ゼロフラグが立っているとき[$4241]分岐
;ゼロフラグOFF 移動先マスに自身はいない
$C1/423D BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/423F BNE $1C    [$425D]      ;ゼロフラグが立っていないとき[$425D]分岐(サブルーチン抜ける)
;$C1/423BでゼロフラグON
$C1/4241 DEC $08    [$00:0308]   ;[$00:0308](横幅)をデクリメント -1
$C1/4243 BNE $EE    [$4233]      ;ゼロフラグが立っていないとき[$4233]分岐
;横幅ループここまで
$C1/4245 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/4247 TYA                     ;Yレジスタの値をAレジスタに転送
$C1/4248 ADC $12    [$00:0312]   ;A + [$00:0312]($08-横幅)
$C1/424A AND #$00FF              ;Aと$00FFで論理積
$C1/424D TAY                     ;Aの値をYレジスタに転送
$C1/424E SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/4250 DEC $09    [$00:0309]   ;[$00:0309](縦幅)をデクリメント -1
$C1/4252 BNE $DB    [$422F]      ;ゼロフラグが立っていないとき[$422F]分岐
;ループ処理ここまで

$C1/4254 JSR $4277  [$C1:4277]   A:0019 X:1B00 Y:0019 P:envMxdIZc;[$C1:4277]へジャンプ

上のループ処理で、フィールド$00:04C8$00:04FEの移動先アドレスに、他キャラがいないかを確認している。
1マス移動するだけなので、敵サイズによっては、移動先に既に自身のアドレスがある場合もあるが、この場合は移動可能なので問題ない。
移動可能なのは、移動先に何もない($00)か、ダメージフィールドだけが入っている時、または$11(自分自身の一部)または$11にダメージフィールド分を加算した値が入っている場合。

$C1/423Bのゼロフラグ判定は、移動先に自分自身が入っているかどうかの判定である。
$C1/423Fのゼロフラグ判定は、移動先に自分自身以外のキャラがいるかどうかのため、もし他キャラがいる(ゼロフラグOFF)なら、移動不可でサブルーチンを抜ける($C1/425Dへ)。
こうして、ループ処理で自身の移動先マスを全てチェックし、$C1/423Fのゼロフラグ判定で$C1/425Dに抜けない限り、最終的にループを抜けて$C1/4254に到達する。
これで、移動先マスに移動可能なことが確定である。

$C1/4277 LDA $000B,x[$00:1B0B]   ;Aに[$00:1B0B](敵Y座標)をロード
$C1/427A ASL A                   ;Aを算術左シフト *2
$C1/427B ASL A                   ;Aを算術左シフト *2
$C1/427C ASL A                   ;Aを算術左シフト *2
$C1/427D ADC $000A,x[$00:1B0A]   ;A + [$00:1B0A](敵X座標)
$C1/4280 STA $14    [$00:0314]   ;A(移動前敵座標1バイト)を[$00:0314]に書き込み
$C1/4282 STZ $15    [$00:0315]   ;[$00:0315]に$00を書き込み
$C1/4284 LDY $14    [$00:0314]   ;Yに[$00:0314]をロード
$C1/4286 LDA $0B    [$00:030B]   ;Aに[$00:030B]をロード
$C1/4288 STA $09    [$00:0309]   ;Aを[$00:0309]に書き込み

;$00:04C8~$00:04FEの戦闘フィールド
;移動前のマスから敵位置のみ削除(ダメージフィールドある場合は残す)
;ループ処理
$C1/428A LDA $0A    [$00:030A]   ;Aに[$00:030A](横幅)をロード
$C1/428C STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
$C1/428E LDA $04C8,y[$00:04C8]   ;Aに[$00:04C8+y]をロード
$C1/4291 AND #$E0                ;Aと$E0で論理積
$C1/4293 STA $04C8,y[$00:04C8]   ;Aを[$00:04C8+y]に書き込み
$C1/4296 INY                     ;Yをインクリメント +1
$C1/4297 DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/4299 BNE $F3    [$428E]      ;ゼロフラグが立っていないとき[$428E]分岐
$C1/429B REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/429D TYA                     ;Yレジスタの値をAレジスタに転送
$C1/429E ADC $12    [$00:0312]   ;A + [$00:0312]
$C1/42A0 AND #$00FF              ;Aと$00FFで論理積
$C1/42A3 TAY                     ;Aの値をYレジスタに転送
$C1/42A4 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/42A6 DEC $09    [$00:0309]   ;[$00:0309]をデクリメント -1
$C1/42A8 BNE $E0    [$428A]      ;ゼロフラグが立っていないとき[$428A]分岐
;ループ処理ここまで

$C1/42AA RTS                     ;サブルーチン戻り

$C1/4257 JSR $42AB  [$C1:42AB]   ;[$C1:42AB]へジャンプ

移動が確定したので、ループ処理で、移動前のマスから敵位置のみ削除する。
$C1/428E$C1/4293で、マスの値と$E0で論理積を取ることにより、ダメージフィールドの値だけ残してキャラを削除できる。

$C1/42AB LDA $0B    [$00:030B]   ;Aに[$00:030B]をロード
$C1/42AD STA $09    [$00:0309]   ;Aを[$00:0309]に書き込み
$C1/42AF LDY $10    [$00:0310]   ;Yに[$00:0310]をロード

;$00:04C8~$00:04FEの戦闘フィールド
;移動先のマスに敵の値書き込み
;ループ処理
$C1/42B1 LDA $0A    [$00:030A]   ;Aに[$00:030A]をロード
$C1/42B3 STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
$C1/42B5 LDA $04C8,y[$00:04C9]   ;Aに[$00:04C9]をロード
$C1/42B8 AND #$E0                ;Aと$E0で論理積
$C1/42BA ORA $13    [$00:0313]   ;Aと[$00:0313]で論理和
$C1/42BC STA $04C8,y[$00:04C9]   ;Aを[$00:04C9]に書き込み
$C1/42BF INY                     ;Yをインクリメント +1
$C1/42C0 DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/42C2 BNE $F1    [$42B5]      ;ゼロフラグが立っていないとき[$42B5]分岐
$C1/42C4 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/42C6 TYA                     ;Yレジスタの値をAレジスタに転送
$C1/42C7 ADC $12    [$00:0312]   ;A + [$00:0312]
$C1/42C9 AND #$00FF              ;Aと$00FFで論理積
$C1/42CC TAY                     ;Aの値をYレジスタに転送
$C1/42CD SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/42CF DEC $09    [$00:0309]   ;[$00:0309]をデクリメント -1
$C1/42D1 BNE $DE    [$42B1]      ;ゼロフラグが立っていないとき[$42B1]分岐
;ループ処理ここまで

$C1/42D3 RTS                     ;サブルーチン戻り

$C1/425A AND #$00                ;Aと$00で論理積
$C1/425C RTS                     ;サブルーチン戻り

最後に、移動先のマスに敵の値を論理和で書き込む。
既にダメージフィールドがある場合、単純に上書きではフィールドの値が消されてしまうため、論理和である。
全てのマスにループ処理で書き込んで、移動処理終了となる。

これで、$C1/425AA$00を書き込み、サブルーチンを抜けるのだが、移動不可で$C1/425Dからサブルーチンを抜けた場合は、A$FFを書き込んでからサブルーチンを抜けていた。
その差が次の処理で出る。

$C1/E3B1 BNE $B8    [$E36B]      ;ゼロフラグが立っていないとき[$E36B]分岐
;ゼロフラグON(移動成功)
$C1/E3B3 BRL $0000  [$E3B6]      ;フラグにかかわりなく常に分岐[$E3B6]
$C1/E3B6 LDA #$08                ;Aに$08をロード
$C1/E3B8 STA $63    [$00:0363]   ;Aを[$00:0363]に書き込み
$C1/E3BA LDA #$80                ;Aに$80をロード
$C1/E3BC STA $0005,x[$00:1B05]   ;Aを[$00:1B05](技関係状態)に書き込み
$C1/E3BF LDA $0001,x[$00:1B01]   ;Aに[$00:1B01]をロード
$C1/E3C2 BIT #$20                ;Aと$20で論理積(ステータスフラグ変更のみ)
$C1/E3C4 BEQ $17    [$E3DD]      ;ゼロフラグが立っているとき[$E3DD]分岐
$C1/E3C6 JSR $E834  [$C1:E834]   ;[$C1:E834]へジャンプ

$C1/E3B1で、移動したかどうかで分岐になる。
ゼロフラグが立つ時は移動に成功したことになり、敵の[$00:1B05](技関係状態)に$80を書き込んで、移動状態であることを記録する。

$C1/E36B JSR $E7BD  [$C1:E7BD]   ;[$C1:E7BD]へジャンプ

ひとまず移動についての紹介はここまでとする。
上で紹介したのはあくまでも行動パターンの中のひとつに過ぎない。
次ページで、行動パターン別にどういう挙動を取るかを紹介する。



このページをシェアする

上へ