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

敵ターンにおける行動選択 > 回復技判定

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

敵ターンにおける行動選択において、使用技判定時に、回復技が選択されたパターンについて、このページで説明する。

回復技判定

$C1/9258で技データ13をロードしてから、反撃専用技ではないという判定後、$C1/9264~が回復技の判定の分岐になる。

;周囲攻撃、反撃専用、回復判定
$C1/9258 LDA $D5000D,x           ;Aに[$D5000D,x]をロード
$C1/925C BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C1/925E BNE $5B    [$92BB]      ;ゼロフラグが立っていないとき[$92BB]分岐
;ゼロフラグON 反撃専用技ではない
$C1/9260 BIT #$80                ;Aと$80で論理積(ステータスフラグ変更のみ)
$C1/9262 BEQ $5A    [$92BE]      ;ゼロフラグが立っているとき[$92BE]分岐
;ゼロフラグON 回復技である
$C1/9264 LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/9266 LDA $0000,x[$00:1B50]   ;Aに[$00:1B50](敵ID)をロード
$C1/9269 CMP #$D7                ;Aと$D7を減算比較(ステータスレジスタ変更のみ)
$C1/926B BNE $11    [$927E]      ;ゼロフラグが立っていないとき[$927E]分岐
;ゼロフラグON 敵ID$D7はスタビライザー
$C1/926D JSR $927E  [$C1:927E]   ;[$C1:927E]へジャンプ

$C1/9264からが回復技用の処理になるのだが、まずやっているのが、敵IDの判断である。
$C1/9266$C1/926Bは、敵IDが$D7かどうかの判定である。
敵ID$D7は、SF編ラストバトルに登場するスタビライザーであり、ここでスタビライザーかどうかで専用の分岐が生じることになる。

ここで、大まかな処理手順を見るため、$C1/926B~の処理を追ってみよう。
途中のジャンプ処理などは省略する。

$C1/926B BNE $11    [$927E]      ;ゼロフラグが立っていないとき[$927E]分岐
;ゼロフラグON 敵ID$D7,スタビライザー
$C1/926D JSR $927E  [$C1:927E]   ;[$C1:927E]へジャンプ
;★$C1:927E(回復技使用判定)へのジャンプ1回目
$C1/9270 BEQ $01    [$9273]      ;ゼロフラグが立っているとき[$9273]分岐
;ゼロフラグOFF
$C1/9272 RTS                     ;サブルーチン戻り
;ゼロフラグON
$C1/9273 PHX                     ;Xをスタックにプッシュ
$C1/9274 LDX #$1B00              ;Xに$1B00をロード
$C1/9277 JSR $927E  [$C1:927E]   ;[$C1:927E]へジャンプ
;★$C1:927E(回復技使用判定)へのジャンプ2回目
$C1/927A PLX                     ;Xレジスタにスタックからプル
$C1/927B AND #$FF                ;Aと$FFで論理積
$C1/927D RTS                     ;サブルーチン戻り
;ゼロフラグOFF スタビライザー以外の敵
$C1/927E PHY                     ;Yをスタックへプッシュ
$C1/927F LDY $0002,x             ;Yに[$0002,x]をロード
$C1/9282 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/9284 LDA $7E8F00,x           ;Aに[$7E8F00,x]をロード
$C1/9288 STA $4204  [$00:4204]   ;Aを[$00:4204]に書き込み
$C1/928B LDA $0010,y             ;Aに[$0010,y]をロード
$C1/928E PLY                     ;Yレジスタにスタックからプル
$C1/928F ASL A                   ;Aを算術左シフト *2
$C1/9290 ASL A                   ;Aを算術左シフト *2
$C1/9291 ASL A                   ;Aを算術左シフト *2
$C1/9292 ASL A                   ;Aを算術左シフト *2
$C1/9293 BNE $03    [$9298]      ;ゼロフラグが立っていないとき[$9298]分岐
$C1/9295 LDA #$0001              ;Aに$0001をロード
$C1/9298 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/929A STA $4206  [$00:4206]   ;Aを[$00:4206]に書き込み
$C1/929D JSR $46C8  [$C1:46C8]   ;[$C1:46C8]へジャンプ
;($C1/46C8~$C1/46C9 計算時間稼ぎ用ジャンプ)
$C1/92A0 LDA $4214  [$00:4214]   ;Aに[$00:4214]をロード
$C1/92A3 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/92A5 LDA #$1F                ;Aに$1Fをロード
$C1/92A7 SEC                     ;キャリーフラグON
$C1/92A8 SBC $10    [$00:0310]   ;A - [$00:0310]
$C1/92AA STA $4202  [$00:4202]   ;Aを[$00:4202]に書き込み
$C1/92AD JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;戦闘乱数計算
$C1/92B0 STA $4203  [$00:4203]   ;Aを[$00:4203]に書き込み
$C1/92B3 JSR $46C9  [$C1:46C9]   ;[$C1:46C9]へジャンプ
;($C1/46C9 計算時間稼ぎ用ジャンプ)
$C1/92B6 LDA $4217  [$00:4217]   ;Aに[$00:4217]をロード
$C1/92B9 BEQ $03    [$92BE]      ;ゼロフラグが立っているとき[$92BE]分岐
;ゼロフラグOFF
$C1/92BB LDA #$00                ;Aに$00をロード
$C1/92BD RTS                     ;サブルーチン戻り
;ゼロフラグON
$C1/92BE LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/92C0 LDA #$FF                ;Aに$FFをロード
$C1/92C2 RTS                     ;サブルーチン戻り

途中のジャンプ先の処理は端折ると、スタビライザー以外の敵は、$C1/927E~の処理をして$C1/92B9まで進み、この時点でゼロフラグが立っているかどうかで、サブルーチンの戻り位置が$C1/92BD$C1/92C2のどちらかになる。
後で処理を見るとわかるが、$C1/92BDだと技使用見送り(Aに$00を入れて戻ってくる)、$C1/92C2だと技使用確定(Aに$FFを入れて戻ってくる)となる。
$C1/927E$C1/92C2が、回復技使用判定ということである。

ということを踏まえて、$C1/926D~のスタビライザー用処理を見てみると、

$C1/926B BNE $11    [$927E]      ;ゼロフラグが立っていないとき[$927E]分岐
;ゼロフラグON 敵ID$D7,スタビライザー
$C1/926D JSR $927E  [$C1:927E]   ;[$C1:927E]へジャンプ
;★$C1:927E(回復技使用判定)へのジャンプ1回目
$C1/9270 BEQ $01    [$9273]      ;ゼロフラグが立っているとき[$9273]分岐
;ゼロフラグOFF
$C1/9272 RTS                     ;サブルーチン戻り
;ゼロフラグON
$C1/9273 PHX                     ;Xをスタックにプッシュ
$C1/9274 LDX #$1B00              ;Xに$1B00をロード(敵番号0、マザーCOMのアドレス呼び出し用)
$C1/9277 JSR $927E  [$C1:927E]   ;[$C1:927E]へジャンプ
;★$C1:927E(回復技使用判定)へのジャンプ2回目
$C1/927A PLX                     ;Xレジスタにスタックからプル
$C1/927B AND #$FF                ;Aと$FFで論理積
$C1/927D RTS                     ;サブルーチン戻り

$C1/926Dで、$C1/927E$C1/92C2の回復技使用判定に進む。
$C1/9270で戻ってきたらゼロフラグ判定をしており、ゼロフラグOFFは技使用確定でサブルーチンを抜ける。
ゼロフラグONは技使用見送りになるのだが、そうなると$C1/9273に進み、X$1B00をロードしてから、$C1/9277で再び$C1/927E$C1/92C2の回復技使用判定に進んでいるのである。
つまり、スタビライザーだけ、回復技を使用する判定に進んだ時、

  • 1回目の回復技使用判定はスタビライザーのステータスで行う
  • 2回目の回復技使用判定はマザーCOMのステータスで行う

というように、使用判定を2回行っていることがわかる。
スタビライザーは使用技1~4がすべて「システムリカバー」だから、上処理は実質、スタビライザーが通常攻撃として「システムリカバー」を使う場合の専用処理である。
スタビライザーとマザーCOMが隣接しているかどうか、といった位置関係は関係なく、必ず2回判定である。

では、$C1/927E$C1/92C2の回復技使用判定の処理について、詳しく見てみる。

;ゼロフラグOFF
$C1/927E PHY                     ;Yをスタックへプッシュ
$C1/927F LDY $0002,x             ;Yに[$0002,x]をロード
$C1/9282 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/9284 LDA $7E8F00,x           ;Aに[$7E:AAn1][$7E:AAn0](現在HP)をロード
$C1/9288 STA $4204  [$00:4204]   ;Aを[$00:4205][$00:4204]に書き込み
$C1/928B LDA $0010,y             ;Aに[$00:1A11+$40n][$00:1A10+$40n](最大HP)をロード
$C1/928E PLY                     ;Yレジスタにスタックからプル
$C1/928F ASL A                   ;Aを算術左シフト *2
$C1/9290 ASL A                   ;Aを算術左シフト *2
$C1/9291 ASL A                   ;Aを算術左シフト *2
$C1/9292 ASL A                   ;Aを算術左シフト *2
$C1/9293 BNE $03    [$9298]      ;ゼロフラグが立っていないとき[$9298]分岐
;ゼロフラグON(最大HP * $10が$0000)
$C1/9295 LDA #$0001              ;Aに$0001をロード
;ゼロフラグOFF(最大HP * $10が$0000ではない)
$C1/9298 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/929A STA $4206  [$00:4206]   ;Aを[$00:4206]に書き込み
$C1/929D JSR $46C8  [$C1:46C8]   ;[$C1:46C8]へジャンプ
;
;符号付16bit / 8bit
$C1/46C8 NOP                     ;計算待ち
$C1/46C9 RTS                     ;サブルーチン戻り
;
$C1/92A0 LDA $4214  [$00:4214]   ;Aに[$00:4214](商、下1バイト)をロード
$C1/92A3 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/92A5 LDA #$1F                ;Aに$1F(31)をロード
$C1/92A7 SEC                     ;キャリーフラグON
$C1/92A8 SBC $10    [$00:0310]   ;A - [$00:0310]
$C1/92AA STA $4202  [$00:4202]   ;Aを[$00:4202]に書き込み
$C1/92AD JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
;戦闘乱数計算
$C1/6150 PHX                     ;戦闘乱数計算
;(省略)
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/92B0 STA $4203  [$00:4203]   ;Aを[$00:4203]に書き込み
$C1/92B3 JSR $46C9  [$C1:46C9]   ;[$C1:46C9]へジャンプ
;
;符号付8bit x 8bit
$C1/46C9 RTS                     ;サブルーチン戻り
;
$C1/92B6 LDA $4217  [$00:4217]   ;Aに[$00:4217](乗算結果上1バイト)をロード
$C1/92B9 BEQ $03    [$92BE]      ;ゼロフラグが立っているとき[$92BE]分岐
;ゼロフラグOFF(技使用見送り)
$C1/92BB LDA #$00                ;Aに$00をロード
$C1/92BD RTS                     ;サブルーチン戻り
;ゼロフラグON(技使用確定)
$C1/92BE LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/92C0 LDA #$FF                ;Aに$FFをロード
$C1/92C2 RTS                     ;サブルーチン戻り

敵の現在HP、最大HP、戦闘乱数から技を使用するかどうかを計算・判定しているのだが、計算式が少々ややこしい。

$C1/9284$C1/92A0は、符号付16bit / 8bit、つまり割り算である。
$00:4204$00:4205に被除数2バイト、$00:4206に除数を書き込むと、一定時間後に$00:4214$00:4215に商2バイト、$00:4216に余りが入る仕組み。
サブルーチンを見ると、

trunc(現在HP / (最大HP * $10の下1バイト))
=trunc(現在HP / ((最大HP * $10) & $00FF))

を計算し、$C1/92A0[$00:4214](商の下1バイト)をAにロードしている。
ポイントは「最大HP * $10の下1バイト」である。
16進数で最大HPが下1桁が0の時、$10を掛け算すると、2バイト表記の時、下1バイトは必ず$00である。
例えばスタビライザーなら、最大HPは$30なので、2バイトで$0030とした時、$10倍すると$0300である。
「最大HP * $10 & $00FF」が$00になるので、

=trunc(現在HP / ((最大HP * $10) & $00FF))

を計算すると、割り算部分で、

現在HP / $00

いわゆるゼロ除算になってしまう。難しい話は横におくが、「0で割ることはできないので答えが出せない」のである。
ゼロ除算 - Wikipedia
ただ、65C816の符号付16bit / 8bitの計算では、こういう場合にエラーが出ないよう、商2バイトは$FFFF、余りは被除数を返すようになっている。
ここから、

16進数で最大HPが下1桁が0の敵は、現在HPに関係なく、「=trunc(現在HP / ((最大HP * $10) & $00FF))」の商として$FFFFが返ってくる

ということがわかる。
よって、$C1/92A0では、[$00:4214]$FFが入っているから、A$FFがロードされる。

さて、敵の最大HPだが、敵キャラデータ04に入っており、数値が$80未満ならそのままHPとするが、$80以上の時は、
実際のHP = (敵キャラデータ04 & $7F) * $10
で計算している。
つまり、HPが$80(10進数128)以上の敵はすべて、16進数で下1桁が0であり、回復技を使う場合、上のゼロ除算のケースが当てはまることになる。
$80未満でも、スタビライザーのように最大HPの下1桁が0の敵なら、もちろん当てはまる。

下1桁が0以外かつ回復技を使う敵ももちろんいる。
回復技「クォ~ン」は、ちびマンモス(HP:$28 = 40)が使うのならHPの下1桁が0ではない。
マンモス♀も「クォ~ン」を使うが、最大HPが480(敵キャラデータ04が$9E)なので、HPの下1桁が0である。

ということで、最大HPの下1桁次第で分岐と見なしてよい。
最大HPの下1桁が0以外なら、$C1/92A0Aにロードされるのは、

=trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF

最大HPの下1桁が0なら、$FFがロードされる、ということになる。
とりあえずこの値を暫定的にNとしておく。

なお、最大HPの下1桁が0以外の時、Nがとる範囲は$00$07のはずである。
最大HPが$79以下として、Nが最大になるのは、最大HPの下1桁ができるだけ小さい、1の時になる。
最大HPが$71として考えてみる(ちなみに最大HPが$71の敵はいないのだが、便宜上である)。
HPが満タンの時、

trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF
= trunc($71 / ($71 * $10) & $00FF ) & $00FF
= ($71 / $10) & $00FF
= $07 & $00FF
= $07

(最大HP * $10) & $00FFの解は$10, $20, ... $90のいずれかであり、最大HPが$00$71であるから、割り算をするとどうやっても商が$00$07にしかならない。
よって、Nが取る範囲は、$00$07または$FFしかない。

先に上げた、ちびマンモス(HP:$28)であれば、
trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF
= trunc(現在HP($00~$28) / $80) & $00FF
なのだが、trunc(現在HP($00~$28) / $80)は、現在HPが幾つであっても答えは$00で確定である。
現在HPが$80以上の時計算値は1以上の値になるが、最大HPが$28だからありえない。
最大HPの下1桁の値次第で、現在HPは計算には全く関係ない値になってしまうこともある、ということがわかる。

続いて$C1/92A0~だが、$C1/92A8で、キャリーフラグを立てた状態で

$1F - N

を計算している。
先に述べた通り、Nが取る範囲は、$00$07または$FFである。
よって、

最大HPの下1桁が0以外なら、

$1F - N($00~$07のいずれか)
=$18~$1F

最大HPの下1桁が0なら、

$11F - $FF
=$20

この計算値をMとしておく。
Mと、$C1/92B0Aにロードされた戦闘乱数($00$FF)を掛け算している(符号付8bit x 8bit)。
$C1/92B6で、乗算結果の上1バイトをロードし、$C1/9270のゼロフラグ判定で、最終的に回復技使用判定となる。

M * 戦闘乱数($00~$FF)

の結果、$0100を越えたら乗算結果の上1バイトは$01以上になりゼロフラグが立たない。
$0100未満なら、ゼロフラグが立つ。
という分岐である。

ここまでの計算で、M$18(%0001 1000)~$20(%0010 0000)のいずれかを取ることがわかった。
最大HPの下1桁が0のケースで考えると、

M * 戦闘乱数($00~$FF)
= $20 * 戦闘乱数($00~$FF)

この乗算結果が$0100を越える乱数は$08以上である。
つまり、乱数が$00$07なら、$C1/9270のゼロフラグ判定でゼロフラグがONになる(技使用確定)。
$08以上なら、ゼロフラグ判定でゼロフラグがOFFになる(技使用見送り)。

ゼロフラグが立つ確率は8/256 = 1/32ということだから、回復技使用確定になる確率はかなり低いことがわかる。

最大HPの下1桁が0以外のケースなら、

M * 戦闘乱数($00~$FF)
= $18~$1F * 戦闘乱数($00~$FF)

M$18なら乱数$10以上で、M$1Fなら乱数$09以上で、乗算結果が$0100を越える。
要するに、最大HPの下1桁が0以外のケースだと、ゼロフラグが立つ確率は9/25610/256のどちらか。
結局、最大HPや現在HPがいくつでも、回復技使用確定になる確率は8/256, 9/256, 10/256のいずれかであり、大差ないのである。

計算式からして、HPが減ってきたら(現在HP/最大HPが小さくなってきたら)、回復技使用確率を上げる、という処理のようだが、実際のプレイでどれほど差が出ているかというと謎である。
(差が出にくかったからこそ、スタビライザーは回復技を頻繁に使うように、2回判定にしているのかもしれない)

最後に、$C1/92BDまたは$C1/92C2でサブルーチンを終えた後の続きを見ておく。

$C1/92B9 BEQ $03    [$92BE]      ;ゼロフラグが立っているとき[$92BE]分岐
;ゼロフラグOFF(技使用見送り)
$C1/92BB LDA #$00                ;Aに$00をロード
$C1/92BD RTS                     ;サブルーチン戻り
;ゼロフラグON(技使用確定)
$C1/92BE LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード
$C1/92C0 LDA #$FF                ;Aに$FFをロード
$C1/92C2 RTS                     ;サブルーチン戻り

$C1/8F23 BEQ $24    [$8F49]      ;ゼロフラグが立っているとき[$8F49]分岐
;ゼロフラグOFF(技使用確定)
$C1/8F25 JSR $920C  [$C1:920C]   ;[$C1:920C]へジャンプ
;ゼロフラグON(技使用見送り、次判定へ)
$C1/8F49 LDX $AC    [$00:03AC]   ;Xに[$00:03AC]をロード

戻り先の$C1/8F23でゼロフラグ判定となり、$C1/92BEの戻りでA$FFを入れている場合はゼロフラグOFF、回復技使用確定となり、回復技の処理[$C1:920C]にジャンプする。
$C1/92BBの戻りでA$00を入れている場合はゼロフラグON、攻撃技の時と同じように技使用見送りとなって、次の技判定の$C1/8F49に飛ぶ。

以上が回復技に関する判定である。



このページをシェアする

上へ