TOP > プログラミング関係解説&調査 > 最終編敵パーティ

最終編敵パーティ

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

最終編はランダムエンカウントで、味方の合計レベル及びエリアで遭遇する敵パーティが決まる。
仕様は攻略にも記してある。
最終編 > データ集 > 敵データ(敵パーティ一覧)
またこれらの情報は既にネットでも広く知られており、筆者は攻略情報と呼ぶには半端なやつ | Campus Note様の情報を参考にさせていただいた。

ここでは、最終編でランダムエンカウント出現するザコ敵パーティが、実際のプログラムではどのようにして計算され決定されているかを紹介する。

まず、最終編の敵パーティにまとめると以下のとおり。

  • 最終編で出現するランダムエンカウントの敵パーティは32通りあり、独自にランク付けされている。
    当攻略では便宜上、わかりやすさ優先でランク1~ランク32としているが、プログラム上ではランク00~31($00$1F)になる。
    また、ランク29以上だと、出現する敵はランク29,30,31,32で固定なので、出現する敵パターンの計算で必要になるのは実質ランク29までである。
    当ページでは、敵パーティがどのように計算されて決まるのかを知りたいため、ランク最大値29以上は29と同一と考えて処理することにする。
  • ランクの計算方法は、初期値をランク1とした場合、
    ランク = min(trunc(味方パーティの合計レベル/4) + 1 + エリア固有値, 29)
    初期値をランク00とした場合、
    ランク = min(trunc(味方パーティの合計レベル/4) + エリア固有値, 28)
    上の式で求めたランクに+0+3した4種類の敵パーティから各1/4の確率で敵パーティが決まる仕組みである。
    ランク一覧はこちら
  • エリア固有値は以下の通り。
    • 力のダンジョン:6または4
      ※「最強バンデージ」取得後からランダムエンカウント。1フロア目に入口から入った時だけ6で、それ以外の状況だと4。
    • 魔王山:4
    • 鍵のダンジョン、心のダンジョン、本能のダンジョン:2
    • ルクレチアの森、ルクレチア城内、勇者の山:0
    なお、「鍵のダンジョン」のみ、上計算とは別に、「影」という敵が1/2で出現し、他の4パターンは1/8ずつとなる。

以上が仕様となる。
また、ややこしくなるので、以下では初期値をランク00とした、

ランク = min(trunc(味方パーティの合計レベル/4) + エリア固有値, 28)

この計算式で統一して説明していく。

実際のプログラムの紹介前に、必要となるデータ類について説明する。

敵パーティデータ

敵パーティデータの実際の数値などは、世界の合言葉は森部様を参照のこと。

本作の敵パーティに関するデータは、$D6:4280$D6:512Fに収納されている。
敵パーティは全256種で、IDが$00$FFと割り振られている。中には未使用データも含まれる。
敵パーティ1種類につき最大で37バイト分の領域を使用している。
というのは、8バイト目以降は、初期座標&向きの1バイト+敵IDの1バイトの合計2バイト分で、敵1体分のデータとしており、敵の総数によって各敵パーティの容量が変動しているため、最小(敵1体)だと9バイト、最大(敵15体)だと37バイトのデータになる。

敵パーティデータの具体的な内容は以下の通り。
Noは1バイト目からのナンバリングで、No.9以降は敵の総数によるので省略。
また、敵パーティデータ自体には敵パーティIDは入っていない。

No.内容
00敵パーティレベル
01$00:通常
$01:敵対敵(ブリキ大王戦など)
$10または$20:原始編のざき戦
$40:味方対味方(功夫編修行など)
02味方1 向き/初期座標
03味方2 向き/初期座標
04味方3 向き/初期座標
05味方4 向き/初期座標
06上位8ビット:アイテム入手率補正
下位8ビット:敵の総数
07敵ID(0)
08向き/初期座標(0)

さて、最終編のランクと敵パーティデータにおける敵パーティIDの対応表は以下の通りである。
初期ランクを10進数の1と16進数$00とした場合の両方を記しておく。

ランク敵パーティ
ID
敵パーティ
10進数16進数
1$00$60ザビエール×1
2$01$61オイディプス×1, ダイダロス×1, ポリディクティス×1
3$02$62クイーンテイル×3
4$03$63ポウンバード×2
5$04$64ルビータイラント×1, ケルベロ×4
6$05$65ヌーベルルミエル×1
7$06$66ソウルイーター×2, ホラーバルブ×2
8$07$67ファントム×1, 亡拳士×2
9$08$68クロノレギオン×3
10$09$69ベアナックル×1
11$0A$6Aねこまた×4
12$0B$6B強腕戦車×2, メカサタケ98×1
13$0C$6Cブラキオペルタ×1, エントモパイラム×2
14$0D$6Dヌーベルルミエル×1, バーバリアン×6
15$0E$6E大顔面×3, マスタードラゴン×1
16$0F$6Fイシュタール×1
17$10$70グラシャラボラス×2
18$11$71ツナヨシ×1, おイヌ様×14
19$12$72グラングラス×2
20$13$73マスタードラゴン×1, ピスタチオ×2
21$14$74ティタンブラッド×1
22$15$75ティタンブラッド×1, ピスタチオ×14
23$16$76次元 源左衛門×3
24$17$77ころころムシ×1
25$18$C4ホラーシップ×1
26$19$C6リンバースキュラ×4
27$1A$C7バロクレスト×2
28$1B$C8岩×5, マスタードラゴン×4, ピスタチオ×6
29$1C$C9ポワッシー×1
30$1D$1F岩×4, ころころムシ×8, ピスタチオ×2, マスタードラゴン×1
31$1E$C5ヘリオスハウント×2
32$1F$CAワールダーク×1

敵パーティIDの並び順は、ランクと大まかに一致していることがわかるが、ランク25($18)以上がズレている。
単純に、味方の合計レベルから敵パーティIDを算出しているのではない、ということがわかる。
先に記した通り、敵パーティデータ$D6:4280$D6:512Fに敵パーティID自体は収録されておらず、敵パーティIDの順番にデータが並んでいるのみである。

エリア固有値

エリア固有値は$00:12FD$00:12FFに入る値などから計算される。
この$00:12FD$00:12FFは、シナリオ進行フラグ一覧表で紹介している、シナリオ進行フラグ$00:1200$00:12FFの最後の3つ分のアドレスである。
ほとんどのシナリオでエンカウント関係のフラグが入る($01が入る)が、最終編はエリア毎に値が異なり、エリア移動時に値が切り替わる。
以下、ランダムエンカウントのないエリアは省く。
$00:12FD$00:12FFの値を合計して2倍するとエリア固有値と同一になるので、一番右の列に合計値を記した。

エリア$00:12FD$00:12FE$00:12FF合計値
ルクレチア城$00$00$00$00
ルクレチアの森$00$00$00$00
勇者の山$00$00$00$00
鍵のダンジョン$00$00$01$01
心のダンジョン$00$00$01$01
本能のダンジョン$00$00$01$01
魔王山$00$01$01$02
力のダンジョン
1フロア目入った直後
$01$01$01$03
力のダンジョン
それ以外
$00$01$01$02

力のダンジョンは入口から入った時の1フロア目だけ$00:12FDに値が入るが、そこから上のフロアに移動すると$00になり、手前の1フロア目に引き返してみても$01にならない。
入った時のみ$01というのも何か妙な気がするが、何か意味があっての仕様なのか、実は何かしらのミスなのか、そのあたりは不明。
リメイク版では、○○のダンジョンのエリア固有値はすべて同じであるが、これがSFC版でも本来の仕様だったかもわからない(リメイク版では魔王山のエリア固有値が低めだったり、敵パーティランクテーブル自体がまったく別ものなので、簡単に比較はできないだろうが)。

また、鍵のダンジョンのみ出現する「影」とのエンカウント判定には、シナリオ進行フラグ$00:123Eの値が使われる。
この$00:123Eには、鍵のダンジョン内にいる場合のみ$01が入り、それ以外は$00が入る。

サブルーチン

ここからは具体的な処理を見ていく。
以下のような順序で敵パーティが決定する。

  1. エリア固有値の算出(その1)
  2. 鍵のダンジョンかどうか判定
    鍵のダンジョンなら戦闘乱数生成で影エンカウント判定(敵パーティ決定)
  3. パーティメンバーの合計レベル計算
  4. エリア固有値の算出(その2)&ランク決定
  5. 戦闘乱数生成し、4パターンの敵パーティからどれにするか決定(敵パーティ決定)

エリア固有値の算出(その1)

上に書いた通り、「$00:12FD$00:12FFの値を合計して2倍するとエリア固有値と同一になる」のだが、$00:12FD$00:12FFの合計値を計算する部分のサブルーチンから紹介する。

$C0/1874 LDA $0018,x[$CD:0019] ;Aに[$0018,x]をロード
$C0/1877 ASL A                 ;算術左シフト *2
$C0/1878 ASL A                 ;算術左シフト *2
$C0/1879 STA $30    [$00:0030] ;Aを[$00:0030]に書き込み
$C0/187B PLB                   ;DBレジスタにプル
$C0/187C LDA $0ACC  [$00:0ACC] ;Aに[$00:0ACC]をロード
$C0/187F BEQ $07    [$1888]    ;ゼロフラグが立っているとき[$1888]分岐
$C0/1888 LDA $12FD  [$00:12FD] ;Aに[$00:12FD]をロード
$C0/188B CLC                   ;キャリーフラグクリア
$C0/188C ADC $12FE  [$00:12FE] ;A + [$00:12FE]
$C0/188F CLC                   ;キャリーフラグクリア
$C0/1890 ADC $12FF  [$00:12FF] ;A + [$00:12FF]
$C0/1893 ORA $30    [$00:0030] ;Aと[$00:0030]で論理和
$C0/1895 BRA $02    [$1899]    ;フラグにかかわりなく常に分岐[$1899]
$C0/1899 STA $41    [$00:0041] ;Aを[$00:0041]に書き込み

$00:12FD$00:12FFの読み込みと合計は、$C0/1888~から行われている。
$C0/1888A[$00:12FD]を読み込みし、キャリーフラグをクリアしつつ、A[$00:12FE][$00:12FF]を加算している。ここまでが$C0/1890である。
その次の$C0/1893で、$00:12FD$00:12FF合計値と[$00:0030]の論理和を取り、それを$C0/1899[$00:0041]に書き込んでいる。
[$00:0041]の値が後でエリア固有値の算出(その2)に使われるのだが、[$00:0030]は何なのか。
少し戻ってみると、$C0/1874A[$0018,x]の値をロードし、算術左シフト(×2)を2回行い[$00:0030]に入れていることがわかる。
ここから前を更に見てみると、[$0018,x]Xはランダムエンカウント関係の定数により決まる値であることがわかるのだが、ここではとりあえず横に置いておく。
というのは、[$0018,x]に入っている値が何であれ、その後算術左シフト(×2)を2回行っているから、2進数で考えると下2ビット分は必ず0である。
たとえば、ルクレチアの森だとここで呼び出される[$0018,x][$CD:0019] = $17(2進数で%0001 0111)になるのだが、算術左シフト(×2)を2回行うと$17*2*2$5C(2進数で%0101 1100)である。
2進数で見ると8桁の数値が左に2つずつズレて、下2桁(2ビット)に0がふたつ入った形である。
よって、$C0/1893にて$00:12FD$00:12FF合計値と[$00:0030]の論理和を取ると、下2桁にそのまま$00:12FD$00:12FF合計値が入ることになる。

以上から、$C0/1899[$00:0041]に入った値は、下2ビットに、$00:12FD$00:12FF合計値が入っている、ということになる。ここだけ覚えておいていただければ問題ない。

鍵のダンジョンかどうか判定

今いる場所が、「影」が出現する鍵のダンジョンかどうかの判定は以下で行われる。

$C1/F504 LDA $123E  [$00:123E]   ;[$00:123E] = シナリオ進行フラグ、鍵のダンジョンにいるかどうか
$C1/F507 CMP #$01                ;Aと$01を減算比較
$C1/F509 BNE $0A    [$F515]      ;ゼロフラグが立っていないとき[$F515]分岐(鍵のダンジョンではない)
$C1/F50B JSR $6150  [$C1:6150]   ;ゼロフラグが立っているとき[$C1:6150]へ(鍵のダンジョンである)

最終編では、[$00:123E]に、鍵のダンジョン内にいるかどうかのフラグが入る。ダンジョン内なら$01、それ以外は$00である。
$C1/F507$01と減算比較することにより、鍵のダンジョン内かどうか判定し、ゼロフラグが立たない場合は鍵のダンジョンではないので[$C1:F515]に飛ぶ。
鍵のダンジョンの場合は[$C1:6150]に飛ぶことになるが、この飛び先は戦闘乱数計算用のサブルーチンである。
この先の戦闘乱数で、影が出現するかどうか判定になる。

$C1/6150 PHX                     ;乱数計算
$C1/6151 PHB                     ;↓
$C1/6152 LDA #$00                ;↓
$C1/6154 PHA                     ;↓
$C1/6155 PLB                     ;↓
$C1/6156 LDA $38    [$00:0338]   ;↓
$C1/6158 ORA $39    [$00:0339]   ;↓
$C1/615A ORA $3A    [$00:033A]   ;↓
$C1/615C ORA $3B    [$00:033B]   ;↓
$C1/615E BNE $24    [$6184]      ;↓
$C1/6184 LDX $38    [$00:0338]   ;↓
$C1/6186 STX $4204  [$00:4204]   ;↓
$C1/6189 LDA #$0D                ;↓
$C1/618B STA $4206  [$00:4206]   ;↓
$C1/618E JSR $46C8  [$C1:46C8]   ;↓
;
$C1/46C8 NOP                     ;↓
$C1/46C9 RTS                     ;↓
;
$C1/6191 LDA $39    [$00:0339]   ;↓
$C1/6193 STA $38    [$00:0338]   ;↓
$C1/6195 LDA $3A    [$00:033A]   ;↓
$C1/6197 STA $39    [$00:0339]   ;↓
$C1/6199 LDA $3B    [$00:033B]   ;↓
$C1/619B STA $3A    [$00:033A]   ;↓
$C1/619D LDA $4214  [$00:4214]   ;↓
$C1/61A0 STA $3B    [$00:033B]   ;↓
$C1/61A2 PLB                     ;↓
$C1/61A3 PLX                     ;↓
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/F50E  BIT #$80               ;$80と乱数で論理積(フラグ変更だけ)
$C1/F510  BEQ $03    [$F515]     ;ゼロフラグが立っているとき[$F515]分岐
$C1/F512  LDA #$BD               ;Aに$BDをロード(敵パーティID$BDは「影×1」)
$C1/F514  RTS                    ;サブルーチン戻り

$C1/6150$C1/61A4はこれまでに説明した戦闘乱数生成サブルーチンなので、説明は省く。
$C1/F50Eでは、$80と生成した乱数($00$FF)で論理積を取り、ゼロフラグが立っているとき[$F515]分岐になる。
$80と論理積を取ってゼロフラグが立つのは、乱数が$80未満、つまり$00$79の場合である。
ゼロフラグが立った時の飛び先の[$F515]は、先の$C1/F509と同じ、「鍵のダンジョンではない」時と同じ。
つまり、乱数が$00$79なら、「鍵のダンジョンではない」時の処理と合流する。影とはエンカウントしない。
一方、乱数が$80$FFならゼロフラグは立たず、$C1/F512に処理が進むが、ここでAに読み込んでいる$BDという値は、敵パーティIDにおける「影×1」に当たる。
ここで、影×1の敵パーティが出現することが決定したということになる。
鍵のダンジョンにおいては、味方の合計レベルなどの計算より前に、影が出現するかどうかの判定が入り、乱数($00$FF)が$80$FFなら「影×1」で敵パーティが決定される、ということがわかった。
確率としては1/2である。

パーティメンバーの合計レベル計算

パーティメンバーの合計レベルの計算に必要なのは、最終編の主人公のIDが入っている[$00:0A10]と、パーティメンバーのIDが入っている[$00:0A11][$00:0A12][$00:0A13]である。入っているIDの値は、シナリオ別キャラデータIDになる。
パーティメンバー数が入っている[$00:0A14]から、[$00:0A11][$00:0A12][$00:0A13]のどこまでを選んでレベルを加算していくかを決める。

シナリオ別キャラデータのアドレスは、データマップに記してあるが、シナリオ別キャラデータから、レベルの値を取り出して加算していくことになる。
シナリオ別キャラデータが入っているアドレスに+$06するとレベルの値になる。
例えば高原日勝なら、[$00:0E40]からシナリオ別キャラデータが入っているが、+$06した[$00:0E46]に高原日勝の現在のレベルが入っている、といった具合である。

以上を踏まえた上で、鍵のダンジョンではない、または鍵のダンジョンでも影×1が敵パーティに選ばれなかった場合の飛び先である$C1/F515以降の処理を見ていく。

$C1/F515 STZ $10    [$00:0310] ;[$00:0310]に0を書き込み
$C1/F517 LDA $0A10  [$00:0A10] ;Aに[$00:0A10](最終編主人公ID)を読み込み
$C1/F51A JSR $F54F  [$C1:F54F] ;[$C1:F54F]へ
;
$C1/F54F JSR $EC4B  [$C1:EC4B] ;[$C1:EC4B]へ
;
$C1/EC4B XBA                   ;Aレジスタの上位バイトと下位バイトを交換
$C1/EC4C LDA #$00              ;Aに$00をロード
$C1/EC4E REP #$20              ;Mフラグをクリア Aレジスタは16bit幅
$C1/EC50 LSR A                 ;論理右シフト /2
$C1/EC51 LSR A                 ;論理右シフト /2
$C1/EC52 ADC #$0D00            ;A+$0D00(主人公のシナリオ別キャラデータ開始アドレスの値)
$C1/EC55 TAY                   ;Aレジスタの値をYレジスタに転送
$C1/EC56 SEP #$20              ;MフラグON Aレジスタは8bit幅
$C1/EC58 RTS                   ;サブルーチン戻り

パーティメンバーの合計レベルの計算は、まず$C1/F517で最終編主人公のシナリオ別キャラデータIDを読み出すところから始まる。

キャラID
ポゴ$01
キューブ$02
心山拳師範$03
サンダウン$04
高原 日勝$05
アキラ$06
おぼろ丸$07

この値から、シナリオ別キャラデータのアドレスの開始位置を計算しているのが$C1/EC4B$C1/EC58の処理である。
行っている処理は、キャラID(16進数2桁)の下に00をつけて16進数4桁としてから、16bitモードでLSR(論理右シフト)2回で/2を2回行っている。
その値に$0D00を足すと、ちょうどシナリオ別キャラデータの開始アドレス(下4桁)になる。
例えば先に上げた高原なら、キャラIDが$05なので、$05の下に00をつけて$0500としてから、

$0500を論理右シフト→$0280
$0280を論理右シフト→$0140
$0140 + $0D00 = $0E40

となり、高原のシナリオ別キャラデータ開始アドレスの$00:0E40を呼び出すための「0E40」部分が計算できた。
この値を$C1/EC55Yレジスタに送り、以降のアドレス指定に使っている。

$C1/F552 LDA $10    [$00:0310] ;Aに[$00:0310](= $00)をロード
$C1/F554 CLC                   ;キャリーフラグクリア
$C1/F555 ADC $0006,y           ;A+最終編主人公のレベル
$C1/F558 BCS $02    [$F55C]    ;キャリーフラグが立っているとき[$F55C]分岐
$C1/F55A STA $10    [$00:0310] ;Aを[$00:0310]に書き込み
$C1/F55C RTS                   ;サブルーチン戻り

$C1/F552[$00:0310]Aにロードしているが、$C1/F515[$00:0310]$00を書き込みしているので、ここで読み込みされるのは必ず$00である。
$C1/F555では、[$0006,y]の値をAに加算している。
Yには先程、主人公のシナリオ別キャラデータ開始アドレスの値が入っている。
例えば高原なら、$0E40 + $0006で、[$00:0E46]がロードされる。
シナリオ別キャラデータ開始アドレス+6のアドレスには、該当キャラのレベルの値が入っているので、$00 + 主人公のレベルが計算されたことになる。
$C1/F558ではキャリーフラグが立っていると分岐だが、とりあえずここでは主人公のレベルが入るだけであり、最大値は99(16進数だと$63)だから、キャリーフラグが立つことはない。
$C1/F55Aで、[$00:0310]$00 + 主人公のレベルが入る。
この後は[$00:0310]に味方キャラのレベルが加算されていくことになる。

$C1/F51D LDA $0A14  [$00:0A14] ;Aに[$00:0A14](パーティの仲間の人数)をロード
$C1/F520 BEQ $10    [$F532]    ;ゼロフラグが立っているとき[$F532]分岐
$C1/F522 STA $08    [$00:0308] ;A(パーティの仲間の人数)を[$00:0308]に書き込み
$C1/F524 LDX #$0000            ;Xに$0000をロード
$C1/F527 LDA $0A11,x[$00:0A11] ;Aに[$00:0A11](キャラID パーティの仲間1人目)をロード
$C1/F52A JSR $F54F  [$C1:F54F] ;[$C1:F54F]へ
;
$C1/F54F JSR $EC4B  [$C1:EC4B] ;[$C1:EC4B]へ

ここからは主人公以外の味方キャラ用サブルーチンになる。
[$00:0A14]には、パーティ人数が収納されている。

[$00:0A14]パーティ人数
$001人(主人公のみ)
$012人
$023人
$034人

上のようになっているので、入っている数値は実質、パーティメンバー中の仲間の数になる。
$C1/F520はゼロフラグでの分岐だが、パーティが主人公のみだとゼロフラグなので[$F532]にジャンプする。
主人公のみなら、パーティメンバーのレベルの値の合計値はこれ以上計算する必要がないためのジャンプである。
以降は仲間キャラがいる場合の処理のループになる。
まずはロードした[$00:0A14]の値を[$00:0308]に書き込む。これはループ回数処理用で、仲間キャラのレベルを加える度に-1してループ回数のカウントを行う。
$C1/F527で、シナリオ別キャラデータIDの仲間1人目のIDを[$00:0A11]からAにロードする。
この先は[$C1:F54F][$C1:EC4B]とジャンプしているが、この飛び先は先に主人公キャラIDからシナリオ別キャラデータのアドレスを計算するための処理先である。

$C1/EC4B XBA                   ;Aレジスタの上位バイトと下位バイトを交換
$C1/EC4C LDA #$00              ;Aに$00をロード
$C1/EC4E REP #$20              ;Mフラグをクリア Aレジスタは16bit幅
$C1/EC50 LSR A                 ;論理右シフト /2
$C1/EC51 LSR A                 ;論理右シフト /2
$C1/EC52 ADC #$0D00            ;A+$0D00
$C1/EC55 TAY                   ;Aレジスタの値をYレジスタに転送
$C1/EC56 SEP #$20              ;MフラグON Aレジスタは8bit幅
$C1/EC58 RTS                   ;サブルーチン戻り
;
$C1/F552 LDA $10    [$00:0310] ;Aに[$00:0310](味方キャラのレベル合計値)をロード
$C1/F554 CLC                   ;キャリーフラグクリア
$C1/F555 ADC $0006,y           ;A+仲間キャラのレベル
$C1/F558 BCS $02    [$F55C]    ;キャリーフラグが立っているとき[$F55C]分岐
$C1/F55A STA $10    [$00:0310] ;Aを[$00:0310]に書き込み
$C1/F55C RTS                   ;サブルーチン戻り
;
$C1/F52D INX                   ;Xレジスタをインクリメント(X + 1)
$C1/F52E DEC $08    [$00:0308] ;[$00:0308]-1
$C1/F530 BNE $F5    [$F527]    ;ゼロフラグが立っていないとき[$F527]分岐
$C1/F527 LDA $0A11,x[$00:0A12] ;Aに[$00:0A12](キャラID パーティの仲間2人目)をロード
$C1/F52A JSR $F54F  [$C1:F54F] ;[$C1:F54F]へ
;
$C1/F54F JSR $EC4B  [$C1:EC4B] ;[$C1:EC4B]へ

$C1/EC4B$C1/F55Cの処理は、主人公のレベル処理と同じ。
[$00:0310]には$00 + 主人公のレベルが入っていたが、$C1/F555でパーティの仲間1人目のレベルの値が加算され、$00 + 主人公のレベル + 仲間1人目のレベルとなった。
$C1/F52Dからは、次の仲間キャラがいるならそれを呼び出すための処理である。
$C1/F52Eで、ループ回数処理用の[$00:0308]-1している。
この時点で仲間キャラが1人のみなら、[$00:0308]の中は0になり、ゼロフラグが立つのでレベル加算処理は終了して[$F527]へジャンプ。
そうでない場合は仲間キャラ2人目の処理が開始になり、[$00:0A12](キャラID パーティの仲間2人目)をロードして、後は$C1/EC4Bに戻り3人目の処理開始である。

なお、パーティメンバーが2人までなら、$C1/F555でレベル加算する時、キャリーフラグは立たない。
レベルの最大値は99だから、99 + 99 = 198(16進数$C6)で、3桁目に溢れないからである。
キャリーフラグが立つのはパーティメンバーが3人以上の時であり、合計値が256以上(16進数$100)で3桁目に溢れてキャリーフラグが立つ。
その場合の処理は$C1/F558の通り、合計値を[$00:0310]に加算しない。

ということは、たとえば主人公と仲間1人目のレベル合計値が157で、仲間2人目のレベルが99の時、加算すると256になるからキャリーフラグが立ち、[$00:0310]157のままである。仲間3人目がいないならこの後数値が加算されず、最終的にレベル合計値が157と判定されてしまう。
だが、これでも問題はない。
ランク計算は、

ランク = min(trunc(味方パーティの合計レベル/4) + エリア固有値, 28)

だが、trunc(157/4) = 39であるから、この時点でランクの最大値28をオーバーしており、min(39 + エリア固有値, 28) = 28確定である。
ランク計算の仕様上、味方パーティの合計レベルが112を越えた時点でランク28が確定のため、それ以上加算する必要はないことになる。

最大で仲間3キャラ分のループを行い、[$00:0310]にパーティメンバー分のレベル合計値が入ったら(または上の通りに157 = $9D以上)、パーティメンバーの合計レベル計算は終了である。

エリア固有値の算出(その2)&ランク決定

便宜上、最後の仲間キャラのレベル加算が終了した時点からのサブルーチンを記す。

$C1/F52D INX                   ;Xレジスタをインクリメント(X+1)
$C1/F52E DEC $08    [$00:0308] ;[$00:0308]-1
$C1/F530 BNE $F5    [$F527]    ;ゼロフラグが立っていないとき[$F527]分岐
$C1/F532 LSR $10    [$00:0310] ;[$00:0310](レベル合計値)を論理右シフト(/2)
$C1/F534 LSR $10    [$00:0310] ;[$00:0310](レベル合計値/2)を論理右シフト(/2)
$C1/F536 LDA $0041  [$00:0041] ;Aに[$00:0041]をロード
$C1/F539 AND #$03              ;[$00:0041]と$03で論理積
$C1/F53B ASL A                 ;Aを算術左シフト(*2) = エリア固有値
$C1/F53C ADC $10    [$00:0310] ;エリア固有値 + [$00:0310]
$C1/F53E CMP #$1C              ;Aと$1C(10進数28)を減算比較
$C1/F540 BCC $02    [$F544]    ;キャリーフラグが立っていないとき[$F544]分岐
$C1/F542 LDA #$1C              ;(キャリーフラグON処理)Aに$1Cを書き込み
$C1/F544 STA $10    [$00:0310] ;Aを[$00:0310]に書き込み
$C1/F546 JSR $6150  [$C1:6150] ;[$C1:6150]へジャンプ

$C1/F52Eでは、仲間キャラの残りが居なくなっているため[$00:0308]-1の答えが0となる。
よって$C1/F530でゼロフラグが立ち、ループに戻らない。つまりレベル合計値の計算が終了。
続いて、$C1/F532から、LSR[$00:0310](レベル合計値)を2回論理右シフトしている。つまり÷2を2回、÷4の計算である。
仕様上端数切り捨てなので、

ランク = min(trunc(味方パーティの合計レベル/4) + エリア固有値, 28)

この式の「trunc(味方パーティの合計レベル/4)」を計算したことになる。

続いてA[$00:0041]をロードするが、これは最初に紹介した、下2ビットに$00:12FD$00:12FF合計値が入っているアドレスである。
$C1/F539では、[$00:0041]$03で論理積を取っている。$03は2進数で%0000 0011であり、論理積を取ると下2ビットだけ取り出せる。
つまり、ここで$00:12FD$00:12FF合計値がAに入ったことになる。
$C1/F53Bで算術左シフト(*2)しているので、$00:12FD$00:12FF合計値を2倍したことになる。
これがエリア固有値である。
$C1/F53Cで、計算したエリア固有値と、[$00:0310]に入っている「trunc(味方パーティの合計レベル/4)」を加算したので、ランク計算式の、

trunc(味方パーティの合計レベル/4) + エリア固有値

が計算されたことになる。
更に、$C1/F53Eで、上の計算値と$1Cを減算比較している。
$1Cは10進数28、つまりランクの事実上の最大値になる。
$1C以上ならキャリーフラグが立ち、$C1/F542の処理に進むが、ここでA$1Cを書き込んでいる。
一方、キャリーフラグが立たない、つまり$1C以下だったら、そのままA[$00:0310]に書き込んでいる。
$1C以上ならA = $1C[$00:0310]に書き込むから、

min(trunc(味方パーティの合計レベル/4) + エリア固有値, 28)

という計算が行われたのと同じである。
つまり、$C1/F544[$00:0310]に書き込まれた値がランクということである。

戦闘乱数生成し、4パターンの敵パーティからどれにするか決定

ランクが決まったところで、ランク+0+3の4パターンのどの敵パーティが出現するか、最後の処理が行われる。
まずは戦闘乱数生成を行っているが、乱数生成の説明は省略する。

$C1/6150 PHX                     ;乱数計算
$C1/6151 PHB                     ;↓
$C1/6152 LDA #$00                ;↓
$C1/6154 PHA                     ;↓
$C1/6155 PLB                     ;↓
$C1/6156 LDA $38    [$00:0338]   ;↓
$C1/6158 ORA $39    [$00:0339]   ;↓
$C1/615A ORA $3A    [$00:033A]   ;↓
$C1/615C ORA $3B    [$00:033B]   ;↓
$C1/615E BNE $24    [$6184]      ;↓
$C1/6184 LDX $38    [$00:0338]   ;↓
$C1/6186 STX $4204  [$00:4204]   ;↓
$C1/6189 LDA #$0D                ;↓
$C1/618B STA $4206  [$00:4206]   ;↓
$C1/618E JSR $46C8  [$C1:46C8]   ;↓
;
$C1/46C8 NOP                     ;↓
$C1/46C9 RTS                     ;↓
;
$C1/6191 LDA $39    [$00:0339]   ;↓
$C1/6193 STA $38    [$00:0338]   ;↓
$C1/6195 LDA $3A    [$00:033A]   ;↓
$C1/6197 STA $39    [$00:0339]   ;↓
$C1/6199 LDA $3B    [$00:033B]   ;↓
$C1/619B STA $3A    [$00:033A]   ;↓
$C1/619D LDA $4214  [$00:4214]   ;↓
$C1/61A0 STA $3B    [$00:033B]   ;↓
$C1/61A2 PLB                     ;↓
$C1/61A3 PLX                     ;↓
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/F549 AND #$03                ;乱数と$03で論理積
$C1/F54B CLC                     ;キャリーフラグクリア
$C1/F54C ADC $10    [$00:0310]   ;A+[$00:0310]
$C1/F54E RTS                     ;サブルーチン戻り

生成した乱数($00$FF)は、$C1/F549で、$03との論理積を取っている。
$03は2進数%0000 0011だから、乱数の下2ビットを取り出す計算である。
結果、

%0000 0000 = $00
%0000 0001 = $01
%0000 0010 = $02
%0000 0011 = $03

の4パターンのどれかが各確率1/4で得られることになる。
(また、論理積を取った結果は、乱数を4で割った余りと同一である)

$C1/F54Cでは、乱数から得られた$00$03と、ランクが入っている[$00:0310]を加算している。
これで、最終的にどのランクの敵パーティが選ばれるのかが決まる。
味方合計レベルとエリア固有値から計算されたランクに、乱数から得られた$00$03を加えて敵パーティ決定、という計算がここで終了である。
ランクは$00$1Fとなる。

といっても、ランクが決まっただけで、敵パーティ自体はまだ得られていない。
以降の処理で、ランクから敵パーティを算出している。
先に記した通り、敵パーティデータ$D6:4280$D6:512Fに敵パーティID自体は収録されていない。
敵パーティIDの順番にデータが並んでいるだけ、という点を念頭に置いていただきたい。

$C1/F5B9 BRA $35    [$F5F0]     ;無条件分岐[$F5F0]
$C1/F5F0 STA $50    [$00:0350]  ;A(ランク)を[$00:0350]へ書き込み
$C1/F5F2 STZ $51    [$00:0351]  ;[$00:0351]に$00を書き込み
$C1/F5F4 CMP #$D3               ;Aと$D3を減算比較(ステータスレジスタ変更のみ)
$C1/F5F6 BNE $03    [$F5FB]     ;ゼロフラグが立っていないとき[$F5FB]分岐

[$00:0350]にここまでで算出したランク($00$1F)を、[$00:0351]$00を書き込んでいる。
$C1/F5F4で、A$D3を減算比較しているが、A$00$1Fなので、ゼロフラグは必ず立たないことになる(はず)。

$C1/F5FB REP #$20               ;Mフラグをクリア Aレジスタは16bit幅
$C1/F5FD LDA $50    [$00:0350]  ;Aに[$00:0351][$00:0350]をロード
$C1/F5FF ASL A                  ;算術左シフト *2
$C1/F600 ADC $D60020[$D6:0020]  ;A+[$D6:0021][$D6:0020]($4080)
$C1/F604 TAX                    ;Aレジスタの値をXレジスタに転送
$C1/F605 LDA $D60000,x          ;Aに[$$D60000,x+1][$D60000,x]をロード
$C1/F609 TAX                    ;Aレジスタの値をXレジスタに転送
$C1/F60A STA $52    [$00:0352]  ;Aを[$00:0353][$00:0352]に書き込み
$C1/F60C SEP #$20               ;MフラグON Aレジスタは8bit幅
$C1/F60E LDA $D60000,x          ;Aに[$D60000,x](敵パーティレベル)をロード
$C1/F612 STA $55    [$00:0355]  ;Aを[$00:0355]に書き込み

$C1/F5FB以降は16bitモード演算である。
$C1/F5FDA[$00:0350]をロードしているが、16bitモードだから、上1バイトは[$00:0351]、下1バイトは[$00:0350]の4桁の16進数である。
先に[$00:0351]には$00を書き込みしているから、ここでAにロードされたのは、$00????はランク$00$1F)になる。
$C1/F5FFでこの値を算術左シフト(*2)しているので、$00??*2である。
$C1/F600で、$00??*2[$D6:0020]の数値を加算しているが、実際には上1バイト[$D6:0021]と下1バイト[$D6:0020]の4桁の16進数を加算しており、その数値は$4080である。
つまり、$00??*2 + $4080である。
この値を$C1/F604Xレジスタに転送し、$C1/F605では$D60000Xレジスタの値を加えたアドレスの数値を呼び出している。

ここまでをまとめると、$C1/F605LDA $D60000,xは、
$00??*2 + $4080 + $D60000
のアドレスを呼び出している、ということになる。
16bitモードのままだから、ここでAに呼び出す値は、実際には上のアドレスが下1バイト、上のアドレス+1が上1バイトの4桁の16進数である。

さて、$C1/F605で呼び出された4桁の16進数だが、最終的にはこの値に+$D60000したアドレスに敵パーティレベルが入っていて、$C1/F60Eでロードしている。
つまり、$C1/F605の呼び出し先には、敵パーティデータの最初の値、0番目にあたる位置のアドレスが格納されていたのである。
実質ここで、敵パーティを呼び出したことになる。

ということは、
$0000*2 + $4080 + $D60000 = $D64080
から、
$001F*2 + $4080 + $D60000 = $D640BE (+1)
までのアドレスに、最終編の各ザコ敵パーティデータの0番目アドレス用の値が入っていた、ということだ。

該当箇所は$D6:4080$D6:40BFということになるが、例えばランク00なら、[$D6:4081][$D6:4080]という順で読み出すことになる。
この2つのアドレスの値を「呼び出し先アドレス」として、AA BBの形で最初の表に追記する。

ランク敵パーティ
ID
呼び出し先
アドレス
敵パーティ
10進数16進数
1$00$6048 64ザビエール×1
2$01$6148 6Dオイディプス×1, ダイダロス×1, ポリディクティス×1
3$02$6248 7Aクイーンテイル×3
4$03$6348 87ポウンバード×2
5$04$6448 92ルビータイラント×1, ケルベロ×4
6$05$6548 A3ヌーベルルミエル×1
7$06$6648 ACソウルイーター×2, ホラーバルブ×2
8$07$6748 BBファントム×1, 亡拳士×2
9$08$6848 C8クロノレギオン×3
10$09$6948 D5ベアナックル×1
11$0A$6A48 DEねこまた×4
12$0B$6B48 ED強腕戦車×2, メカサタケ98×1
13$0C$6C48 FAブラキオペルタ×1, エントモパイラム×2
14$0D$6D49 07ヌーベルルミエル×1, バーバリアン×6
15$0E$6E49 1C大顔面×3, マスタードラゴン×1
16$0F$6F49 2Bイシュタール×1
17$10$7049 34グラシャラボラス×2
18$11$7149 3Fツナヨシ×1, おイヌ様×14
19$12$7249 64グラングラス×2
20$13$7349 6Fマスタードラゴン×1, ピスタチオ×2
21$14$7449 7Cティタンブラッド×1
22$15$7549 85ティタンブラッド×1, ピスタチオ×14
23$16$7649 AA次元 源左衛門×3
24$17$7749 B7ころころムシ×1
25$18$C44E 5Dホラーシップ×1
26$19$C64E 71リンバースキュラ×4
27$1A$C74E 80バロクレスト×2
28$1B$C84E 8B岩×5, マスタードラゴン×4, ピスタチオ×6
29$1C$C94E B0ポワッシー×1
30$1D$1F44 89岩×4, ころころムシ×8, ピスタチオ×2, マスタードラゴン×1
31$1E$C54E 66ヘリオスハウント×2
32$1F$CA4E B9ワールダーク×1

呼び出し先アドレスの頭に$D6をつけると、敵パーティデータの0番目アドレス(敵パーティレベル値)になっている。
例えば[$D6:4E8B]なら、ランク$1B、敵パーティID$C8の「岩×5, マスタードラゴン×4, ピスタチオ×6」の敵パーティレベルの値である。
最初に記したとおり、敵パーティデータは敵の総数により容量が変動し、単純に「幾つ足せば次の敵パーティアドレス」とはならない。
更に、ホラーシップ×1以降はランクの順に敵パーティIDが並んでいない。
$D6:4080$D6:40BFに、アドレス位置を直接登録することで、最終編用敵パーティランク一覧データとしていることがわかる。

ちなみに上の後は以下のように続くが、$C1/F61Aの処理はドロップアイテム判定で紹介した通り、$D60006,xのロードで、敵パーティデータの06番目の値で、上位8ビットにアイテム入手率補正、下位8ビットに敵の総数が格納されている。
$C1/F614における$D60001,xだが、戦闘の状態を示す数値のようで、$00:通常、$01:敵対敵(ブリキ大王戦など)、$10または$20:原始編のざき戦、$40:味方対味方(功夫編修行など)が入る模様。

$C1/F614 LDA $D60001,x ;Aに敵パーティデータ01をロード
$C1/F618 STA $56       ;[$00:0356]に書き込み
$C1/F61A LDA $D60006,x ;Aに敵パーティデータ06をロード
$C1/F61E STA $58       ;[$00:0358]に書き込み
$C1/F620 PHX           ;Xをスタックにプッシュ
$C1/F621 STZ $60       ;[$00:0360]に0を書き込み
$C1/F623 JSR $FB2B     ;[$C1:FB2B]へジャンプ

ひとまず敵パーティの決定方法の説明はここまでとする。



このページをシェアする

上へ