DQ3 あなほりバグとあなほりの解析

DQ3 K.Mixのバグ指摘で「ゾーマの城B2F(方向変更パネルがたくさんあるフロア)であなほりをすると出現するモンスターがドロップしない毒消し草が拾える」という指摘を受け、内心「えー」と思ったものの、よく調べたらオリジナルから存在するバグでした。今回はこれをネタにしつつ、個人的にはろくに使ったことのないあなほりという特技の中身を見ていきます。

あなほりの特徴

あなほりは商人がLv12で覚える移動中専用特技です。効果は「そのフロアでエンカウントするモンスターのドロップするアイテムを5回まで拾える」(フロアに入り直したらリセット)くらいの意識しかしていませんでした。もちろんドロップ確率はモンスターのドロップ率に準拠するので特段効率がいいというわけでもないです。根気さえあれば戦闘無しでドロップアイテムが手に入るので使う人は使うのかもしれません。ちなみにDQ3 K.Mixではメタルキングの悟りの書、パンドラボックスのちいさなメダルは例外として掘り当てられないように手当してあります。

あなほりバグとは?

下の動画を見てもらうとわかりますが、ゾーマの城B2Fであなほりをしていると、そのうち毒消し草が入手できるのがわかると思います(使用ROMはもちろんオリジナル)。以前動画作成の手間のかかりように懲りたので解説動画にはせず例のごとく撮って出しです。

このフロアでエンカウントするモンスターは以下の通り。

モンスター名 ドロップアイテム 確率
バルログ ふしぎなぼうし 1/128
ドラゴンゾンビ ちからのたね 1/128
アークマージ さざなみのつえ 1/128
マントゴーア なげきのたて 1/128
固定パーティ7(大魔神1+マドハンド5)→バブルスライムと誤認識 どくけしそう 1/8

固定パーティID:7がモンスターID:7(バブルスライム)と間違って扱われているため、そのドロップアイテムの毒消し草が拾えてしまう、ということのようです。さらに毒消し草のドロップ率が1/8とかなり高いために余計に顕在化しやすかったようです。上の動画は問題が再現する動画が採取できるまで相当のやり直しを覚悟しましたが、数回のトライで再現できてしまいました。

あなほりの実装1(大枠)

次に実装を見ていきます。

  • SR:$091582 あなほり(移動中)
$091582 LDA #$000A A=#$000A
$091585 JSR $15E0 SR: $0915E0 移動中特技発動時の効果音・メッセージ表示
$091588 JSR $11BD SR: $0911BD 呪文無効判定・処理
$09158B BCS #$0C if(c==on) goto $091599
$09158D JSL $C1D50F SR: $01D50F あなほりが有効な環境か調べる(可能c=off)
$091591 BCS #$0C if(c==on) goto $09159F
$091593 JSL $C1A8D4 SR: $01A8D4 引数:1#$004B メッセージ表示「しかし ここでは ほることができない!」
$091599 JSL $C340F7 SR: $0340F7 ウィンドウでのキー入力結果取得(キャンセルc=on)
$09159D CLC c=off
$09159E RTL return
$09159F JSR $1209 SR: $091209 現在MP減算
$0915A2 JSL $C1D51E SR: $01D51E あなほり結果取得
$0915A6 BCS #$09 if(c==on) goto $0915B1
$0915A8 JSL $C1A8D4 SR: $01A8D4 引数:1#$004A メッセージ表示「しかし 何も 見つからなかった。」
$0915AE JMP $1599 ($091599) goto $091599
$0915B1 CPX #$0000 X==#$0000?
$0915B4 BEQ #$12 if(z==on) goto $0915C8
$0915B6 JSL $C44824 SR: $044824 引数:1#$FF 引数:2#$40 アイテムを入手する
$0915BC STA $BE79 $BE79=A
$0915BF JSL $C1A8D4 SR: $01A8D4 引数:1#$0049 メッセージ表示「なんと! 〇〇を みつけた!」
$0915C5 JMP $1599 ($091599) goto $091599
$0915C8 JSL $C45B1A SR: $045B1A 引数:1#$F9 所持金増額
$0915CD SEP #$10 x=on(X/Y:8b)
$0915CF STY $BE83 $BE83=Y
$0915D2 REP #$10 x=off(X/Y:16b)
$0915D4 STA $BE81 $BE81=A
$0915D7 JSL $C1A8D4 SR: $01A8D4 引数:1#$0048 メッセージ表示「なんと! 〇〇ゴールドを みつけた!」
$0915DD JMP $1599 ($091599) goto $091599

ざっと見て「まあそうですね」という感じの実装で特に感想はありませんww。次があなほりのコアの処理です。

  • SR:$01D51E あなほり結果取得(c=offスカ、X=#$0000 所持金プラス、X=#$0002 アイテム入手)
$01D51E LDA $99B5 A=$99B5
$01D521 CMP #$0005 A>=#$0005? あなほりの回数チェック(1フロア5回まで)
$01D524 BCS #$54 if(c==on) goto $01D57A
$01D526 JSL $C012E3 SR: $0012E3 乱数テーブル更新?
$01D52A BMI #$4E if(n==on) goto $01D57A 1/2の確率でスカ?
$01D52C JSL $C012E3 SR: $0012E3 乱数テーブル更新?
$01D530 BMI #$36 if(n==on) goto $01D568 1/2の確率でアイテムドロップ?
$01D532 JSL $C012E3 SR: $0012E3 乱数テーブル更新?
$01D536 CMP #$0002 A>=#$0002? 3/256の確率で所持金の半分ゲット?
$01D539 BCC #$0E if(c==off) goto $01D549
$01D53B JSL $C012E3 SR: $0012E3 乱数テーブル更新?
$01D53F AND #$0001 A&=#$0001
$01D542 INC A++ 1or2Gゲット?
$01D543 LDX #$0000 X=#$0000
$01D546 TXY Y=X
$01D547 BRA #$2C goto $01D575
$01D549 JSL $C45AB0 SR: $045AB0 引数:1#$00 所持金取得
$01D54E LDA $02 A=DP($02)
$01D550 AND #$00FF A&=#$00FF 所持金情報を3バイトでクリップ
$01D553 STA $02 DP($02)=A
$01D555 LSR $02 DP($02)>>1 上位1バイトを半分に
$01D557 ROR $00 DP($00)>>1 下位2バイトを半分に
$01D559 LDA $00 A=DP($00)
$01D55B ORA $02 Aor=DP($02)
$01D55D BEQ #$DC if(z==on) goto $01D53B 所持金を1/2した結果が0Gならスカ扱い
$01D55F LDA $00 A=DP($00)
$01D561 LDY $02 Y=DP($02)
$01D563 LDX #$0000 X=#$0000
$01D566 BRA #$0D goto $01D575
$01D568 JSL $C690A1 SR: $0690A1 足元のパネル情報に応じたエンカウント閾値取得?
$01D56C JSL $C67EC1 SR: $067EC1 あなほりアイテム決定
$01D570 BCC #$08 if(c==off) goto $01D57A
$01D572 LDX #$0002 X=#$0002
$01D575 JSR $D57F SR: $01D57F あなほり回数カウントアップ
$01D578 SEC c=on
$01D579 RTL return
$01D57A JSR $D57F SR: $01D57F あなほり回数カウントアップ
$01D57D CLC c=off
$01D57E RTL return

1/2 * 1/2 * 2/256 = 2/1024の確率で所持金の半分を掘り当てることができるような実装になっているようです。ときどき大金を掘り当てられたような気がしたのですが、どうやらこのせいだったようです。

あなほりの実装2(アイテム入手部分)

  • SR:$067EC1 あなほりアイテム決定
$067EC1 STA $18 DP($18)=A
$067EC3 JSR $8195 SR: $068195 エンカウントモンスター固定情報設定
$067EC6 LDX #$0000 X=#$0000
$067EC9 PHX Push X
$067ECA LDA $F798,X A=$F798+X
$067ECD BEQ #$25 if(z==on) goto $067EF4
$067ECF LDA $F7B8,X A=$F7B8+X
$067ED2 TAY Y=A
$067ED3 JSL $C2CC92 SR: $02CC92 引数:1#$001D 引数:2#$0038 モンスター情報取得 インデックス:Y(ドロップ率取得)
$067EDB ASL A< <1
$067EDC TAX X=A
$067EDD LDA $C2AA34,X A=$02AA34+X
$067EE1 JSL $C0135F SR: $00135F 乱数発生 00-A A(2B)
$067EE5 BNE #$0D if(z==off) goto $067EF4
$067EE7 JSL $C2CC92 SR: $02CC92 引数:1#$000C 引数:2#$00FF モンスター情報取得 インデックス:Y(アイテムID取得)
$067EEF CMP #$0000 A==#$0000?
$067EF2 BNE #$0A if(z==off) goto $067EFE
$067EF4 PLX Pull X
$067EF5 INX X++
$067EF6 INX X++
$067EF7 CPX #$001C X>=#$001C?
$067EFA BCC #$CD if(c==off) goto $067EC9
$067EFC CLC c=off
$067EFD RTL return
$067EFE PLX Pull X
$067EFF SEC c=on
$067F00 RTL return

このSRでエンカウントするモンスターのリストをエンカウントIDから取得してメモリ上に展開し、先頭から順にドロップ判定を行っていく、ということをしているようです。ドロップ判定でONになった時点で終了なので、先に設定してあるモンスターのほうが優先順位が高い、ということになりそうです。

  • SR:$068195 エンカウントモンスター固定情報設定(省略)

くっそ長い割に同じことの繰り返しなので実装は省略しますが、あなほり関連では以下のメモリに情報をセットしています。

  • $7EF798-F7B3:固定長データ:エンカウントタイプ($08B6D8-)の混成モンスター1~固定パーティ2(夜のみ)の閾値(2*14バイト)
  • $7EF7B8-F7D3:固定長データ:エンカウント($08ADD1-)の混成モンスター1~固定パーティ2(夜のみ)のID(2*14バイト)

問題なのが$7EF7B8-F7D3のうちの$7EF7D0-D3部分で、ここにセットされているのは固定長データ:固定パーティ($08B736-)のIDであり、モンスターIDではありません。ところが、SR:$067EC1 あなほりアイテム決定ではこのIDもモンスターIDとして見てしまっているのが問題である、ということになります。ちなみに固定パーティIDは$00から$13まで設定されているので、スライムからギズモまでのアイテムがドロップされる可能性があります。

修正方針

これに対してどうするべきかという方針ですが、

  1. 軽微なので無視する
  2. 固定パーティのIDをモンスターIDに展開する
  3. 固定パーティのIDは潰す(あなほりの対象にしない)

のどれかになります。1はともかくとして、真面目にやれば2が良さそうな感じがしますが、実は$7EF798-f7D3は通常のエンカウント時の出現モンスター決定にも使用されているので、下手にいじると通常エンカウントにまで影響してしまいます。なので、無難に対処するのであれば3がよいということになります。

コメント

  1. 匿名 より:

    まだまだ発見されていないバグがあるものですね。
    これからも解析情報の公開を楽しみにしております。