基礎知識
戦闘関係
攻撃技の中で、範囲攻撃かつ、多段ヒットする場合、範囲内に複数の敵がいる時には1ヒットずつ目標をどの敵にするか乱数で決定している。
ここでは、どのようなサブルーチンで処理しているかを紹介する。
攻撃技のヒット数の判断は前ページにある通りだが、ヒット数が決定した後は、どの敵に命中するかの判定を行っている。
実際にはヒット数判断前に、技の範囲内にいる敵に対し、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レジスタの値$0000をAに転送しているが、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で、
[$00:0309]の値を+1する。[$0450+Y]に、Xの下2桁の値を書き込む。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/D748のINYで、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/E1C8でAに$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)
と、ちょうど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~$24(0~36) | $00 | 37/256 |
| $25~$49(37~73) | $01 | 37/256 |
| $4A~$6D(74~109) | $02 | 36/256 |
| $6E~$92(110~146) | $03 | 37/256 |
| $93~$B6(147~182) | $04 | 36/256 |
| $B7~$DB(183~219) | $05 | 37/256 |
| $DC~$FF(220~255) | $06 | 36/256 |
乱数$00~$FF、つまり256個の乱数を7で割る場合は端数が出てしまうため、7体すべての確率が同一にはならないことがわかる。
敵の数が256の約数だったら確率は同一になるが、そうでなければ確率は同一にはならない、ということである。
(このあたりは、「ランダムに減」パターンのヒット数計算で、各ヒット数の出る確率が少しずつズレるのと同じ考え方である)
といっても、ズレはわずかである。
37/256が約14.4%で、36/256が約14.1%である。
大雑把に言えば、各敵1/7(約14.2%)の確率で選ばれる、と考えても特に問題はない。