DQ3 エンカウント時の出現モンスター決定方法2

今回からプログラム部分の実装を見ていきます。エンカウントの解析する上で一番やっかいなのは「RAMアクセスが多すぎて、そのアドレスの意味が何を表しているのかよくわからない」ことです。普通はその場で固定長のデータアクセス用のSRをコールするので、その場その場でこのアドレスにはどの値がセットされるというのはすぐわかるのですが、必要なパラメーターが多すぎるためにあらかじめ用意したRAM領域にごそっとコピーしてから処理を開始する、という実装になっているようです。この処理はSR: $068195で行っているので、それぞれの値がどのアドレスにセットされるのかをまずまとめておきます(その1のエントリ参照のこと)。なお、このSRでは延々とエンカウント、エンカウント種別から値を取得してRAMにセット、という処理しかしていないのでここでは省略します。

  • $067DF5 エンカウント準備
c=on
067E06 JSR $8195 SR: $068195 エンカウント固定情報設定
067E09 LDA #$3FFF A=#$3FFF 混成モンスター1から固定パーティ2まで14種類が選択対象
067E0C JSR $8059 SR: $068059 出現モンスター決定
067E0F JSR $7E89 SR: $($067E89+X)
067E12 JSR $7E19 SR: $067E19 最大出現数を見て出現モンスター数調整(画面幅考慮なし)
c=on
  • SR: $068059 出現モンスター決定
068059 STA $1C $00001C=A 選択する種別をセット
06805B STZ $1E $00001E=#$00
06805D STZ $20 $000020=#$00
06805F LDX #$0000 X=#$0000
068062 LSR $1C $00001C>>1
068064 BCC #$0B if(c==off) goto $068071
068066 LDA $F798,X A=$7EF798+X
068069 CLC c=off
06806A BEQ #$05 if(z==on) goto $068071
06806C ADC $20 A+=$000020
06806E STA $20 $000020=A
068070 SEC c=on
068071 ROR $1E ($00001E+C)>>1
068073 INX X++
068074 INX X++
068075 CPX #$0020 X>=#$0020 ?
068078 BCC #$E8 if(c==off) goto $068062
06807A LDA $20 A=$000020
06807C DEC A–
06807D BMI #$24 if(n==on) goto $0680A3
06807F JSL $00133E SR: $00133E 乱数発生 00-A A(1B)
068083 PHA Push A
068084 STZ $20 $000020=#$00
068086 LDX #$0000 X=#$0000
068089 LSR $1E $00001E>>1
06808B BCC #$0E if(c==off) goto $06809B
06808D LDA $F798,X A=$7EF798+X
068090 CLC c=off
068091 ADC $20 A+=$000020
068093 STA $20 $000020=A
068095 LDA $01,S A=Stack($01)
068097 CMP $20 A>=$000020?
068099 BCC #$0F if(c==off) goto $0680AA
06809B INX X++
06809C INX X++
06809D CPX #$0020 X>=#$0020 ?
0680A0 BCC #$E7 if(c==off) goto $068089
0680A2 PLA Pull A
0680A3 LDX #$001C X=#$001C
0680A6 LDA #$0000 A=#$0000
0680A9 RTS return
0680AA PLA Pull A
0680AB LDA $F7B8,X A=$7EF7B8+X 出現モンスターIDを取得
0680AE RTS return

よくわからない処理が延々と続きます。ここで覚えておいてもらいたいのは、混成モンスター1から固定パーティ2まで順番に0から13のインデックスが振られていること、さらにエンカウント種別の0~13バイトの1バイトずつの値がこれに対応した閾値?であるということです。このSRの処理は、各閾値を順番に合計していき、0から合計値までの乱数をとり、乱数の値を含んでいる閾値を特定する、というものです。早い話が幅が一定でないルーレットみたいなものです。同じ仕組みはすごろくのパネルに止まったときのイベント発生判定時にも使用されていました。

当然ながらモンスターIDが設定されていないインデックスはそもそも選択肢から除外されます。さらに、このSRを呼ぶ直前でAレジスタにセットされた値も影響します。$067E0A-で#$3FFFがセットされていますが、これを2進数にすると0011111111111111となり、右から順番に各ビットが混成モンスター1から固定パーティ2に相当します。これがOFFになっていると、これまたモンスター選択から除外されます。例えば、#0FFFの場合は、固定パーティ1,2は選択されないことを意味します。ここでは#$03FFFとなっているので14種すべてが選択される可能性があることを意味します。ちなみに、昼の場合は夜のみ出現モンスター(ID:10)、固定パーティ2(夜のみ出現,ID:13)はSR: $068195の時点で0がセットされているのでそもそも選択肢には入りません。

  • アリアハン周辺の例

エンカウント(ID:#$09)

混1 混2 混3 混4 混5 単1 単2 単3 単4 ソロ 固1 固2
おおがらす スライム いっかくうさぎ おおありくい (空) (空) いっかくうさぎ スライム おおがらす おおありくい (空) (空) 00 00

エンカウント種別(ID:#$01)

混1 混2 混3 混4 混5 単1 単2 単3 単4 ソロ 固1 固2
20 20 15 15 30 20 20 10 15 10 40 11 15 15

(赤字は対象外)

合計値 125(=20+20+15+15+20+10+15+10)で、0から125までの乱数を取る。乱数の結果が73だったら、単1(71~90)が選択される。

※SR: $068059をコールする前にAレジスタに#000Fをセットしていた場合(混1-4のみ有効になる)

エンカウント種別(ID:#$01)

混1 混2 混3 混4 混5 単1 単2 単3 単4 ソロ 固1 固2
20 20 15 15 30 20 20 10 15 10 40 11 15 15

(赤字・青字は対象外)

合計値 70(=20+20+15+15)で、0から70までの乱数を取る。乱数の結果が30だったら、混2(21~40)が選択される。

ちなみに、この閾値の合計は255以下であることが必須になります。超えると乱数の取られる範囲が下位1バイトのみになって出現パターンが偏ることになる(多分)なので閾値をいじる場合は注意が必要です。

このSRを出るとAレジスタには決定されたモンスターID、Xレジスタには0-13のオフセット(0-26)がセットされています。このXの値によって$067E0Fで呼ばれるSRが変わります。

  • $67E89- エンカウント時モンスター決定処理SRアドレス(2Byte*14rec)
インデックス 開始ドレス
00 7F01 混成モンスター1用
01 7F01 混成モンスター2用
02 7F01 混成モンスター3用
03 7F01 混成モンスター4用
04 7F01 混成モンスター5用
05 7F58 1体のみ+混成5種をお供にするモンスター用
06 7F71 単一種出現モンスター1用
07 7F71 単一種出現モンスター2用
08 7F71 単一種出現モンスター3用
09 7F71 単一種出現モンスター4用
0A 7F71 夜のみモンスター用
0B 7FAE 1体のみ出現用
0C 7FB5 固定パーティ1用
0D 7FB5 固定パーティ2用
0E 8058

長くなったのでこの辺で。次回はそれぞれのSRでどのように1体目以降の出現モンスターが決定されるか見ていきます。