TOP > プログラミング関係解説&調査 > オディオモールのダメージ量処理

オディオモールのダメージ量処理

前ページの戦闘用乱数の計算の続き。

ダメージ計算が行われるサブルーチンがどのあたりで、単発ダメージがどう計算されるかはダメージ計算式の解説で判明した。
そこで気になるのが、ダメージ計算の例外となる、オディオモールである。
オディオモールは、「ほいこーろー」以外の攻撃技の単発ダメージ量を1にしてしまう(ついでに「リベンジストーム」の回復量も1にする)。
この時の計算・処理はどうなっているのだろうか。

ここまでで、サブルーチン$C1/77CE~にて単発ダメージ量、$C1/782D~で乱数分を計算し、実際の単発ダメージ量がメモリ「$00:0320」に書き込まれてサブルーチンが終了する($C1/7851)という説明をした。
実は、オディオモールに攻撃した時にも、同じサブルーチンが使われ、ダメージ量も計算される。
そして、サブルーチンが終了する$C1/7851の時点では、メモリ「$00:0320」には「1」ではなく、味方とオディオモールのステータスなどから計算されたダメージ量がきちんと書き込まれているのである。
つまり、ダメージ量が「1」に変更されるのは、この先の処理ということだ。
では、この先はどうなっているのか。

$C1/7851 RTS        ;サブルーチン戻り($C1/D9A4の続きへ)

であるから、$C1/D9A4以降のサブルーチンを見てみるとしよう。
サブルーチンの開始は$C1/D9A7である。
まずはオディオモールへの攻撃以外だとどうなるか、同じ戦闘におけるオディオアイへ攻撃した場合を見てみる。
後々の説明のため、このサブルーチンを実行中のインデックスレジスタX(以下X)を併記する。

$C1/D9A7 REP #$21                ; X:1B00 Aを16bit幅に変更、Cフラグクリア
$C1/D9A9 LDA $20    [$00:0320]   ; X:1B00 Aに[$00:0320]をロード
$C1/D9AB STA $7E9004,x[$7E:AB04] ; X:1B00 Aを[$7E:AB04]に書き込み
$C1/D9AF SEP #$20                ; X:1B00 MフラグON。Aは8bit幅
$C1/D9B1 LDA $7E8F07,x[$7E:AA07] ; X:1B00 Aに[$7E:AA07]をロード
$C1/D9B5 AND #$DF                ; X:1B00 Aと$DFの論理積
$C1/D9B7 STA $7E8F07,x[$7E:AA07] ; X:1B00 Aを[$7E:AA07]に書き込み
$C1/D9BB LDA #$00                ; X:1B00 Aに$00をロード
$C1/D9BD STA $7E8F0D,x[$7E:AA0D] ; X:1B00 Aを[$7E:AA0D]に書き込み
$C1/D9C1 LDA $0000,x[$00:1B00]   ; X:1B00 Aに[$00:1B00]をロード
$C1/D9C4 CMP #$D0                ; X:1B00 Aと$D0を減算比較
$C1/D9C6 BNE $11    [$D9D9]      ; X:1B00 ゼロフラグが立っていない場合$C1/D9D9へジャンプ
$C1/D9D9 RTS                     ; X:1B00 サブルーチン戻り

それぞれ説明していく。

$C1/D9A7 REP #$21                ; X:1B00 Aを16bit幅に変更、Cフラグクリア
$C1/D9A9 LDA $20    [$00:0320]   ; X:1B00 Aに[$00:0320]をロード
$C1/D9AB STA $7E9004,x[$7E:AB04] ; X:1B00 Aを[$7E:AB04]に書き込み
$C1/D9AF SEP #$20                ; X:1B00 MフラグON。Aは8bit幅

$C1/D9A9[$00:0320]、つまり計算済みの単発ダメージ量がAにロードされている。
そして$C1/D9ABで、単発ダメージ量が[$7E:AB04]に書き込まれた。
ここで単発ダメージ量の数値は、[$7E:AB04]へ移動したことになる。
実際には「STA $7E9004,x」という命令だから、$7E9004Xの値が足された分のメモリへ書き込みという指示である。
このあたりでは同じように「Xの分だけ足されたメモリに書き込み・読み込み」が続いているのだが、Xの下2桁目は、この戦闘における敵の番号である。
このラストバトル(前半戦)では、オディオアイが2体、オディオモール、オディオマウスの合計4体の敵が出現するが、区別するため内部で番号がふってある。

敵名番号
オディオアイ(画面左)0
オディオアイ(画面右)1
オディオモール2
オディオマウス3

この数値が、Xの下2桁目に表示されるのである(表示はあくまでもこのサブルーチンの間であって、戦闘中、常にこの数値という意味ではない)。
この戦闘においては、X:1B00はオディオアイ(画面左)だし、X:1B10はオディオアイ(画面右)、X:1B20はオディオモール、X:1B30はオディオマウスである。
本作は一度の戦闘で最大15体まで敵が出現するが、15体までなら16進数を使って、$0$Fと1文字で番号が振り分けできる、ということである。
ここで、オディオモールの番号が「2」であることを覚えておいていただきたい。

$C1/D9B1 LDA $7E8F07,x[$7E:AA07] ; X:1B00 Aに[$7E:AA07](敵の状態異常)をロード
$C1/D9B5 AND #$DF                ; X:1B00 Aと$DFの論理積
$C1/D9B7 STA $7E8F07,x[$7E:AA07] ; X:1B00 Aを[$7E:AA07](敵の状態異常)に書き込み
$C1/D9BB LDA #$00                ; X:1B00 Aに$00をロード
$C1/D9BD STA $7E8F0D,x[$7E:AA0D] ; X:1B00 Aを[$7E:AA0D](眠り状態回復までの残り時間)に書き込み

$C1/D9B1以降で何をしているのかだが、簡単にまとめると$C1/D9B1$C1/D9BDは、攻撃した相手が眠り状態がどうかの確認である。
眠り状態の敵を攻撃すると、眠り状態から解除されるので、その判定と眠り状態解除の処理を行っている。
オディオアイは眠り状態を無効化できないので、何らかの方法で眠らせることが可能な敵である。よってこのような処理が挟まるのだと思われる。

LDA $7E8F07,xAに読み込まれた[$7E:AA07]は、敵の状態異常の状況である。
本作の状態異常は、石化、酔い、眠り、マヒ、毒、腕かため、足かため、更に隠し状態異常の首かためで8種類である。
8種類の状態異常になっているかどうかは、2進法8桁=16進法の2桁で表すことができる。
たとえば、眠り状態と毒状態が同時に発生している時は、該当の桁に「1」が立つ。

2進法の位2726252423222120
状態異常石化酔い眠りマヒ腕かため足かため首かため
2進法00101000

2進法の00101000 = 16進法の$28なので、眠り状態と毒状態が同時に発生している時は該当のアドレスに「$28」と書き込まれる。
逆に、状態異常に一切かかっていない、正常な状態なら$00ということである。
なお、すべての状態異常にかかっている状態「11111111」は戦闘不能を表すことになっている、と世界の合言葉は森部様にある。

$C1/D9B5では、状態異常の状態と$DFで論理積をとっている。
$DFは2進法の「11011111」であり、状態異常でいえば「眠り以外の状態異常にすべてかかっている」である。
論理積をとることで、「眠り状態だったら、眠り状態を解除し、それ以外の状態異常はそのまま維持」という状態を作ることができる。
上の眠り状態と毒状態が同時に発生している時に攻撃された場合は、$28$DFで論理積をとって、
$28 AND $DF = $08
2進数では「00001000」であり、毒状態のみ維持されている、という意味になる。
論理積の結果を、新たな状態異常として、$C1/D9B7[$7E:AA07]にセットし直したことになる。

また、$C1/D9BDで読み込みされた[$7E:AA0D]には、攻撃相手が眠り状態だった時に眠り状態回復までの残り時間が記録されている。
[$7E:AA0D]$00を入れて、眠り状態ではなくなった(残り時間が0になった)という処理を行ったのである。

$C1/D9C1 LDA $0000,x[$00:1B00]   ; X:1B00 Aに[$00:1B00]をロード
$C1/D9C4 CMP #$D0                ; X:1B00 Aと$D0を減算比較
$C1/D9C6 BNE $11    [$D9D9]      ; X:1B00 ゼロフラグが立っていない場合$C1/D9D9へジャンプ
$C1/D9D9 RTS                     ; X:1B00 サブルーチン戻り

ここが今回の本題の箇所になる。
$C1/D9C1では、LDA $0000,xで、[$00:1B00]Aにロードしている。
メモリ[$00:1B00]に記されているのは、「この戦闘の番号0の敵の名前ID」なのである。
戦闘開始時、メモリ[$00:1B00]以降には、戦闘における敵の名前のIDが読み込みされている。
敵の名前のIDは、世界の合言葉は森部様の敵データの「ID」で確認できるが、$00$FFの256体が記録されている(ゲームでは未使用の敵も含まれる)。

ラストバトルの敵の名前IDは以下のとおり。

敵名番号敵の名前ID
オディオアイ(画面左)0$CD
オディオアイ(画面右)1$CD
オディオモール2$D0
オディオマウス3$CE

以上を踏まえた上で、改めて$C1/D9C4の処理を見ていただきたい。
CMP #$D0」とある。
$C1/D9C1Aに読み込んだ敵の名前IDと、「$D0」を減算比較しろ、という命令である。
敵の名前ID$D0は上の通り、オディオモールなのである。
$C1/D9C6では、減算比較した結果、ゼロフラグが立っていない場合は$C1/D9D9へジャンプ、とある。
ゼロフラグは、直前の命令で結果が「0」だった時に立つフラグである。
もし敵がオディオモールだった場合、$D0 - $D0を計算するので答えは0。ゼロフラグが立つ。

オディオモール以外、ここでゼロフラグが立つ敵は存在しない。

では、オディオモールだったら、$C1/D9C6後の処理はどうなるのだろうか。
その前に、オディオモールに攻撃した時、計算したダメージ量が記録されたメモリの位置を確認しておく。
この戦闘においてオディオモールの番号は「2」なので、以下のように、ダメージ量は[$7E:AB24]に記録される。

$C1/D9A9 LDA $20    [$00:0320]   ; X:1B20 
$C1/D9AB STA $7E9004,x[$7E:AB24] ; X:1B20 

間は省略し、$C1/D9C6後の処理を見てみよう。

$C1/D9C1 LDA $0000,x[$00:1B20]   ; X:1B20 Aに[$00:1B20]をロード
$C1/D9C4 CMP #$D0                ; X:1B20 Aと$D0を減算比較(ID:D0はオディオモール)
$C1/D9C6 BNE $11    [$D9D9]      ; X:1B20 ゼロフラグが立っていない場合$C1/D9D9へジャンプ
$C1/D9C8 REP #$20                ; X:1B20 Mフラグをクリア。Aは16bitモード
$C1/D9CA LDA #$0001              ; X:1B20 Aに$0001をロード
$C1/D9CD STA $7E9004,x[$7E:AB24] ; X:1B20 [$7E:AB24]に$0001を書き込み

計算されたダメージ量は[$7E:AB24]に書き込みされている。
だが、$C1/D9CDでは、その[$7E:AB24]に、$C1/D9CAAにロードした「$0001」という値で上書きしているのだ。
$0001」は16進数でも10進数でも「1」。
つまり、ここまでの計算で算出したダメージ量を、「1」で上書きされてしまった。
これが、「オディオモールへの単発ダメージ量が必ず1」の理由である。
未確認だが、おそらく回復技「リベンジストーム」の回復量も、同じ処理で「1」なのだろう。
また、サモの「ほいこーろー」も、このサブルーチンを通っており、単発ダメージ量を1にする処理が入っていたので、「ほいこーろー」のダメージ量はこの後に他の技とは違う方法で計算されている、ということである。
また、オディオモールにだけ、「ほいこーろー」がステップ数+1のダメージを与えられるのも、ここに起因している。
詳細は次ページの「ほいこーろー」の仕様に記してある。

なお、以上の処理はあくまでも「単発ダメージ量」の計算の話である。
実際は、

総ダメージ = 単発ダメージ*ヒット数 - 防御力
(敵の防御力は0)

なので、この後にヒット数を掛け算する処理などが続くはずだが、今回は省く。
ヒット数が1の技かつ敵への攻撃であったら、ここまでの計算で算出されたダメージ量がそのまま総ダメージになる。
例えば「ハリケンショット」で攻撃した時、ヒット数が最大の12であったら、オディオモールへのダメージ量は1×12 = 12となる。

まとめると、
「オディオモールへの攻撃は、オディオモールのみの専用処理の結果、強制的に単発ダメージを1にされる」(「ほいこーろー」のみ例外)
と、いうことになる(知られていたことではあるが)。
その専用処理は上に記したとおりである。



このページをシェアする

上へ