TOP > プログラミング関係解説&調査 > ダメージフィールド(2)

ダメージフィールド(2)

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

前ページの続き。

ダメージフィールドの消去

ダメージフィールドは、発動後に一定確率で消去されて通常マスに戻るようになっている。
消去するかどうかは、1マス毎に判定される。
発動時に、ダメージフィールドマスにキャラがいるかどうかは関係ない。
このため、「ダメージフィールドがあるが、発動ターンに誰もダメージフィールドの上に乗っていなかったため、一瞬フィールドが光って一部のダメージフィールドが消える」という現象が起こることもある。本作をプレイしていると時々見かける現象である。

消去判定のサブルーチンは以下の通り。
前ページでの処理の通りに、[$00:0310] = $C0(火炎フィールド)が入った状態で開始になる。

$C1/1ECE STZ $0A    [$00:030A]   ;[$00:030A]に$00を書き込み
$C1/1ED0 LDX #$0000              ;Xに$0000をロード
$C1/1ED3 STZ $09    [$00:0309]   ;[$00:0309]に$00を書き込み
$C1/1ED5 STZ $08    [$00:0308]   ;[$00:0308]に$00を書き込み
;
;ループここから
;1マス毎に、ダメージフィールドがあるか判定
$C1/1ED7 LDA $04C8,x             ;Aに[$00:04C8~$00:04FE]をロード
$C1/1EDA AND #$E0                ;Aと$E0で論理積(ダメージフィールドありだと00以外)
$C1/1EDC CMP $10    [$00:0310]   ;Aと[$00:0310](=$C0 火炎)を減算比較(ステータスレジスタ変更のみ)
$C1/1EDE BNE $11    [$1EF1]      ;ゼロフラグが立っていないとき[$1EF1]分岐
;ダメージフィールドあり判定
$C1/1ED7 LDA $04C8,x             ;Aに[$00:04C8~$00:04FE](火炎フィールド$C0)をロード
$C1/1EDA AND #$E0                ;Aと$E0で論理積
$C1/1EDC CMP $10    [$00:0310]   ;Aと[$00:0310]($C0)を減算比較(ステータスレジスタ変更のみ)
$C1/1EDE BNE $11    [$1EF1]      ;ゼロフラグが立っていないとき[$1EF1]分岐
$C1/1EE0 LDA #$0F                ;Aに$0Fをロード
$C1/1EE2 STA $0488,x             ;A($0F)を[$00:0488~$00:04BE]に書き込み
$C1/1EE5 INC $0A    [$00:030A]   ;[$00:030A](初期値00)をインクリメント +1
$C1/1EE7 JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
;戦闘乱数計算
$C1/6150 PHX                     ;戦闘乱数計算
;(省略)
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/1EEA BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/1EEC BNE $03    [$1EF1]      ;ゼロフラグが立っていないとき[$1EF1]分岐
;ゼロフラグON
$C1/1EEE JSR $1F5C  [$C1:1F5C]   ;[$C1:1F5C]にジャンプ
;ゼロフラグOFF
$C1/1EF1 INX                     ;Xをインクリメント +1
$C1/1EF2 INC $08    [$00:0308]   ;[$00:0308]をインクリメント +1
$C1/1EF4 LDA $08    [$00:0308]   ;Aに[$00:0308]をロード
$C1/1EF6 CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/1EF8 BNE $DD    [$1ED7]      ;ゼロフラグが立っていないとき[$1ED7]分岐
;ループここまで
;
$C1/1EFA INX                     ;Xをインクリメント +1
$C1/1EFB INC $09    [$00:0309]   ;[$00:0309]をインクリメント +1
$C1/1EFD LDA $09    [$00:0309]   ;Aに[$00:0309]をロード
$C1/1EFF CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/1F01 BNE $D2    [$1ED5]      ;ゼロフラグが立っていないとき[$1ED5]分岐
$C1/1F03 LDA $0A    [$00:030A]   ;Aに[$00:030A]をロード
$C1/1F05 RTS                     ;サブルーチン戻り

戦闘フィールドの状態が入っている$00:04C8$00:04FEをループ処理で1マスずつチェックしていくことになるが、もし該当のマスにダメージフィールドがない場合、$C1/1EDEの判定でゼロフラグが立って$C1/1EF1までジャンプする。実質、何の処理もせず次のマスの処理に進むことになる。
ダメージフィールドの有無のみの判定なので、キャラがいるかどうかは関係ない。

$C1/1EDC$C1/1EDEで、該当マスにダメージフィールドがある場合($C1/1EDEでゼロフラグON)、$C1/1ED7~の処理に進む。
$C1/1EDCでは、[$00:0310](火炎フィールドの値$C0が入っている)と比較している。現在、火炎フィールド発動ターンのため、火炎フィールドがあるかどうかの判定を行うということである。他フィールドの場合は$C1/1EF1にジャンプ、つまりループを抜ける。

$C1/1EE2で、計算用のフィールド状態アドレス$00:0488$00:04BE$0Fを書き込んでから、戦闘乱数計算用のサブルーチン$C1/6150$C1/61A4で、戦闘乱数を取得する(サブルーチンの解説は省略)。
$C1/1EEAでは、計算した戦闘乱数$00$FF$0Fで論理積を取り、$C1/1EECでゼロフラグ分岐がある。
ゼロフラグが立つ場合は[$C1:1F5C]にジャンプするが、このジャンプ先で該当マスからダメージフィールドを消去するため、
「乱数$00$FF$0Fの論理積が0の時、ダメージフィールドを消去する」
ということがわかる。
ゼロフラグが立つのは乱数の下1桁が0の時、つまり$00, $10 ,$20, ... $F0の時であり、消去される確率は16/2561/16)であることがわかる。

ゼロフラグが立たない場合は該当マスには何の処理もせず、$C1/1EF1へ進んでループを抜ける。つまりダメージフィールドのマスは維持される。

では、$C1:1F5C以降、ダメージフィールドを消去する処理を見てみよう。

;ダメージフィールドを消去
$C1/1F5C LDA $04C8,x[$00:04EC]   ;Aに[$00:04C8~$00:04FE](消去するマス)をロード
$C1/1F5F AND #$1F                ;Aと$1Fで論理積
$C1/1F61 STA $04C8,x[$00:04EC]   ;Aを[$00:04C8~$00:04FE](消去するマス)に書き込み
(中略)
$C1/1FAB RTS                     ;サブルーチン戻り
;
;($C1/1EF1に戻る)

消去だけなら最初の3行で処理が終わる。
前にも、マス目の処理で、$1Fとの論理積を取るとフィールドの値だけ消してキャラの値だけ残すことができる、と書いたが、そのままの処理をして該当マスに値を入れ直すというだけである。
その後の処理も続いているが、とりあえず今回の話には関係がないので省略する。
最終的には$C1/1EF1に戻って、ループ処理が終了する。

今回はこれで問題ないのだが、フィールドの一番右下のマス、つまり最後に判定を行うマスで消去処理が入った時だけ、その後に本来発生するはずのフィールドダメージ発生処理が入らない。
(消去されない場合は通常通りにダメージ発生処理などが入る)
上のサブルーチンの処理自体は通常通りなのだが、後でフィールドダメージ発生時のサブルーチンを通らなくなるのである。

この後なのだが、マス目の消去処理判定をした後に、フィールドダメージの計算を行う。
もちろん、消去される前についてのダメージ計算になる。

ダメージ計算

先にダメージフィールドの消去処理をしてから、フィールドダメージの計算を行うという、普通に考えると逆ではないかと思われる順で処理が入る。
現時点では、$00:04C8$00:04FEでは既に、発動後にダメージフィールドが消えたことになっている。
消える前のフィールドの状態が残っているのは、計算値が入る$00:0488$00:04BEである。フィールドが存在していた(している)場合$0Fが入っている。
よって、$00:0488$00:04BEの値から、ダメージフィールド上にキャラが乗っている場合、ダメージを与えるか、無効化か、吸収かという計算を行うことになる。

$C1/D1EA CPX #$1CC0              ;Xと$1CC0を減算比較(ステータスレジスタ変更のみ)
$C1/D1ED BNE $F5    [$D1E4]      ;ゼロフラグが立っていないとき[$D1E4]分岐
$C1/D1EF RTS                     ;サブルーチン戻り
;
;フィールド右下マス消去時のみ、ここから先の処理に入らない
$C1/1FB2 LDA #$07                ;Aに$07をロード
$C1/1FB4 STA $09    [$00:0309]   ;A($07)を[$00:0309]に書き込み
$C1/1FB6 LDY #$0000              ;Yに$0000をロード
$C1/1FB9 LDA #$07                ;Aに$07をロード
$C1/1FBB STA $08    [$00:0308]   ;A($07)を[$00:0308]に書き込み
;
;ループ処理開始
;1マス毎に判定
$C1/1FBD STY $1A    [$00:031A]   ;Y(初期値$0000)を[$00:031A]に書き込み
$C1/1FBF LDA $0488,y             ;Aに[$00:0488~$00:04BE]をロード
$C1/1FC2 BEQ $15    [$1FD9]      ;ゼロフラグが立っているとき[$1FD9]分岐
;ゼロフラグOFF
$C1/1FC4 LDA $04C8,y             ;Aに[$00:04C8~$00:04FE]をロード
$C1/1FC7 STY $1A    [$00:031A]   ;Yを[$00:031A]に書き込み
$C1/1FC9 BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/1FCB BEQ $0C    [$1FD9]      ;ゼロフラグが立っているとき[$1FD9]分岐
;ゼロフラグOFF
$C1/1FCD BIT #$10                ;Aと$10で論理積(ステータスフラグ変更のみ)
$C1/1FCF BNE $05    [$1FD6]      ;ゼロフラグが立っていないとき[$1FD6]分岐
$C1/1FD1 JSR $1FEA  [$C1:1FEA]   ;[$C1:1FEA]へジャンプ→味方キャラのフィールド処理へ
$C1/1FD6 JSR $2029  [$C1:2029]   ;[$C1:2029]へジャンプ→敵キャラのフィールド処理へ
;ゼロフラグON
$C1/1FD9 LDY $1A    [$00:031A]   ;Yに[$00:031A]をロード
$C1/1FDB INY                     ;Yをインクリメント +1
$C1/1FDC DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/1FDE BNE $DD    [$1FBD]      ;ゼロフラグが立っていないとき[$1FBD]分岐
;ループここまで
;
$C1/1FE0 INY                     ;Yをインクリメント +1
$C1/1FE1 DEC $09    [$00:0309]   ;[$00:0309]をデクリメント -1
$C1/1FE3 BNE $D4    [$1FB9]      ;ゼロフラグが立っていないとき[$1FB9]分岐
$C1/1FE5 LDA #$10                ;Aに$10をロード
$C1/1FE7 STA $85    [$00:0385]   ;Aを[$00:0385]に書き込み
$C1/1FE9 RTS                     ;サブルーチン戻り

例によって、左上のマスから右下方向へ順に1マスずつの判定をループ処理で行う。
$C1/1FB2$C1/1FE9の処理が、フィールドの一番右下のマスの消去判定が入った直後だとサブルーチンに入らず、処理してくれないので、ダメージフィールド上にキャラがいてもダメージ処理が抜けてしまうようである。
単純にスキップされるだけのようであり、重大な不具合は発生しない。

それはさておき、上のループ内処理を見てみる。
$00:0488$00:04BEには消去処理前にダメージフィールドが入っていたマスに$0Fが入っており、その場合は$C1/1FC4に進む。
$C1/1FC4では、$00:0488$00:04BEに対応した$00:04C8$00:04FEを呼び出す。
$0Fと論理積を取った時、味方または敵キャラがいるマスなら、ゼロフラグが立たず$C1/1FCDに進む。
$C1/1FCDでは、$10と論理積を取っているが、ここでゼロフラグが立つ場合、該当マスには味方キャラが居ることになる($01$04)。
ゼロフラグが立たない場合は、敵キャラが居ることになる($11$1E)。
よって、それぞれの飛び先が、味方キャラまたは敵キャラがダメージフィールドに乗っている場合のダメージ処理ということになる。
ここまでで該当しない場合は、ループ処理の最後$C1/1FDEまでたどりつく仕組みである。
最終的に全てのマスのチェックと計算が終わると、$C1/1FE9に到達して処理終了となる。

味方キャラのフィールドダメージ計算

では、ダメージフィールドに乗っている場合のダメージ処理を見てみる。
味方キャラと敵キャラで処理が分岐しているので、まずは味方キャラの場合、$C1:1FEA~の処理を見てみる。

;味方キャラのフィールドダメージ処理
$C1/1FEA AND #$0F                ;Aと$0Fで論理積
$C1/1FEC DEC A                   ;Aをデクリメント -1
$C1/1FED CLC                     ;キャリーフラグクリア
$C1/1FEE ROR A                   ;Aをキャリーフラグを含めた9bitで右ローテート
$C1/1FEF ROR A                   ;Aをキャリーフラグを含めた9bitで右ローテート
$C1/1FF0 ROR A                   ;Aをキャリーフラグを含めた9bitで右ローテート
$C1/1FF1 STA $1C    [$00:031C]   ;Aを[$00:031C]に書き込み
$C1/1FF3 LDA #$1C                ;Aに$1Cをロード
$C1/1FF5 STA $1D    [$00:031D]   ;A($1C)を[$00:031D]に書き込み
$C1/1FF7 LDX $1C    [$00:031C]   ;Xに[$00:031C]をロード
$C1/1FF9 LDA $07    [$00:0307]   ;Aに[$00:0307]($18)をロード
$C1/1FFB STA $10    [$00:0310]   ;A($18)を[$00:0310]に書き込み
$C1/1FFD STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
$C1/1FFF LDY $0002,x[$00:1C02]   ;Yに[$00:1C02]をロード
$C1/2002 LDA $0031,y[$00:0EF1]   ;Aに[$00:0EF1](フィールド吸収 下4ビット)をロード
$C1/2005 AND $06    [$00:0306]   ;Aと[$00:0306]($02)で論理積
$C1/2007 BNE $10    [$2019]      ;ゼロフラグが立っていないとき[$2019]分岐
;ゼロフラグON(フィールド吸収不可)
$C1/2009 LDY $08    [$00:0308]   ;Yに[$00:0308]をロード
$C1/200B PHY                     ;Yをスタックへプッシュ
$C1/200C LDY $1A    [$00:031A]   ;Yに[$00:031A]をロード
$C1/200E PHY                     ;Yをスタックへプッシュ
$C1/200F JSR $1DF8  [$C1:1DF8]   ;[$C1:1DF8]へジャンプ
;ゼロフラグOFF(フィールド吸収可能)
$C1/2019 LDY $08    [$00:0308]   ;Yに[$00:0308]をロード
$C1/201B PHY                     ;Yをスタックへプッシュ
$C1/201C LDY $1A    [$00:031A]   ;Yに[$00:031A]をロード
$C1/201E PHY                     ;Yをスタックへプッシュ
$C1/201F JSR $1E26  [$C1:1E26]   ;[$C1:1E26]へジャンプ

$C1/1FF9~で、[$00:0307][$00:0310]に書き込んでいるが、[$00:0307]はフィールドダメージ量が入った状態である。
火炎フィールドなら$18で、[$00:0310]に書き込まれたことになる。

フィールド[$00:0307][$00:0306]
電撃$20(32)$01
$08(8)$08
$10(16)$04
火炎$18(24)$02

$C1/2002では、該当マスに居る味方キャラのシナリオ別キャラデータ$31、フィールド吸収の値をロードしている。
今回は「おぼろ丸が水神のたび装備で水フィールドのみ吸収」という前提で上の処理を記しているが、おぼろ丸ならシナリオ別キャラデータ$31[$00:0EF1]に、水フィールドのみ吸収の「$08」が入っている。
この値と、[$00:0306]とで論理積を取るのだが、現在[$00:0306]には上のように値が入っている。
つまり、[$00:0306]に入っていた値は、フィールド吸収の値に対応していたことになる。
論理積を取ってゼロフラグが立つ時は、マス目のフィールドと、フィールド吸収の値が一致していない。今回で言えば火炎フィールドの上に水フィールドのみ吸収でいるから一致せず、ダメージを受けることになる。
一方、ゼロフラグが立たない時は、マス目のフィールドと、フィールド吸収の値が一致だから、ダメージ量を吸収する。

$C1/2007~の分岐後の処理を見ると、フィールド吸収ができない(ダメージをくらう)場合は、[$C1:1DF8]へ、フィールド吸収ができる場合は[$C1:1E26]へジャンプしている。
つまりここで処理が分岐になる。

まずはフィールド吸収ができない、ダメージをくらう場合のサブルーチン$C1:1DF8~を見てみる。

;フィールド吸収ができない場合の処理(おぼろ丸)
$C1/1DF8 LDA #$01                ;Aに$01をロード
$C1/1DFA STA $003A,x[$00:1C3A]   ;A($01)を[$00:1C3A]に書き込み
$C1/1DFD STA $003B,x[$00:1C3B]   ;A($01)を[$00:1C3B]に書き込み
$C1/1E00 LDA #$00                ;Aに$00をロード
$C1/1E02 STA $7ECE33,x[$7E:EA33] ;A($00)を[$7E:EA33]に書き込み
$C1/1E06 STA $7ECE35,x[$7E:EA35] ;A($00)を[$7E:EA35]に書き込み
$C1/1E0A REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/1E0C LDA $10    [$00:0310]   ;Aに[$00:0311][$00:0310](火炎ダメージ $0018)をロード
$C1/1E0E STA $003C,x[$00:1C3C]   ;A($0018)を[$00:1C3D][$00:1C3C]に書き込み
$C1/1E11 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/1E13 LDA #$01                ;Aに$01をロード
$C1/1E15 STA $10    [$00:0310]   ;A($01)を[$00:0310]に書き込み
$C1/1E17 STX $60    [$00:0360]   ;Xを[$00:0360]に書き込み
$C1/1E19 JSR $CBA6  [$C1:CBA6]   ;[$C1:CBA6]へジャンプ
;
$C1/CBA6 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CBA8 LDA $003C,x[$00:1C3C]   ;Aに[$00:1C3D][$00:1C3C]($0018)をロード
$C1/CBAB AND #$7FFF              ;Aと$7FFFで論理積
;符号付16bit x 8bit $0018*$01
$C1/CBAE SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CBB0 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/CBB3 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/CBB4 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/CBB7 LDA $10    [$00:0310]   ;Aに[$00:0310]($01)をロード
$C1/CBB9 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CBBC STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CBBF JSR $CD72  [$C1:CD72]   ;[$C1:CD72]へジャンプ
;
$C1/CD72 LDA $77    [$00:0377]   ;Aに[$00:0377]をロード
$C1/CD74 AND #$0F                ;Aと$0Fで論理積
$C1/CD76 BEQ $20    [$CD98]      ;ゼロフラグが立っているとき[$CD98]分岐
$C1/CD98 STZ $20    [$00:0320]   ;[$00:0320]に$00を書き込み
$C1/CD9A STZ $21    [$00:0321]   ;[$00:0321]に$00を書き込み
$C1/CD9C JSR $CDE7  [$C1:CDE7]   ;[$C1:CDE7]へジャンプ
;ここで合流

分岐部分は上で、$C1/1DF8$C1/1E19$C1/CBA6$C1/CBBF$C1/CD72$C1/CD9Cと処理してから[$C1:CDE7]へジャンプするが、$C1:CDE7からはフィールド吸収可能処理と合流する。
また、合流してからしばらく処理が続き、$C1/CDE6まで処理が終わった直後に、ゲーム画面ではフィールドダメージが発動して、フィールドが光り、キャラが乗っていればダメージ処理が行われる。
$C1/CDE6の処理はRTS、つまりサブルーチンの戻りなので、ひとつ前のサブルーチンの続きに飛ぶのだが、その飛び先は、上の$C1/CBBFの続きなのである。
フィールド吸収可能な場合は$C1/CBA6$C1/CBBFではなく、$C1:CB62$C1/CB84という処理を通る。
そして$C1/CDE6の後は、$C1/CB84の後に戻るため、フィールドダメージでダメージを食らう場合と吸収する場合での処理が別になる、という手順である。

と、少し先の処理まで説明してみたが、上のサブルーチン、肝心の$C1/CBA6$C1/CBBFで何を行っているのか。
少し前から追いかけてみると、火炎フィールドのダメージ量$18が入った[$00:0310]を、ひとつ前のアドレス[$00:0311](=$00)も含めて、[$00:1C3D][$00:1C3C]にコピーした後、[$00:0310]$01を入れてから、[$00:1C3D][$00:1C3C]×[$00:0310]($01)という符号付16bit x 8bitの乗算を行っていることがわかる。
つまり$0018 * $01だから、答えは$18のままである。
乗算をしたので、答えは[$00:2134][$00:2136]に入っているが、上の処理では特に読み込みはしていない(答えは24bit、つまり3個のアドレスに1バイトずつ分かれて入るため、[$00:2134]$18、他アドレスは$00となる)。

なお、$C1/1E0ESTA $003C,xというニーモニックで、味方キャラ1人目のおぼろ丸はX = $1C00であるから、[$00:1C3D][$00:1C3C]というアドレスに$0018が入ったという点に注意。
また、以下のように値が書き込まれてもいるが、Xにより書き込むアドレスが変わる、つまり味方キャラの何人目かでアドレスが変わる(2人目以降は+$40)。

アドレス指定おぼろ丸のアドレス
$003A,x[$00:1C3A]$01
$003B,x[$00:1C3B]$01
$7ECE33,x[$7E:EA33]$00
$7ECE35,x[$7E:EA35]$00

では、フィールド吸収が可能な場合の処理$C1:1E26~はどうなるか。
ここでは、とらわれの男が「げんじのタビ」を装備していて、火炎フィールドを吸収可能として処理を追いかけてみよう。

;フィールド吸収ができる場合の処理(とらわれの男)
$C1/1E26 LDA #$FF                ;Aに$FFをロード
$C1/1E28 STA $003A,x[$00:1C7A]   ;A($FF)を[$00:1C7A]に書き込み
$C1/1E2B INC A                   ;Aをインクリメント +1
$C1/1E2C STA $7ECE35,x[$7E:EA75] ;A($00)を[$7E:EA75]に書き込み
$C1/1E30 STA $003B,x[$00:1C7B]   ;A($00)を[$00:1C7B]に書き込み
$C1/1E33 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/1E35 LDA $10    [$00:0310]   ;Aに[$00:0311][$00:0310](火炎ダメージ $0018)をロード
$C1/1E37 STA $003C,x[$00:1C7C]   ;A($0018)を[$00:1C7D][$00:1C7C]に書き込み
$C1/1E3A SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/1E3C LDA #$01                ;Aに$01をロード
$C1/1E3E STA $10    [$00:0310]   ;A($01)を[$00:0310]に書き込み
$C1/1E40 STX $60    [$00:0360]   ;Xを[$00:0360]に書き込み
$C1/1E42 JSR $CB62  [$C1:CB62]   ;[$C1:CB62]へジャンプ
;
$C1/CB62 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CB64 LDA $003C,x[$00:1C7C]   ;Aに[$00:1C7D][$00:1C7C]($0018)をロード
$C1/CB67 AND #$7FFF              ;Aと$7FFFで論理積
$C1/CB6A SEP #$20                ;MフラグON Aレジスタは8bit幅
;符号付16bit x 8bit $0018*$01
$C1/CB6C STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/CB6F XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/CB70 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/CB73 LDA $10    [$00:0310]   ;Aに[$00:0310]($01)をロード
$C1/CB75 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CB78 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CB7B LDY $2134  [$00:2134]   ;Yに[$00:2134](乗算の解・下1バイト)をロード
$C1/CB7E LDA #$00                ;Aに$00をロード
$C1/CB80 STA $7ECE35,x[$7E:EA75] ;A($00)を[$7E:EA75]に書き込み
$C1/CB84 JSR $CD98  [$C1:CD98]   ;[$C1:CD98]へジャンプ
;
$C1/CD98 STZ $20    [$00:0320]   ;[$00:0320]に$00を書き込み
$C1/CD9A STZ $21    [$00:0321]   ;[$00:0321]に$00を書き込み
$C1/CD9C JSR $CDE7  [$C1:CDE7]   ;[$C1:CDE7]へジャンプ
;ここで合流

実は処理自体はほとんど変わらないのである。
違いは、$0018 * $01の解である$00:2134$C1/CB7BYにロードしていることと、以下のように各アドレスに値が入ることである。

アドレス指定とらわれの男のアドレス
$003A,x[$00:1C7A]$FF
$003B,x[$00:1C7B]$00
$7ECE35,x[$7E:EA75]$00

おぼろ丸、つまりフィールド吸収不可の時と見比べると、[$003A,x][$003B,x]に入る値が異なることがわかる。

アドレス指定おぼろ丸のアドレス
$003A,x[$00:1C3A]$01
$003B,x[$00:1C3B]$01
$7ECE33,x[$7E:EA33]$00
$7ECE35,x[$7E:EA35]$00

さて、ジャンプ先の$C1:CDE7なのだが、このアドレス以降のサブルーチンは既に「ほいこーろー」の仕様で紹介している。

$C1/CDE7 STZ $22    [$00:0322]   ;[$00:0322]に$00を書き込み
$C1/CDE9 STZ $23    [$00:0323]   ;[$00:0323]に$00を書き込み
$C1/CDEB LDA $77    [$00:0377]   ;Aに[$00:0377]をロード
$C1/CDED AND #$0F                ;Aと$0Fで論理積
$C1/CDEF CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C1/CDF1 BNE $17    [$CE0A]      ;ゼロフラグが立っていないとき[$CE0A]分岐
$C1/CE0A RTS                     ;サブルーチン戻り

$C1/CDF1でゼロフラグが立つ時は「ほいこーろー」の処理に続くことになる。
今回は$C1/CE0Aにジャンプして分岐になるが、この$C1/C???あたりの処理は、ダメージ量計算関係のサブルーチンが集中しているようである。

$C1/CD9F REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/CDA1 LDA $2134  [$00:2134]   ;Aに[$00:2135][$00:2134]($0018)をロード
$C1/CDA4 SEC                     ;キャリーフラグON
$C1/CDA5 SBC $20    [$00:0320]   ;A($0018) - [$00:0321][$00:0320]($0000)
$C1/CDA7 BCS $03    [$CDAC]      ;キャリーフラグが立っているとき[$CDAC]分岐
$C1/CDAC STA $20    [$00:0320]   ;A($0018)を[$00:0321][$00:0320]に書き込み
$C1/CDAE LDA $7ECE35,x[$7E:EA35] ;Aに[$7E:EA36][$7E:EA35]($0000)をロード
$C1/CDB2 AND #$00FF              ;Aと$00FFで論理積
$C1/CDB5 BEQ $04    [$CDBB]      ;ゼロフラグが立っているとき[$CDBB]分岐
$C1/CDBB LDA $20    [$00:0320]   ;Aに[$00:0321][$00:0320]($0018)をロード
$C1/CDBD CLC                     ;キャリーフラグクリア
$C1/CDBE ADC $22    [$00:0322]   ;A($0018) + [$00:0323][$00:0322]($0000)
$C1/CDC0 CMP #$03E7              ;Aと$03E7(10進数999)を減算比較(ステータスレジスタ変更のみ)
$C1/CDC3 BCC $03    [$CDC8]      ;キャリーフラグが立っていないとき[$CDC8]分岐
$C1/CDC8 STA $7ECE2E,x[$7E:EA2E] ;A($18)を[$7E:EA2E]に書き込み
$C1/CDCC STA $4204  [$00:4204]   ;符号付16bit / 8bit 被除数
$C1/CDCF SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CDD1 JSR $CE0B  [$C1:CE0B]   ;[$C1:CE0B]へジャンプ

上処理の最後のあたり、[$7ECE2E,x](おぼろ丸なら[$7E:EA2E])が、ダメージ吸収不可の場合は後で必要になる。
どういう計算をしているか追いかけてみると、

  1. [$00:2135][$00:2134]($0018*$01の乗算結果2バイト) - [$00:0321][$00:0320]($0000) = $0018
  2. 上の結果を[$00:0321][$00:0320]に書きこみ($0018)
  3. [$00:0321][$00:0320] + [$00:0323][$00:0322]($0000) = $0018
  4. 上の結果$0018$03E7(10進数999)を減算比較、負の値なら[$7E:EA2E]$18書き込み

結局のところ、火炎フィールドのダメージ量$18[$7E:EA2E]に書き込まれたことになる。
一見してよく意味がわからない処理になっているが、ダメージ量関係の処理が兼用になっているサブルーチンになっていると思われ、本来ならダメージ量が999になったら値の処理を分岐させるなどが入るはずだが、フィールドダメージは1マスずつ計算しているので、1回のダメージ量は最大でも電撃フィールドの32であり、カンストに関する計算は必要ない。

とにかく、[$7E:EA2E]にフィールドダメージの値が入った、ということが重要である。
この後の処理は実際にフィールドダメージが発動する箇所まで省略する。
先に記したが、この先、$C1/CDE6まで、ダメージ吸収かどうか問わず、処理は共通である。
$C1/CDE6RTSの処理直後にダメージフィールドが発動するが、その先は、

  • ダメージフィールド吸収不可:$C1/CBC2
  • ダメージフィールド吸収可能:$C1/CB87

と、分岐する。
まず、ダメージフィールド吸収不可(おぼろ丸の場合)を紹介する。

$C1/CDE6 RTS                     ;サブルーチン戻り
;
;※ここで火炎フィールド発動
;ダメージフィールド吸収不可
$C1/CBC2 LDY $2134  [$00:2134]   ;Yに[$00:2134]をロード
$C1/CBC5 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CBC7 LDY $0002,x[$00:1C02]   ;Yに[$00:1C03][$00:1C02](計算用アドレス)をロード
$C1/CBCA LDA $0008,y[$00:0EC8]   ;Aに[$00:0EC9][$00:0EC8](おぼろ丸現在HP $0165)をロード
$C1/CBCD SEC                     ;キャリーフラグON
$C1/CBCE SBC $7ECE2E,x[$7E:EA2E] ;A($0165) - [$7E:EA2F][$7E:EA2E](火炎フィールド$18) = $014D
$C1/CBD2 BCS $03    [$CBD7]      ;キャリーフラグが立っているとき[$CBD7]分岐
$C1/CBD4 LDA #$0000              ;(キャリーフラグOFF)Aに$0000をロード
$C1/CBD7 JSR $CE60  [$C1:CE60]   ;[$C1:CE60]へジャンプ

$C1/CBCAで呼び出しているのは、シナリオ別キャラデータの現在HP(2バイト)にあたる。
おぼろ丸であれば[$00:0EC9][$00:0EC8]が該当する。
$C1/CBCEでは、[$00:0EC9][$00:0EC8] - [$7E:EA2F][$7E:EA2E]を計算していることになる。
[$7E:EA2E]は、先の計算で、火炎フィールドのダメージ量$18が入っている。
つまり、「現在HP(2バイト) - 火炎フィールドのダメージ量$18」の計算である。
火炎フィールドでダメージを受けた後のおぼろ丸のHPを計算したことになる。
現在HPの方が火炎フィールドダメージより低い場合は減算で負になるので、キャリーフラグが立たない場合、つまり減算結果が負の時はA$0000をロードしている。
つまり負の値になったら、減算結果を0に修正している。HPが負の値になることはないからだろう。
要するに、Aには、火炎フィールドダメージをくらった場合のおぼろ丸の残りHPが入った状態で[$C1:CE60]へジャンプする。

一方、ダメージフィールド吸収可能(とらわれの男の場合)は以下のようになる。

;※ここで火炎フィールド発動
;ダメージフィールド吸収可能
$C1/CB87 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CB89 LDY $0002,x[$00:1C42]   ;Yに[$00:1C43][$00:1C42](計算用アドレス)をロード
$C1/CB8C LDA $0008,y[$00:0F08]   ;Aに[$00:0F09][$00:0F08](とらわれの男 現在HP 2バイト $00EF)をロード
$C1/CB8F ADC $2134  [$00:2134]   ;A + [$00:2135][$00:2134]($0018*$01の乗算結果2バイト/$0018)
$C1/CB92 CMP $000A,y[$00:0F0A]   ;Aと[$00:0F0B][$00:0F0A](最大HP 2バイト $00F0)を減算比較(ステータスレジスタ変更のみ)
$C1/CB95 BCC $03    [$CB9A]      ;キャリーフラグが立っていないとき[$CB9A]分岐
$C1/CB97 LDA $000A,y[$00:0F0A]   ;(キャリーフラグOFF)Aに[$00:0F0B][$00:0F0A](最大HP 2バイト $00F0)をロード
$C1/CB9A JSR $CE60  [$C1:CE60]   ;[$C1:CE60]へジャンプ

$C1/CB8Cで呼び出しているのは、シナリオ別キャラデータの現在HP(2バイト)で、とらわれの男なら[$00:0F09][$00:0F08]である。
ダメージフィールド吸収不可処理と異なるのは次で、前に計算した$0018 * $01の解が入った状態の[$00:2135][$00:2134]を、とらわれの男の現在HPと加算している。
つまり、「現在HP(2バイト) + 火炎フィールドのダメージ量$18」の計算である。
火炎フィールドダメージ量を吸収(加算)した後のとらわれの男のHPを計算したことになる。
$C1/CB92では、計算値ととらわれの男の最大HP[$00:0F0B][$00:0F0A]を減算比較している。
現在HP + 吸収分が、最大HPを越えていたら、$C1/CB95でキャリーフラグが立つので、Aに最大HPの値[$00:0F0B][$00:0F0A]を書き込む。
キャリーフラグが立っていない、つまり最大HPを越えていなければそのままである。
ダメージを吸収して最大HPを越えていたら、HPを最大HPの値に戻す、という処理である。
要するに、Aには、火炎フィールドダメージを吸収した場合のとらわれの男の残りHPが入った状態で[$C1:CE60]へジャンプする。

さて、この後の処理は[$C1:CE60]に飛ぶから、フィールドダメージ吸収かどうか関係ない共通処理になる。
どちらもAに「火炎フィールド発生時のキャラのHPの計算値」が入った状態で[$C1:CE60]に飛ぶ。

$C1/CE60 STA $20    [$00:0320]   ;A(=$014D)を[$00:0321][$00:0320]に書き込み
$C1/CE62 SEC                     ;キャリーフラグON
(中略)
$C1/CE83 STA $7E9EF0[$7E:9EF0]   ;Aを[$7E:9EF1][$7E:9EF0]に書き込み
$C1/CE87 LDA $20    [$00:0320]   ;Aに[$00:0321][$00:0320]をロード
$C1/CE89 RTS                     ;サブルーチン戻り

Aに入っていた、「火炎フィールド発生時のキャラのHPの計算値」は、[$00:0321][$00:0320]に書き込まれる。
そして上の最後のあたりの$C1/CE87で、[$00:0321][$00:0320]をロードしている。
実はこの後の$C1/CE89の処理で、[$00:0321][$00:0320]を現在HPのアドレスに上書きするので、今回の解説において$C1/CE62$C1/CE83の処理は特に関係ないため、説明は端折る。
$C1/CE89でサブルーチンが戻るが、戻り先はダメージフィールド吸収不可か可能かで変わる。

;ダメージフィールド吸収不可
$C1/CBDA STA $0008,y[$00:0EC8]   A:014D X:1C00 Y:0EC0 P:envmxdIzc;Aを[$00:0EC9][$00:0EC8](現在HP)に書き込み
$C1/CBDD TAY                     A:014D X:1C00 Y:0EC0 P:envmxdIzc;Aレジスタの値をYレジスタに転送
$C1/CBDE SEP #$20                A:014D X:1C00 Y:014D P:envmxdIzc;MフラグON Aレジスタは8bit幅
$C1/CBE0 LDA #$FF                A:014D X:1C00 Y:014D P:envMxdIzc;Aに$FFをロード
$C1/CBE2 RTS                     A:01FF X:1C00 Y:014D P:eNvMxdIzc;サブルーチン戻り

;ダメージフィールド吸収可能
$C1/CB9D STA $0008,y[$00:0F08]   A:00F0 X:1C40 Y:0F00 P:envmxdIzc;Aを[$00:0F09][$00:0F08](現在HP)に書き込み
$C1/CBA0 TAY                     A:00F0 X:1C40 Y:0F00 P:envmxdIzc;Aレジスタの値をYレジスタに転送
$C1/CBA1 SEP #$20                A:00F0 X:1C40 Y:00F0 P:envmxdIzc;MフラグON Aレジスタは8bit幅
$C1/CBA3 LDA #$FF                A:00F0 X:1C40 Y:00F0 P:envMxdIzc;Aに$FFをロード
$C1/CBA5 RTS                     A:00FF X:1C40 Y:00F0 P:eNvMxdIzc;サブルーチン戻り

戻り先が変わるといっても、上のように、アドレスが異なるだけで処理は同じである。
「火炎フィールド発生時のキャラのHPの計算値」が[$00:0321][$00:0320]を経由して、シナリオ別キャラデータの現在HP2バイト分に書き込まれており、これでダメージフィールド発生時の処理が終了になる。

以上が味方キャラの場合の処理である。
では、敵ではどうなるか。

敵キャラのフィールドダメージ計算

かなり前に戻るが、マス目毎のサブルーチンのループ処理中、[$C1:2029]へジャンプしてからが、敵キャラのフィールドダメージ処理になる。
説明を簡略化するため、ここでは、敵種類1、敵番号0の敵に対する処理とする。

$C1/2029 AND #$0F                ;Aと$0Fで論理積
$C1/202B DEC A                   ;Aをデクリメント -1
$C1/202C CLC                     ;キャリーフラグクリア
$C1/202D ASL A                   ;Aを算術左シフト *2
$C1/202E ASL A                   ;Aを算術左シフト *2
$C1/202F ASL A                   ;Aを算術左シフト *2
$C1/2030 ASL A                   ;Aを算術左シフト *2
$C1/2031 STA $1E    [$00:031E]   ;Aを[$00:031E]に書き込み
$C1/2033 LDA #$1B                ;Aに$1Bをロード
$C1/2035 STA $1F    [$00:031F]   ;Aを[$00:031F]に書き込み
$C1/2037 LDX $1E    [$00:031E]   ;Xに[$00:031E]をロード
$C1/2039 LDA $07    [$00:0307]   ;Aに[$00:0307]をロード
$C1/203B STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/203D STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
$C1/203F LDA $7E9003,x[$7E:AB03] ;Aに[$7E:AB03](ヒット数)をロード
$C1/2043 BNE $24    [$2069]      ;ゼロフラグが立っていないとき[$2069]分岐
$C1/2045 LDY $0002,x[$00:1B02]   ;Yに[$00:1B02]をロード
$C1/2048 LDA $003F,y[$00:1A3F]   ;Aに[$00:1A3F](敵データ31)をロード
;敵データ31の下1ビットはダメージフィールド無効だと%1
$C1/204B BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C1/204D BNE $1A    [$2069]      ;ゼロフラグが立っていないとき[$2069]分岐
;ゼロフラグOFFは、ダメージフィールド無効時
$C1/204F LDA $0021,y[$00:1A21]   ;Aに[$00:1A21](敵データ01 上4ビット:フィールド吸収)をロード
$C1/2052 LSR A                   ;Aを論理右シフト (/2)
$C1/2053 LSR A                   ;Aを論理右シフト (/2)
$C1/2054 LSR A                   ;Aを論理右シフト (/2)
$C1/2055 LSR A                   ;Aを論理右シフト (/2)
$C1/2056 AND $06    [$00:0306]   ;Aと[$00:0306]($02)で論理積
$C1/2058 BNE $10    [$206A]      ;ゼロフラグが立っていないとき[$206A]分岐
;ゼロフラグOFFはフィールド吸収
$C1/205A LDY $08    [$00:0308]   ;Yに[$00:0308]をロード
$C1/205C PHY                     ;Yをスタックへプッシュ
$C1/205D LDY $1A    [$00:031A]   ;Yに[$00:031A]をロード
$C1/205F PHY                     ;Yをスタックへプッシュ
$C1/2060 JSR $1E71  [$C1:1E71]   ;[$C1:1E71]へジャンプ
;
;$C1/2043と$C1/204Dの飛び先
$C1/2069 RTS                     ;サブルーチン戻り
;
;$C1/2058でゼロフラグOFF
$C1/206A LDY $08    [$00:0308]   ;Yに[$00:0308]をロード
$C1/206C PHY                     ;Yをスタックへプッシュ
$C1/206D LDY $1A    [$00:031A]   ;Yに[$00:031A]をロード
$C1/206F PHY                     ;Yをスタックへプッシュ
$C1/2070 JSR $1EA3  [$C1:1EA3]   ;[$C1:1EA3]へジャンプ

$C1/203Dまではアドレス呼び出しなどになるが、$C1/203F[$7E9003,x]のロードは、該当の敵番号の被ヒット数である。
敵番号0なら[$7E:AB03]になる。
フィールドダメージにヒット数の概念はないので、現状では$00が入っており、ゼロフラグが立つ。
実はこのヒット数こそが後々で重要になる。

火炎フィールドのダメージ量が入っている[$00:0307]は、$C1/203B[$00:0310]にコピーされている。

$C1/2048[$003F,y]をロードするが、これは該当の敵種類の敵データ31である。敵種類0なら[$00:1A3F]である。
敵データ31は上7ビットに回避属性、下1ビットはダメージフィールド無効だと%1で、有効なら%0が入る。
$C1/204B$01と論理積を取ってゼロフラグが立つかどうか判定しているから、ここでダメージフィールドが有効な敵か無効な敵か分岐になる。
ダメージフィールドが有効であるなら、$C1/204Fに処理が続く。

$C1/204F[$0021,y]をロードするが、これは該当の敵種類の敵データ01である。敵種類0なら[$00:1A21]である。
上4ビットにフィールド吸収かどうか、合計値が入る。

フィールド
$1
$2
$4
$8

$C1/2052$C1/2055で論理右シフトを4回行っているから、上4ビットを下4ビット位置まで移動させていることがわかる。
$C1/2056[$00:0306]と論理積を取るが、ダメージフィールド発生の時点で以下のように値が入ったままである。

フィールド[$00:0306]
電撃$01
$08
$04
火炎$02

要するに、敵データ01の上4ビットのフィールド吸収の値に対応した値が入った状態である。
今回は火炎フィールド発生状態という前提なので[$00:0306]$02が入っているが、$C1/2056の時点でA$02が入っていればダメージフィールド吸収、それ以外ならダメージフィールドでダメージ、ということになる。
よって、$C1/2058のゼロフラグ分岐は、フィールド吸収なら$C1/206Aへジャンプする、という意味になる。
敵キャラの場合、ここでフィールド吸収かどうかの分岐が起きている。
以降の処理を見ると、フィールド吸収ができない(ダメージをくらう)場合は、[$C1:1E71]へ、フィールド吸収ができる場合は[$C1:1EA3]へジャンプしている。
味方キャラの時と同じく処理の分岐が起こることがわかる。

;フィールド吸収ができない場合の処理
$C1/1E71 LDA #$01                ;Aに$01をロード
$C1/1E73 STA $7E9002,x[$7E:AB02] ;A($01)を[$7E:AB02](被ダメージ/回復量のヒット数)に書き込み
$C1/1E77 STA $7E9003,x[$7E:AB03] ;A($01)を[$7E:AB03](ヒット数)に書き込み
$C1/1E7B LDA #$00                ;Aに$00をロード
$C1/1E7D STA $7E9202,x[$7E:AD02] ;A($00)を[$7E:AD02]に書き込み
$C1/1E81 STA $7E920A,x[$7E:AD0A] ;A($00)を[$7E:AD0A]に書き込み

$C1:1E71に飛んだ直後に、重要な処理がある。
$C1/1E77[$7E:AB03](ヒット数)に$01を書き込んでいる。
この少し前の処理$C1/203Fで、[$7E:AB03](ヒット数)が$00だったから、フィールドダメージの計算をする処理に進んでいた。
だが、ここで[$7E:AB03]$01を書き込んでいる。
もしこの敵が、2マス以上にまたがる敵で、かつ複数マスがダメージフィールドに乗っている状態であったら、他のマスの処理の時、敵がいると判定した時に、
[$7E:AB03](ヒット数)が$01
という判断・分岐でフィールドダメージの計算を行わない。
この仕様により、マス目毎の判定で、大型の敵の最も左上のマスでのみフィールドダメージの計算をして、他のマスでは計算をしないということになる。
これが、「大型の敵であっても、フィールドダメージでのダメージ量は1マス分になる」という仕組みの正体なのである。
ヒット数が書き込まれるアドレスは、敵番号により、$7E:AB03, $7E:AB13, $7E:AB23……と+$10ずつされるため、個々の敵で判定がなされることになる。

$C1/1E85 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/1E87 LDA $10    [$00:0310]   ;Aに[$00:0311][$00:0310](火炎ダメージ $0018)をロード
$C1/1E89 STA $7E9004,x[$7E:AB04] ;Aを[$7E:AB05][$7E:AB04](被ダメージ量)に書き込み
$C1/1E8D LDA #$0000              ;Aに$0000をロード
$C1/1E90 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/1E92 LDA #$01                ;Aに$01をロード
$C1/1E94 STA $10    [$00:0310]   ;A($01)を[$00:0310]に書き込み
$C1/1E96 STZ $81    [$00:0381]   ;[$00:0381]に$00を書き込み
$C1/1E98 STX $60    [$00:0360]   ;Xを[$00:0360]に書き込み
$C1/1E9A JSR $CC72  [$C1:CC72]   ;[$C1:CC72]へジャンプ
;
$C1/CC72 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CC74 LDA $7E9004,x[$7E:AB04] ;Aに[$7E:AB05][$7E:AB04](火炎ダメージ $0018)をロード
$C1/CC78 AND #$7FFF              ;Aと$7FFFで論理積
;符号付16bit x 8bit $0018*$01
$C1/CC7B SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CC7D STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/CC80 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/CC81 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/CC84 LDA $10    [$00:0310]   ;Aに[$00:0310]($01)をロード
$C1/CC86 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CC89 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CC8C LDY $2134  [$00:2134]   ;Yに[$00:2134](乗算の解・下1バイト)をロード
$C1/CC8F LDA $7E920A,x[$7E:AD0A] ;Aに[$7E:AD0A]($00)をロード
$C1/CC93 AND $81    [$00:0381]   ;Aと[$00:0381]で論理積
$C1/CC95 STA $7E920A,x[$7E:AD0A] ;Aを[$7E:AD0A]($00)に書き込み
$C1/CC99 JSR $CD2F  [$C1:CD2F]   ;[$C1:CD2F]へジャンプ
;
$C1/CD2F JSR $CDE7  [$C1:CDE7]   ;[$C1:CDE7]へジャンプ
;味方キャラとの処理と合流
;(中略)
$C1/CD32 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/CD34 LDA $2134  [$00:2134]   ;Aに[$00:2134]($18)をロード
$C1/CD37 STA $20    [$00:0320]   ;A($18)を[$00:0320]に書き込み
$C1/CD39 LDA $7E920A,x[$7E:AD0A] ;Aに[$7E:AD0A]($00)をロード
$C1/CD3D AND #$00FF              ;Aと$00FFで論理積
$C1/CD40 BEQ $04    [$CD46]      ;ゼロフラグが立っているとき[$CD46]分岐
$C1/CD46 LDA $20    [$00:0320]   ;Aに[$00:0320]($18)をロード
$C1/CD48 CLC                     ;キャリーフラグクリア
$C1/CD49 ADC $22    [$00:0322]   ;A + [$00:0322]
$C1/CD4B CMP #$03E7              ;Aと$03E7を減算比較(ステータスレジスタ変更のみ)
$C1/CD4E BCC $03    [$CD53]      ;キャリーフラグが立っていないとき[$CD53]分岐
$C1/CD53 STA $7E9200,x[$7E:AD00] ;A($18)を[$7E:AD00]に書き込み
$C1/CD57 STA $4204  [$00:4204]   ;A($18)を[$00:4204]に書き込み
$C1/CD5A SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CD5C JSR $CE0B  [$C1:CE0B]   ;[$C1:CE0B]へジャンプ

このあたりは、アドレスこそ異なるが、実のところは味方側の処理と大きな違いはない。
[$00:0311][$00:0310]に入っている火炎フィールドのダメージ量$0018を、敵番号0の被ダメージ量[$7E:AB05][$7E:AB04]に書き込み、$C1/CC72~は$0018 * $01を計算している。乗算結果は[$00:2134][$00:2136]に入っている。
乗算結果の下1バイト、つまり$18は、[$00:2134][$00:0320][$7E:AD00][$00:4204]とコピーされ、この後の計算に使われる。

最終的に$C1:CDE7にジャンプするが、これは味方キャラの処理との合流である。
途中まではだいたい同じ処理なのだが、途中で行動異常による順序ポイント関係の処理$C1/1CF7$C1/1D1Dが入る。
該当の処理の詳細は行動異常にて説明しているので参照のこと。

$C1/CCA5 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/CCA7 LDA $7E8F00,x[$7E:AA00] ;Aに[$7E:AA01][$7E:AA00](敵番号0現在HP)をロード
$C1/CCAB SEC                     ;キャリーフラグON
$C1/CCAC SBC $7E9200,x[$7E:AD00] ;A(現在HP) - [$7E:AD01][$7E:AD00]($0018) ※火炎ダメージ後の残りHP
$C1/CCB0 BCS $03    [$CCB5]      ;キャリーフラグが立っているとき[$CCB5]分岐
;キャリーフラグOFF
$C1/CCB2 LDA #$0000              ;Aに$0000をロード
;処理合流
$C1/CCB5 JSR $CE70  [$C1:CE70]   ;[$C1:CE70]へジャンプ
;
$C1/CE70 STA $20    [$00:0320]   ;A(残りHP)を[$00:0320]に書き込み
;(中略)
$C1/CE87 LDA $20    [$00:0320]   ;Aに[$00:0320](残りHP)をロード
$C1/CE89 RTS                     ;サブルーチン戻り
;
$C1/CCB8 STA $7E8F00,x[$7E:AA00] ;A(残りHP)を[$7E:AA01][$7E:AA00](現在HP)に書き込み
$C1/CCBC TAY                     ;Aレジスタの値をYレジスタに転送
$C1/CCBD SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CCBF JSR $0C14  [$C1:0C14]   ;[$C1:0C14]へジャンプ

少し処理を飛ばして、最終的にHPの計算をしている部分だけ抜粋した。
敵の現在HPが入っている、[$7E:AAx1][$7E:AAx0]xは敵番号)から、[$7E:AD00]に入った火炎フィールドダメージ量を引いて、減算結果が負の値だったら$0000を残りHPとし、$C1/CCB8[$7E:AA01][$7E:AA00]に書き込み、という処理である。
大型の敵であっても、この処理が1回しか行われず、他のマスでは行われないため、大型の敵がどれだけダメージフィールドマスを踏んでいても、ダメージ量は1マス分、となることがわかった。

敵のフィールド吸収処理

敵についての問題はHP吸収処理である。
大型の敵だと、1マス分のダメージ量×ダメージフィールドマスを踏んでいる数だけHPが回復する仕様である。ただし画面に表示される回復量の数値は1マス分という、ミスなのか、わざとなのかわからない仕様になっている。

以下では、キングマンモー戦で、キングマンモーが発生させた火炎フィールドでキングマンモーが回復する処理を見てみよう。
キングマンモーは3×3サイズだが、3×3すべてに火炎フィールドがある、つまりキングマンモーは9マスの火炎フィールドを踏んでいる状態とする。
敵がフィールドダメージを吸収する場合の飛び先[$C1:1EA3]以降を見ていく。

;フィールド吸収が可能な場合の処理
$C1/1EA3 LDA #$FF                ;Aに$FFをロード
$C1/1EA5 STA $7E9002,x[$7E:AB02] ;A($FF)を[$7E:AB02](被ダメージ/回復量のヒット数)に書き込み
$C1/1EA9 LDA #$00                ;Aに$00をロード
$C1/1EAB STA $7E9003,x[$7E:AB03] ;A($00)を[$7E:AB03](ヒット数)に書き込み
$C1/1EAF STA $7E920A,x[$7E:AD0A] ;A($00)を[$7E:AD0A]に書き込み
$C1/1EB3 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/1EB5 LDA $10    [$00:0310]   ;Aに[$00:0310](火炎フィールドのダメージ$18)をロード
$C1/1EB7 STA $7E9004,x[$7E:AB04] ;A($18)を[$7E:AB04](被ダメージの単発値)に書き込み
$C1/1EBB LDA #$0000              ;Aに$0000をロード
$C1/1EBE SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/1EC0 LDA #$80                ;Aに$80をロード
$C1/1EC2 STA $81    [$00:0381]   ;Aを[$00:0381]に書き込み
$C1/1EC4 LDA #$01                ;Aに$01をロード
$C1/1EC6 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/1EC8 STX $60    [$00:0360]   ;Xを[$00:0360]に書き込み
$C1/1ECA JSR $CC72  [$C1:CC72]   ;[$C1:CC72]へジャンプ

フィールド吸収ができない場合は、[$7E:AB03](ヒット数)に$01を書き込んでいたが、フィールド吸収が可能な場合は[$7E:AB03](ヒット数)に$00を書き込んでいる。
このため、フィールド吸収が可能な場合、敵が乗っているマス全てでフィールド吸収処理が入る。
これがフィールド吸収ができない場合との大きな相違点であり、今回であればキングマンモーは自身が乗っている3×3マス全てでダメージ吸収計算を行うことになるから、$18×9で10進数216ものHP回復を行うことになってしまう。
ただ、HP回復は1マスずつ行うので、最後のマスの回復量$18をゲーム画面に表示することになる。合計値の表示ではないのである。

「ミスなのかわざとなのかわからない仕様」と上に書いたが、この処理を見る限り意図した仕様なのは間違いないようだ。
回復量の表示については、わざとなのか、うまく処理ができなかったのか、よくわからないが。

;フィールド吸収不可と共通処理
$C1/CC72 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CC74 LDA $7E9004,x[$7E:AB04] ;Aに[$7E:AB04](火炎フィールドのダメージ$18)をロード
$C1/CC78 AND #$7FFF              ;Aと$7FFFで論理積
;符号付16bit x 8bit $0018*$01
$C1/CC7B SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CC7D STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/CC80 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/CC81 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/CC84 LDA $10    [$00:0310]   ;Aに[$00:0310]をロード
$C1/CC86 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CC89 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/CC8C LDY $2134  [$00:2134]   ;Yに[$00:2134](乗算の解・下1バイト)をロード
$C1/CC8F LDA $7E920A,x[$7E:AD0A] ;Aに[$7E:AD0A]($00)をロード
$C1/CC93 AND $81    [$00:0381]   ;Aと[$00:0381]で論理積
$C1/CC95 STA $7E920A,x[$7E:AD0A] ;Aを[$7E:AD0A]($00)に書き込み
$C1/CC99 JSR $CD2F  [$C1:CD2F]   ;[$C1:CD2F]へジャンプ
;
$C1/CD2F JSR $CDE7  [$C1:CDE7]   ;[$C1:CDE7]へジャンプ
;味方キャラとの処理と合流
;(中略)
$C1/CD32 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/CD34 LDA $2134  [$00:2134]   ;Aに[$00:2134]をロード
$C1/CD37 STA $20    [$00:0320]   ;Aを[$00:0320]に書き込み
$C1/CD39 LDA $7E920A,x[$7E:AD0A] ;Aに[$7E:AD0A]をロード
$C1/CD3D AND #$00FF              ;Aと$00FFで論理積
$C1/CD40 BEQ $04    [$CD46]      ;ゼロフラグが立っているとき[$CD46]分岐
$C1/CD46 LDA $20    [$00:0320]   ;Aに[$00:0320]をロード
$C1/CD48 CLC                     ;キャリーフラグクリア
$C1/CD49 ADC $22    [$00:0322]   ;A + [$00:0322]
$C1/CD4B CMP #$03E7              ;Aと$03E7を減算比較(ステータスレジスタ変更のみ)
$C1/CD4E BCC $03    [$CD53]      ;キャリーフラグが立っていないとき[$CD53]分岐
$C1/CD53 STA $7E9200,x[$7E:AD00] ;Aを[$7E:AD00]に書き込み
$C1/CD57 STA $4204  [$00:4204]   ;Aを[$00:4204]に書き込み
$C1/CD5A SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CD5C JSR $CE0B  [$C1:CE0B]   ;[$C1:CE0B]へジャンプ

$C1/CC72~からはフィールド吸収不可と共通処理である。
敵番号0の被ダメージ量$0018[$7E:AB05][$7E:AB04]に書き込み、$C1/CC72~は$0018 * $01を計算している。乗算結果は[$00:2134][$00:2136]に入る。
乗算結果の下1バイト、つまり$18は、[$00:2134][$00:0320][$7E:AD00][$00:4204]とコピーされる。
その先は再びフィールド吸収不可とは処理が分岐する。
フィールド吸収不可の時にはあった、途中の行動異常の処理は入らないようである。
以下、重要な部分だけ抜粋していく。

$C1/03F1 LDY $0002,x[$00:1B02]   ;Yに[$00:1B02]をロード
$C1/03F4 LDA $002F,y[$00:1A2F]   ;Aに[$00:1A2F](敵データ15 タイプ)をロード
$C1/03F7 BIT #$04                ;Aと$04(OBJECT)で論理積(ステータスフラグ変更のみ)
$C1/03F9 RTS                     ;サブルーチン戻り

上は、フィールド吸収不可時には通らなかった処理である。
$C1/03F4でロードしている[$00:1A2F]は、敵種類1の敵データ15にあたり、BREAKDOWNやLEADERなど敵のタイプが入っている。
次の$C1/03F7$04と論理積を取っているが、タイプの値が$04なのは「OBJECT」である。
「OBJECT」は西部編の岩や幕末編のしょく台のような、自発的に行動しない障害物の敵の分類になる。

$C1/CD09 BEQ $01    [$CD0C]      ;ゼロフラグが立っているとき[$CD0C]分岐
;ゼロフラグON
$C1/CD0B RTS                     ;サブルーチン戻り

続きの処理だが、タイプの値が$04の場合はゼロフラグ分岐で$C1/CD0Bに進み、サブルーチンを抜けていることがわかる。
この後のゼロフラグが立たない場合の処理は、HP吸収時の計算になるため、「タイプがOBJECTだと、フィールドダメージ吸収設定があっても、フィールドダメージを吸収しない」ということがわかる。
ただ、本作の敵の設定を見る限り、タイプがOBJECTの敵の中に、何らかのダメージフィールドを吸収する敵はいない。
よって実際のゲームでは、OBJECTによる分岐は起きないはずである。
開発中の何らかの名残なのかもしれない。

;ゼロフラグOFF
$C1/CD0C REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/CD0E LDA $0010,y[$00:1A10]   ;Aに[$00:1A11][$00:1A10](敵種類1最大HP)をロード
$C1/CD11 STA $12    [$00:0312]   ;A(最大HP)を[$00:0313][$00:0312]に書き込み
$C1/CD13 TAY                     ;Aレジスタの値をYレジスタに転送
$C1/CD14 LDA $7E8F00,x[$7E:AA00] ;Aに[$7E:AA01][$7E:AA00](敵番号0現在HP)をロード
$C1/CD18 ADC $7E9200,x[$7E:AD00] ;A + [$7E:AD01][$7E:AD00](火炎フィールド$0018)
$C1/CD1C CMP $12    [$00:0312]   ;Aと[$00:0313][$00:0312](最大HP)を減算比較(ステータスレジスタ変更のみ)
$C1/CD1E BCC $02    [$CD22]      ;キャリーフラグが立っていないとき[$CD22]分岐
;キャリーフラグON
$C1/CD20 LDA $12    [$00:0312]   ;Aに[$00:0313][$00:0312](最大HP)をロード
;キャリーフラグOFF
$C1/CD22 JSR $CE70  [$C1:CE70]   ;[$C1:CE70]へジャンプ
;
$C1/CE70 STA $20    [$00:0320]   ;Aを[$00:0321][$00:0320]に書き込み
;(中略)
$C1/CE87 LDA $20    [$00:0320]   ;Aに[$00:0321][$00:0320]をロード
$C1/CE89 RTS                     ;サブルーチン戻り

$C1/CD25 STA $7E8F00,x[$7E:AA00] ;Aを[$7E:AA01][$7E:AA00](敵番号0現在HP)に書き込み
$C1/CD29 TAY                     ;Aレジスタの値をYレジスタに転送
$C1/CD2A SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/CD2C LDA #$FF                ;Aに$FFをロード
$C1/CD2E RTS                     ;サブルーチン戻り

タイプがOBJECTではない場合、つまり実際のゲームではおそらく上の処理を必ず通ることになるはずである。
まず、敵の最大HP2バイト分[$00:1A11][$00:1A10][$00:0313][$00:0312]に書き込む。
$C1/CD18で敵番号0の現在のHP[$7E:AA01][$7E:AA00]と、火炎フィールドのダメージ量[$7E:AD01][$7E:AD00]($0018)を加算することでHPが回復することになるが、単純に足し算するだけでは、最大HPを越えてしまう可能性がある。
$C1/CD1C[$00:0313][$00:0312](最大HP)と減算比較し、キャリーフラグが立つ、つまり最大HP以上に回復して減算が負の時は、$C1/CD20A[$00:0313][$00:0312](最大HP)を読み込み、「最大HPの値をHP吸収後のHPの値」としている。
こうして計算された回復後のHPは$C1/CE70[$00:0321][$00:0320]に書き込まれ、$C1/CD25で更に[$7E:AA01][$7E:AA00](敵番号0現在HP)に書き込まれて処理終了である。
サブルーチンのアドレスこそ異なるが、味方キャラのHP吸収処理と手順としては変わらない。

以上が、火炎フィールド1マス分の処理である。
これを9マス分繰り返すので、キングマンモーのHPは$18×9マス、最大216回復ということになる。

例外について

ダメージフィールドの仕様は以上の通りなのだが、いくつか例外があるため、紹介しておく。
最終編ラストバトル(オディオ戦)ではダメージフィールドが発生しない例外については、前ページで説明した通りである。
オディオ戦と判定するフラグがあって、ダメージフィールド発生処理のみスキップされるのである。

最終編オルステッドルート

最終編オルステッド主人公時は、各シナリオのラストボスを操作して各シナリオの味方キャラを倒していくが、この時は操作キャラがダメージフィールド吸収能力を持っていない場合、フィールドマスの数だけダメージをくらう。
おーでぃーおー戦において、ゴリが「ウキッ」で毒フィールドを発生させた場合、おーでぃーおーが毒フィールドに乗っていると、ダメージ量=毒フィールドダメージ×踏んだマスの数と計算されるのが有名だろう。
上の通り、通常、敵キャラならば、踏んでいる1マスだけがダメージ計算に使われ、他のマスは計算がスキップされるはずだが、最終編オルステッドルートではどうなっているのだろうか?

最終編オルステッド主人公時だと、おーでぃーおーは「味方キャラ」扱いである。
よって、ダメージフィールドでダメージをくらう場合、味方キャラとしてダメージ量を計算・処理する。
そして、味方キャラは1×1マスサイズなので、ダメージを食らう場合も吸収する場合も1マス分のみ計算することになる。敵のダメージ時のように「複数マスにまたがっていたら、最初の1マスだけダメージ計算をする」という処理は、あってもなくても問題はない。
なのだが、最終編オルステッド主人公時だけは、味方キャラが複数マスにまたがる上、戦闘でダメージフィールドも発生する。
(ブリキ大王操作時はダメージフィールドを発生させる戦闘がないから、考慮しなくても良かったのだが)

ということで、味方キャラのダメージフィールド処理は、キャラが複数マスにまたがっている場合、敵キャラの吸収処理と同じく、1マス毎にすべて行う。
敵キャラがダメージを食らう場合の処理だけがむしろ例外だった、と見ることもできるのだ。
よって、おーでぃーおー戦では毒フィールドのダメージをおーでぃーおーが踏んでいるマス分くらうし、ガマヘビ変化戦の毒フィールドは、ガマヘビ変化が踏んでいるマス分吸収となる。



このページをシェアする

上へ