TOP > プログラミング関係解説&調査 > 敵ターゲット決定方法

敵ターゲット決定方法

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

攻撃技の中で、範囲攻撃かつ、多段ヒットする場合、範囲内に複数の敵がいる時には1ヒットずつ目標をどの敵にするか乱数で決定している。
ここでは、どのようなサブルーチンで処理しているかを紹介する。
攻撃技のヒット数の判断は前ページにある通りだが、ヒット数が決定した後は、どの敵に命中するかの判定を行っている。

実際にはヒット数判断前に、技の範囲内にいる敵に対し、1から順にナンバリングする(当ページでは便宜上「範囲攻撃用敵番号」とする)という処理も行われているのだが、ここでは説明を省く。
ヒット数判断の手順は、

  1. 使用技の攻撃範囲内にいる敵の総数をカウント
  2. ヒット数が1以上なら、ヒット数ごとに戦闘乱数を計算し、「範囲攻撃用敵番号」のどの番号の敵に当たるかを計算

となる。
実は、攻撃範囲が1×1でも、ヒット数が1でも、戦闘乱数は計算され、どの敵に当てるかを計算している。その場合は当たる敵が1体のみなので、確率100%で該当の敵に当たるという判定になる。
つまりどの攻撃技でも共通の処理になる。

以下、前ページで、ヒット数パターンの判断とヒット数が決定した後($C1/D725より後)の処理を紹介していく。
決定したヒット数は、[$00:0310]に入っている。

$C1/D723 LDA #$FF                ;
$C1/D725 RTS                     ;
;
$C1/D5F5 BEQ $04    [$D5FB]      ;ゼロフラグが立っているとき[$D5FB]分岐
$C1/D5F7 JSR $D726  [$C1:D726]   ;[$C1:D726]にジャンプ
;
$C1/D726 LDA $10    [$00:0310]   ;Aに[$00:0310](ヒット数)をロード
$C1/D728 BNE $01    [$D72B]      ;ゼロフラグが立っていないとき[$D72B]分岐
$C1/D72B STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
$C1/D72D STZ $09    [$00:0309]   ;[$00:0309]に$00を書き込み
$C1/D72F LDY #$0000              ;Yに$0000を書き込み
$C1/D732 LDX #$1AF0              ;Xに$1AF0を書き込み
$C1/D735 JSR $E1A9  [$C1:E1A9]   ;[$C1:E1A9]にジャンプ

まず、「使用技の攻撃範囲内にいる敵の総数をカウント」というループ処理のサブルーチンの前の準備を行う。
$C1/D726で、計算をし終えたヒット数[$00:0310]Aにロードし、ゼロフラグが立つ、つまりヒット数が0でない場合は処理が進む。
ヒット数が0ならミスだから、当然、処理をする必要はない。
ループ処理のサブルーチンの前に、ヒット数[$00:0310][$00:0308]に書き込まれ、更に[$00:0309]$00を書き込む。
初期値として$00が入った[$00:0309]には、使用技の攻撃範囲内にいる敵の数が+1されていき、最終的には攻撃範囲内の敵の総数になる。

飛び先[$C1:E1A9]からが、敵1体ごとのループ処理になるのだが、その前に[$00:1B?1][$7E:AB?1]について説明する。
戦闘において、敵は最大15体まで出現するが、便宜上敵番号0~敵番号Eと番号をつける。
敵1体ごとに各種データが記録されているが、敵番号部分を?とした時に、

  • [$00:1B?1]:戦闘状態 戦闘離脱$00/敵操作状態$40/敵総数+1の敵番号$80/通常$FF
  • [$7E:AB?1]:攻撃/回復技の対象の範囲内にいる敵には順に$01~(範囲攻撃用敵番号)が入り、範囲外だと$00

このような値が入る。

[$00:1B?1]はキャラの状態にあたり、通常は$FFが入る。
ブリキ大王操作時のように、本来敵として登録されているキャラを操作する時は$40が入るが、攻撃相手(敵)に$40が入ることはない。
$80が入る状況だが、例えば出現した敵が合計3体だと、実際にはいない4体目の[$00:1B31]$80が入るようになっている。
つまり、敵の総数の判断をするための値と考えて良い。例えばループ処理で[$00:1B?1]の値をロードして$80だったら、ループ処理を停止する、という分岐のために使う。

[$7E:AB??]に入るのは、技使用中の各種計算値なのだが、[$7E:AB?1]には、先に説明した通り、ヒット数計算前に行われた、「使用技の範囲内にいる敵に$01から順に番号を入れる」「範囲外の敵に$FFを入れる」という処理の結果の数値が入っている。
以降、「範囲攻撃用敵番号」とする。

ここでは例として、敵が3体出現し、その中の敵番号0と敵番号2の2体が範囲攻撃の範囲内、敵番号1だけ範囲外にいる、という状況だと処理がどうなるかを紹介する。
この場合、「範囲攻撃用敵番号」には、

  • [$7E:AB01] = $01
  • [$7E:AB11] = $00
  • [$7E:AB21] = $02

というように値が入っている。

[$00:1B?1]は、本来いない4体目に$80が入るため、

  • [$00:1B01] = $FF
  • [$00:1B11] = $FF
  • [$00:1B21] = $FF
  • [$00:1B31] = $80

である。

以上を踏まえた上で続きを見ていただきたい。
また、先の$C1/D72F$0000が入ったYレジスタ、$C1/D732$1AF0が入ったXレジスタの値も、後々のために併記する。

$C1/E1A9 REP #$21                ;X:1AF0 Y:0000 Aを16bit幅に変更、キャリーフラグクリア
$C1/E1AB TXA                     ;X:1AF0 Y:0000 Xレジスタの値をAレジスタに転送
$C1/E1AC ADC #$0010              ;X:1AF0 Y:0000 A + $0010
$C1/E1AF TAX                     ;X:1AF0 Y:0000 Aレジスタの値をXレジスタに転送
$C1/E1B0 SEP #$20                ;X:1B00 Y:0000 MフラグON Aレジスタは8bit幅
$C1/E1B2 CPX #$1C00              ;X:1B00 Y:0000 Xレジスタと$1C00を減算比較
$C1/E1B5 BEQ $11    [$E1C8]      ;X:1B00 Y:0000 ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1B7 LDA $0001,x[$00:1B01]   ;X:1B00 Y:0000 [$00:1B01](敵番号0の戦闘状態 = $FF)をロード
$C1/E1BA BIT #$40                ;X:1B00 Y:0000 Aと$40で論理積(ステータスフラグ変更のみ)
$C1/E1BC BNE $07    [$E1C5]      ;X:1B00 Y:0000 ゼロフラグが立っていないとき[$E1C5]分岐
$C1/E1C5 LDA #$7F                ;X:1B00 Y:0000 Aに$7Fをロード
$C1/E1C7 RTS                     ;X:1B00 Y:0000 サブルーチン戻り
;
$C1/D738 BEQ $FB    [$D735]      ;X:1B00 Y:0000 ゼロフラグが立っているとき[$D735]分岐
$C1/D73A BMI $0F    [$D74B]      ;X:1B00 Y:0000 ネガティブフラグが立っているとき[$D74B]分岐
$C1/D73C LDA $7E9001,x[$7E:AB01] ;X:1B00 Y:0000 Aに[$7E:AB01](敵番号0の「範囲攻撃用敵番号」 = $01)をロード
$C1/D740 BEQ $07    [$D749]      ;X:1B00 Y:0000 ゼロフラグが立っているとき[$D749]分岐
$C1/D742 INC $09    [$00:0309]   ;X:1B00 Y:0000 [$00:0309]($00)をインクリメント(+1)
$C1/D744 TXA                     ;X:1B00 Y:0000 Xレジスタの値($00)をAレジスタに転送
$C1/D745 STA $0450,y[$00:0450]   ;X:1B00 Y:0000 A($00)を[$00:0450]に書き込み
$C1/D748 INY                     ;X:1B00 Y:0000 Yレジスタ($0000)をインクリメント(+1)
$C1/D749 BRA $EA    [$D735]      ;X:1B00 Y:0001 フラグにかかわりなく常に分岐[$D735]
$C1/D735 JSR $E1A9  [$C1:E1A9]   ;X:1B00 Y:0001 [$C1:E1A9]へジャンプ(ループ戻り)

以上が敵番号0への処理で、$C1/D735[$C1:E1A9]にジャンプ、つまり処理の最初へ戻り、次の敵番号の処理に移っている。
$C1/E1AB$C1/E1AFの処理で、「Xレジスタ + $0010」をXレジスタに入れている。要するにXの値を+$0010である。
これにより、X$1B00となっている。

$C1/E1B7[$00:1B01](敵番号0の戦闘状態)をロードし、$40と論理積を取っている。
次でゼロフラグが立っているかどうかで分岐しているが、戦闘状態と$40で論理積を取った時に$00になるのは、戦闘離脱$00か、敵総数+1の敵番号$80の時である。
通常状態$FFの時はゼロフラグが立たず先に進むが、もし本来いない4体目の敵のループ時だと$80だから、ループから抜け出すことになる。

ループが続く場合、A$7Fをロードして更に続く。
この$7Fという値は処理分岐用で、$C1/D738ではゼロフラグが立っていないことになり、$C1/D73Aではネガティブフラグが立っていないという判定になって、$C1/D73Cへ進む。

$C1/D73Cで、A[$7E:AB01]、敵番号0の「範囲攻撃用敵番号」をロードしている。
次でゼロフラグで分岐だが、「範囲攻撃用敵番号」は、技の範囲外の敵だと$00が入り、範囲内の敵は$01から順にナンバリングされているため、ここで攻撃範囲外の敵だと処理が飛ばされて$C1/D749へ進む。
範囲内の敵の場合は、$C1/D742に進み、[$00:0309]をインクリメント、つまり+1している。
先に記したとおり、[$00:0309]には初期値として$00が入っており、範囲内に敵がいるとインクリメントするため、「範囲内の敵がいると+1されるアドレス」である。
$C1/D744では、Yレジスタの値$0000Aに転送しているが、Aは8bitモードだから下2桁の$00だけがAに転送される。
$C1/D745で、$0450+Yのアドレス、ここでは[$00:0450]にAの$00を書き込む。
$C1/D748で、Yレジスタ($0000)をインクリメント(+1)するので、ここでY = $0001となり、ループ処理である。

結果として、敵番号0の処理は、戦闘状態判定後、「範囲攻撃用敵番号」から、技の範囲内か範囲外かを判定し、範囲内なら以下の処理をする。範囲外なら行わない。
Yの初期値$0000[$00:0309]の初期値$00で、

  1. 範囲内の敵の数をカウントアップする[$00:0309]の値を+1する。
  2. [$0450+Y]に、Xの下2桁の値を書き込む。
  3. Y+1する。

となる。

では、続いてループ処理の2回目、敵番号1の処理を見てみる。
敵番号1の敵は、技の範囲外にいる。

$C1/E1A9 REP #$21                ;X:1B00 Y:0001 Aを16bit幅に変更、キャリーフラグクリア
$C1/E1AB TXA                     ;X:1B00 Y:0001 Yレジスタの値をAレジスタに転送
$C1/E1AC ADC #$0010              ;X:1B00 Y:0001 A + $0010
$C1/E1AF TAX                     ;X:1B00 Y:0001 Aレジスタの値をXレジスタに転送
$C1/E1B0 SEP #$20                ;X:1B10 Y:0001 MフラグON Aレジスタは8bit幅
$C1/E1B2 CPX #$1C00              ;X:1B10 Y:0001 Xレジスタと$1C00を減算比較
$C1/E1B5 BEQ $11    [$E1C8]      ;X:1B10 Y:0001 ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1B7 LDA $0001,x[$00:1B11]   ;X:1B10 Y:0001 [$00:1B11](敵番号1の戦闘状態 = $FF)をロード
$C1/E1BA BIT #$40                ;X:1B10 Y:0001 Aと$40で論理積(ステータスフラグ変更のみ)
$C1/E1BC BNE $07    [$E1C5]      ;X:1B10 Y:0001 ゼロフラグが立っていないとき[$E1C5]分岐
$C1/E1C5 LDA #$7F                ;X:1B10 Y:0001 Aに$7Fをロード
$C1/E1C7 RTS                     ;X:1B10 Y:0001 サブルーチン戻り
;
$C1/D738 BEQ $FB    [$D735]      ;X:1B10 Y:0001 ゼロフラグが立っているとき[$D735]分岐
$C1/D73A BMI $0F    [$D74B]      ;X:1B10 Y:0001 ネガティブフラグが立っているとき[$D74B]分岐
$C1/D73C LDA $7E9001,x[$7E:AB11] ;X:1B10 Y:0001 Aに[$7E:AB11](敵番号1の「範囲攻撃用敵番号」 = $00)をロード
$C1/D740 BEQ $07    [$D749]      ;X:1B10 Y:0001 ゼロフラグが立っているとき[$D749]分岐
$C1/D749 BRA $EA    [$D735]      ;X:1B10 Y:0001 フラグにかかわりなく常に分岐[$D735]
$C1/D735 JSR $E1A9  [$C1:E1A9]   ;X:1B10 Y:0001 [$C1:E1A9]へジャンプ(ループ戻り)

$C1/E1AB$C1/E1AFの処理で、Xの値を+$0010し、X = 1B10となった。
この計算はループの度に行われているが、アドレスの呼び出し先を+10ずつしているということである。
変化したのは$C1/D73C以降で、技範囲外の敵であるから、$C1/D73Cでゼロフラグが立ち、$C1/D742$C1/D748がスキップされてループが終わる。

続いて、敵番号2の処理を見てみる。敵番号2の敵は技の範囲内である。

$C1/E1A9 REP #$21                ;X:1B10 Y:0001 Aを16bit幅に変更、キャリーフラグクリア
$C1/E1AB TXA                     ;X:1B10 Y:0001 Yレジスタの値をAレジスタに転送
$C1/E1AC ADC #$0010              ;X:1B10 Y:0001 A + $0010
$C1/E1AF TAX                     ;X:1B10 Y:0001 Aレジスタの値をXレジスタに転送
$C1/E1B0 SEP #$20                ;X:1B20 Y:0001 MフラグON Aレジスタは8bit幅
$C1/E1B2 CPX #$1C00              ;X:1B20 Y:0001 Xレジスタと$1C00を減算比較
$C1/E1B5 BEQ $11    [$E1C8]      ;X:1B20 Y:0001 ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1B7 LDA $0001,x[$00:1B21]   ;X:1B20 Y:0001 [$00:1B21](敵番号2の戦闘状態 = $FF)をロード
$C1/E1BA BIT #$40                ;X:1B20 Y:0001 Aと$40で論理積(ステータスフラグ変更のみ)
$C1/E1BC BNE $07    [$E1C5]      ;X:1B20 Y:0001 ゼロフラグが立っていないとき[$E1C5]分岐
$C1/E1C5 LDA #$7F                ;X:1B20 Y:0001 Aに$7Fをロード
$C1/E1C7 RTS                     ;X:1B20 Y:0001 サブルーチン戻り
;
$C1/D738 BEQ $FB    [$D735]      ;X:1B20 Y:0001 ゼロフラグが立っているとき[$D735]分岐
$C1/D73A BMI $0F    [$D74B]      ;X:1B20 Y:0001 ネガティブフラグが立っているとき[$D74B]分岐
$C1/D73C LDA $7E9001,x[$7E:AB21] ;X:1B20 Y:0001 Aに[$7E:AB21](敵番号2の「範囲攻撃用敵番号」 = $02)をロード
$C1/D740 BEQ $07    [$D749]      ;X:1B20 Y:0001 ゼロフラグが立っているとき[$D749]分岐
$C1/D742 INC $09    [$00:0309]   ;X:1B20 Y:0001 [$00:0309]($01)をインクリメント(+1)
$C1/D744 TXA                     ;X:1B20 Y:0001 Xレジスタの値($20)をAレジスタに転送
$C1/D745 STA $0450,y[$00:0451]   ;X:1B20 Y:0001 A($20)を[$00:0451]に書き込み
$C1/D748 INY                     ;X:1B20 Y:0001 Yレジスタ($0001)をインクリメント(+1)
$C1/D749 BRA $EA    [$D735]      ;X:1B20 Y:0002 フラグにかかわりなく常に分岐[$D735]
$C1/D735 JSR $E1A9  [$C1:E1A9]   ;X:1B20 Y:0002 [$C1:E1A9]へジャンプ(ループ戻り)

こちらは敵番号0と同一の処理である。
X = $1B20となり、範囲内の敵の数が入る[$00:0309]+1され、$02になった。
$C1/D745の[$0450+Y]は[$00:0451]であり、Xレジスタの値($20)が書き込まれた。
$C1/D748INYで、Y$0002に増加し、ループ処理が終了である。
だがこの時点では、[$C1:E1A9]に戻される。

$C1/E1A9 REP #$21                ;X:1B20 Y:0002 Aを16bit幅に変更、キャリーフラグクリア
$C1/E1AB TXA                     ;X:1B20 Y:0002 Yレジスタの値をAレジスタに転送
$C1/E1AC ADC #$0010              ;X:1B20 Y:0002 A + $0010
$C1/E1AF TAX                     ;X:1B20 Y:0002 Aレジスタの値をXレジスタに転送
$C1/E1B0 SEP #$20                ;X:1B30 Y:0002 MフラグON Aレジスタは8bit幅
$C1/E1B2 CPX #$1C00              ;X:1B30 Y:0002 Xレジスタと$1C00を減算比較
$C1/E1B5 BEQ $11    [$E1C8]      ;X:1B30 Y:0002 ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1B7 LDA $0001,x[$00:1B31]   ;X:1B30 Y:0002 [$00:1B31](敵番号3の戦闘状態 = $80)をロード
$C1/E1BA BIT #$40                ;X:1B30 Y:0002 Aと$40で論理積(ステータスフラグ変更のみ)
$C1/E1BC BNE $07    [$E1C5]      ;X:1B30 Y:0002 ゼロフラグが立っていないとき[$E1C5]分岐
$C1/E1BE CMP #$80                ;X:1B30 Y:0002 Aと$80で論理積
$C1/E1C0 BEQ $06    [$E1C8]      ;X:1B30 Y:0002 ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1C8 LDA #$80                ;X:1B30 Y:0002 Aに$80をロード
$C1/E1CA RTS                     ;X:1B30 Y:0002 サブルーチン戻り

実際にはいない敵4体目(敵番号3)の処理。X = $1B30である。
敵番号3の戦闘状態[$00:1B31]には、存在していないことを示す$80が入っているため、$C1/E1BA$40と論理積を取ると0となりゼロフラグが立つ。
このため、$C1/E1BC[$E1C5]にジャンプせず、ここでループ処理を抜ける。
$C1/E1BE$80と論理積を取ることで、ゼロフラグが立ち、[$E1C8]にジャンプする。
$C1/E1C8A$80をロードし(この先の処理分岐用)、処理は次に進む。

ここまでで、

  • [$00:0309]:範囲内の敵の総数($02
  • [$00:0450]$00
  • [$00:0451]$20

が入ったことになる。

$C1/D738 BEQ $FB    [$D735]      ;ゼロフラグが立っているとき[$D735]分岐
$C1/D73A BMI $0F    [$D74B]      ;ネガティブフラグが立っているとき[$D74B]分岐
$C1/D74B LDA $09    [$00:0309]   ;Aに[$00:0309](範囲内の敵の総数 = $02)をロード
$C1/D74D BEQ $35    [$D784]      ;ゼロフラグが立っているとき[$D784]分岐
$C1/D74F LDX #$1B00              ;Xに$1B00をロード
$C1/D752 STX $14    [$00:0314]   ;X($1B00)を[$00:0314]に書き込み
$C1/D754 STZ $13    [$00:0313]   ;[$00:0313]に$00を書き込み
$C1/D756 JSR $6150  [$C1:6150]   ;[$C1:6150]へ

A$80が入っているので、$C1/D738はゼロフラグが立っていない、$C1/D73Aでネガティブフラグが立っている(2進数で一番上の桁に1が立っている)分岐になり、$C1/D74Bに進む。
$C1/D74Bで、A[$00:0309]、つまり範囲内の敵の総数をロードする。
X($1B00)を[$00:0314]に、$00[$00:0313]に書き込んだ後、戦闘乱数生成のサブルーチンである[$C1:6150]にジャンプする。

$C1/6150 PHX                     ;戦闘乱数計算
$C1/6151 PHB                     ;↓
$C1/6152 LDA #$00                ;↓
$C1/6154 PHA                     ;↓
$C1/6155 PLB                     ;↓
$C1/6156 LDA $38    [$00:0338]   ;↓
$C1/6158 ORA $39    [$00:0339]   ;↓
$C1/615A ORA $3A    [$00:033A]   ;↓
$C1/615C ORA $3B    [$00:033B]   ;↓
$C1/615E BNE $24    [$6184]      ;↓
$C1/6184 LDX $38    [$00:0338]   ;↓
$C1/6186 STX $4204  [$00:4204]   ;↓
$C1/6189 LDA #$0D                ;↓
$C1/618B STA $4206  [$00:4206]   ;↓
$C1/618E JSR $46C8  [$C1:46C8]   ;↓
;
$C1/46C8 NOP                     ;↓
$C1/46C9 RTS                     ;↓
;
$C1/6191 LDA $39    [$00:0339]   ;↓
$C1/6193 STA $38    [$00:0338]   ;↓
$C1/6195 LDA $3A    [$00:033A]   ;↓
$C1/6197 STA $39    [$00:0339]   ;↓
$C1/6199 LDA $3B    [$00:033B]   ;↓
$C1/619B STA $3A    [$00:033A]   ;↓
$C1/619D LDA $4214  [$00:4214]   ;↓
$C1/61A0 STA $3B    [$00:033B]   ;↓
$C1/61A2 PLB                     ;↓
$C1/61A3 PLX                     ;↓
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/D759 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/D75C LDA #$00                ;Aに$00をロード
$C1/D75E STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/D761 LDA $09    [$00:0309]   ;Aに[$00:0309](範囲内の敵の総数)をロード
$C1/D763 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/D766 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/D769 LDA $2135  [$00:2135]   ;Aに[$00:2135](乗算結果24bitの中央1バイト)をロード
$C1/D76C STA $12    [$00:0312]   ;Aを[$00:0312]に書き込み
$C1/D76E LDY $12    [$00:0312]   ;Yに[$00:0312]をロード
$C1/D770 LDA $0450,y[$00:0450]   ;Aに[$0450,y]($00:0450)をロード
$C1/D773 STA $14    [$00:0314]   ;Aを[$00:0314]に書き込み
$C1/D775 LDX $14    [$00:0314]   ;Xに[$00:0314]をロード
$C1/D777 LDA $7E9002,x[$7E:AB02] ;Aに[$7E9002,x]をロード
$C1/D77B INC A                   ;A+1
$C1/D77C STA $7E9002,x[$7E:AB02] ;Aを[$7E9002,x]に書き込み
$C1/D780 DEC $08    [$00:0308]   ;[$00:0308](ヒット数)をデクリメント(-1)
$C1/D782 BNE $D2    [$D756]      ;ゼロフラグが立っていないとき[$D756]分岐
$C1/D756 JSR $6150  [$C1:6150]   ;戦闘乱数計算サブルーチンへ

$C1/6150$C1/61A4は、戦闘用乱数生成のサブルーチンであり、これまでに解説済みなので説明を省く。
生成した乱数($00$FF)は、A[$00:033B]に入る。

乱数計算後は、$C1/D759から符号付16bit x 8bitの乗算を行っている。
符号付16bit x 8bitは、[$00:211B]に被乗数の下位バイトをセット、もう一度[$00:211B]に被乗数の上位バイトをセット、[$00:211C]に乗数をセットすると、乗算結果24bit(16進数6桁)が、上から順に[$00:2136][$00:2135][$00:2134]に1バイトずつ入る仕組みである。
よって、$C1/D759$C1/D75Eで、被乗数$00????は乱数)、$C1/D761[$00:0309](範囲内の敵の総数)を乗数としてセットしている。
要するに「乱数×範囲内の敵の総数」の乗算である。
乗算の結果だが、$C1/D769で、[$00:2135]Aにロードしている。
これは乗算結果24bitの中央の1バイト部分、下から3桁目と4桁目の部分である。
乗算結果が$123456だったら、中央の$34のところにあたるが、要するに乗算結果の小数点を2つ左にズラした値であり、$100で割った商部分に当たる。
計算式だと、

trunc(乱数×範囲内の敵の総数/$100)

である。
この値は、[$00:0312]に書き込まれる。

さて、trunc(乱数×範囲内の敵の総数/$100)とは何だろうか。
今回は、範囲内の敵の総数が$02だから、

trunc(乱数×範囲内の敵の総数/$100)
= trunc(「$00~$FFの乱数」*$02/$100)

  • 乱数が$00$79だと、trunc(「$00~$FFの乱数」*$02/$100)$00
  • 乱数が$80$FFだと、trunc(「$00~$FFの乱数」*$02/$100)$01

と、ちょうど1/2の確率で、$00$01のどちらかになる。
この後の処理を見るとわかるが、$C1/D76Eではこの値をYにロードし、$C1/D770で[$0450,y]を呼び出している。
Y$00$01のどちらかになるはずだから、呼び出されるのは、

  • [$00:0450]$00
  • [$00:0451]$20

このどちらかだ。
ロードした値は[$00:0314]に入り、$C1/D775[$00:0314]からXにロードされる。
Xには$C1/D74Fで、$1B00が書き込まれており、[$00:0314]からXにロードされるのは、下2桁分、$00$20のどちらかである。
ここでXが、$1B00と、$1B20のどちらかになった。
そして$C1/D777で、[$7E9002,x]をロードしている。
つまり、[$7E9002,x]は、[$7E:AB02]か、[$7E:AB22]のどちらかになる。
呼び出されたアドレスに、$C1/D77Bでは+1している。

ここまでで、乱数によって選ばれたアドレス[$7E:AB?2]+1される、ということがわかる。
[$7E:AB?2]?は敵番号に対応しており、このアドレスにヒット数が加算されていく。

$C1/D780で、[$00:0308](ヒット数)をデクリメント(-1)し、ゼロフラグが立っていないのなら再び乱数計算を行っている。
多段ヒット技なら、ここで1ヒット分の処理が終わり、2ヒット目の処理へループすることになる。
単発ヒット技なら、ここで[$00:0308](ヒット数)が0になるので、ループしない。
このループ処理をヒット数の回数だけ繰り返すことで、[$7E:AB02]か、[$7E:AB22]に値が+1ずつされて、最終的にそれぞれの敵に何ヒットするかが決まる。
乱数次第で、片方の敵にヒット数が集中することもあり得る、ということがわかる。

では、ヒット数の回数だけループ処理が終わった後を以下で見てみる。
つまり、$C1/D780[$00:0308]をデクリメント(-1)して0となった後である。

$C1/D780 DEC $08    [$00:0308]   ;
$C1/D782 BNE $D2    [$D756]      ;
$C1/D784 RTS                     ;サブルーチン戻り
;
$C1/D5FA RTS                     ;サブルーチン戻り
;
$C1/D587 JSR $D7D9  [$C1:D7D9]   ;[$C1:D7D9]にジャンプ
;
$C1/D7D9 LDX #$1AF0              ;Xに$1AF0をロード
$C1/D7DC JSR $E1A9  [$C1:E1A9]   ;[$C1:E1A9]にジャンプ
;
$C1/E1A9 REP #$21                ;Aレジスタは16bit幅, Cフラグクリア
$C1/E1AB TXA                     ;Xレジスタの値をAレジスタに転送
$C1/E1AC ADC #$0010              ;A + $0010
$C1/E1AF TAX                     ;Aレジスタの値をXレジスタに転送
$C1/E1B0 SEP #$20                ;Aレジスタは8bit幅, CフラグON
$C1/E1B2 CPX #$1C00              ;Xレジスタと$1C00を減算比較
$C1/E1B5 BEQ $11    [$E1C8]      ;ゼロフラグが立っているとき[$E1C8]分岐
$C1/E1B7 LDA $0001,x[$00:1B01]   ;[$00:1B01](敵番号0の戦闘状態 = $FF)をロード
$C1/E1BA BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C1/E1BC BNE $07    [$E1C5]      ;ゼロフラグが立っていないとき[$E1C5]分岐
$C1/E1C5 LDA #$7F                ;Aに$7Fをロード
$C1/E1C7 RTS                     ;サブルーチン戻り
;
$C1/D7DF BEQ $FB    [$D7DC]      ;ゼロフラグが立っているとき[$D7DC]分岐
$C1/D7E1 BMI $05    [$D7E8]      ;ネガティブフラグが立っているとき[$D7E8]分岐
$C1/D7E3 JSR $D7E9  [$C1:D7E9]   ;[$C1:D7E9]にジャンプ
;
$C1/D7E9 STX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/D7EB LDA $7E9002,x[$7E:AB02] ;Aに[$7E:AB02](敵番号0へのヒット数)をロード
$C1/D7EF BNE $01    [$D7F2]      ;ゼロフラグが立っていないとき[$D7F2]分岐
$C1/D7F2 STA $0F    [$00:030F]   ;Aを[$00:030F]に書き込み
$C1/D7F4 JSR $D870  [$C1:D870]   ;[$C1:D870]にジャンプ
;
;命中判定に続く

各敵へのヒット数が決まった後は、ヒット数ループ処理の中にもあった、$C1/E1A9$C1/E1C7でもう一度敵番号0の戦闘状態を確認していることがわかる。
この後の$C1/D7E9からが、先程までの計算で決まった、敵番号0へのヒット数が入っている[$7E:AB02]の読み込みである。
$C1/D7EFで、ゼロフラグが立っているかどうか確認している。敵番号0へのヒット数が0、一切攻撃されない場合は$C1/D7F2からの処理がスキップになる。
ゼロフラグが立たない、つまり敵番号0へのヒット数が1以上なら、ヒット数を[$00:030F]に書き込んでから処理が続くのだが、この先の処理は既に紹介済みの命中判定処理である。
つまりこの先で、1ヒット毎の命中判定を行う。
敵番号0の命中判定が終わったら、続いて敵番号1に同じ処理をするが、今回は敵番号1は技の範囲外なので、[$7E:AB12]に入っている値が$00のままであり、命中判定がスキップされる。
次の敵番号2は、技の範囲内なので、[$7E:AB22]1以上の値が入っているかもしれない。乱数次第で、敵番号0に全ヒットしているかもしれないからである。1以上なら命中判定である。

というように処理が続いていくのである。
当コンテンツでは順番が前後してしまい申し訳ないが、

攻撃技のヒット数判定

ヒット数毎に命中する敵の判定(このページ)

各敵に対し、1ヒットずつ命中判定

という順序で処理が行われているということがわかった。


最後に、
trunc(乱数×範囲内の敵の総数/$100)
の計算について、もう少し解説を加えておく。
今回は範囲内の敵の総数が2の前提で説明をした。
実際には最大で$0E(10進数15)が敵の総数となる可能性がある。
最終編で敵の数が多い雑魚戦(ツナヨシ×1+おイヌ様×14や、ティタンブラッド×1+ピスタチオ×14など)と戦闘を行う場合に、多段ヒットで攻撃範囲が5×5と広めの「ハリケンショット」「砂ジンの術」で攻撃する時、うまく範囲内に収められれば範囲内の敵の総数が15になる。
どれだけ数が多くても、各敵が選ばれる確率は(ほぼ)同一である。

例えば範囲内の敵の総数が7体の時は、
trunc(「$00~$FFの乱数」×$07/$100)
計算結果は$00$06のいずれかになる。

乱数(カッコ内10進数)計算結果確率
$00$240~36$0037/256
$25$4937~73$0137/256
$4A$6D74~109$0236/256
$6E$92110~146$0337/256
$93$B6147~182$0436/256
$B7$DB183~219$0537/256
$DC$FF220~255$0636/256

乱数$00$FF、つまり256個の乱数を7で割る場合は端数が出てしまうため、7体すべての確率が同一にはならないことがわかる。
敵の数が256の約数だったら確率は同一になるが、そうでなければ確率は同一にはならない、ということである。
(このあたりは、「ランダムに減」パターンのヒット数計算で、各ヒット数の出る確率が少しずつズレるのと同じ考え方である)
といっても、ズレはわずかである。
37/256が約14.4%で、36/256が約14.1%である。
大雑把に言えば、各敵1/7(約14.2%)の確率で選ばれる、と考えても特に問題はない。



このページをシェアする

上へ