TOP > プログラミング関係解説&調査 > 敵ターンにおける行動選択 > 技の範囲判定

敵ターンにおける行動選択 > 技の範囲判定

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

前ページの敵ターンにおける行動選択に続き、攻撃技が選択された場合の技の範囲判定・標的判定についてこのページで説明する。

技の範囲判定

$C1/3D2Bから先の処理だが、まず、$C1/3D2B$C1/3D38の処理は、フィールド関係の値を計算するためのメモリアドレス$00:0480$00:04BFを初期値$00で埋めるサブルーチンである。
$00:04????部分が以下に対応。

8081828384858687
88898A8B8C8D8E8F
9091929394959697
98999A9B9C9D9E9F
A0A1A2A3A4A5A6A7
A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7
B8B9BABBBCBDBEBF

上の状態だと8×8マスになるのだが、戦闘のフィールドは7×7マスである。
実際には$00:0488を原点(0,0)とした、白背景マス部分が7×7フィールド位置に対応している。
灰色背景部分はフィールド外になる。

その後は$C1/E26Cに進む。

$C1/E269 JSR $3D2B  [$C1:3D2B]   ;[$C1:3D2B]へジャンプ
;
$C1/3D2B LDA #$00                ;Aに$00をロード
;中略
;$00:0480~$00:04BFを初期値$00で埋める
$C1/3D38 RTS                     ;サブルーチン戻り

$C1/E26C JSR $3924  [$C1:3924]   ;[$C1:3924]へジャンプ

$C1/3924 LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/3926 LDY $0002,x[$00:1B12]   ;Yに[$00:1B12]をロード
$C1/3929 LDA $0005,y[$00:1A05]   ;Aに[$00:1A05](敵の縦の幅)をロード
$C1/392C STA $0D    [$00:030D]   ;Aを[$00:030D](敵の縦の幅)に書き込み
$C1/392E DEC A                   ;Aをデクリメント -1
$C1/392F ASL A                   ;Aを算術左シフト *2
$C1/3930 ASL A                   ;Aを算術左シフト *2
$C1/3931 ASL A                   ;Aを算術左シフト *2
$C1/3932 STA $19    [$00:0319]   ;Aを[$00:0319]に書き込み (縦幅-1)*8
$C1/3934 LDA $0004,y[$00:1A04]   ;Aに[$00:1A04](敵の横の幅)をロード
$C1/3937 STA $0B    [$00:030B]   ;Aを[$00:030B](敵の横の幅)に書き込み
$C1/3939 DEC A                   ;Aをデクリメント -1
$C1/393A STA $18    [$00:0318]   ;Aを[$00:0318]に書き込み (横幅-1)
$C1/393C LDX $68    [$00:0368]   ;Xに[$00:0368]をロード
$C1/393E LDA $D50006,x           ;Aに[$D50006,x](技データ06 技の方向)をロード
$C1/3942 STA $12    [$00:0312]   ;Aを[$00:0312]に書き込み
$C1/3944 LDA $D50007,x           ;Aに[$D50007,x](技データ07)をロード
$C1/3948 STA $13    [$00:0313]   ;Aを[$00:0313]に書き込み
$C1/394A LDY $AC    [$00:03AC]   ;Yに[$00:03AC]をロード
$C1/394C LDA $000B,y[$00:1B1B]   ;Aに[$00:1B1B](Y座標)をロード
$C1/394F ASL A                   ;Aを算術左シフト *2
$C1/3950 ASL A                   ;Aを算術左シフト *2
$C1/3951 ASL A                   ;Aを算術左シフト *2
$C1/3952 ADC $000A,y[$00:1B1A]   ;A + [$00:1B1A](X座標)
$C1/3955 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/3957 AND #$00FF              ;Aと$00FFで論理積
$C1/395A STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/395C STA $1A    [$00:031A]   ;Aを[$00:031A]に書き込み
$C1/395E SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/3960 LDA $D5000D,x           ;Aに[$D5000D,x](技データ13)をロード
$C1/3964 BIT #$20                ;Aと$20で論理積(ステータスフラグ変更のみ)
$C1/3966 BNE $24    [$398C]      ;ゼロフラグが立っていないとき[$398C]分岐
;ゼロフラグON 周囲攻撃ではない
$C1/3968 LDA $D50007,x           ;Aに[$D50007,x](技データ07)をロード 
$C1/396C BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/396E BEQ $14    [$3984]      ;ゼロフラグが立っているとき[$3984]分岐
;ゼロフラグOFF 射程が0ではない
$C1/3970 JSR $3B8E  [$C1:3B8E]   ;[$C1:3B8E]へジャンプ
;
$C1/3B8E LDX $68    [$00:0368]   ;Xに[$00:0368]をロード
$C1/3B90 LDA $D5000D,x           ;Aに[$D5000D,x](技データ13)をロード
$C1/3B94 BIT #$80                ;Aと$80で論理積(ステータスフラグ変更のみ)
$C1/3B96 BEQ $18    [$3BB0]      ;ゼロフラグが立っているとき[$3BB0]分岐
;ゼロフラグON (回復技ではない)
$C1/3BB0 RTS                     ;サブルーチン戻り

;ゼロフラグOFF 射程が0
$C1/3984 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/3986 LDA #$F0                ;Aに$F0をロード
$C1/3988 STA $0488,x[$00:474E]   ;Aを[$00:474E]に書き込み
$C1/398B RTS                     ;サブルーチン戻り

;ゼロフラグOFF 周囲攻撃
$C1/398C LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/398E JSR $3AB2  [$C1:3AB2]   ;[$C1:3AB2]へジャンプ

$C1/3924$C1/3970は、この先の計算・判定で必要となるパラメータのコピー及び分岐処理である。

コピー先内容
[$00:030D]敵の縦幅
[$00:030B]敵の横幅
[$00:0319](縦幅-1)*8
[$00:0318](横幅-1)
[$00:0312]技データ06
技の方向
[$00:0313]技データ07
上1~2ビット:自身依存係数, 上3~4ビット:自身依存ステータス, 下4ビット:射程
[$00:0310]
[$00:031A]
敵座標(左上基準)
下3ビットがX座標、その上3ビットがY座標(%00yy yxxx)

この先も座標表記の方法は8ビットで%00yy yxxxとなる点に注意。
下3ビット分にX座標で0~6(%000~%110)、その上の3ビットにY座標で0~6(%000~%110)が入るのだが、この8ビットの数値をメモリアドレス$00:0480のアドレスに加算すると、該当マスのアドレスを示すという点も重要である。

以上のコピーが$C1/395Cまで。
$C1/395Eから判定となる。
まず、技データ13 & $20のゼロフラグ判定がある。
技データ13は、上4ビットに周囲攻撃なら$2(%0010)、反撃専用なら$4(%0100)、回復なら$8(%1000)が入るので、$20(%0010 0000)と論理積を取ってゼロフラグ判定する場合、ゼロフラグが立つのは周囲攻撃以外である。
周囲攻撃の場合、$C1/398Cにジャンプして別処理になる。
今回はひとまず、周囲攻撃以外の場合の処理でこのまま説明を続ける。

周囲攻撃ではないのなら、$C1/3968に進む。
この先は技データ07 & $0Fでゼロフラグ判定である。
技データ07は、上1~2ビット:自身依存係数, 上3~4ビット:自身依存ステータス, 下4ビット:射程であり、$0Fとの論理積だから下4ビットの判定である。
技データ07の下4ビットは射程にあたり、射程0だと味方への攻撃技ではない(射程なし)。
射程が0ではないのなら$C1/3970に進み、[$C1:3B8E]へジャンプして処理が続く。

$C1/3B8E~でもう一度技データ13を判定しているが、今回は$80と論理積を取ってゼロフラグ判定である。
つまり回復技かどうかの判定で、既に$C1/9258$C1/92C2で回復技ではないという判定済みのため、通常はゼロフラグが立って$C1/3BB0に進み、サブルーチンが終了となる。
続きは、$C1/3970からの戻り位置の$C1/3973である。

;向き判定
$C1/3973 LDA $0004,y[$00:1B14]   ;Aに[$00:1B14](向き)をロード
$C1/3976 AND #$C0                ;Aと$C0で論理積
$C1/3978 BEQ $1D    [$3997]      ;ゼロフラグが立っているとき[$3997]分岐
;ゼロフラグON 右下$00
($C1/3997 JSR $39FB  [$C1:39FB]   ;[$C1:39FB]へジャンプ)
;
$C1/397A CMP #$40                ;Aと$40を減算比較(ステータスレジスタ変更のみ)
$C1/397C BEQ $32    [$39B0]      ;ゼロフラグが立っているとき[$39B0]分岐
;ゼロフラグON 左下$40
($C1/39B0 JSR $3A0C  [$C1:3A0C]   ;[$C1:3A0C]へジャンプ)
;
$C1/397E CMP #$80                ;Aと$80を減算比較(ステータスレジスタ変更のみ)
$C1/3980 BEQ $47    [$39C9]      ;ゼロフラグが立っているとき[$39C9]分岐
;ゼロフラグON 右上$80
($C1/39C9 JSR $3A23  [$C1:3A23]   ;[$C1:3A23]へジャンプ)
;
;左上$C0
$C1/3982 BRA $5E    [$39E2]      ;フラグにかかわりなく常に分岐[$39E2]
($C1/39E2 JSR $3A1B  [$C1:3A1B]   ;[$C1:3A1B]へジャンプ)

$C1/3973[$00:1Bn4]、敵の向き(右下$00, 左下$40, 右上$80, 左上$C0)をロードし、敵の向きにより処理先が分岐する。

向き飛び先
右下$00$C1/39FB
左下$40$C1/3A0C
右上$80$C1/3A23
左上$C0$C1/3A1B

それぞれの向きでの処理を見てみよう。

右下向き

;向き 右下$00
$C1/39FB LDX $1A    [$00:031A]   ;Xに[$00:031A](敵座標)をロード
$C1/39FD STX $10    [$00:0310]   ;Xを[$00:0310]に書き込み
$C1/39FF LDA $10    [$00:0310]   ;Aに[$00:0310](敵座標)をロード
$C1/3A01 CLC                     ;キャリーフラグクリア
$C1/3A02 ADC $18    [$00:0318]   ;A + [$00:0318](横幅-1)
$C1/3A04 ADC $19    [$00:0319]   ;A + [$00:0319](縦幅-1)*8
$C1/3A06 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/3A08 JSR $3C1A  [$C1:3C1A]   ;[$C1:3C1A]へジャンプ

$C1/3C1A LDX #$0009              ;Xに$0009をロード
$C1/3C1D BRA $1E    [$3C3D]      ;フラグにかかわりなく常に分岐[$3C3D]
$C1/3C3D LSR $12    [$00:0312]   ;[$00:0312](技データ06)を論理右シフト (/2)
$C1/3C3F BCS $01    [$3C42]      ;キャリーフラグが立っているとき[$3C42]分岐
;キャリーフラグOFF
$C1/3C41 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/3C42 STZ $08    [$00:0308]   ;[$00:0308]に$00を書き込み
$C1/3C44 STX $14    [$00:0314]   ;Xを[$00:0314]に書き込み
$C1/3C46 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/3C48 JSR $3C61  [$C1:3C61]   ;[$C1:3C61]へジャンプ
左下向き

;向き 左下$40
$C1/3A0C LDX $1A    [$00:031A]   ;Xに[$00:031A](敵座標)をロード
$C1/3A0E STX $10    [$00:0310]   ;Xを[$00:0310]に書き込み
$C1/3A10 LDA $10    [$00:0310]   ;Aに[$00:0310](敵座標)をロード
$C1/3A12 CLC                     ;キャリーフラグクリア
$C1/3A13 ADC $19    [$00:0319]   ;A + [$00:0319](縦幅-1)*8
$C1/3A15 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/3A17 JSR $3C24  [$C1:3C24]   ;[$C1:3C24]へジャンプ

$C1/3C24 LDX #$0007              ;Xに$0007をロード
$C1/3C27 BRA $14    [$3C3D]      ;フラグにかかわりなく常に分岐[$3C3D]
$C1/3C3D LSR $12    [$00:0312]   ;[$00:0312](技データ06)を論理右シフト (/2)
$C1/3C3F BCS $01    [$3C42]      ;キャリーフラグが立っているとき[$3C42]分岐
;キャリーフラグOFF
$C1/3C41 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/3C42 STZ $08    [$00:0308]   ;[$00:0308]に$00を書き込み
$C1/3C44 STX $14    [$00:0314]   ;Xを[$00:0314]に書き込み
$C1/3C46 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/3C48 JSR $3C61  [$C1:3C61]   ;[$C1:3C61]へジャンプ
右上向き

;向き 右上$80
$C1/3A23 LDX $1A    [$00:031A]   ;Xに[$00:031A](敵座標)をロード
$C1/3A25 STX $10    [$00:0310]   ;Xを[$00:0310]に書き込み
$C1/3A27 LDA $10    [$00:0310]   ;Aに[$00:0310](敵座標)をロード
$C1/3A29 CLC                     ;キャリーフラグクリア
$C1/3A2A ADC $18    [$00:0318]   ;A + [$00:0318](横幅-1)
$C1/3A2C STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/3A2E JSR $3C38  [$C1:3C38]   ;[$C1:3C38]へジャンプ

$C1/3C38 LDX #$FFF9              ;Xに$FFF9をロード
$C1/3C3B BRA $00    [$3C3D]      ;フラグにかかわりなく常に分岐[$3C3D]
$C1/3C3D LSR $12    [$00:0312]   ;[$00:0312](技データ06)を論理右シフト (/2)
$C1/3C3F BCS $01    [$3C42]      ;キャリーフラグが立っているとき[$3C42]分岐
;キャリーフラグOFF
$C1/3C41 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/3C42 STZ $08    [$00:0308]   ;[$00:0308]に$00を書き込み
$C1/3C44 STX $14    [$00:0314]   ;Xを[$00:0314]に書き込み
$C1/3C46 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/3C48 JSR $3C61  [$C1:3C61]   ;[$C1:3C61]へジャンプ
左上向き

;向き 左上$C0
$C1/3A1B LDX $1A    [$00:031A]   ;Xに[$00:031A](敵座標)をロード
$C1/3A1D STX $10    [$00:0310]   ;Xを[$00:0310]に書き込み
$C1/3A1F JSR $3C2E  [$C1:3C2E]   ;[$C1:3C2E]へジャンプ

$C1/3C2E LDX #$FFF7              ;Xに$FFF7をロード
$C1/3C31 BRA $0A    [$3C3D]      ;フラグにかかわりなく常に分岐[$3C3D]
$C1/3C3D LSR $12    [$00:0312]   ;[$00:0312](技データ06)を論理右シフト (/2)
$C1/3C3F BCS $01    [$3C42]      ;キャリーフラグが立っているとき[$3C42]分岐
;キャリーフラグOFF
$C1/3C41 RTS                     ;サブルーチン戻り
;キャリーフラグON
$C1/3C42 STZ $08    [$00:0308]   ;[$00:0308]に$00を書き込み
$C1/3C44 STX $14    [$00:0314]   ;Xを[$00:0314]に書き込み
$C1/3C46 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/3C48 JSR $3C61  [$C1:3C61]   ;[$C1:3C61]へジャンプ

以上が向きによる4パターンの処理なのだが、結局何をしているのかというと、敵が向いている方向の角にあたる座標の計算である。
敵の座標は左上マスが基準なので、例えば横2マス、縦3マスサイズの敵なら、

★■
■■
■■

★の位置、左上を座標としており、座標の値は現時点では[$00:031A][$00:0310]に入っている。
だが、技を出す場合、向いている方向を基準として技の方向や範囲が決まる。
例えば敵が右下向きなら、

■■
■■
■★

右下の★の位置を基準として、どのマスに技を出せるのかを決める。
真正面1マス方向に出す技なら、★マスからX軸に+1、Y軸に+1した座標が技の出せる位置、というような計算である。
そのための計算が、上の4パターンのサブルーチンである。
この★位置の座標を、便宜上、技原点座標とする。
左上向きなら敵の座標 = 技原点座標だから、敵の幅などの加算の必要がない。

最終的に、$C1/3C3Dから処理が合流している。
その直前に、方向ごとにXに読み込まれる値([$00:0315][$00:0314]にコピー)が変わり、

向きXにロードする値
右下$0009
左下$0007
右上$FFF9
左上$FFF7

となっているが、これらは後々で技原点座標に加算する値である(座標をマイナスする必要がある場合もあるので、符号付き16ビット整数)。

$C1/3C3D~は、[$00:0312](技データ06)を論理右シフトしてキャリーフラグ判定をしている。
技データ06には技の方向が入っており、8ビットがそれぞれ出せる向きにあたる。
例えば右下向きの場合、上から1ビット、2ビット……と番号をつけると、

ビット向き
1
2右上
3
4左上
5
6左下
7
8右下

に対応している。
論理右シフトしてキャリーフラグが立つのは、一番下のビット、つまり右下方向(真正面)に1が立っている時で、この場合は$C1/3C42に進むが、そうでない場合は$C1/3C41でサブルーチンを抜ける。
この先で、真正面方向に技を出せるのならどのマスになるかを計算していくのが$C1/3C42~、真正面方向に技が出せないなら次の方向(上から7ビット目の下方向)の判定へ移るためサブルーチンを抜けた、ということである。
つまりこの後、各方向に対し、その方向へ技を出せるか([$00:0312](技データ06)の論理右シフトのキャリーフラグで判定)、更に技データ06(技の方向)でどのマスまで該当か、といったことを判定していくのだが、サブルーチンが長くなるので適当に端折りつつ説明する。

ここでは、右下向きで真正面方向に技が出せる、つまり$C1/3C3FでキャリーフラグがONになった後をざっと説明する。

$C1/3C61 LDA $08    [$00:0308]   ;Aに[$00:0308]をロード
$C1/3C63 BEQ $4B    [$3CB0]      ;ゼロフラグが立っているとき[$3CB0]分岐
;ゼロフラグON(真正面判定時)
$C1/3CB0 INC $08    [$00:0308]   ;[$00:0308]をインクリメント +1
$C1/3CB2 RTS                     ;サブルーチン戻り
;
$C1/3C4B REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/3C4D TXA                     ;Xレジスタの値をAレジスタに転送
$C1/3C4E ADC $14    [$00:0314]   ;A(技原点座標) + [$00:0314]($0009)
$C1/3C50 TAX                     ;Aの値をXレジスタに転送
$C1/3C51 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/3C53 AND #$07                ;Aと$07(%0000 0111)で論理積
$C1/3C55 CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/3C57 BEQ $07    [$3C60]      ;ゼロフラグが立っているとき[$3C60]分岐
$C1/3C59 TXA                     ;Xレジスタの値をAレジスタに転送
$C1/3C5A AND #$38                ;Aと$38(%0011 1000)で論理積
$C1/3C5C CMP #$38                ;Aと$38を減算比較(ステータスレジスタ変更のみ)
$C1/3C5E BNE $E8    [$3C48]      ;ゼロフラグが立っていないとき[$3C48]分岐

$C1/3C48 JSR $3C61  [$C1:3C61]   ;[$C1:3C61]へジャンプ

$C1/3C61 LDA $08    [$00:0308]   ;Aに[$00:0308]($01)をロード
$C1/3C63 BEQ $4B    [$3CB0]      ;ゼロフラグが立っているとき[$3CB0]分岐
$C1/3C65 CMP #$01                ;Aと$01を減算比較(ステータスレジスタ変更のみ)
$C1/3C67 BEQ $10    [$3C79]      ;ゼロフラグが立っているとき[$3C79]分岐
;[$00:0308]=$01
$C1/3C79 LDA $13    [$00:0313]   ;Aに[$00:0313](技データ07 下4ビット:射程)をロード
$C1/3C7B BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C1/3C7D BEQ $31    [$3CB0]      ;ゼロフラグが立っているとき[$3CB0]分岐
;射程1が範囲内である
$C1/3C7F BRA $10    [$3C91]      ;フラグにかかわりなく常に分岐[$3C91]
$C1/3C91 LDA #$F0                ;Aに$F0をロード
$C1/3C93 STA $0488,x[$00:04AA]   ;Aを[$00:04AA]に書き込み
$C1/3C96 JSR $3CE7  [$C1:3CE7]   ;[$C1:3CE7]へジャンプ

$C1/3C4Eで、技原点座標に、方向ごとに[$00:0315][$00:0314]に入った値を加算する。
右下向きの時に加算するのは$0009 (%0000 1001)であり、これはX座標を+1、Y座標を+1することを意味する。
その結果が、真正面方向1マスの座標にあたるのだが、7×7のフィールドからはみ出していないか$C1/3C51$C1/3C5Eで判定している。
はみ出していない場合、$C1/3C79で射程が1であるかを判定し、ここで該当のマスが射程範囲内であることが決まる。
ここではあくまでも範囲内という判定だけで、そこに味方キャラがいるかどうかは判定していない。
$C1/3C91~で、$00:0488$00:04BEに対応した座標位置に$F0を書き込む。

$00:04????部分が以下に対応。

8081828384858687
88898A8B8C8D8E8F
9091929394959697
98999A9B9C9D9E9F
A0A1A2A3A4A5A6A7
A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7
B8B9BABBBCBDBEBF

という手順で、技範囲にのみ$F0を書き込んでいく。

さて、すべての技範囲の処理を終えたことにして、その後の処理を見てみる。
$C1/E26C JSR $3924の戻り先から続きになる。

$C1/E26F LDX $68    [$00:0368]   ;Xに[$00:0368]をロード
$C1/E271 LDY $AC    [$00:03AC]   ;Yに[$00:03AC]をロード
$C1/E273 LDA $D50011,x           ;Aに[$D50011,x](技データ17)をロード
$C1/E277 AND #$F0                ;Aと$F0で論理積
$C1/E279 STA $0006,y             ;Aを[$00:1Bn6](技のタメ時間)に書き込み
$C1/E27C LDA $D5000D,x           ;Aに[$D5000D,x](技データ13)をロード
$C1/E280 BIT #$20                ;Aと$20で論理積(ステータスフラグ変更のみ)
$C1/E282 BEQ $03    [$E287]      ;ゼロフラグが立っているとき[$E287]分岐
$C1/E287 BIT #$80                ;Aと$80で論理積(ステータスフラグ変更のみ)
$C1/E289 BEQ $08    [$E293]      ;ゼロフラグが立っているとき[$E293]分岐
$C1/E293 JSR $2857  [$C1:2857]   ;[$C1:2857]へジャンプ
;
$C1/2857 LDA #$10                ;Aに$10をロード
$C1/2859 BRA $02    [$285D]      ;フラグにかかわりなく常に分岐[$285D]
$C1/285D STA $12    [$00:0312]   ;A($10)を[$00:0312]に書き込み
$C1/285F JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
;戦闘乱数計算
$C1/6150 PHX                     ;戦闘乱数計算
;(省略)
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/2862 AND #$3F                ;A(乱数$00~$FF)と$3Fで論理積
$C1/2864 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/2866 STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
$C1/2868 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/286A LDA #$38                ;Aに$38をロード
$C1/286C STA $08    [$00:0308]   ;A($38)を[$00:0308]に書き込み

$C1/E273で技データ17をロードし、$F0と論理積を取っている。
技データ17の上4ビットは技の発動時間(タメ時間)である。
このタメ時間を[$00:1Bn6](敵番号nの技のタメ時間)に書き込み、ここから技発動までの時間がカウントになる。

$C1/E27C$C1/E293は、技データ13から周囲攻撃や回復技ではないかの判定。

$C1/2857~は、[$00:0312]$10を書き込んでから、戦闘乱数を生成している。
戦闘乱数と$3F (%0011 1111)で論理積を取った値が[$00:0310]に入るので、[$00:0310]$00$3F(10進数0~63)ということになる。
更に、[$00:0311]に00を書き込み、X[$00:0311][$00:0310]をロードしているから、Xは$0000~$003Fのいずれかである。
最後に[$00:0308]$38を書き込んでから、ループ処理に入る。

;ループ処理ここから
$C1/286E CPX #$0038              ;Xと$0038を減算比較(ステータスレジスタ変更のみ)
$C1/2871 BCC $03    [$2876]      ;キャリーフラグが立っていないとき[$2876]分岐
;キャリーフラグON(減算の結果が0か正)
$C1/2873 LDX #$0000              ;Xに$0000をロード
;キャリーフラグOFF(減算の結果が負)
$C1/2876 LDA $0488,x             ;Aに[$0488,x]をロード
$C1/2879 BEQ $0F    [$288A]      ;ゼロフラグが立っているとき[$288A]分岐
;ゼロフラグOFF 敵の攻撃範囲内
$C1/287B LDA $04C8,x             ;Aに[$04C8,x]をロード
$C1/287E EOR $12    [$00:0312]   ;Aと[$00:0312]($10)で排他的論理和XOR
$C1/2880 BIT #$10                ;Aと$10で論理積(ステータスフラグ変更のみ)
$C1/2882 BEQ $06    [$288A]      ;ゼロフラグが立っているとき[$288A]分岐
;ゼロフラグOFF(上4ビットが0)
$C1/2884 BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/2886 BEQ $02    [$288A]      ;ゼロフラグが立っているとき[$288A]分岐
;ゼロフラグOFF(下4ビットが0以外、味方キャラで確定。ループを抜ける)
$C1/2888 BRA $08    [$2892]      ;フラグにかかわりなく常に分岐[$2892]
;ゼロフラグON
$C1/288A INX                     ;Xをインクリメント +1
$C1/288B DEC $08    [$00:0308]   ;[$00:0308](初期値$38)をデクリメント -1
$C1/288D BNE $DF    [$286E]      ;ゼロフラグが立っていないとき[$286E]分岐
;ループ処理ここまで

ループ全体を見ると、ループ処理の最後でX+1[$00:0308](初期値$38)をデクリメント(-1)している。
それを踏まえた上で最初を見ると、X$0038を減算比較して、キャリーフラグが立つかどうかで分岐がある。
Xは直前の乱数により、$0000$003Fのいずれかのはずであるが、X$38以上ならX$0000をロードしろ、という処理である。
また、ループ処理でXをインクリメントしていく都合で、Xの初期値次第で$38を越えることもあるが、その場合も$0000をロードすることになる。
よって、ここでXの値は$0000$0037に収まることになる。

$C1/2876では$0488,xをロードする。
Xの値により、$00:0488$00:04BFのいずれかになる。
つまり、先ほど、技の範囲内だったら$F0を書き込んだフィールドマスに対応したアドレスの範囲内である。
ロードしたアドレスの値が$00なら、技範囲外だから、このマスに誰がいても技が当たらないので、これ以上判定する必要はなく、$C1/2879$C1/288Aに抜けて、ループ処理の最後に到達する。
一方、技範囲内なら$F0が書き込まれているから、ゼロフラグが立たず、$C1/287Bに進む。
ここでロードする$04C8,xは、戦闘フィールドマスの現在の状態が記された$00:04C8$00:04FFであり、7×7マスに対応している。
$00:04????の値部分が以下。

C8C9CACBCCCDCECF
D0D1D2D3D4D5D6D7
D8D9DADBDCDDDEDF
E0E1E2E3E4E5E6E7
E8E9EAEBECEDEEEF
F0F1F2F3F4F5F6F7
F8F9FAFBFCFDFEFF

入る数値は以下の通り。

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

味方キャラが居るのなら、上4ビットが$0、下4ビットが$1$4のいずれかである。
その判定を行っているのが、$C1/287B$C1/2886で、$C1/2886でゼロフラグが立たない場合、そのマスに味方キャラがいることが確定で、ループを抜ける。
そうでない場合はループの最後に到達するが、X+1することで、次のループ時に呼び出すアドレスを+1し続ける、という仕組みである。

ループ回数は[$00:0308]0になるまで、つまり最大で$38回だが、ループが終了した時点で、「技の範囲内に味方キャラはいなかった」ことが確定である。
その場合、以下処理でループを抜け、技を使用しないことが決まる。

$C1/288F LDA #$00                ;Aに$00をロード
$C1/2891 RTS                     ;サブルーチン戻り

一方、技範囲内に味方がいる場合、$C1/2892に処理が続く。
つまり、攻撃技を当てる相手が決まったことになる。
また、この時点でのXの値がそのまま攻撃する座標でもある(下3ビットがX座標、その上3ビットがY座標)。
$C1/2892以降は以下の通り。

;$C1/2886のゼロフラグOFFでループ処理抜ける
$C1/2888 BRA $08    [$2892]      ;フラグにかかわりなく常に分岐[$2892]
$C1/2892 STX $10    [$00:0310]   ;Xを[$00:0310]に書き込み
$C1/2894 LDA $10    [$00:0310]   ;Aに[$00:0310]をロード
$C1/2896 AND #$07                ;Aと$07(%0000 0111)で論理積
$C1/2898 STA $ED    [$00:03ED]   ;Aを[$00:03ED]に書き込み
$C1/289A LDA $10    [$00:0310]   ;Aに[$00:0310]をロード
$C1/289C AND #$38                ;Aと$38(%0011 1000)で論理積
$C1/289E LSR A                   ;Aを論理右シフト (/2)
$C1/289F LSR A                   ;Aを論理右シフト (/2)
$C1/28A0 LSR A                   ;Aを論理右シフト (/2)
$C1/28A1 STA $EE    [$00:03EE]   ;Aを[$00:03EE]に書き込み
$C1/28A3 LDA #$FF                ;Aに$FFをロード
$C1/28A5 RTS                     ;サブルーチン戻り

[$00:0310]$07(%0000 0111)で論理積を取った値を[$00:03ED]に、$38(%0011 1000)と論理積を取って$8で割った値を[$00:03EE]に書き込んでいる。
これが攻撃する座標のX座標[$00:03ED]とY座標[$00:03EE]である。

この後の戻り先が$C1/E296だが、戻った時点で、技の範囲内に味方キャラはいなかった時はA$00、いた時はA$FFが入っているため、処理が分岐する。

$C1/E296 BEQ $27    [$E2BF]      ;ゼロフラグが立っているとき[$E2BF]分岐
;ゼロフラグOFF 範囲内に味方キャラがいる
$C1/E298 BRL $0029  [$E2C4]      ;フラグにかかわりなく常に分岐[$E2C4]
$C1/E2C4 LDX $68    [$00:0368]   ;Xに[$00:0368]をロード
$C1/E2C6 LDA $D5000D,x           ;Aに[$D5000D,x](技データ13)をロード
$C1/E2CA LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/E2CC BIT #$20                ;Aと$20で論理積(ステータスフラグ変更のみ)
$C1/E2CE BNE $0C    [$E2DC]      ;ゼロフラグが立っていないとき[$E2DC]分岐
;ゼロフラグON 周囲攻撃ではない
$C1/E2D0 LDA $ED    [$00:03ED]   ;Aに[$00:03ED](攻撃する味方キャラのX座標)をロード
$C1/E2D2 STA $7E9102,x           ;Aを[$7E:ACn2]に書き込み
$C1/E2D6 LDA $EE    [$00:03EE]   ;Aに[$00:03EE](攻撃する味方キャラのY座標)をロード
$C1/E2D8 STA $7E9103,x           ;Aを[$7E:ACn3]に書き込み
;ゼロフラグOFF 周囲攻撃
$C1/E2DC LDA #$00                ;Aに$00をロード
$C1/E2DE STA $7E9101,x           ;Aを[$7E:ACn1]に書き込み
$C1/E2E2 STZ $63    [$00:0363]   ;[$00:0363]に$00を書き込み
$C1/E2E4 LDA #$40                ;Aに$40をロード
$C1/E2E6 STA $0005,x             ;Aを[$00:1Bn5](技関係状態に$40)に書き込み
$C1/E2E9 RTS                     ;サブルーチン戻り

;$C1/E296でゼロフラグON 範囲内に味方キャラがいない
$C1/E2BF LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/E2C1 LDA #$00                ;Aに$00をロード
$C1/E2C3 RTS                     ;サブルーチン戻り

この後は命中判定やダメージ量計算などに続くことになるが、ひとまず紹介はここまでとしておく。

まとめ

ここまでを整理してみると、攻撃技の範囲から標的を決める場合、

  1. 乱数で、判定初期マス$00:0488$00:04BFを決める。
  2. アドレスを+1しながら、技範囲と味方のいるマスを探し、最初に見つかったマス位置の味方キャラが攻撃対象になる。

と、いうことになる。

技の範囲だが、射程上のマスは判定対象になっているものの、効果範囲は考慮されていない。
技データ12の下2ビットに、技の範囲(1×1、3×3、5×5)が記録されているのだが、3×3や5×5における射程上以外のマスは判定から外れている。
これにより、例えば敵が真正面方向射程1、範囲3×3の技を使うとしても、下図で■の位置に味方がいないと技を使わないことになる。

□□□□
敵□□
□■味
□□□

実際には範囲3×3なら、黄色背景マスが範囲だから、「味」の位置に味方キャラがいる場合、■に技を撃てば巻き込むことができるのに、敵はそういう方法で攻撃をしてこない。
これが、味方側が攻撃する時、敵が周囲攻撃を使わない限り、3×2対角線位置から攻撃すれば敵から攻撃を食らわないという、本作の仕様の正体である。

もうひとつ重要な点として、上の処理から、技範囲に複数の味方キャラがいた場合、どのようにして標的を決めるのかがわかった。

例えば幅1マス×縦2マス幅の敵が右下を向いていて、射程が1~2の攻撃技を使うとしたら、下図で攻撃範囲は■マスである。

□□□□□□□
□敵■■□□□
□敵■■□□□
□■■□□□□
□■□■□□□
□□□□□□□
□□□□□□□

AとBに味方キャラがいる時、どちらが攻撃されるかは、$C1/2876でロードされた$0488,x$00:0488$00:04BF)の位置次第である。
Aより右の黄色背景マスが選ばれた時だけ、Bが先に判定に引っかかるから、Bが攻撃されるし、それ以外のマスだったらAが選ばれる。
このため、Aの方が攻撃対象に選ばれる確率が高い。
一応覚えておくと役立つ知識かもしれない。

□□□□□□□
□敵■■□□□
□敵■A□□□
□B■□□□□
□■□■□□□
□□□□□□□
□□□□□□□

判定開始マスは乱数$00$3F(10進数0~63)で決まるが、ループ処理開始時に、「乱数が$38以上なら$00に変更」という処理が入っている。
つまり、

  • 乱数$00,$38$3Fなら、座標(0,0)が判定開始マス
  • それ以外は、座標(0,0)+乱数したマスが判定開始マス

だから、座標(0,0)が選ばれる確率は他のマスより高いことがわかる。
他のマスは1/64の確率なのに、(0,0)だけ、9/64の確率になる。

ただ、8×8で初期マスを計算する都合で、フィールド外の一番右端の列が選ばれた時は、実質その次のマスからの判定と見なしても良いため、(0,0)以外の一番左の列も2/64の確率と考えても良い。

88898A8B8C8D8E8F
9091929394959697
98999A9B9C9D9E9F
A0A1A2A3A4A5A6A7
A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7
B8B9BABBBCBDBEBF

一番右端部分の$00:048F, $00:0497, ……, $00:04BFが該当のマスで、乱数次第で、このフィールド外マスが判定開始マスに選ばれることもあるが、ここには誰もいないので、次のマスから判定開始と考えても良いことになる。

次ページでは、敵ターンにおける行動選択において、使用技判定時に、回復技が選択されたパターンを紹介する。



このページをシェアする

上へ