TOP > プログラミング関係解説&調査 > ドロップアイテム判定

ドロップアイテム判定

世界の合言葉は森部様の敵データページには、敵を倒した時に落とすアイテム(ドロップアイテム)のデータも掲載されている。
アイテムの項目は、「アイテム(1/2)」「アイテム(1/4)」「アイテム(1/8)」の3種類あり、カッコ内の数値はドロップ率である。
更に、「アイテム入手確率には、この値に、敵PTごとの係数がかかります。」とある通り、敵パーティページにある「アイテム入手率補正」という数値を掛け算して、アイテムドロップ率が計算される。

以上の仕様さえ知っていれば、ゲームをプレイする上で充分であるが、せっかくなのでプログラムではどうなっているのかをざっと説明する。

以下、「アイテム(1/2)」「アイテム(1/4)」「アイテム(1/8)」を便宜上「アイテム1」「アイテム2」「アイテム3」とする。
敵データを見てのとおり、敵によっては3つの枠の中でカラが存在していることもあるし、枠の中すべてが同一アイテムだったり、すべて異なる場合もある。

実際のゲーム内ではどのように判定されているのか。
ドロップの判定は、戦闘終了時に行われる。
画面上の「勝利ッ!!」の文字が表示されたあたりで判定用サブルーチンが開始になり、味方がレベルアップするならレベルアップ表示→ドロップアイテム表示となるが、敵種類が複数の場合、敵種類ごとにドロップ判定がされる。
敵1ドロップ判定→(敵1がアイテムドロップしていたら画面に表示)→敵2ドロップ判定→(敵2がアイテムドロップしていたら画面に表示)→……
と、最大4種の敵のドロップを判定していくが、ここでは「敵種類1種に対してのドロップの判定開始から終わりまで」について説明をする。

なお、判定用サブルーチンは、BREAK DOWNで敵を倒した時には読み込まれない設定になっている。つまりBREAK DOWNで倒すとドロップ判定そのものがされないため、ドロップアイテムが一切手に入らないのである。

さて、ドロップの判定手順は以下の通りである。

  1. 戦闘終了時に新たに乱数生成後、敵パーティのドロップ率判定(各パーティ毎に設定された確率で2.へ、選ばれないのならドロップなし)
  2. 新たに乱数生成後、アイテム1の判定(確率1/2でアイテム1が選ばれ、選ばれないのなら3.へ)
  3. 新たに乱数生成後、アイテム2の判定(確率1/2でアイテム2が選ばれ、選ばれないのなら4.へ)
  4. 新たに乱数生成後、アイテム3の判定(確率1/2でアイテム3が選ばれ、選ばれないのならドロップなし)

1. ~4. の実行前には必ず新たな乱数(0~255)が生成される。
この時に使われるサブルーチンは、戦闘乱数計算ページで紹介した$C1/6150$C1/61A4で、計算した乱数はAに書き込まれる。
乱数と$80(10進数128)で論理積を取り、ゼロフラグが立つ(乱数が128未満)か、ゼロフラグが立たない(乱数が128以上)で分岐させることで、確率1/2の判定を最大3回行い、どのアイテム枠が選ばれるか決まる、という手順である。
なお、アイテム1~3に空枠があり、その空枠が選ばれる場合も、ドロップなしの扱いである。

たとえば原始編のキングマンモーなら、敵パーティとしてのドロップ率は15/16である。
上でいえば、1. で15/16の確率でドロップする判定になり、1/16でドロップなし判定になる。
そして、アイテムの枠は、

  • アイテム1:(空欄)
  • アイテム2:コーラのビン
  • アイテム3:コーラのビン

と設定されている。
このため、上の2. で1/2の確率でアイテム1が選ばれると、アイテム1の欄は空欄なので結局「何もドロップしない」ことになる。
アイテム2が選ばれる場合は、

  • 1. で15/16でドロップ成功判定が選ばれる
  • 2. で1/2の確率でアイテム1が選ばれない
  • 3. で1/2の確率でアイテム2が選ばれる

という手順が必要なので、15/16*1/2*1/2が「アイテム2枠のコーラのビンが選ばれる確率」である。
更に、アイテム3枠のコーラのビンが選ばれる確率は15/16*1/2*1/2*1/2
まとめると、キングマンモー戦で「コーラのビン」がドロップされる確率は、

15/16*1/2*1/2 + 15/16*1/2*1/2*1/2
=15/16 * (1/4 + 1/8)

である(約35%)。

以下では判定に使われるプログラム(サブルーチン)を紹介する。
引き続き、キングマンモー戦を例として説明する。

ドロップされるアイテムのデータ、アイテム1・アイテム2・アイテム3だが、戦闘中、メモリのアドレス

  • $00:1A38
  • $00:1A39
  • $00:1A40

に読み込みされている。
(これが敵が1種類の時の読み込み先アドレスで、2種類以上いる場合、2種類目からは別の場所に読み込まれる)
本作のアイテムは、装備品も含め、アイテムデータに256種が登録されている(未使用アイテム含む)。
アイテムIDは16進数だと$00~$FFと番号が振ってあるが(以下便宜上アイテムIDとする)、アイテムIDの$00は空欄である。
アイテムIDの具体的な数値は、世界の合言葉は森部様のアイテムデータのID欄を参照のこと。

戦闘開始時、味方に関する様々なデータが読み込まれる。
キングマンモー戦においては、「コーラのビン」のアイテムIDが「$4A」なので、

  • $00:1A38 : $00
  • $00:1A39 : $4A
  • $00:1A40 : $4A

というように数値が読み込みされる。
また、敵パーティとしてのキングマンモーのアイテムドロップ率は先に述べた通りに15/16であるが、実際には「15」の部分だけが読み込まれ、後々で15/16の判定に使われるようになっている。
ここではこの15のことを「ドロップ率計算用数値」と仮に名前をつけておく。
ドロップ率計算用数値は、戦闘開始直後、以下で読み込まれている。

$C1/F61A LDA $D60006,x[$D6:4F88] ;[$D6:4F88]の値$F1をAにロード
$C1/F61E STA $58    [$00:0358]   ;A($F1)を[$00:0358]に書き込み

[$D6:4F88]に収納された「$F1」という値が読み込まれている。
この「$F1」の上位8ビット、つまり最初の16進数「F」(= 10進数15)が「ドロップ率計算用数値」である。
敵パーティデータでの「ドロップ率計算用数値」はすべて「0~15」に設定されているのだが、0~15は16進数だと0~Fの1文字で表すことができるため、16進数1文字で全ての敵パーティの「ドロップ率計算用数値」が記録されている。
[$00:0358]に書き込まれた数値の上位8ビットのみが「ドロップ率計算用数値」であるという点に注意。

戦闘開始の時点では、上のように各数値が読み込みされただけである。アイテムドロップの判定自体は、戦闘終了時に行われる。
アイテムドロップ判定は$C1/166E以降のサブルーチンで行われている。
以下は、「キングマンモー撃破時、アイテム2が選ばれてコーラのビンをドロップする」という場合のサブルーチンの状態である。
AXの数値も併記した。「??」はその時点での乱数である。

;■敵パーティのドロップ率判定開始
$C1/166E LDA $58    [$00:0358]   ; A:1F?? X:1A00 Aに[$00:0358]を読み込み
$C1/1670 AND #$F0                ; A:1FF1 X:1A00 Aと$F0で論理積(上位8bitそのまま、下位8bitを00にする)
$C1/1672 STA $10    [$00:0310]   ; A:1FF0 X:1A00 A(上位8bitは「ドロップ率計算用数値」 = F)を[$00:0310]に書き込み
$C1/1674 JSR $6150  [$C1:6150]   ; A:1FF0 X:1A00 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる)
$C1/1677 CMP $10    [$00:0310]   ; A:1F?? X:1A00 A(乱数)と[$00:0310]の値を減算比較(乱数 - $F0)
$C1/1679 BCS $10    [$168B]      ; A:1F?? X:1A00 キャリーフラグが立っているなら[$C1/167B]へ
;■アイテム1~3の判定準備
$C1/167B LDA #$03                ; A:1F?? X:1A00 Aに$03をロード
$C1/167D STA $08    [$00:0308]   ; A:1F03 X:1A00 Aの値を[$00:0308]に書き込み
$C1/167F JSR $6150  [$C1:6150]   ; A:1F03 X:1A00 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる) 
;■アイテム1の判定開始 
$C1/1682 BIT #$80                ; A:1F?? X:1A00 A(乱数)と$80(10進数128)で論理積
$C1/1684 BEQ $06    [$168C]      ; A:1F?? X:1A00 ゼロフラグが立っているなら[$C1/168C]にジャンプ
$C1/1686 INX                     ; A:1F?? X:1A00 Xをインクリメント
$C1/1687 DEC $08    [$00:0308]   ; A:1F?? X:1A01 [$00:0308]をデクリメント($03→$02)
$C1/1689 BNE $F4    [$167F]      ; A:1F?? X:1A01 ゼロフラグが立っていないなら[$C1/167F]にジャンプ
$C1/167F JSR $6150  [$C1:6150]   ; A:1F?? X:1A01 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる) 
;■アイテム2の判定開始 
$C1/1682 BIT #$80                ; A:1F?? X:1A01 Aと$80(10進数128)で論理積
$C1/1684 BEQ $06    [$168C]      ; A:1F?? X:1A01 ゼロフラグが立っているなら[$C1/168C]にジャンプ
$C1/168C LDA $0038,x[$00:1A39]   ; A:1F?? X:1A01 Aに[$00:1A39]をロード
$C1/168F BEQ $0C    [$169D]      ; A:1F4A X:1A01 ゼロフラグが立っているなら[$C1/169D]にジャンプ
$C1/1691 STA $17    [$00:0317]   ; A:1F4A X:1A01 Aを[$00:0317]に書き込み

それぞれの部分を解説する。

;■敵パーティのドロップ率判定開始
$C1/166E LDA $58    [$00:0358]   ; A:1F?? X:1A00 Aに[$00:0358]を読み込み
$C1/1670 AND #$F0                ; A:1FF1 X:1A00 Aと$F0で論理積(上位8bitそのまま、下位8bitを00にする)
$C1/1672 STA $10    [$00:0310]   ; A:1FF0 X:1A00 A(上位8bitは「ドロップ率計算用数値」 = F)を[$00:0310]に書き込み
$C1/1674 JSR $6150  [$C1:6150]   ; A:1FF0 X:1A00 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる)  
$C1/1677 CMP $10    [$00:0310]   ; A:1F?? X:1A00 A(乱数)と[$00:0310]の値を減算比較(乱数 - $F0)
$C1/1679 BCS $10    [$168B]      ; A:1F?? X:1A00 キャリーフラグが立っているなら[$C1/167B]へ

まず、敵パーティのドロップ率を判定している。
$C1/166Eで、[$00:0358]= $F1)がAにロードされている。
先に述べたとおり、[$00:0358]に収納されているのは、上位8ビットに「ドロップ率計算用数値」が記されている値である。
次の$C1/1670で、A$F0(2進法11110000)で論理積を取っている。
この計算で、上位8ビットを残し下位8ビットを0とする処理が行われ、Aはアイテムドロップ率だけ上位8ビットに残った「$F0」になる。
この値は、$C1/1672[$00:0310]に書き込まれている。
その後は乱数計算用サブルーチン[$C1:6150]に飛んだため、サブルーチンから戻ってきた時($C1/1677)には新たな乱数($00~$FF)がAに収納されている。
$C1/1677では、Aに入っている新たな乱数と、[$00:0310]を減算比較している。
その後の$C1/1679でキャリーフラグが立ったかどうかで分岐しているが、これまでにもあったとおり、

  • 引き算で繰り下がりが起きなかった時、つまり乱数≧[$00:0310]= $F0)なら、キャリーフラグが立つ
  • 引き算で繰り下がりが起きた時、つまり乱数<[$00:0310]= $F0)なら、キャリーフラグが立たない

という処理を行うことになる。

ここでの引き算は、「乱数 - $F0」である。
だが、減数(ここでは$F0)の下1桁、160の位は0だから、実際には2桁目、161の位の引き算結果だけでキャリーフラグが立つかどうかが決まる。
乱数の161の位は$0~Fのいずれか(16通り)である。
「ドロップ率計算用数値」が16より大きいか小さいかで判定される、ということになるので、敵パーティのアイテムドロップ率は、
「ドロップ率計算用数値」($0~F)/16
となる。

ややこしい説明になってしまったが、$C1/1677で行われた「乱数 - $F0」は、

  • 乱数が「$00(10進法0)~$EF(10進法239)」だと繰り下がりが起きてキャリーフラグが立つ → アイテムドロップ判定が続く
  • 乱数が「$F0(10進法240)~$FF(10進法255)」だと繰り下がりが起きずキャリーフラグが立たない → アイテムドロップなし

というように分岐する。
240/256 = 15/16の確率でアイテムドロップ判定が続くということである。
キングマンモーの場合、この「ドロップ率計算用数値」が大きいので、続いてのアイテム1~3の判定に辿り着く確率が高い。
一方で一部イベント戦闘やSF編・現代編・西部編の戦闘では0/16なので、一切アイテムをドロップしない。
それ以外だと、本作では確率が3/16に設定されている敵パーティが大半のため、敵ドロップが割と渋い。
ただし原始編の動物系の敵パーティではやや高めに設定されているのでアイテム合成用素材を集めやすくなっていたり、近未来編の全体マップの敵は15/16に設定されているためBREAK DOWNで倒さなければ高確率で敵がアイテムをドロップする。

;■アイテム1~3の判定準備
$C1/167B LDA #$03                ; A:1F?? X:1A00 Aに$03をロード
$C1/167D STA $08    [$00:0308]   ; A:1F03 X:1A00 Aの値を[$00:0308]に書き込み
$C1/167F JSR $6150  [$C1:6150]   ; A:1F03 X:1A00 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる)  

敵パーティでのアイテムドロップ率判定が終わった後はアイテム1~3の判定が始まるのだが、まずその下準備として、$C1/167B$C1/167Dで、メモリ[$00:0308]$03を書き込んでいる。$03は10進数でも「3」である。
[$00:0308]は後でアイテム1・アイテム2・アイテム3を判定していくと1ずつ減っていく処理が行われることから、「何回判定したか(どこで判定を終わらせるか)」の判断を行うための数値だと思われる。
乱数計算用サブルーチン[$C1:6150]に飛び、新たな乱数を生成した後、アイテム1の判定になる。

;■アイテム1の判定開始 
$C1/1682 BIT #$80                ; A:1F?? X:1A00 A(乱数)と$80(10進数128)で論理積
$C1/1684 BEQ $06    [$168C]      ; A:1F?? X:1A00 ゼロフラグが立っているなら[$C1/168C]にジャンプ
$C1/1686 INX                     ; A:1F?? X:1A00 Xをインクリメント
$C1/1687 DEC $08    [$00:0308]   ; A:1F?? X:1A01 [$00:0308]をデクリメント($03→$02)
$C1/1689 BNE $F4    [$167F]      ; A:1F?? X:1A01 ゼロフラグが立っていないなら[$C1/167F]にジャンプ
$C1/167F JSR $6150  [$C1:6150]   ; A:1F?? X:1A01 乱数計算用サブルーチン[$C1:6150]にジャンプ
;(※乱数計算処理省略。乱数生成し、Aに書き込まれる) 

アイテム1~アイテム3の判定は、$C1/1682から開始になる共通サブルーチンを使う。
まずはアイテム1の判定だが、上はアイテム1が選ばれなかったパターンになる。
最初に、$C1/1682で、生成した乱数($00~$FF)と$80とで論理積を取る。
$80は、10進数の「128」であり、2進数の「10000000」である。
$80未満だと2進数の8桁目が0$80以上だと2進数の8桁目が1になるため、乱数「$00~$FF」の数値と$80で論理積をとると、

  • 「乱数が$80未満だと論理積は0
  • 「乱数が$80以上だと論理積は0以外の数」

と、各1/2に分岐する。
よって$C1/1684の「ゼロフラグが立つ=論理積の結果が0の時」では、確率1/2で分岐して[$C1/168C]にジャンプし、アイテム1が選ばれて判定終了となる。
上は、乱数が$80以上のパターンで、[$C1/168C]にジャンプせずそのまま計算が進む。
$C1/1686では、Xをインクリメント(+1)している。これは次のサブルーチン計算時に意味を持つ。
$C1/1687では、[$00:0308]をデクリメント(-1)している。
先に、[$00:0308]$03が書き込まれており、デクリメントの結果[$00:0308]$02となった。
$C1/1689では、$C1/1687のデクリメント(-1)の結果、ゼロフラグが立っていない場合は[$C1/167F]にジャンプする。
ゼロフラグが立つのは、[$00:0308]$00まで減った時である。これは「アイテム3まで判定してもアイテム3が選ばれなかった時」に当たるので、ここでサブルーチンのループを抜けて判定終了となるはずである。
ここでは、$C1/167Fにジャンプしており、再び乱数計算用サブルーチン[$C1:6150]へ飛んで乱数計算、アイテム2判定へと進む。

;■アイテム2の判定開始 
$C1/1682 BIT #$80                ; A:1F?? X:1A01 Aと$80(10進数128)で論理積
$C1/1684 BEQ $06    [$168C]      ; A:1F?? X:1A01 ゼロフラグが立っているなら[$C1/168C]にジャンプ
$C1/168C LDA $0038,x[$00:1A39]   ; A:1F?? X:1A01 Aに[$00:1A39]をロード
$C1/168F BEQ $0C    [$169D]      ; A:1F4A X:1A01 ゼロフラグが立っているなら[$C1/169D]にジャンプ
$C1/1691 STA $17    [$00:0317]   ; A:1F4A X:1A01 Aを[$00:0317]に書き込み

乱数生成し$C1/1682へ戻ってきた。ここではアイテム2の判定が行われる。
上はアイテム2が選ばれた場合。
$C1/1684で「ゼロフラグが立つ=論理積の結果が0の時」に該当し、$C1/168Cへジャンプした。
$C1/168Cの命令は「LDA $0038,x」である。
これは、「$0038」にXを足したアドレスのメモリの数値をロードしろ、という意味である。
最初に記したとおり、アイテム1・アイテム2・アイテム3は、以下のアドレスに読み込まれている。

  • $00:1A38 : アイテム1
  • $00:1A39 : アイテム2
  • $00:1A40 : アイテム3

つまり、X0なら$00:1A38(アイテム1)を読み込み、X1なら$00:1A39(アイテム2)を読み込み、X2なら$00:1A40(アイテム3)を読み込む、という仕組みである。
また、$C1/1686でXをインクリメント(+1)しているから、

$C1/1682$C1/1684でアイテムが選ばれない時、X+1し、新たに乱数を生成してから、$C1/1682へ戻る

というループによって、アイテムが格納されているアドレスを+1しつつ、アイテム1→アイテム2→アイテム3と判定をしていることがわかる。
更に、$C1/168Fではゼロフラグの判定をしていることから、読み込んだアイテムIDの数値が$00かどうか、つまりドロップアイテム欄が空欄かどうかを判定している。
空欄だった時はドロップアイテムなしということであり、[$C1/169D]にジャンプして戦闘終了である。
一方、空欄ではなく何かのアイテムだった場合はメモリ[$00:0317]にドロップアイテムのIDを書き込んで、実際のゲーム画面に獲得アイテムを表示する。

このようにしてドロップアイテムが入手できるかどうかが決まるのだが、判定は敵を倒した時点の乱数で決まるため、バーチャルコンソールでプレイしているのなら、最後の一撃の前に「まるごと保存」しておくと良い。
これまでも述べたとおり、戦闘中の乱数は、技を出したりアイテムを使ったり敵に行動させるなどしなければ新たに計算されない。
倒した時に欲しいアイテムがドロップされなかった時は、最後の一撃の前のデータを「まるごと復元」し、別の行動を挟んでから(この後にもう一度「まるごと保存」を忘れずに)撃破することで乱数を変えてみると良い。
技をあえてカラマスに出して空振りさせたり、回復技があるのなら使ってみたり、パスやYボタン足踏みなどで敵を行動させるなど(敵を移動させるだけでも乱数が動く)。
それから敵を撃破してみると乱数が変わったことにより、ドロップアイテム計算結果も変動するはずである。
一度しか戦えない強敵・キングマンモーから「コーラのビン」を狙う時はもちろん、最終編でザコ敵からしか入手できない「マンティスハンド」や「黒ねこのくつ」といったレアな装備品を狙ってみる時にも役立つ。



このページをシェアする

上へ