あなほりの特徴
あなほりは商人が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まで設定されているので、スライムからギズモまでのアイテムがドロップされる可能性があります。
修正方針
これに対してどうするべきかという方針ですが、
- 軽微なので無視する
- 固定パーティのIDをモンスターIDに展開する
- 固定パーティのIDは潰す(あなほりの対象にしない)
のどれかになります。1はともかくとして、真面目にやれば2が良さそうな感じがしますが、実は$7EF798-f7D3は通常のエンカウント時の出現モンスター決定にも使用されているので、下手にいじると通常エンカウントにまで影響してしまいます。なので、無難に対処するのであれば3がよいということになります。



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