DQ3 まんたん処理の仕組み4

今回からHP回復処理を見ていきます。

  • SR: $0921AC まんたんHP回復処理
0921AC JSR $22ED SR: $0922ED ベホマズン回復倍率取得
0921AF JSR $2286 SR: $092286 ベホマラー回復倍率取得
0921B2 JSR $2342 SR: $092342 単体回復呪文回復倍率取得
0921B5 LDA $2BBE A=$2BBE ここからベホマズン実行判断
0921B8 BMI #$0D if(n==on) goto $0921C7
0921BA CMP $2BBC A>=$2BBC?
0921BD BCS #$08 if(c==on) goto $0921C7
0921BF CMP $2BC0 A>=$2BC0?
0921C2 BCS #$03 if(c==on) goto $0921C7
0921C4 JMP $223C ($09223C) goto $09223C ベホマズン実行決定
0921C7 LDA $2BBC A=$2BBC ここからベホマラー実行判断
0921CA BMI #$0A if(n==on) goto $0921D6
0921CC CMP $2BC0 A==$2BC0?
0921CF BEQ #$05 if(z==on) goto $0921D6
0921D1 BCC #$03 if(c==off) goto $0921D6
0921D3 JMP $2252 ($092252) goto $092252 ベホマラー実行決定
0921D6 LDA $2BC0 A=$2BC0 単体回復実行判断
0921D9 BPL #$02 if(n==off) goto $0921DD
0921DB CLC c=off 回復不要
0921DC RTS return
0921DD LDY #$0000 Y=#$0000 ここからは単体回復処理
0921E0 LDA $2BE8,Y A=$2BE8+Y
0921E3 TAX X=A
0921E4 AND #$4000 A&=#$4000 死亡中なら飛ばす
0921E7 BNE #$4B if(z==off) goto $092234
0921E9 TXA A=X
0921EA AND #$0800 A&=#$0800 回復不要なら飛ばす
0921ED BEQ #$45 if(z==on) goto $092234
0921EF TXA A=X
0921F0 AND #$0080 A&=#$0080 ホイミで回復
0921F3 BNE #$1D if(z==off) goto $092212
0921F5 TXA A=X
0921F6 AND #$0100 A&=#$0100 ベホイミで回復
0921F9 BNE #$28 if(z==off) goto $092223
0921FB TXA A=X
0921FC AND #$0200 A&=#$0200 ベホマで回復
0921FF BEQ #$33 if(z==on) goto $092234
092201 LDA #$0010 A=#$0010
092204 LDX #$0025 X=#$0025
092207 JSR $2456 SR: $092456 指定の回復呪文を使えるキャラクターを決定(いないc=off)
09220A BCC #$28 if(c==off) goto $092234
09220C LDA #$0025 A=#$0025 ベホマの戦闘行動ID
09220F JMP $2231 ($092231) goto $092231
092212 LDA #$0004 A=#$0004
092215 LDX #$001F X=#$001F
092218 JSR $2456 SR: $092456 指定の回復呪文を使えるキャラクターを決定(いないc=off)
09221B BCC #$17 if(c==off) goto $092234
09221D LDA #$001F A=#$001F ホイミの戦闘行動ID
092220 JMP $2231 ($092231) goto $092231
092223 LDA #$0008 A=#$0008
092226 LDX #$0022 X=#$0022
092229 JSR $2456 SR: $092456 指定の回復呪文を使えるキャラクターを決定(いないc=off)
09222C BCC #$06 if(c==off) goto $092234
09222E LDA #$0022 A=#$0022 ベホイミの戦闘行動ID
092231 JSR $2481 SR: $092481 単体回復処理
092234 INY Y++
092235 INY Y++
092236 CPY $2BB4 Y>=$2BB4?
092239 BCC #$A5 if(c==off) goto $0921E0
09223B RTS return
09223C LDA $2BC4 A=$2BC4 ここから下はベホマラー・ベホマズンの回復処理
09223F STA $BE7D $BE7D=A
092242 LDX $2BC8 X=$2BC8
092245 JSL $C433BA SR: $0433BA 引数:1#$01 引数:2#$FF 引数:3#$FE 現在MP減算
09224C LDY #$002B Y=#$002B ベホマズンの戦闘行動ID
09224F JMP $2265 ($092265) goto $092265
092252 LDA $2BC2 A=$2BC2
092255 STA $BE7D $BE7D=A
092258 LDX $2BC6 X=$2BC6
09225B JSL $C433BA SR: $0433BA 引数:1#$01 引数:2#$FF 引数:3#$FE 現在MP減算
092262 LDY #$0028 Y=#$0028 ベホマラーの戦闘行動ID
092265 STY $33DA $33DA=Y
092268 JSL $C2CC0A SR: $02CC0A 引数:1#$1860 戦闘行動構造体アクセスSR インデックス:Y
09226E STA $BE77 $BE77=A
092271 JSL $C1E32E SR: $01E32E 引数:1#$0065 再生BGM設定?
092277 JSL $C1A8D4 SR: $01A8D4 引数:1#$0002 メッセージ表示
09227D JSL $C912A4 SR: $0912A4 生存者全員回復
092281 INC $2BBA $2BBA++
092284 SEC c=on
092285 RTS return

この処理がまんたん処理のHP回復行動の1セットです。$0921AC-DCでそれぞれの回復呪文による回復倍率(後述)を求め、それに従ってどの呪文を実行するかを判断していますが、ここに論理バグがあります(後述)。$0921DD-23Bが単体回復処理、$0923C-85がベホマラー・ベホマズンの回復処理です。単体回復の場合は先頭から順番に回復して終わりまで、ベホマラー・ベホマズンの場合は1回だけ実行したらこの処理を抜けます。その後HP差分を求める処理を再度実行し、更に回復行動が必要かを判断します。これはまんべんなく回復呪文を実行することによってまんたん中にMP切れが起きた場合にも誰か一人だけ完全回復して残りは瀕死のまま、という状況に陥らないようにするためだと思われます。

  • SR: $092286 ベホマラー回復倍率取得
092286 STZ $2BBC $2BBC=#$00 0で初期化
092289 LDX $2BB4 X=$2BB4
09228C DEX X–
09228D DEX X–
09228E BMI #$54 if(n==on) goto $0922E4
092290 LDA $2BE8,X A=$2BE8+X
092293 BMI #$F7 if(n==on) goto $09228C 行動不能なら飛ばす
092295 AND #$0020 A&=#$0020
092298 BEQ #$F2 if(z==on) goto $09228C ベホマラーを覚えていないなら飛ばす
09229A PHX Push X
09229B TXA A=X
09229C LSR A>>1
09229D STA $2BC2 $2BC2=A ベホマラー実行者IDをセット
0922A0 TAX X=A
0922A1 LDY #$0028 Y=#$0028 ベホマラーの戦闘行動ID
0922A4 JSL $C2B44A SR: $02B44A 消費MP計算(MPオーバーc=off)
0922A8 STA $2BC6 $2BC6=A 消費MPをセット
0922AB PLX Pull X
0922AC BCC #$DE if(c==off) goto $09228C MPオーバーなら調査を続行
0922AE LDY $2BB4 Y=$2BB4
0922B1 DEY Y–
0922B2 DEY Y–
0922B3 BMI #$2F if(n==on) goto $0922E4
0922B5 LDA $2BE8,Y A=$2BE8+Y
0922B8 AND #$4000 A&=#$4000 死亡中なら飛ばす
0922BB BNE #$F4 if(z==off) goto $0922B1
0922BD LDA $2BE8,Y A=$2BE8+Y
0922C0 AND #$0800 A&=#$0800 回復不要なら飛ばす
0922C3 BEQ #$EC if(z==on) goto $0922B1
0922C5 PHX Push X
0922C6 LDA $2BCE,Y A=$2BCE+Y HP差分をセット
0922C9 STZ $70 DP($70)=#$00
0922CB STA $71 DP($71)=A
0922CD LDA $2BCA A=$2BCA
0922D0 LDX #$0070 X=#$0070
0922D3 JSL $C01295 SR: $001295 $00,x(3B)=$00,X(3B)/A(2B?) 剰余=A,$30
0922D7 LDA $70 A=DP($70)
0922D9 CLC c=off
0922DA ADC $2BBC A+=($2BBC+c) 回復倍率を加算
0922DD STA $2BBC $2BBC=A
0922E0 PLX Pull X
0922E1 JMP $22B1 ($0922B1) goto $0922B1
0922E4 LDA $2BBC A=$2BBC 回復倍率が0のままなら1引いて#$FFFFにしてリターン
0922E7 BNE #$03 if(z==off) goto $0922EC
0922E9 DEC $2BBC $2BBC–
0922EC RTS return

前半部分($092286-AD)では、誰がベホマラーを実行するかを決めています。これまた例によってパーティの後ろからチェックしていきます。そして後半部分($0922AE-E3)では生存者のHP差分に対する回復倍率を求め、それを合計しています。「回復倍率」というのは、「HP差分を特定の回復行動の最小値で割ったもの」です。要は、「全快するのにその呪文を何回唱える必要があるか」ということです。当然この値が小さければ小さいほど効率がいいということになるので、ベホマラーが使用できない場合は#$FFFFを回復倍率とすることで使用されるのを回避しています。また、回復倍率を求める際にHP差分を1バイト左にシフトすることで、得られた商の上1バイトが整数部分、下1バイトが小数部分(分母は256)ということになり、より高い精度で倍率が得られることになります。これは他の呪文との効果比較のために使用されます。これを踏まえてベホマズンの回復倍率取得を見ると何をやっているか見えてきます。

  • SR: $0922ED ベホマズン回復倍率取得
092315 LDY $2BB4 Y=$2BB4
092318 DEY Y–
092319 DEY Y–
09231A BMI #$1D if(n==on) goto $092339
09231C LDA $2BE8,Y A=$2BE8+Y
09231F AND #$4000 A&=#$4000 死亡中なら飛ばす
092322 BNE #$F4 if(z==off) goto $092318
092324 LDA $2BE8,Y A=$2BE8+Y
092327 AND #$0800 A&=#$0800
09232A BEQ #$EC if(z==on) goto $092318
09232C LDA #$0100 A=#$0100 完全回復なので回復倍率は#$0100
09232F CLC c=off
092330 ADC $2BBE A+=($2BBE+c)
092333 STA $2BBE $2BBE=A
092336 JMP $2318 ($092318) goto $092318

ベホマ・ベホマズンは完全回復なのでHP差分がいくつであろうが回復倍率は#$0100ということになります。

  • SR: $092342 単体回復呪文回復倍率取得
092342 STZ $2BC0 $2BC0=#$00
092345 LDY #$0000 Y=#$0000
092348 LDA #$0080 A=#$0080
09234B ORA #$0100 Aor=#$0100
09234E ORA #$0200 Aor=#$0200
092351 EOR #$FFFF A^=#$FFFF
092354 AND $2BE8,Y A&=$2BE8+Y
092357 STA $2BE8,Y $2BE8+Y=A
09235A TAX X=A
09235B AND #$4000 A&=#$4000 死亡中なら飛ばす
09235E BNE #$60 if(z==off) goto $0923C0
092360 TXA A=X
092361 AND #$0800 A&=#$0800 回復不要なら飛ばす
092364 BEQ #$5A if(z==on) goto $0923C0
092366 LDA #$FFFF A=#$FFFF
092369 STA $2BB8 $2BB8=A テンポラリの回復倍率用バッファ
09236C STZ $2BD6 $2BD6=#$00
09236F LDX $2BB4 X=$2BB4 詠唱可能者を後ろから探す
092372 DEX X–
092373 DEX X–
092374 BMI #$35 if(n==on) goto $0923AB
092376 LDA $2BE8,X A=$2BE8+X
092379 BMI #$F7 if(n==on) goto $092372 行動不能なら飛ばす
09237B STA $2BB6 $2BB6=A
09237E LDA #$0004 A=#$0004 ホイミを習得しているか
092381 AND $2BB6 A&=$2BB6
092384 BEQ #$06 if(z==on) goto $09238C
092386 LDA #$001F A=#$001F ホイミの戦闘行動ID
092389 JSR $23D3 SR: $0923D3 指定呪文の回復倍率取得
09238C LDA #$0008 A=#$0008 ベホイミを習得しているか
09238F AND $2BB6 A&=$2BB6
092392 BEQ #$06 if(z==on) goto $09239A
092394 LDA #$0022 A=#$0022 ベホイミの戦闘行動ID
092397 JSR $23D3 SR: $0923D3 指定呪文の回復倍率取得
09239A LDA #$0010 A=#$0010 ベホマを習得しているか
09239D AND $2BB6 A&=$2BB6
0923A0 BEQ #$06 if(z==on) goto $0923A8
0923A2 LDA #$0025 A=#$0025 ベホマの戦闘行動ID
0923A5 JSR $23D3 SR: $0923D3 指定呪文の回復倍率取得
0923A8 JMP $2372 ($092372) goto $092372
0923AB LDA $2BB8 A=$2BB8
0923AE BMI #$10 if(n==on) goto $0923C0 #$FFFFのままなら回復倍率加算処理は飛ばす
0923B0 CLC c=off
0923B1 ADC $2BC0 A+=($2BC0+c) 回復倍率を加算
0923B4 STA $2BC0 $2BC0=A
0923B7 LDA $2BE8,Y A=$2BE8+Y
0923BA ORA $2BD6 Aor=$2BD6 回復フラグをセット
0923BD STA $2BE8,Y $2BE8+Y=A
0923C0 INY Y++
0923C1 INY Y++
0923C2 CPY $2BB4 Y>=$2BB4?
0923C5 BCS #$03 if(c==on) goto $0923CA
0923C7 JMP $2348 ($092348) goto $092348
0923CA LDA $2BC0 A=$2BC0 回復倍率が0のままなら1引いて#$FFFFにしてリターン
0923CD BNE #$03 if(z==off) goto $0923D2
0923CF DEC $2BC0 $2BC0–
0923D2 RTS return

ループが入れ子になっているのでちょっとわかりにくいですが、$092345-C6までが各キャラクターの回復倍率を得るための大きなループで、その中に$09236F-AAでどの呪文を実行するかを決めるループがあります。

  • SR: $0923D3 指定呪文の回復倍率取得
0923D3 PHA Push A
0923D4 PHX Push X
0923D5 PHY Push Y
0923D6 LDA $2BCE,Y A=$2BCE+Y
0923D9 STZ $70 DP($70)=#$00
0923DB STA $71 DP($71)=A
0923DD LDA $05,S A=Stack($05)
0923DF TAX X=A
0923E0 JSR $24ED SR: $0924ED 回復最小量取得
0923E3 CMP #$FFFF A==#$FFFF? ベホマなら#$0100をセットする
0923E6 BNE #$06 if(z==off) goto $0923EE
0923E8 LDA #$0100 A=#$0100
0923EB JMP $23F7 ($0923F7) goto $0923F7
0923EE LDX #$0070 X=#$0070
0923F1 JSL $C01295 SR: $001295 $00,x(3B)=$00,X(3B)/A(2B?) 剰余=A,$30
0923F5 LDA $70 A=DP($70)
0923F7 LDX $2BB8 X=$2BB8
0923FA CPX #$0100 X>=#$0100?
0923FD BCS #$0F if(c==on) goto $09240E
0923FF CMP #$0100 A>=#$0100?
092402 BCS #$0A if(c==on) goto $09240E
092404 CMP $2BB8 A>=$2BB8?
092407 BCC #$49 if(c==off) goto $092452
092409 BEQ #$47 if(z==on) goto $092452
09240B JMP $2413 ($092413) goto $092413
09240E CMP $2BB8 A>=$2BB8?
092411 BCS #$3F if(c==on) goto $092452
092413 PHA Push A
092414 LDA $05,S A=Stack($05)
092416 LSR A>>1
092417 TAX X=A
092418 LDA $07,S A=Stack($07)
09241A TAY Y=A
09241B JSL $C2B44A SR: $02B44A 消費MP計算(MPオーバーc=off)
09241F BCC #$30 if(c==off) goto $092451
092421 PLA Pull A
092422 STA $2BB8 $2BB8=A テンポラリの回復倍率を更新
092425 LDA $05,S A=Stack($05)
092427 CMP #$001F A==#$001F? このSRに入った時のAレジスタの値がホイミか
09242A BNE #$09 if(z==off) goto $092435
09242C LDA #$0080 A=#$0080 ホイミで回復フラグON
09242F STA $2BD6 $2BD6=A
092432 JMP $2452 ($092452) goto $092452
092435 CMP #$0022 A==#$0022? このSRに入った時のAレジスタの値がベホイミか
092438 BNE #$09 if(z==off) goto $092443
09243A LDA #$0100 A=#$0100 ベホイミで回復フラグON
09243D STA $2BD6 $2BD6=A
092440 JMP $2452 ($092452) goto $092452
092443 CMP #$0025 A==#$0025? このSRに入った時のAレジスタの値がベホマか
092446 BNE #$0A if(z==off) goto $092452
092448 LDA #$0200 A=#$0200 ベホマで回復フラグON
09244B STA $2BD6 $2BD6=A
09244E JMP $2452 ($092452) goto $092452
092451 PLA Pull A
092452 PLY Pull Y
092453 PLX Pull X
092454 PLA Pull A
092455 RTS return

$7E2BB8, $7E2BD6をグローバル変数のように使い、回復倍率が一番小さい値とその呪文を探しています。ホイミ・ベホイミ・ベホマについて4人分、最大12回繰り返すと対象の一人に対する回復倍率の最小値とその呪文が何かが取得出来ます。大体の流れがわかったところで、最初に言及した論理バグについてもう一度見てみます。$0921B5の時点で、回復倍率については以下のメモリにセットされています。

$7E2BBC ベホマラー回復倍率

$7E2BBE ベホマズン回復倍率

$7E2BC0 単体回復倍率(全員分)

  • SR: $0921AC まんたんHP回復処理(再掲)
0921B5 LDA $2BBE A=$2BBE ここからベホマズン実行判断
0921B8 BMI #$0D if(n==on) goto $0921C7
0921BA CMP $2BBC A>=$2BBC?
0921BD BCS #$08 if(c==on) goto $0921C7
0921BF CMP $2BC0 A>=$2BC0?
0921C2 BCS #$03 if(c==on) goto $0921C7
0921C4 JMP $223C ($09223C) goto $09223C ベホマズン実行決定
0921C7 LDA $2BBC A=$2BBC ここからベホマラー実行判断
0921CA BMI #$0A if(n==on) goto $0921D6
0921CC CMP $2BC0 A==$2BC0?
0921CF BEQ #$05 if(z==on) goto $0921D6
0921D1 BCC #$03 if(c==off) goto $0921D6 <-BCSが正しい?
0921D3 JMP $2252 ($092252) goto $092252 ベホマラー実行決定
0921D6 LDA $2BC0 A=$2BC0 単体回復実行判断
0921D9 BPL #$02 if(n==off) goto $0921DD

ここで、パーティ全員のレベルが高く、瀕死の状態を想定してみます。ベホマズン回復倍率・単体回復倍率はベホマ4回分でどちらも#$0400、ベホマラー回復倍率は各人5回必要と仮定すると#$1400となります。$0921C2でc=onで見てしまっているために、ベホマズンと単体回復倍率のどちらも#$0400の場合にもc=onとなってしまい、ベホマラーの実行判断に飛ばされてしまいます。さらに$0921D1でAレジスタのベホマラーの回復倍率と単体回復の回復倍率の合計($7E2BC0)を比較してc=offなら単体回復に飛ばすようになっているため、上記の例では#1400と#0400を比較してベホマラーの方が回復効率が悪いにもかかわらずベホマラーが選択される、という結果になります。この実装のためにいかなる状況でもベホマズンは使用されず、ベホマ4回の方が効率が良くてもベホマラーを延々と実行するようになっています。ベホマズンを1回実行するよりもベホマ4回のほうが消費MPが少ないのでベホマズンを使わない点については実害はないと思いますが、ベホマラー連発についてはバグっぽい感じです。ところが、試しに$0921D1のBCCをBCSに変えてみると今度はベホマラーは一切使用されませんwww。習得レベルの関係上、ベホマラー(僧侶・賢者:Lv34)を習得していていてベホマ(僧侶・賢者:Lv30)を習得していないという状態がないため、単純に回復倍率のみで比較した場合に今度はベホマラーはベホマを上回るケースがない上に、ベホイミで代替されてしまうという状態になります。消費MPでの比較がない以上しょうがない気もしますが。最終的にどっちがいいのかという話になりますが、ベホマラーをむやみに連発するオリジナルのほうが効率面からは良くないといえるでしょう。

まんたんの内部の処理を見てきましたが、オリジナルDQ3のまんたん処理における問題は以下の点にあると考えます。

  • ベホマズンが選択肢に入っているのにいかなる状況でも使用されない(これはこれでいい?)
  • ベホマが使える状態でもベホマラーを乱発する(バグ?)
  • 使用者が無条件でパーティ最後列に固定されるため、複数人が回復呪文を使用出来る状態でも最後尾のキャラクターがベホマラー乱発する実装によりベホマラーを乱発し、あっという間にMPが枯渇するケースがある

オリジナルをプレイする場合、パーティ全員が瀕死に近い場合は手動でベホマを4回使えばこの状況は回避されます。しかしこんな大欠陥があるにもかかわらずなんでRTAなどで問題にならないのかと思ったのですが、よくよく考えて見ればベホマラーを習得する前にクリアしてしまうからでした。以上の解析結果を踏まえて次回からはDQ3 K.Mixではどう修正するかを考えていきます。