ゲームの内部で起こっている処理を推測するのはなかなか難しいものです。ユーザーサイドから見れば、ゲームの内部処理はほとんど「ブラックボックス」のようなものです。ユーザーサイドでは「(内部で複雑な処理が行われた末の)最終結果」しかわかりませんし、ゲーム中の様々な要素(各種パラメータなど)がどのように「最終結果」に影響を及ぼしているのかについてもわかりません。解明するためには地道な試行錯誤を要します。
中でも「確率」というのはユーザーサイドからは見積もりするのが難しい要素です。特に「めったに起きないこと」が起きる確率を調べる場合などは、多大なサンプル数が必要となるために調査は困難を極めます。
そこで、「ROMイメージ内のプログラムコードの部分を解析する」というアプローチをもって、「外側」から見ただけではわかりにくいことを「内側」から調査してみました。この文書では、各種ゲームのプログラムコードを解析した結果、判明したことについてまとめてみました。現在のところは、主に各種ゲームの「確率」関連の処理について調べていますが、基本的には知的好奇心の赴くままに調査をしています。
対象とするプラットフォームは、今のところスーパーファミコンのゲームのみです。他のROMベースのゲーム機については、ROMイメージをファイルにダンプできるハードウェアを持っていないからです。まあ、CD-ROMベースのゲーム機については特殊なハードウェアがなくても(CD-ROMの)内容を読めるわけですが……さすがにプレイステーションなどのゲームのプログラムコードを解析するスキルはありません。
なお、解析結果には誤りが存在している可能性が多分にあります。ご注意ください。
ファイナルファンタジーIVには、モンスターを倒した際のお宝としてしか入手することのできないアイテムが多数存在します。「プリンプリンセス」の「ピンクのしっぽ」などは有名です。そして、これらのいわゆる「レアアイテム」は入手確率が非常に低く設定されています。
プリンプリンセスからピンクのしっぽを入手できる確率については、よく「1/256の確率である」などと言われていますが、果たして本当なのでしょうか? ここでは、「レアアイテム」が実際にはどの程度の確率で入手できるのかを解析した結果について述べます。
お宝入手の処理は、大きく分けて「お宝を残すかどうかの判定処理」と「(お宝を残した場合)4種類のアイテムの選択処理」の2つの部分になります。以下、順に説明します。
まずは「お宝を残すかどうかの判定処理」を行います。お宝を残す確率はモンスターによって決まっていて、それぞれ0〜3の4段階に分かれています。
なお、ザコモンスターの場合はたいてい"1"に設定されています。(詳しくはff_analyzerの『モンスターの所持アイテムデータ(IV)(ff4monitem.txt)』をご覧ください)
さて、この「5%」「25%」という確率ですが、ファイナルファンタジーIVでは「百分率」の扱いに若干癖があり、わずかに誤差があります。例えば、「5%の確率でお宝を残すか?」という判定を行う際には、「0〜98の範囲の乱数値を取得して、その値が5未満であるか?」という判定を行います。「0〜99の範囲」とか「0〜100の範囲」ではありません(注1)。よって、「5%の確率」は、実際には「5/99(≒ 5.05%)」であると言えます。
さらに、乱数生成の処理自体に起因する大きな誤差があります。例えば、上記の例でいう「0〜98の範囲の乱数値」を取得する際の処理は、「乱数テーブルから0〜255の範囲の乱数値を取得して、99で除算した余を算出する」という処理になっています。乱数テーブルには0〜255の範囲の乱数が偏りなく並んでいるのですが、このアルゴリズムだと「0〜57」が出る確率が少し高く(それぞれ3/256)、逆に「58〜98」が出る確率が少し低い(それぞれ2/256)のです。そうなると、「0〜98の範囲の乱数値」が「0〜4の範囲」になる確率は、「3/256 × 5 = 15/256(≒ 5.86%)」となります。
というわけで、一般的なザコモンスターがお宝を残す確率は「15/256(≒ 5.86%)」となります。
そして、お宝を残したと判定された場合、「4種類のアイテムの選択処理」を行います。こちらは単純で、0〜255の範囲の乱数値を取得して、その値の範囲によってアイテムの種類が決まります。
乱数値 | 確率 | アイテム |
---|---|---|
0〜127 | 128/256 | アイテム1(「ぬすむ」で入手できるアイテム) |
128〜207 | 80/256 | アイテム2 |
208〜251 | 44/256 | アイテム3 |
252〜255 | 4/256 | アイテム4(いわゆる「レアアイテム」) |
以上のことから、一般的なザコモンスターから「レアアイテム」を入手できる確率は、「15/256 × 4/256 ≒ 0.0916%」となります。だいたい1092体くらい倒してようやく1個入手できるかどうか、といったところでしょうか。
おそらくこの確率はすべてのモンスターにおいて同じです。よって、「プリンプリンセスからピンクのしっぽを入手できる確率」は「0.0916%(約1/1092)」となります。「1/256」よりはるかに低い確率です(注2)。ただ、プリンプリンセスは必ず5体で出現するわけで、「5体倒した際の確率」ということになると、単純に計算して「0.0916% × 5 ≒ 0.458%(≒ 1/218)」となります。これならば「当たらずとも遠からず」といったところでしょうか。
しかしながら、ここで一つ問題点が浮上します。基本的に、コンピュータで生成する疑似乱数は数の並びが事前にすべて決まっていて、乱数生成の試行は独立ではありません。お宝入手時の処理では「お宝を残すかどうかの判定処理」と「4種類のアイテムの選択処理」とで2回の乱数生成処理が行われるわけですが、もしこの2つの判定が連続して行われるとするならば、「レアアイテム」が出るには乱数テーブル内で以下の条件を満たした数字A・Bが隣接して並んでいなくてはならないことになります(乱数生成処理1回ごとに乱数の現在位置が1つ進むと仮定した場合)。
そして、実際に乱数テーブルを見ると、そのような数字A・Bが隣接している箇所はありません。これでは「レアアイテム」が出ないのではないでしょうか?
7 | 182 | 240 | 31 | 85 | 91 | 55 | 227 | 174 | 79 | 178 | 94 | 153 | 246 | 119 | 203 |
96 | 143 | 67 | 62 | 167 | 76 | 45 | 136 | 199 | 104 | 215 | 209 | 194 | 242 | 193 | 221 |
170 | 147 | 22 | 247 | 38 | 4 | 54 | 161 | 70 | 78 | 86 | 190 | 108 | 110 | 128 | 213 |
181 | 142 | 164 | 158 | 231 | 202 | 206 | 33 | 255 | 15 | 212 | 140 | 230 | 211 | 152 | 71 |
244 | 13 | 21 | 237 | 196 | 228 | 53 | 120 | 186 | 218 | 39 | 97 | 171 | 185 | 195 | 125 |
133 | 252 | 149 | 107 | 48 | 173 | 134 | 0 | 141 | 205 | 126 | 159 | 229 | 239 | 219 | 89 |
235 | 5 | 20 | 201 | 36 | 44 | 160 | 60 | 68 | 105 | 64 | 113 | 100 | 58 | 116 | 124 |
132 | 19 | 148 | 156 | 150 | 172 | 180 | 188 | 3 | 222 | 84 | 220 | 197 | 216 | 12 | 183 |
37 | 11 | 1 | 28 | 35 | 43 | 51 | 59 | 151 | 27 | 98 | 47 | 176 | 224 | 115 | 204 |
2 | 74 | 254 | 155 | 163 | 109 | 25 | 56 | 117 | 189 | 102 | 135 | 63 | 175 | 243 | 251 |
131 | 10 | 18 | 26 | 34 | 83 | 144 | 207 | 122 | 139 | 82 | 90 | 73 | 106 | 114 | 40 |
88 | 138 | 191 | 14 | 6 | 162 | 253 | 250 | 65 | 101 | 210 | 77 | 226 | 92 | 29 | 69 |
30 | 9 | 17 | 179 | 95 | 41 | 121 | 57 | 46 | 42 | 81 | 217 | 93 | 166 | 234 | 49 |
129 | 137 | 16 | 103 | 245 | 169 | 66 | 130 | 112 | 157 | 146 | 87 | 225 | 61 | 241 | 249 |
238 | 8 | 145 | 24 | 32 | 177 | 165 | 187 | 198 | 72 | 80 | 154 | 214 | 127 | 123 | 233 |
118 | 223 | 50 | 111 | 52 | 168 | 208 | 184 | 99 | 200 | 192 | 236 | 75 | 232 | 23 | 248 |
しかし、そのような心配は無用です。実際の処理では、「お宝を残すかどうかの判定処理」が終わった後、「4種類のアイテムの選択処理」を行う直前に、乱数の現在位置を0〜255個分進めているからです。この「0〜255個分」ですが、ファイナルファンタジーIVのプログラムには「1/60秒ごとに常に1ずつ増加している16ビットのカウンタ」なるものがあり、このカウンタの下位8ビット分を使って乱数の現在位置を「0〜255個分」進めているのです。
さらに、戦闘中は常に「1/60秒ごとに乱数の現在位置を1ずつ増加する処理」も行っています。
これらの処理を行っているため、普通にゲームをプレイしているかぎりでは、「お宝を残すかどうかの判定」と「4種類のアイテムの選択処理」の2つは独立試行であると言えます(注3)。
なぜ「0〜99」ではなくて「0〜98」の範囲で判定を行うのでしょうか? これは、通常の百分率が0〜100%の範囲なのに対して、ファイナルファンタジーIVにおける百分率は0〜99%の範囲になっているからです。普通、百分率で判定を行うとすると「0%: 必ず失敗」「100%: 必ず成功」ということになりますが、ファイナルファンタジーIVではこれが「0%: 必ず失敗」「99%: 必ず成功」となっているわけです。
ちなみに、『ファイナルファンタジーIV イージータイプ』の場合、プリンプリンセスの「お宝を残す確率」は"2"に設定されているため、「25%」となっています(正確には「75/256(≒ 29.23%)」)。「プリンプリンセスからピンクのしっぽを入手できる確率」は「0.458%(約1/218)」となります。
ただし、1回の戦闘で多数のモンスターを倒した場合だと、「独立試行」と言えるかどうかはかなり微妙です。宝箱入手判定は、戦闘終了時に倒したモンスターの分だけ繰り返し行われるので、「1/60秒」の間に何度も判定が行われることになります。その間は「1/60秒ごとに常に1ずつ増加している16ビットのカウンタ」の値は変化しませんし、「1/60秒ごとに乱数の現在位置を1ずつ増加する処理」も行われません。そのため、場合によっては「倒したモンスターの数に比べて、お宝の数が非常に多い(少ない)」とか「4種類のアイテムの内訳が偏っている」といったことが起こるかもしれません。
ファイナルファンタジーVIにおいて、通常のモンスター出現の際には最大8種類のモンスターパーティの中から出現するモンスターが決まります。決定方法は単純で、0〜255の範囲の乱数値を取得して、その値の範囲によってモンスターパーティの種類が決まります。なお、アラームを使ってモンスターが出現する場合は、乱数値で255を取得したことにして判定されます(つまり、必ず「モンスターパーティ8」が出現する)。
乱数値 | 確率 | モンスターパーティ |
---|---|---|
0〜42 | 43/256 | モンスターパーティ1 |
43〜85 | 43/256 | モンスターパーティ2 |
86〜128 | 43/256 | モンスターパーティ3 |
129〜171 | 43/256 | モンスターパーティ4 |
172〜203 | 32/256 | モンスターパーティ5 |
204〜235 | 32/256 | モンスターパーティ6 |
236〜251 | 16/256 | モンスターパーティ7 |
252〜255 | 4/256 | モンスターパーティ8 |
モンスターパーティ1〜8の実際の割り振りについては、ff_analyzerの『モンスター遭遇テーブルデータ(IV)(ff4monenc.txt)』をご覧ください。
「ぬすむ」を実行してアイテムを入手できる確率は、基本的には敵味方のレベルによって決まります(以下、「ぬすむ」を行うキャラクタのレベルを「自分レベル」、「ぬすむ」の対象となるモンスターのレベルを「相手レベル」と呼称します)。
まず、戦闘中のみに使用される「隠しパラメータ」が存在していて、戦闘開始時に初期値として「レベル + 10」の値が設定されています(モンスターの場合)。そして、この隠しパラメータは「ぬすむ」の成功率に影響していて、確率の計算式は「(自分レベル + 50 - 相手の隠しパラメータ) ÷ 99」となります。戦闘中に隠しパラメータが変動することはあまりないので、上記の式は「(自分レベル + 40 - 相手レベル) ÷ 99」と解釈しても問題ないでしょう。また、算出した成功率が0%以下になった場合でも、成功率は最低でも1%(正確には3/256(= 1.17%))はあります。
なお、この隠しパラメータは「うそなき」によって変動します。詳しくは『「うそなき」の効果』をご覧ください。
また、戦闘後に必ずお宝を残すモンスターの場合、「ぬすむ」の判定に成功してもアイテムを盗むことはできません。
「ぬすむ」の判定に失敗した場合、「モンスターにみつかった」となってダメージを受けることがあります。モンスターに見つかる確率についても上記の隠しパラメータが影響しています。具体的には、「相手の隠しパラメータ ÷ 99」の確率でモンスターに見つかります。この時にダメージを受けることがありますが、ダメージ値の算出方法は不明です(注1)。
プログラムを見た限りでは、本来は「自分の最大ヒットポイントの1/16のダメージを受ける」という処理を意図していたようにも見えます。
ポロムの「うそなき」は、その効果が謎に包まれているコマンドです。攻略本には「敵が動揺して、逃亡することが多くなる」などと書かれていますが、実際には何度「うそなき」をしたところで敵が逃亡することはありません。
さて、上記の『「ぬすむ」処理』にて、盗み処理に関する「隠しパラメータ」が存在することについて説明しましたが、「うそなき」は敵全員の隠しパラメータを下げる効果があります。低下する値は「自分の隠しパラメータ ÷ 2」です。
そして、味方キャラクタの隠しパラメータは、戦闘開始時に初期値として「初登場時のレベル」と同じ値が設定されています。ポロムの場合、初登場レベルは10なので、この値が隠しパラメータの初期値として使用されます。従って、「うそなき」使用時に敵の隠しパラメータは「10 ÷ 2 = 5」ポイント下がることになります。
以上のことから「うそなき」の効果をわかりやすくまとめると、「盗みに成功する確率を5%上げる(& 盗み失敗時にモンスターに見つかる確率を5%下げる)」ということになります。
ただし、「ぬすむ」を使用できるエッジが仲間になるのは冒険の中盤以降であるのに対して、「うそなき」を使用できるポロムは冒険序盤の間しかパーティに加入しません。ですから、「うそなき」に盗み成功率を上げる効果があってもまったく意味がないわけです。おそらく、この隠しパラメータには盗み処理以外にもなんらかの働きがあるのでしょうが、現在のところは不明です。また、「うそなき」以外の要因で隠しパラメータが変動することがあるのかもしれませんが、こちらについても現在のところ不明です。
それにしても、この隠しパラメータは意味不明な存在です。敵モンスターの初期値「レベル + 10」はいいとして、味方キャラクタの初期値が「初登場時のレベル」というのはどういう意図があるのでしょうか。どうもパラメータの意味するところが見えてきません。
ファイナルファンタジーVにおいて、通常のモンスター出現の際には最大4種類のモンスターパーティの中から出現するモンスターが決まります。決定方法は単純で、0〜255の範囲の乱数値を取得して、その値の範囲によってモンスターパーティの種類が決まります。
乱数値 | 確率 | モンスターパーティ |
---|---|---|
0〜89 | 90/256 | モンスターパーティ1 |
90〜179 | 90/256 | モンスターパーティ2 |
180〜239 | 60/256 | モンスターパーティ3 |
240〜255 | 16/256 | モンスターパーティ4 |
モンスターパーティ1〜4の実際の割り振りについては、ff_analyzerの『モンスター遭遇テーブルデータ(V)(ff5monenc.txt)』をご覧ください。
青魔法「マイティガード」やレアアイテム「りゅうのひげ」を入手できることで有名な「スティングレイ」ですが、このモンスターは非常に出現率が低いことで知られています。実際、スティングレイは最も出現率が低い「モンスターパーティ4」に割り振られています(ff5monenc.txtのNo.110参照)。しかし、最も出現率が低いとはいえ確率は16/256(= 6.25%)はあるわけで、せいぜい30回程度も戦闘を繰り返せばたいてい遭遇できます。スティングレイの場合、その出現範囲が非常に限られている(注1)ことが、「出現しにくい」という印象をより強めているのでしょう。
スティングレイが出現する場所は、第3世界の海域のごく一部分です。具体的には、『モンスター遭遇マップデータ(V)(ff5monmap.txt)』内で"110"とある場所です。位置的には、カーウェンの町の東にある湖と、沈んだウォルスの塔周辺の内海部分に相当します。
ファイナルファンタジーVIでは、通常モンスターの出現と固定モンスターの出現とを同じような処理として扱っています。また、獣ヶ原でのモンスター出現では独自のアルゴリズムが使われています。これらについて順に説明します。
通常モンスター出現の際には最大4種類のモンスターパーティの中から出現するモンスターが決まります。決定方法は単純で、0〜255の範囲の乱数値を取得して、その値の範囲によってモンスターパーティの種類が決まります。見てわかるように、ファイナルファンタジーVのアルゴリズムとほとんど同じです。
乱数値 | 確率 | モンスターパーティ |
---|---|---|
0〜79 | 80/256 | モンスターパーティ1 |
80〜159 | 80/256 | モンスターパーティ2 |
160〜239 | 80/256 | モンスターパーティ3 |
240〜255 | 16/256 | モンスターパーティ4 |
固定モンスター出現の際には最大2種類のモンスターパーティの中から出現するモンスターが決まります。「固定モンスター」なのに「最大2種類」の中から決まるというのも変ですが、たいていの場合は「2種類」の両方に同じモンスターパーティが設定されているために完全に「固定」されています。魔大陸上空の帝国空軍のように、固定モンスター出現なのに出現するモンスターの種類がランダムで変わるようなケースの場合は、「2種類」に別々のモンスターパーティが設定されていて「最大2種類」となっているわけです。
モンスターパーティの決定方法は通常モンスターの出現と同じです。0〜255の範囲の乱数値を取得して、その値の範囲によってモンスターパーティの種類が決まります。
乱数値 | 確率 | モンスターパーティ |
---|---|---|
0〜191 | 192/256 | モンスターパーティ1 |
192〜255 | 64/256 | モンスターパーティ2 |
通常モンスター出現時のモンスターパーティ1〜4、および固定モンスター出現時のモンスターパーティ1・2の実際の割り振りについては、ff_analyzerの『モンスター遭遇テーブルデータ(VI)(ff6monenc.txt)』をご覧ください。
「ぬすむ」が成功する確率について調べてみました。「ぶんどる」についてはまだ調べていません。「とうぞくのナイフ」で攻撃した際に発動する「ぶんどる」についても調べていません。
「ぬすむ」を実行してアイテムを入手できる確率は、「ぬすむ」を行うキャラクタのレベル(以下、「自分レベル」)と、「ぬすむ」の対象となるモンスターのレベル(以下、「相手レベル」)に影響されます。具体的な確率の計算式は「(自分レベル + 50 - 相手レベル) ÷ 99」となります。
このことから、自分レベルと相手レベルとの差が49以上になると、確率に関係なく「ぬすむ」の判定が成功(あるいは失敗)するということになります。例えば、「自分レベル - 相手レベル ≧ 49」という状態だと必ず成功します(注: 「ぬすむ」の判定が成功したからといって、必ずアイテムを入手できるわけではありません)。逆に、「相手レベル - 自分レベル ≧ 49」という状態だと必ず失敗します。
さて、ファイナルファンタジーVIには「とうぞくのうでわ」というアイテムが存在します。メニューでの説明には『「ぬすむ」の成功率がアップします』とあります。具体的にはどのような効果があるのでしょうか。
実際の効果を簡単に表現すると「盗める確率が2倍になる」といえます。前述した確率の計算式が「(自分レベル + 50 - 相手レベル)×2 ÷ 99」となります。注意すべき点は、「自分レベル + 50 - 相手レベル」の値が0(あるいはそれ以下)であるならば、確率が2倍になっても0は0なので意味はない、ということです。レベル差が49以上あるために盗めない場合は、「とうぞくのうでわ」を装備したところでやはり盗めないというわけです。
「ぬすむ」の判定に成功した後は、アイテムの選択処理が行われます。ファイナルファンタジーVIでは、盗めるアイテムには「低確率アイテム」「高確率アイテム」の2種類があります。判定処理は単純で、0〜255の範囲の乱数値を取得して、乱数値が32未満であれば「低確率アイテム」に、32以上であれば「高確率アイテム」になります。よって、「低確率アイテム」を入手できる確率は「32/256 = 1/8」となります。
実際にモンスターから入手できるアイテムについては、ff_analyzerの『モンスターの所持アイテムデータ(VI)(ff6monitem.txt)』をご覧ください。
なお、上記の『モンスターの所持アイテムデータ(VI)』によると、「低確率アイテムしか持っていないモンスター」や「高確率アイテムしか持っていないモンスター」も多数存在します。これらのモンスターに対して「持っていない方のアイテム」を盗んだ場合、「ぬすむ」に失敗したとみなされます。つまり、「低確率アイテムしか持っていないモンスター」から「高確率アイテム」を盗む場合や、「高確率アイテムしか持っていないモンスター」から「低確率アイテム」を盗む場合は、どちらも「ぬすむ」に失敗することになります。
幻獣「ラグナロック」を使用した際にモンスターをアイテム変化できる確率(難易度)は、各モンスターによって決まっていて、それぞれ0〜7の8段階に分かれています。難易度の値と成功確率の関係は、以下のとおりです。
難易度 | 確率 |
---|---|
0 | 256/256 |
1 | 192/256 |
2 | 128/256 |
3 | 64/256 |
4 | 32/256 |
5 | 16/256 |
6 | 8/256 |
7 | 0 |
各モンスターの難易度の値については、ff_analyzerの『モンスターのアイテム変化データ(VI)(ff6monragna.txt)』をご覧ください。
アイテム変化の判定に成功した後は、アイテムの選択処理が行われます。4種類のアイテムが選択される確率は、基本的にはそれぞれほぼ等確率です。
宝箱の入手確率(難易度)はモンスターごとに決まっていて、それぞれ0〜7の8段階に分かれています。公式ガイドブックではアイテムの落としやすさは★1〜3個で表されていますが、実際にはもっと細かく分かれているわけです。実際の難易度の値については、dq_analyzerの『モンスターの所持アイテムデータ(V)(dq5monitem.txt)』をご覧ください。
難易度の値と入手確率の関係は、以下のとおりです。
難易度 | 確率 |
---|---|
0 | 1/1 |
1 | 1/8 |
2 | 1/16 |
3 | 1/32 |
4 | 1/64 |
5 | 1/128 |
6 | 1/256 |
7 | 1/4096 |
なお、詳しく調べたわけではありませんが、複数のグループからなるモンスターを倒した場合、宝箱を入手することができないようです。
それにしても、難易度7の「確率1/4096」というのはあまりにも低すぎます。公式ガイドブックでは難易度5〜7の確率をまとめて「★1個」としていますが、同じ★1個でも難易度6と7ではまったく確率が違うので注意しましょう。
味方キャラクタの攻撃で「会心の一撃」が出る確率は、1/32です。基本的に、ドラゴンクエストVでは会心の一撃が出やすいキャラクタなどは存在せず、どのキャラクタでも確率は同じようです。
なお、ノンプレイヤーキャラクタが攻撃する場合、内部ではモンスターの行動処理と同レベルで処理されているようで、上の会心の一撃の確率は当てはまりません。例えば、パパスの場合は6種類の行動のうち2つが「会心/痛恨の一撃(の可能性がある)」となっているので、通常のキャラクタよりもやや高い確率で会心の一撃を出します。
また、鞭やブーメランといった、会心の一撃が出ないように設定されている武器で攻撃した場合は、会心の一撃は出ません。
何かと謎の多い「モンスターが仲間になる確率」について調べてみました。実のところ、まだ調査の途中なのでほとんど謎は解明できていないのですが、とりあえず基本的な確率については調査が完了しました。
なお、調査の過程で、仲間モンスターに関するとんでもない裏技が隠されていることがわかりました。
最初に、「モンスターを仲間にできる状況であるかどうか」の判定を行います。このあたりについてはまだ調査していませんが、主人公のレベルや現在の場所などが関係しているようです。後日調査する予定です。
さて、「モンスターを仲間にできる状況である」場合、乱数値を取得してその値に基づいて仲間になるかどうかを判定します。仲間になる確率ですが、モンスターごとに設定されている「仲間になりやすさ」と、「(馬車かモンスターじいさんのところにいる)同種のモンスターの数」によって決まります。
まず、「仲間になりやすさ」は文字通りモンスターが仲間になる確率を示しています。公式ガイドブックでは「仲間になりやすさ」を♥1〜3個で表していますが、実際には0〜7の8段階のランクに分かれています。実際に設定されている値については、dq_analyzerの『モンスターのその他のデータ(V)(dq5monmisc.txt)』をご覧ください。
そして、馬車かモンスターじいさんのところに同種のモンスターがすでにいる場合、2匹目・3匹目のモンスターが仲間になる確率は下がります。また、すでに同種のモンスターが3匹いる場合は、4匹目が仲間になることはありません。なお、モンスターじいさんのところで別れたモンスターはカウントされません。
これらの2つの要素をもとにして、最終的な確率は以下のように決まります。
ランク | 確率(1匹目) | 確率(2匹目) | 確率(3匹目) |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1/256 | 1/1024 | 1/1024 |
2 | 1/64 | 1/128 | 1/256 |
3 | 1/32 | 1/64 | 1/128 |
4 | 1/16 | 1/64 | 1/64 |
5 | 1/4 | 1/64 | 1/64 |
6 | 1/2 | 1/64 | 1/64 |
7 | 1/2 | 1/32 | 1/16 |
2匹目からは確率が大きく低下していることがわかります。しかし、ランク7のモンスターの場合、2匹目よりも3匹目の方が仲間になりやすいことになっています。このあたりは意図したものなのか不具合なのか、よくわからないところです。
なお、公式ガイドブックには「仲間にできるモンスターは、戦闘の一番最後に倒すと、仲間になりやすい」とありますが、この件についてはまだ未調査です。
さて、モンスターが仲間になるかどうかの判定処理には一つとんでもない処理が含まれています。それは、「主人公に特定のアイテムを特定の順番で持たせておくと、確率に関係なくモンスターが必ず仲間になる」というものです。
まずは、主人公に以下のアイテムを持たせてください。
「ひのきのぼう」を主人公に装備させてはいけません。主人公にはこれら以外のアイテムを持たせてはいけませんし、アイテムの順番も変えてはいけません。
この状態で戦闘をすると、戦闘終了後「モンスターを仲間にできる状況である」状態ならば、確率に関係なくモンスターが必ず仲間になります。ただし、もともとモンスターが仲間にならない状況の場合は効果はありません。例えば、主人公のレベルが低かったり、すでに同種のモンスターが3匹仲間にいる場合などです。
この処理は、おそらくはデバッグやテスト用に用意されたものだと思われます。なお、「モンスターが仲間になる確率」の処理以外でも使われている箇所があるようです。少し調べた限りでは、「会心の一撃が出る確率」の処理で、必ず会心の一撃が出るという現象を確認しました。
予想されたことですが、プレイステーション2でリリースされたリメイク版には、この裏技は残っていませんでした。
宝箱の入手確率(難易度)はモンスターごとに決まっていて、それぞれ0〜7の8段階に分かれています。公式ガイドブックではアイテムの落としやすさは★1〜3個で表されていますが、実際にはもっと細かく分かれているわけです。実際の難易度の値については、dq_analyzerの『モンスターの所持アイテムデータ(VI)(dq6monitem.txt)』をご覧ください。
難易度の値と入手確率の関係は、以下のとおりです。
難易度 | 確率 |
---|---|
0 | 1/1 |
1 | 1/8 |
2 | 1/16 |
3 | 1/32 |
4 | 1/64 |
5 | 1/128 |
6 | 1/256 |
7 | 1/4096 |
見ての通り、ドラゴンクエストVの『宝箱入手判定時の処理』とまったく同じです。根拠のない予想ですが、ドラゴンクエストVIIでもまったく同じ確率なのではないかと思っています(上の表のランク1〜7が、公式ガイドブックの入手確率A〜Gに対応する)。
何かと謎の多い「モンスターが仲間になる確率」について調べてみました。実のところ、解析しきれなかった部分が多いのでまだまだ謎は解明できていないのですが、それについては今後調査していく予定です。
最初に、「モンスターを仲間にできる状況であるかどうか」の判定を行います。このあたりについては未調査の部分が多いのですが、とりあえずは以下の条件を満たしている必要があります。
「熟練度の高低の判定」と「場所の判定」については、まだ解析できていません。ただ、「熟練度が高いと、仲間になる確率も高くなる」といったことはなさそうです。「熟練度の高低」は「(倒したモンスターを)仲間にできるか/できないか」だけに影響があるようです。
さて、「モンスターを仲間にできる状況である」場合、乱数値を取得してその値に基づいて仲間になるかどうかを判定します。仲間になる確率ですが、モンスターごとに設定されている「仲間になりやすさ」と、「(馬車かルイーダの酒場にいる)同種のモンスターの数」によって決まります。
まず、「仲間になりやすさ」は文字通りモンスターが仲間になる確率を示しています。公式ガイドブックでは「仲間になりやすさ」を★1〜3個で表していますが、実際には0〜7の8段階のランクに分かれています。実際に設定されている値については、dq_analyzerの『モンスターのその他のデータ(VI)(dq6monmisc.txt)』をご覧ください。
そして、馬車かルイーダの酒場に同種のモンスターがすでにいる場合、2匹目・3匹目のモンスターが仲間になる確率は下がります。また、すでに同種のモンスターが3匹いる場合は、4匹目が仲間になることはありません。なお、ルイーダの酒場で別れたモンスターはカウントされません。
これらの2つの要素をもとにして、最終的な確率は以下のように決まります。
ランク | 確率(1匹目) | 確率(2匹目) | 確率(3匹目) |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1/256 | 0 | 0 |
2 | 1/256 | 1/1024 | 1/1024 |
3 | 1/64 | 1/1024 | 1/1024 |
4 | 1/32 | 1/256 | 1/256 |
5 | 1/16 | 1/256 | 1/256 |
6 | 1/4 | 1/128 | 1/256 |
7 | 1/2 | 1/32 | 1/128 |
2匹目からは確率が大きく低下していることがわかります。また、ランク1のモンスターは2匹目が仲間になりません。「ランプのまおう」が1匹しか仲間にならないのは、これが原因です。
なお、公式ガイドブックには「仲間になるモンスターが2種類以上登場したときは、あとに倒す方が仲間になりやすい」とありますが、この件についてはまだ未調査です。
ドラゴンクエストVには「モンスターが必ず仲間になる裏技」が存在しますが、ドラゴンクエストVIにもそういったものはあるのでしょうか?
実は、モンスターが仲間になるかどうかの判定処理の中には「ある条件を満たすと、(乱数による判定処理をスキップして)必ず仲間になる」という処理が存在しています。しかし、その「ある条件」には「ROM内の特定のアドレスをリードして、特定のビットがONになっている(注1)」という条件が含まれています。そして、その「ROM内の特定のアドレス」には、出荷バージョンでは値"00"が設定されています。ですから、この条件は常に成立することはありません(注2)。おそらくは、デバッグ・テスト用途のために、開発者向けに用意されていたものなのでしょう。
どうにかして、実機でのプレイで「モンスターが必ず仲間になる処理」を有効に活用する方法はないものでしょうか。一応、まったく方法がないわけではありません。かなりの荒技ですが、カートリッジ端子の「接触不良」を利用するという方法があります。接触不良などでカートリッジ端子が接続されていない状態では、信号が強制的にON状態になります。これをうまく利用すれば、「ROM内の特定のアドレスをリード」する際に、読み込むアドレスや読み込む値が本来のものとは異なる値になるかもしれません。その結果、「モンスターが必ず仲間になる条件」を満たせる可能性があります。
しかしながら、このような方法は当然ながら危険です。接触不良を起こすための方法としては、「カートリッジに衝撃を与える」とか「カートリッジの斜め挿し」といった方法がありますが、どちらにせよ非常に危険な方法です。フリーズする程度ならまだしも、セーブデータに悪影響を与える可能性もあります。
実際に何度か試してみたところ(注3)、「成功」したことは一度もなく、たいていの場合にフリーズを引き起こしました。また、フリーズ後に冒険の書が消えることも何度かありました。やはり、この方法を実用化するのは無理なようです。
正確な条件は、以下の2つの条件をともに満たす場合です。
いわゆる「改造コード」を使えば条件を満たすことができるかもしれません(ROM領域の読み込み結果を改変できればの話ですが)。具体的には以下のようになります。
C1FFFF 80 (ROM領域) 7E3D00 11 ("10"だとモンスターが出現しない)
ただし、$C1FFFE〜$C1FFFFの値が"00"以外の値である場合、オープニングから先に進みません。出荷バージョンでこの処理が有効になることがないように、プロテクトをかけているようです。
仲間モンスター判定処理前後のタイミング(「○○をやっつけた!」表示)の前後で、カートリッジを傾けたり、圧力をかけてみたりしました。
宝箱の入手確率(難易度)はモンスターごとに決まっていて、それぞれ0〜3の4段階に分かれています。詳しくは、dq_analyzerの『モンスターの所持アイテムデータ(II)(dq2monitem.txt)』をご覧ください。
難易度の値と入手確率の関係は、以下のとおりです。
難易度 | 確率 |
---|---|
0 | 1/8 |
1 | 1/16 |
2 | 1/32 |
3 | 1/128 |
宝箱の入手確率(難易度)はモンスターごとに決まっていて、それぞれ0〜7の8段階に分かれています。公式ガイドブックではアイテムの落としやすさは★1〜3個で表されていますが、実際にはもっと細かく分かれているわけです。実際の難易度の値については、dq_analyzerの『モンスターの所持アイテムデータ(III)(dq3monitem.txt)』をご覧ください。
難易度の値と入手確率の関係は、以下のとおりです。
難易度 | 確率 |
---|---|
0 | 1/1 |
1 | 1/8 |
2 | 1/16 |
3 | 1/32 |
4 | 1/64 |
5 | 1/128 |
6 | 1/256 |
7 | 1/1024 |
攻撃時に技を閃く確率について調べました。カウンター技や見切り技については未調査です。
ロマンシング サ・ガ2において、攻撃時の技の閃きはすべて「ある技を使用した際に、ある技を閃く」というような「派生」の関係のもとにあります。実際の派生関係については、rs_analyzerの『技修得の各種データ(2)(rs2techlearndata.txt)』をご覧ください。
このリストの見方ですが、例えば、「通常攻撃: 剣(No.001)」で攻撃した場合、「なぎ払い(No.015)」「二段斬り(No.017)」「短冊斬り(No.018)」……「カマイタチ(No.036)」「稲妻斬り(No.037)」の全19種類のどれかを閃く可能性があることを示しています。同様に、「なぎ払い(No.015)」で攻撃した場合、「短冊斬り(No.018)」「十文字斬り(No.022)」「つむじ風(No.023)」「真空斬り(No.026)」の4種類のどれかを閃く可能性があるわけです。一般には後者の閃きを「派生」と呼ぶことが多いようですが、内部的には両者は同じ扱いになっています。
さて、実際の閃き処理について説明します。先ほどの「通常攻撃: 剣(No.001)」で攻撃した場合を例に挙げて説明すると、大まかな処理としては「なぎ払い(No.015)」から「稲妻斬り(No.037)」の順に閃くかどうかの判定を一つずつ行い、最後まで閃かなかった場合には剣での通常攻撃を行い、途中で閃いた場合はその閃いた技を使用する(閃き処理はその時点で打ち切り)、という流れになります。
個々の技の閃き処理ですが、いくつかの条件を満たしていないと、乱数による閃き判定を行う前に無条件で「閃き失敗」とみなされます。以下にその条件について説明します。
まず、技を閃こうとするキャラクタが混乱・魅了状態になっている場合は、技を閃かないようです。その他にも、技を閃かなくなる特定の状態が存在するようです。「特定の状態」が具体的に何であるかはわかっていません。
また、当然ですが、技欄に空きがない場合も閃きません。閃こうとする技をすでに修得している場合も閃きません。
そして、キャラクタごとの「閃きタイプ」をもとにして、これから閃こうとする技について、閃くことが可能であるかどうかをチェックします。各キャラクタには閃きタイプ16種類のうちのどれかが設定されています。そして、閃きタイプごとに、すべての技について閃き可能かどうかが設定されています。実際にキャラクタに設定されている「閃きタイプ」については『一般キャラクタのフラグ系データ(2)(rs2chrflag.txt)』『特殊キャラクタのフラグ系データ(2)(rs2chrspflag.txt)』を、閃きタイプごとの閃き可能・不可能については『技修得のフラグ系データ(2)(rs2techlearnflag.txt)』をご覧ください。この判定で、これから閃こうとする技が「閃き不可能」だった場合は閃きません。
それから、閃こうとする技が特定の技である場合は、特殊な条件を満たしていない場合は閃きません。「特定の技」に該当するのは「不動剣(No.028)」「活人剣(No.053)」「エイミング(No.099)」の3つです。「特殊な条件」が具体的に何であるかはわかっていません。
それとは逆に、閃こうとする技が特定の技の場合は、ある条件を満たしていると、乱数による判定を行わずに技を閃きます。具体的には、「でたらめ矢(No.134)」は技を閃こうとするキャラクタが「暗い」状態になっていると、無条件で閃きます。
これらの条件に当てはまらなかった場合、乱数による閃き判定に進みます。
閃き確率の算出には、「攻撃対象の武器レベルの平均値」と「技の閃き難易度」の値が使われます。まず、「攻撃対象の武器レベルの平均値」ですが、攻撃対象がモンスターの場合は5種類の武器レベルがすべて同じ値なので、単に「武器レベル」の値をそのまま使用します。実際の「武器レベル」の値については『モンスターの数値系データ(2)(rs2monparam.txt)』をご覧ください。
「技の閃き難易度」は、先ほどの『技修得の各種データ(2)(rs2techlearndata.txt)』に載っている値で、文字通り難易度が高いほど技を閃きにくくなります。注意すべき点として、同じ技でも「派生元」の技の種類によって難易度が異なることです。例えば、「ツバメ返し(No.046)」を閃こうとする場合、「通常攻撃: 大剣(No.002)」から閃こうとすると難易度は"40"となりますが、「切り落とし(No.045)」からだと難易度は"37"に、「強撃(No.043)」からだと難易度は"34"になります。
さて、実際の閃き確率の算出処理ですが、まずは「攻撃対象の武器レベルの平均値 - 技の閃き難易度」の値を求めます。そして、その値から以下の表をもとにして「閃き判定の乱数の境界値(以下、「境界値」)」を求めます。
差分値 | 乱数の境界値 |
---|---|
-6以下 | 0 |
-5 | 1 |
-4 | 3 |
-3 | 5 |
-2 | 8 |
-1 | 11 |
0 | 50 |
1 | 18 |
2 | 21 |
3 | 24 |
4 | 26 |
5 | 28 |
6 | 29 |
7以上 | 30 |
ここで求めた「境界値」が大きいほど、技の閃きに成功しやすくなります。上の表を見るとわかりますが、「攻撃対象の武器レベルの平均値」と「技の閃き難易度」が一致した場合が、最も技を閃きやすくなるようです。
その後、乱数ルーチンから1〜255の範囲の乱数値を取得して、算出した境界値と比較して閃き判定を行います。具体的には、乱数値が境界値以下であれば、閃きに成功したことになります。よって、閃き成功確率は(およそ)「境界値 / 255」となります。
ところが、ロマンシング サ・ガ2の乱数ルーチンは得られる乱数値に偏りがあり、単純に「境界値 / 255」で計算すると微妙に誤差が生じます。例えば、境界値が"30"の場合だと、1〜255の範囲の乱数値で30以下が出れば閃きに成功することになるので、閃き成功確率は30/255(≒ 11.76%)であるように見えます。しかし、実際には乱数ルーチンが30以下を出す確率は25/255なので、正確な閃き成功確率も25/255(≒ 9.80%)となります。この乱数ルーチンの偏りを考慮した、より正確な閃き成功確率は以下のようになります。
境界値 | 成功確率 |
---|---|
0 | 0 |
1 | 1/255 |
3 | 2/255 |
5 | 4/255 |
8 | 6/255 |
11 | 13/255 |
18 | 20/255 |
21 | 22/255 |
24 | 22/255 |
26 | 24/255 |
28 | 24/255 |
29 | 25/255 |
30 | 25/255 |
50 | 52/255 |
ここで、「覚えられない技」として有名な技である「トリプルヒット」と「ライフスティール」について見てみます。まず、「トリプルヒット」は『技修得の各種データ(2)(rs2techlearndata.txt)』のリスト中に存在していません。よって、「トリプルヒット」は(棍棒の通常攻撃からも「ダブルヒット」からも)閃くことはありません。
「ライフスティール」の方はリスト中に存在していて、小剣の通常攻撃から閃くことになっています。しかし、「閃き難易度」が"80"という極端に高い値になっています。モンスターの中で最も武器レベルが高いのはアルビオンの"43"ですから、「ライフスティール」を閃くのは事実上不可能であるということになります。
ですが、閃き難易度と武器レベルがとの差が大きすぎることが問題であるならば、「極端に武器レベルが高い味方キャラクタ相手に攻撃する」という方法をとれば可能性があるようにも見えます。すなわち、いわゆる「石化皇帝バグ」などによって武器レベルが50を超えたキャラクタを用意して、そのキャラクタに攻撃することで技の閃きをねらうというわけです。味方を攻撃する場合、前述したように、キャラクタが混乱・魅了状態になっている場合は技を閃くことはありませんが、敵がマリオネットを使ったことで攻撃目標が味方に変更された場合は通常どおり技を閃く判定が行われるようです。この方法ならば「ライフスティール」を閃くことができるのでしょうか?
しかし、この方法でも「ライフスティール」を閃くのは不可能だと思います。なぜなら、武器レベルの平均値を求める処理が8ビットで処理されているからです。平均値を求める処理では、まず5種類の武器レベルを合計して、その合計値を5で割るという処理を行っています。この「5種類の武器レベルの合計値」が8ビットで扱われているため、武器レベルが50を超えたキャラクタの場合、合計値がオーバーフローしてしまいます。結局、「武器レベルの平均値」はベストケースでも最大で51(= 255÷5)にしかならないため、「ライフスティール」を閃くのはやはり不可能でしょう。
戦闘終了時に入手できるアイテムは、内部では「高確率で入手できるアイテム」「低確率で入手できるアイテム」の2種類が設定されています。詳しくはrs_analyzerの『モンスターの所持アイテムデータ(2)(rs2monitem.txt)』をご覧ください
2種類のアイテムを入手できる確率は、それぞれ約1/32(高確率)・約1/64(低確率)となっています。内部的な処理では、例えば「1/32」の確率計算の場合、「0〜31までの乱数値を取得して、値が0であれば判定成功」といった処理になっています。しかし、ロマンシング サ・ガ2の乱数ルーチンは得られる乱数値に偏りがあるため、この場合は正確に「1/32」の確率とはいえず、わずかに誤差があります。乱数ルーチンの挙動を考慮した上でのより正確な確率は、以下のようになります(注1)。
アイテム | 確率 |
---|---|
高確率 | 6/255 |
低確率 | 3/255 |
実際の判定処理の流れですが、まずは高確率のアイテムについて、倒したモンスターの数だけ入手判定を行います。その後、低確率のアイテムについて、やはり倒したモンスターの数だけ入手判定を行います。そして、アイテムの入手判定に成功した時点で判定処理を打ち切って、アイテムを入手することになります。
さらに正確な確率を求めるためには、アイテム入手判定を行う順番をも考慮しないといけません。アイテム入手判定には「高確率アイテムが先に判定される」「入手判定に成功した時点で入手判定処理を打ち切る」という2つの性質があるため、低確率アイテムを入手できる確率が低くなっている可能性があります。
攻撃時に技を閃く確率と見切り技を閃く確率について調べました。カウンター技については未調査です。
ロマンシング サ・ガ3において、攻撃時の技の閃きはすべて「ある技を使用した際に、ある技を閃く」というような「派生」の関係のもとにあります。実際の派生関係については、rs_analyzerの『技修得の各種データ(3)(rs3techlearndata.txt)』をご覧ください。
このリストの見方ですが、例えば、「剣(No.001)」で攻撃した場合、「なぎ払い(No.016)」「かすみ二段(No.017)」「失礼剣(No.018)」……「スケアーボイス(No.036)」「スターバースト(No.037)」の全19種類のどれかを閃く可能性があることを示しています。同様に、「なぎ払い(No.016)」で攻撃した場合、「十文字斬り(No.019)」「飛水断ち(No.020)」「龍尾返し(No.022)」「剣閃(No.028)」の4種類のどれかを閃く可能性があるわけです。一般には後者の閃きを「派生」と呼ぶことが多いようですが、内部的には両者は同じ扱いになっています。
(このあたりの基本的な原理は、ロマンシング サ・ガ2のものとほとんど変わりありません)
さて、実際の閃き処理について説明します。先ほどの「剣(No.001)」で攻撃した場合を例に挙げて説明すると、大まかな処理としては「なぎ払い(No.016)」から「スターバースト(No.037)」の順に閃くかどうかの判定を一つずつ行い、最後まで閃かなかった場合には剣での通常攻撃を行い、途中で閃いた場合はその閃いた技を使用する(閃き処理はその時点で打ち切り)、という流れになります。
個々の技の閃き処理ですが、いくつかの条件を満たしていないと、乱数による閃き判定を行う前に無条件で「閃き失敗」とみなされます。以下にその条件について説明します。
まず、閃こうとする技をすでに修得している場合は、当然ですが閃きません。また、武器固有技を閃こうとする場合はその武器で攻撃していないと閃きません。武器固有技でない場合は、技欄に空きがない場合も閃きません。
もう一つ、1戦闘中に閃くことのできる技の数は最大8つまでとなっています。これは、1人のキャラクタが8つということではなく、パーティ全員で8つです。この制限に触れた場合も閃きません。なお、見切り技の閃きは閃き回数に含まれません。カウンター技が閃き回数に含まれるかどうかはわかっていません。
他にも条件は存在すると思われますが、現時点では不明です。これらの条件をクリアした場合、乱数による閃き判定に進みます。
ここで、キャラクタごとの「閃きタイプ」をもとにして、これから閃こうとする技について、閃き適性があるかどうかをチェックします。閃き適性の有無によって、以後の閃き確率の計算が大きく異なります。
まず、各キャラクタには閃きタイプ11種類のうちのどれかが設定されています。そして、閃きタイプごとに、すべての技について閃き適性の有無が設定されています。実際にキャラクタに設定されている「閃きタイプ」については『キャラクタのフラグ系データ(3)(rs3chrflag.txt)』を、閃きタイプごとの閃き適性については『技修得のフラグ系データ(3)(rs3techlearnflag.txt)』をご覧ください。
この後、乱数による閃き判定に進むわけですが、閃き適性の有無によって閃き処理が分岐します。以下、2つの場合について順に説明します。
閃き確率の算出には、主に「モンスターの閃きレベル」と「技の閃き難易度」の値が使われます。まず、「モンスターの閃きレベル」ですが、これは攻撃対象のモンスターに設定されている「閃きレベル」のことを示していて、この値が高いほど技を閃きやすくなります。実際の値については『モンスターの数値系データ(3)(rs3monparam.txt)』をご覧ください。
「技の閃き難易度」は、先ほどの『技修得の各種データ(3)(rs3techlearndata.txt)』に載っている値で、文字通り難易度が高いほど技を閃きにくくなります。注意すべき点として、同じ技でも「派生元」の技の種類によって難易度が異なることです。例えば、「残像剣」を閃こうとする場合、「剣(No.001)」から閃こうとすると難易度は"28"となりますが、「かすみ二段(No.017)」からだと難易度は"23"に、「分身剣(No.027)」からだと難易度は"15"になります。
さて、実際の閃き確率の算出処理ですが、まずは「モンスターの閃きレベル - 技の閃き難易度」の値を求めます。そして、その値から以下の表をもとにして「閃き判定の乱数の境界値(以下、「境界値」)」を求めます。2つのテーブルがありますが、キャラクタが「技の王冠がついている or 最大術ポイントが0である」か「技の王冠がついていない & 最大術ポイントが1以上である」かによって、どちらかのテーブルを選択します(注1)。
差分値 | 王冠有 or 最大JP0 | 王冠無 & 最大JP1以上 |
---|---|---|
-10以下 | 0 | 0 |
-9 | 1 | 0 |
-8 | 2 | 0 |
-7 | 4 | 1 |
-6 | 6 | 3 |
-5 | 9 | 5 |
-4 | 12 | 8 |
-3 | 15 | 10 |
-2 | 18 | 13 |
-1 | 21 | 16 |
0 | 25 | 20 |
1 | 28 | 23 |
2 | 31 | 26 |
3 | 34 | 29 |
4 | 37 | 31 |
5 | 39 | 34 |
6 | 42 | 36 |
7 | 44 | 37 |
8 | 46 | 39 |
9 | 47 | 39 |
10以上 | 50 | 40 |
ここで求めた「境界値」が大きいほど、技の閃きに成功しやすくなります。なお、上の表を見るとわかりますが、「技の王冠がついていない & 最大術ポイントが1以上である」場合は境界値が低くなっています。つまり、技を閃きやすくするには術を極力修得しない方がよいということがいえます。
その後、「1戦闘中の閃き回数」によって境界値に対してマイナス修正がかかります。例えば、同じ戦闘中に他の技を1回閃いている場合、境界値の値が本来の値の7/8となります(端数切り捨て)。以降、閃き回数が2回・3回・4回……と増えるに従って、境界値の値も6/8・5/8・4/8……と減少していきます。なお、「1戦闘中の閃き回数」に見切り技の閃きは含まれません。カウンター技が閃き回数に含まれるかどうかはわかっていません。
最後に、ここで乱数ルーチンから1〜255の範囲の乱数値を取得して、ここまでに算出した最終的な境界値と比較して閃き判定を行います。具体的には、乱数値が境界値未満であれば、閃きに成功したことになります(この時点で境界値が1以下になっていた場合は、判定には必ず失敗することになります)。よって、閃き成功確率は(およそ)「(境界値 - 1) / 255」となります。
ところが、ロマンシング サ・ガ3の乱数ルーチンは得られる乱数値に偏りがあり、単純に「(境界値 - 1) / 255」で計算すると微妙に誤差が生じます。例えば、最終的な境界値が"11"の場合だと、1〜255の範囲の乱数値で10以下が出れば閃きに成功することになるので、閃き成功確率は10/255(≒ 3.92%)であるように見えます。しかし、実際には乱数ルーチンが10以下を出す確率は33/1024なので、正確な閃き成功確率も33/1024(≒ 3.22%)となります。この乱数ルーチンの偏りを考慮した、より正確な閃き成功確率は以下のようになります。
境界値 | 成功確率 |
---|---|
0 | 0 |
1 | 0 |
2 | 9/1024 |
3 | 11/1024 |
4 | 14/1024 |
5 | 18/1024 |
6 | 21/1024 |
7 | 22/1024 |
8 | 24/1024 |
9 | 27/1024 |
10 | 30/1024 |
11 | 33/1024 |
12 | 37/1024 |
13 | 44/1024 |
14 | 50/1024 |
15 | 54/1024 |
16 | 60/1024 |
17 | 64/1024 |
18 | 73/1024 |
19 | 76/1024 |
20 | 82/1024 |
21 | 85/1024 |
22 | 91/1024 |
23 | 97/1024 |
24 | 97/1024 |
25 | 104/1024 |
26 | 108/1024 |
27 | 113/1024 |
28 | 117/1024 |
29 | 123/1024 |
30 | 128/1024 |
31 | 130/1024 |
32 | 132/1024 |
33 | 136/1024 |
34 | 141/1024 |
35 | 144/1024 |
36 | 148/1024 |
37 | 150/1024 |
38 | 161/1024 |
39 | 164/1024 |
40 | 168/1024 |
41 | 170/1024 |
42 | 182/1024 |
43 | 187/1024 |
44 | 188/1024 |
45 | 195/1024 |
46 | 198/1024 |
47 | 202/1024 |
48 | 205/1024 |
49 | 211/1024 |
50 | 214/1024 |
閃き適性がない場合、閃き処理は単純化されたものとなります。まず、閃き適性がある場合の処理と同様に、「モンスターの閃きレベル - 技の閃き難易度」の値をもとに、表から「境界値」を求めます。このとき、王冠や最大術ポイントに関係なく「技の王冠がついている or 最大術ポイントが0である」方のテーブルを強制的に使用します。
ここで求めた「境界値」が1以上の場合、乱数ルーチンから0〜254の乱数値を取得して、"0"が出れば閃きに成功します。それ以外の場合はすべて閃き失敗となります。乱数ルーチンが"0"を出す確率は9/1024なので、この場合の閃き成功確率は9/1024(≒ 0.88%)で固定となります。このように、閃き適性がない場合は、「境界値」がいくら大きくても閃き成功確率は非常に低い値で一定となります。
ただし、閃き適性がある場合だと境界値が2以上ないと閃きに成功しないのに対して、閃き適性がない場合だと境界値が1以上で閃き成功の可能性があります。そして、境界値を求める際に、常に「技の王冠がついている or 最大術ポイントが0である」場合のテーブルを使用することができるので、閃き適性がない場合の方が有利なこともあります。
見切り技の閃きにも、通常の技と同様に「モンスターの閃きレベル」と「見切り技の閃き難易度」の値が関係します。「見切り技の閃き難易度」の値については、『見切り技の各種データ(3)(rs3techevadedata.txt)』をご覧ください。
見切り技の閃き確率の算出は、基本的には前述の「閃き確率(閃き適性がある技の場合)」と同じです。ただし、技の王冠や最大術ポイントなどに関係なく、常に「技の王冠がついている or 最大術ポイントが0である」場合のテーブルを使用することになります。
さて、見切れない技として有名な「ゴブリンの脳天割り」について見てみます。「ゴブリンの閃きレベル」が"3"であるのに対して、「脳天割りの見切り閃き難易度」は"12"となっています。そうすると、「モンスターの閃きレベル - 技の閃き難易度」は"-9"となり、「境界値」は"1"となります。しかし、「境界値」が"1"の場合の閃き成功確率は0%であるため、やはり「ゴブリンから脳天割りの見切りを閃くのは不可能である」ということになります。
ちなみに、技の王冠がつく条件は「(最大技ポイント + 5) ÷ (最大術ポイント + 5) ≧ 10」の式が成り立っている場合です。
ロマンシング サ・ガ3では、キャラクタの成長システムに「経験値」のような概念がなく、戦闘終了後に「確率」によって各パラメータが成長するかどうかが決まります。ここでは、このキャラクタ成長システムを解析した結果について述べます。
最初に、各パラメータが成長する確率を計算します。パラメータが成長する確率の決まり方は『技閃き判定時の処理』での技閃き確率の決まり方と似ています。具体的には、まず「成長しやすさ」を決める値と「成長難易度」を決める値があり、この両者の差分値を求めます。そして、その値から以下の表をもとにして「成長判定の乱数の境界値(以下、「境界値」)」を求めます。ここで求めた「境界値」が大きいほど、パラメータが成長しやすくなります。
差分値 | 乱数の境界値 |
---|---|
-10以下 | 0 |
-9 | 0 |
-8 | 1 |
-7 | 1 |
-6 | 1 |
-5 | 1 |
-4 | 2 |
-3 | 3 |
-2 | 5 |
-1 | 8 |
0 | 16 |
1 | 32 |
2 | 36 |
3 | 40 |
4 | 50 |
5 | 60 |
6 | 60 |
7 | 70 |
8 | 80 |
9 | 90 |
10以上 | 100 |
ここで、この「成長しやすさ」と「成長難易度」の値について説明する前に、まずはこれらの値を算出するのに大きく関わりのあるデータについて説明しておきます。
「モンスターの技能上昇レベル」は、モンスターごとに設定されている数値で、この値が高いほど戦闘終了時にパラメータが上昇しやすくなります。基本的には「成長しやすさ」のベースとなる値です。実際の値については『モンスターの数値系データ(3)(rs3monparam.txt)』をご覧ください。
各キャラクタには、各種パラメータの「成長特性」が設定されています。この値も「成長しやすさ」に関わりのある値です。実際の値については『キャラクタの成長特性データ(3)(rs3chrlvup.txt)』をご覧ください。
なお、「成長特性」が"255"(FFh)の場合、そのパラメータは(基本的には)成長しないことを意味します。
キャラクタごとに、どのパラメータも成長しなかった戦闘の回数を記録するカウンタ(以下、「戦闘回数カウンタ」)が存在していて、戦闘終了時にパラメータの成長がなかった場合は、このカウンタが1増加します。この値は、武器/術レベルの「成長しやすさ」に影響します。
なお、どのキャラクタもパラメータが成長しなかった場合、パーティで最大ヒットポイントが最も低いキャラクタの最大ヒットポイントが1上昇しますが(後述)、ここでの成長によって「戦闘回数カウンタ」が0になることはありません。
さて、この「成長しやすさ」と「成長難易度」の値の算出方法について説明します。値の算出方法はパラメータごとに異なりますが、大きく分けて「最大ヒットポイント」「最大技ポイント/最大術ポイント」「各種武器レベル/術レベル」の3種類に分類されます。以下、順に説明します。
最大ヒットポイントの「成長しやすさ」の値は、「リーダーモンスターの技能上昇レベル」と「(キャラクタの)最大ヒットポイントの成長特性」との合計値です。「成長難易度」の値は、「現在の最大ヒットポイント×5÷128」となります。そして、前述の説明通り、「成長しやすさ」と「成長難易度」との差分値から「境界値」を決定します。
まずは、戦闘で技/術を使用したかどうかを判定します。剣/大剣・斧/棍棒・槍/小剣・弓・体術のどれかに属する技を使用していない場合、最大技ポイントが上昇する確率(境界値)は無条件で0となります。同様に、蒼竜・朱鳥・白虎・玄武・太陽・月のどれかに属する術を使用していない場合、最大術ポイントが上昇する確率(境界値)は無条件で0となります。
最大技(術)ポイントの「成長しやすさ」の値は、「最高値の武器(術)レベル×3 + その他の武器(術)レベル」です。「成長難易度」の値は、「現在の最大技(術)ポイント」です。そして、「成長しやすさ」と「成長難易度」との差分値から「境界値」を決定します。なお、前述の「成長特性」には、最大技(術)ポイントについての「成長特性」も設定されていますが、実際には使用されていないようです。
武器/術レベルの場合も、まずは該当する系統の技/術を戦闘で使用したかどうかを判定します。例えば、剣/大剣レベルの判定では、剣/大剣に属する技を使用していない場合、剣/大剣レベルが上昇する確率(境界値)は無条件で0となります。他の武器/術レベルについても同様の判定を行います。
武器/術レベルの「成長しやすさ」の値は、「リーダーモンスターの技能上昇レベル」と「(キャラクタの)武器/術レベルの成長特性」と「戦闘回数カウンタ×20÷256」との合計値です。「成長難易度」の値は、「現在の武器/術レベル」です。そして、「成長しやすさ」と「成長難易度」との差分値から「境界値」を決定します。
武器/術レベルが高くなると(40を超えるあたりから)、何度も戦闘回数を重ねることでようやく武器/術レベルが上がるようになりますが、これは「戦闘回数カウンタ」による補正の効果です。
次に、ここまでの過程で算出した「各パラメータが成長する確率(境界値)」に基づいて、各パラメータの成長判定処理を行います。
さて、成長判定を行うパラメータは全部で14種類ありますが、どのような順番で成長判定を行うかについては、キャラクタごとに決まっています。成長判定の順番は、『キャラクタの成長特性データ(3)(rs3chrlvup.txt)』の「成長タイプ」によって決まります。「成長タイプ」と「成長判定の順番」との関係は以下のとおりです。なお、なぜ「成長判定の順番」に意味があるのかについては後述します。
順番 | タイプ0 | タイプ1 | タイプ2 | タイプ3 | タイプ4 | タイプ5 | タイプ6 | タイプ7 |
---|---|---|---|---|---|---|---|---|
1 | 剣/大剣 | 最大HP | 太陽 | 最大HP | 弓 | 最大JP | なし | 最大WP |
2 | 最大HP | 槍/小剣 | 月 | 体術 | 最大WP | 蒼龍 | 最大HP | 最大JP |
3 | 最大WP | 剣/大剣 | 最大HP | 最大WP | 最大JP | 朱鳥 | 最大WP | 剣/大剣 |
4 | 槍/小剣 | 最大WP | 槍/小剣 | 斧/棍棒 | 最大HP | 白虎 | 槍/小剣 | 最大HP |
5 | 斧/棍棒 | 弓 | なし | 槍/小剣 | 槍/小剣 | 玄武 | 剣/大剣 | 蒼龍 |
6 | 最大JP | 太陽 | 最大JP | 最大JP | 太陽 | 弓 | 最大JP | 朱鳥 |
7 | 弓 | 月 | 最大WP | 剣/大剣 | 月 | 最大HP | 弓 | 白虎 |
8 | 体術 | 斧/棍棒 | 剣/大剣 | 弓 | 蒼龍 | 太陽 | 太陽 | 玄武 |
9 | 太陽 | 体術 | 蒼龍 | 蒼龍 | 朱鳥 | 月 | 月 | 弓 |
10 | 月 | 最大JP | 朱鳥 | 朱鳥 | 白虎 | 最大WP | 斧/棍棒 | 太陽 |
11 | 蒼龍 | 蒼龍 | 白虎 | 白虎 | 玄武 | 槍/小剣 | 体術 | 月 |
12 | 朱鳥 | 朱鳥 | 玄武 | 玄武 | 剣/大剣 | なし | 蒼龍 | 槍/小剣 |
13 | 白虎 | 白虎 | 弓 | 太陽 | なし | 剣/大剣 | 朱鳥 | 斧/棍棒 |
14 | 玄武 | 玄武 | 体術 | 月 | 体術 | 斧/棍棒 | 白虎 | 体術 |
15 | なし | なし | 斧/棍棒 | なし | 斧/棍棒 | 体術 | 玄武 | なし |
16 | なし | なし | なし | なし | なし | なし | なし | なし |
後は、この順番にしたがって成長判定を行っていきます。判定処理は各パラメータ共通で、乱数ルーチンから1〜255の範囲の乱数値を取得して、パラメータごとの境界値と比較して成長判定を行います。具体的には、乱数値が境界値未満であれば、パラメータが成長することになります。成長確率は(およそ)「(境界値 - 1) / 255」ということになります。しかし、ロマンシング サ・ガ3の乱数ルーチンは得られる乱数値に偏りがあるため、わずかに誤差があります。乱数ルーチンの挙動を考慮した上でのより正確な確率は、以下のようになります。
境界値 | 成長確率 |
---|---|
0 | 0 |
1 | 0 |
2 | 9/1024 |
3 | 11/1024 |
4 | 14/1024 |
5 | 18/1024 |
6 | 21/1024 |
7 | 22/1024 |
8 | 24/1024 |
9 | 27/1024 |
10 | 30/1024 |
11 | 33/1024 |
12 | 37/1024 |
13 | 44/1024 |
14 | 50/1024 |
15 | 54/1024 |
16 | 60/1024 |
17 | 64/1024 |
18 | 73/1024 |
19 | 76/1024 |
20 | 82/1024 |
21 | 85/1024 |
22 | 91/1024 |
23 | 97/1024 |
24 | 97/1024 |
25 | 104/1024 |
26 | 108/1024 |
27 | 113/1024 |
28 | 117/1024 |
29 | 123/1024 |
30 | 128/1024 |
31 | 130/1024 |
32 | 132/1024 |
33 | 136/1024 |
34 | 141/1024 |
35 | 144/1024 |
36 | 148/1024 |
37 | 150/1024 |
38 | 161/1024 |
39 | 164/1024 |
40 | 168/1024 |
41 | 170/1024 |
42 | 182/1024 |
43 | 187/1024 |
44 | 188/1024 |
45 | 195/1024 |
46 | 198/1024 |
47 | 202/1024 |
48 | 205/1024 |
49 | 211/1024 |
50 | 214/1024 |
51 | 217/1024 |
52 | 223/1024 |
53 | 230/1024 |
54 | 232/1024 |
55 | 234/1024 |
56 | 236/1024 |
57 | 242/1024 |
58 | 244/1024 |
59 | 251/1024 |
60 | 254/1024 |
61 | 257/1024 |
62 | 262/1024 |
63 | 266/1024 |
64 | 272/1024 |
65 | 280/1024 |
66 | 289/1024 |
67 | 292/1024 |
68 | 294/1024 |
69 | 297/1024 |
70 | 300/1024 |
71 | 301/1024 |
72 | 307/1024 |
73 | 308/1024 |
74 | 312/1024 |
75 | 316/1024 |
76 | 317/1024 |
77 | 319/1024 |
78 | 321/1024 |
79 | 326/1024 |
80 | 329/1024 |
81 | 333/1024 |
82 | 340/1024 |
83 | 344/1024 |
84 | 347/1024 |
85 | 352/1024 |
86 | 355/1024 |
87 | 359/1024 |
88 | 362/1024 |
89 | 367/1024 |
90 | 372/1024 |
91 | 376/1024 |
92 | 380/1024 |
93 | 383/1024 |
94 | 386/1024 |
95 | 392/1024 |
96 | 395/1024 |
97 | 399/1024 |
98 | 404/1024 |
99 | 407/1024 |
100 | 412/1024 |
成長判定に成功した場合、パラメータが上昇します。最大ヒットポイントの場合、成長一回につき「(体力÷4 + 2) + 乱数値(範囲: 1〜体力÷4)」ポイント上昇します。最大技(術)ポイントの場合は成長一回につき1ポイント、武器/術レベルの場合は成長一回につき1レベル上昇します。
ただし、すでにパラメータが最高値に達している場合は、成長判定に成功しても実際には成長しません。そして、それ以外の場合(実際に成長した場合)は、前述の「戦闘回数カウンタ(どのパラメータも成長しなかった戦闘の回数)」が0に戻ります。
さて、パラメータが成長した際にはもう一つ「全パラメータの成長確率(境界値)を一定値だけ下げる」という処理が行われます。先ほどの「成長判定の順番」が意味を持ってくるのはこの処理があるためです。つまり、一度の戦闘でいくつものパラメータが上昇するような場合(リーダーモンスターの技能上昇レベルが極端に高い場合など)に、成長判定の順番が後の方のパラメータほど成長確率が下がっていくわけです。
この「一定値」の具体的な値ですが、各技能レベルの合計値によって以下のように変化します。なお、各キャラクタの技能レベルには、武器レベル・術レベル以外にもう一つ(レベル扱いの)隠しパラメータが存在していて、ここの「各技能レベルの合計値」の算出にはその隠しパラメータの値も合計して計算します。
合計値 | 確率低下修正値 |
---|---|
0〜3 | 0 |
4〜15 | 1 |
16〜35 | 2 |
36〜63 | 3 |
64〜99 | 4 |
100〜143 | 5 |
144〜195 | 6 |
196〜255 | 7 |
256〜323 | 8 |
324〜399 | 9 |
以上の処理を全キャラクタの全パラメータに対して行っていきます。キャラクタが成長しなかった場合、そのキャラクタの「戦闘回数カウンタ(どのパラメータも成長しなかった戦闘の回数)」が1増加します。
そして、もしパーティのどのキャラクタも成長しなかった場合、パーティ内で最大ヒットポイントが最も低いキャラクタの最大ヒットポイントが1だけ上昇します(パーティ全員の最大ヒットポイントが999の場合を除く)。ここでは、前述の「成長特性」データで最大ヒットポイントの成長特性が"255"(FFh)の場合でも上昇します。
戦闘終了時に入手できるアイテムは、内部では4種類設定されています。その内訳は「100%入手できるアイテム」「高確率で入手できるアイテム」「中確率で入手できるアイテム」「低確率で入手できるアイテム」の4種類です。詳しくはrs_analyzerの『モンスターの所持アイテムデータ(3)(rs3monitem.txt)』をご覧ください。
4種類のアイテムを入手できる確率は、それぞれ1/1(100%)・約1/16(高確率)・約1/32(中確率)・約1/64(低確率)となっています。内部的な処理では、例えば「1/16」の確率計算の場合、「0〜15までの乱数値を取得して、値が0であれば判定成功」といった処理になっています。しかし、ロマンシング サ・ガ3の乱数ルーチンは得られる乱数値に偏りがあるため、この場合は正確に「1/16」の確率とはいえず、わずかに誤差があります。乱数ルーチンの挙動を考慮した上でのより正確な確率は、以下のようになります(注1)。
アイテム | 確率 |
---|---|
100% | 1024/1024 |
高確率 | 85/1024 |
中確率 | 40/1024 |
低確率 | 24/1024 |
実際の判定処理の流れですが、基本的には入手確率の高いアイテムから順番に、倒したモンスターの数だけ入手判定を行っています。そして、アイテムの入手判定に成功した時点で判定処理を打ち切って、アイテムを入手することになります。アイテム入手判定を行う順番は、以下のようになります(注2)。
100% | 高確率 | 中確率 | 低確率 | |
---|---|---|---|---|
モンスター1 | 1 | 6 | 11 | 16 |
モンスター2 | 2 | 7 | 12 | 17 |
モンスター3 | 3 | 8 | 13 | 18 |
モンスター4 | 4 | 9 | 14 | 19 |
モンスター5 | 5 | 10 | 15 | 20 |
また、入手できるアイテムを持ちきれない(アイテム欄に空きがない/すでにそのアイテムを最大個数所持している)場合、いったん入手判定処理を打ち切って、次のアイテムの入手判定処理が行われます。つまり、いわゆる「レアアイテム」を入手したい場合は、あらかじめ他の入手可能アイテムを持ちきれない状態にしておけば、ごくわずかながらレアアイテムの入手確率が上がることになります(注3)。
さらに正確な確率を求めるためには、アイテム入手判定を行う順番をも考慮しないといけません。アイテム入手判定には「高確率アイテムが先に判定される」「入手判定に成功した時点で入手判定処理を打ち切る」という2つの性質があるため、中確率・低確率アイテムを入手できる確率がわずかながら低くなっている可能性があります。乱数表を詳しく調べればわかるはずなのですが、ほとんど誤差程度にしか違わないはずなので調べていません。
この部分の処理を詳しく説明します。ここでは仮に、モンスターを3体倒した場合のアイテム入手判定を行うものとします。まずはこの3体のモンスターについて、「100%入手できるアイテム」から判定していきます(上の表中の"1"〜"3")。大部分のモンスターは「100%入手できるアイテム」を持っていないように設定されているので、無条件で判定に失敗します。
次に「高確率で入手できるアイテム」の入手判定を行います。例えば、ここでモンスター2のアイテム(表中の"7")について乱数による入手判定に成功した場合、この時点でいったん「高確率で入手できるアイテム」の入手判定処理を打ち切ります。そして、すでに「高確率で入手できるアイテム」を持ちきれるかどうかを判定します。持ちきれる場合は、この時点で入手判定処理を終了してアイテムを入手します。
入手アイテムを持ちきれない場合は入手判定処理が再開されるのですが、「高確率で入手できるアイテム」の続き(表中の"8")からではなく、「中確率で入手できるアイテム」の始め(表中の"11")から再開されます。その後は同様に入手判定処理を進めていき、最後(表中の"18")の入手判定までに一度も成功しなかった場合、アイテムは入手できないことになります。
本当にごくわずかしか入手確率は上がりません。ほとんど誤差の範疇といえます。
ロマンシング サ・ガ3には、エンディングや攻略本ではその姿を見ることができるものの、実際のプレイでは出現しない、いわゆる「レアモンスター」が存在します。
これらのモンスターが実際に出現するのかどうかについて調べてみました。
最初に断っておきますが、ロマンシング サ・ガ3の出現モンスター決定処理はかなり複雑であるため、現段階では完全には解析しきれていません。ここでの調査結果も、ごく一般的(と思われる)ケースでの出現モンスター決定処理のみを解析したものであり、この調査結果に当てはまらない例外ケースも存在します。
また、処理の内容が複雑であるため、説明文が非常に煩雑でわかりにくくなってしまいました。
まずは、この項で使用する用語について説明しておきます。
モンスターのシンボルと接触した場合、内部的な処理では、モンスターパーティに追加するモンスターとして、まずはそのシンボルに属する種族からモンスターを1体選択します。このモンスターを、ここでは「リーダーモンスター」と呼びます。リーダーモンスターの種類によって、モンスターパーティの最大モンスター数・最大モンスターサイズ・編成パターンなどが決まります。また、『戦闘終了後のキャラクタ成長処理』では、リーダーモンスターの「技能上昇レベル」の大小がキャラクタのパラメータ上昇確率に大きく影響します。
そして、モンスターパーティにはリーダーモンスター以外の種類のモンスターも追加されます。リーダーモンスター以外の種類のモンスターを、ここでは「従属モンスター」と呼びます。モンスターパーティは一般に「1体以上のリーダーモンスター(単一種類)」+「0体以上の従属モンスター(複数種類)」からなります。
ロマンシング サ・ガ3では、戦闘を重ねるごとに出現するモンスターも徐々に強くなっていきます。これは、内部的に「モンスターレベル」というような値が存在していて、戦闘に勝利するとこの値が徐々に上昇していき、この値が高いほど強力なモンスターが出現するようになっているからです。
この「モンスターレベル」はモンスターの種族(全16種類)ごとに別々に記録されています。また、それに加えて従属モンスターのレベルも別に記録されています。そして、リーダーモンスターを決定する際には「種族ごとのモンスターレベル」を、従属モンスターを決定する際には「従属モンスターのレベル」をそれぞれ使用することになります。
出現モンスターの決定には「モンスターレベル」が大きく関係していることについては前述しましたが、出現モンスター決定処理を説明する前に、まずはモンスターレベルがどのように上昇するかについて説明しておきます。
まず、戦闘に勝利した場合は必ず従属モンスターレベルの上昇処理が行われます。従属モンスターレベルの基本上昇値は、「(該当種族モンスターレベル - 従属モンスターレベル)÷16 (値がマイナスになる場合は0)」となります。さらに、この基本上昇値に、乱数によって0〜8までの修正値が加わります。
イベントバトルでない場合は、接触したシンボルに該当する種族のモンスターレベルの上昇処理が行われます。該当種族のモンスターレベルの基本上昇値は、「(従属モンスターレベル - 該当種族モンスターレベル)÷16 (値がマイナスになる場合は0)」となります。さらに、この基本上昇値に、乱数によって0〜8までの修正値が加わります。
なお、基本上昇値も修正値も0になった場合は、上昇値が0ということになり、モンスターレベルは上昇しません。
「乱数による0〜8までの修正値」の決定方法ですが、乱数ルーチンから0〜255の乱数値を取得して、その乱数値の範囲によって修正値が決まります。乱数値と修正値の関係については以下の表をご覧ください。
系統 | +8Lv | +7Lv | +6Lv | +5Lv | +4Lv | +3Lv | +2Lv | +1Lv | ±0Lv |
---|---|---|---|---|---|---|---|---|---|
骸骨系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
悪魔系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
獣系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
魚系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
獣人系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
植物系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜16 | 17〜34 | 35〜71 | 72〜255 |
水棲系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜15 | 16〜30 | 31〜61 | 62〜255 |
精霊系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜15 | 16〜30 | 31〜61 | 62〜255 |
ゾンビ系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
両棲系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
蛇系 | 0 | 1 | 2〜3 | 4〜5 | 6〜8 | 9〜12 | 13〜26 | 27〜56 | 57〜255 |
無機系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜15 | 16〜30 | 31〜61 | 62〜255 |
昆虫系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜16 | 17〜34 | 35〜71 | 72〜255 |
鳥系 | 0 | 1 | 2〜3 | 4〜6 | 7〜10 | 11〜16 | 17〜34 | 35〜71 | 72〜255 |
亡霊系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
妖精系 | 0 | 1〜2 | 3〜5 | 6〜9 | 10〜14 | 15〜20 | 21〜36 | 37〜68 | 69〜255 |
従属モンスター | 0 | 1 | 2 | 3〜4 | 5〜7 | 8〜11 | 12〜18 | 19〜30 | 31〜255 |
乱数ルーチンの偏りを考慮に入れると、0〜8までのレベル上昇修正値のそれぞれの値をとる確率は以下のようになります。
系統 | +8Lv | +7Lv | +6Lv | +5Lv | +4Lv | +3Lv | +2Lv | +1Lv | ±0Lv |
---|---|---|---|---|---|---|---|---|---|
骸骨系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
悪魔系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
獣系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
魚系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
獣人系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
植物系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 36/1024 | 75/1024 | 160/1024 | 716/1024 |
水棲系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 27/1024 | 68/1024 | 134/1024 | 758/1024 |
精霊系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 27/1024 | 68/1024 | 134/1024 | 758/1024 |
ゾンビ系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
両棲系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
蛇系 | 9/1024 | 2/1024 | 7/1024 | 4/1024 | 8/1024 | 20/1024 | 67/1024 | 127/1024 | 780/1024 |
無機系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 27/1024 | 68/1024 | 134/1024 | 758/1024 |
昆虫系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 36/1024 | 75/1024 | 160/1024 | 716/1024 |
鳥系 | 9/1024 | 2/1024 | 7/1024 | 6/1024 | 13/1024 | 36/1024 | 75/1024 | 160/1024 | 716/1024 |
亡霊系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
妖精系 | 9/1024 | 5/1024 | 8/1024 | 11/1024 | 27/1024 | 31/1024 | 70/1024 | 139/1024 | 724/1024 |
従属モンスター | 9/1024 | 2/1024 | 3/1024 | 7/1024 | 6/1024 | 17/1024 | 38/1024 | 50/1024 | 892/1024 |
また、モンスターレベルには種族ごとに上限値が設定されています。以下に、種族ごとのモンスターレベル上限値を示します。
系統 | 上限Lv |
---|---|
骸骨系 | 159 |
悪魔系 | 168 |
獣系 | 155 |
魚系 | 168 |
獣人系 | 159 |
植物系 | 145 |
水棲系 | 131 |
精霊系 | 131 |
ゾンビ系 | 159 |
両棲系 | 159 |
蛇系 | 126 |
無機系 | 131 |
昆虫系 | 146 |
鳥系 | 153 |
亡霊系 | 159 |
妖精系 | 168 |
従属モンスター | 158 |
これらを踏まえた上で、出現モンスター決定の処理について説明します。
リーダーモンスターの決定処理は、基本的には、接触したシンボルが属する種族のモンスターの中から、現在のモンスターレベルに応じたレベルのモンスターが選択されます。ただし、現在のモンスターレベルの値がそのまま使用されるわけではなく、乱数などによる修正が加わります。
具体的な計算式は、「モンスターレベル×7÷8」+「乱数値(範囲: 1〜モンスターレベル÷8)」+「乱数値(範囲: 1〜モンスターレベル÷8)」+「パーティの人数」-「乱数値(範囲: 1〜8)」となります。整理して、「モンスターレベル±約12.5%」+「パーティの人数」-「乱数値(範囲: 1〜8)」とみなしてもよいでしょう(実際には端数切り捨ての関係上「±12.5%」よりも若干低めになります)。
しかし、乱数取得ルーチンでは「完全な乱数」が得られるわけではないため、実際には上記の計算式どおりにはなりません。これについては後で詳しく説明します。
ここで、「地域の最低モンスターレベル算出処理」を行います。場所ごとに「地域の最低モンスターレベル」が決まっていて、この値と先ほど求めたモンスターレベルとを比較して、大きい方の値が最終的なモンスターレベルとして使用されます。この「地域の最低モンスターレベル」にも、乱数などによる修正が加わります。具体的な計算式は、「地域の最低モンスターレベル」+「パーティの人数」-「乱数値(範囲: 1〜8)」となります。なお、「地域の最低モンスターレベル」については、具体的な値はまだわかっていません。
最終的なモンスターレベルが決まったら、その値に応じてリーダーモンスターが選択されます。モンスターレベルと選択されるモンスターとの関係については、rs_analyzerの『モンスター出現レベルデータ(リーダーモンスター)(3)(rs3monencmain.txt)』をご覧ください。なお、モンスターレベルとリーダーモンスターの種類とによって、モンスターパーティの最大モンスター数・最大モンスターサイズ・編成パターンなども同時に決まります。
表の見方ですが、例えば、無機系のシンボルと接触して、最終的なモンスターレベルが"147"になったとします。この場合、"147"は「144以上(160未満)」であるため、リーダーモンスターとしてNo.9のゴールデンバウムが選択されます。また、「最大数」の項目が"3"となっているため、モンスターパーティは(リーダーモンスターを含めて)最大で3体までということになります。
ここで、モンスターパーティの最大モンスター数が"1"と決まった場合、この時点(リーダーモンスター決定時)でのモンスター数がすでに"1"であるため、出現モンスター決定処理はここで終了となります。
リーダーモンスターの種類が決まった後は、従属モンスターの決定処理に移ります。ここで、「従属モンスタータイプ」によって従属モンスターの決定処理が大きく異なります。
「従属モンスタータイプ」は従属モンスターにどのようなモンスターが選択されるかを決める値で、おそらくは場所ごとに設定されています。従属モンスタータイプは全部で16種類あり、タイプ0〜14は従属モンスターが全種族のモンスターから選択され、タイプ15は従属モンスターはリーダーモンスターと同種族のモンスターから選択されます。
この後、従属モンスター決定処理に進むわけですが、従属モンスタータイプによって処理が2通りに分岐します。以下、この2つの場合について順に説明します。
従属モンスタータイプが0〜14の場合、通常の従属モンスター決定処理となります。まずは、リーダーモンスターを何体か増加して、その後、従属モンスタータイプごとに対応する「従属モンスターテーブル」から従属モンスターを選択して追加していきます。
まずは、リーダーモンスターを増加する処理を行います。基本的には、以下のような処理の流れで、最終的なリーダーモンスターの数が決定します。
「境界値」の計算処理ですが、境界値の基本値は「(リーダーモンスター決定時の)最終的なモンスターレベル」を16で割った余りの値となります。その値に「(最終的なモンスターレベル - 従属モンスターレベル)÷16 (値がマイナスになる場合は0)」を加えて、「現在のモンスター数」を引きます。この値が最終的な境界値となります(値がマイナスになる場合は0)。
この処理の内容から、以下のような傾向があると言えそうです。
次に、従属モンスターを選択する処理を行います。処理の流れは以下のようになります。
構造としては先ほどの「リーダーモンスター増加処理」と似ていますが、ループごとに「境界値」を計算し直さない点が異なります。
「境界値」の計算処理も「リーダーモンスター増加処理」とほとんど同じです。境界値の基本値は「(リーダーモンスター決定時の)最終的なモンスターレベル」を16で割った余りの値となります。その値に「(最終的なモンスターレベル - 従属モンスターレベル)÷16 (値がマイナスになる場合は0)」を加えて、さらに「4」を加えて、「現在のモンスター数」を引きます。この値が最終的な境界値となります(値がマイナスになる場合は0)。
さて、「従属モンスター選択・追加処理」の詳しい内容について説明します。この部分は「リーダーモンスター決定処理」と少し似ていて、現在の従属モンスターレベルに応じたレベルの従属モンスターが選択されます。ただし、現在の従属モンスターレベルの値がそのまま使用されるわけではなく、乱数などによる修正が加わります。
具体的な計算式は、「モンスターレベル×7÷8」+「乱数値(範囲: 1〜モンスターレベル÷8)」+「乱数値(範囲: 1〜モンスターレベル÷8)」+「4」-「乱数値(範囲: 1〜8)」となります。整理して、「モンスターレベル±約12.5%」+「4」-「乱数値(範囲: 1〜8)」とみなしてもよいでしょう(実際には端数切り捨ての関係上「±12.5%」よりも若干低めになります)。
そして、やはり「リーダーモンスター決定処理」と同様に、「地域の最低モンスターレベル算出処理」も行います。処理の内容は、前述のものと同じです。この値と先ほど求めたモンスターレベルとを比較して、大きい方の値が最終的なモンスターレベルとして使用されます。
最終的なモンスターレベルが決まったら、その値に応じて従属モンスターが選択されます。モンスターレベルと選択されるモンスターとの関係については、『モンスター出現レベルデータ(従属モンスター)(3)(rs3monencsub.txt)』をご覧ください。
表の見方ですが、例えば、従属モンスタータイプ2で、最終的なモンスターレベルが"169"になったとします。この場合、"169"は「168以上(172未満)」であるため、従属モンスターとしてNo.3のウエットルビーが選択されます。
しかし、ここで選択されたモンスターが必ず従属モンスターとして追加されるわけではありません。「最大種類数の判定」と「最大モンスターサイズの判定」とを行い、判定に失敗した場合は追加されません。
「最大種類数の判定」では、モンスターパーティ内のモンスターの種類が3種類を超えるかどうかの判定を行います。ロマンシング サ・ガ3では、モンスターパーティに含まれるモンスターは最大3種類となっています。そのため、すでに3種類のモンスターが含まれている状態で、従属モンスターとして4種類目となるモンスターが選択された場合は、追加に失敗します。
「最大モンスターサイズの判定」では、追加しようとする従属モンスターのサイズが大きすぎないかどうかの判定を行います。「リーダーモンスター決定処理」で説明しましたが、モンスターレベルとリーダーモンスターの種類とによって、モンスターパーティの最大モンスターサイズが決まっています。もしも、追加しようとする従属モンスターのサイズが最大モンスターサイズよりも大きかった場合は、追加に失敗します。各モンスターのサイズについては、『モンスターのその他のデータ(3)(rs3monmisc.txt)』をご覧ください。
ここまでの「従属モンスター選択・追加処理」を、追加判定に成功するまで3回まで行います。3回とも追加判定に失敗した場合、ここで「従属モンスター選択処理」自体を打ちきります。前述の通り、「従属モンスター選択処理」は『「モンスターパーティの最大モンスター数 - 現在のモンスター数」回繰り返す』ことになっているのですが、この場合は残りの処理も行われず、出現モンスター決定処理は終了となります。
従属モンスタータイプが15の場合、特殊な従属モンスター決定処理となります。まず、タイプ0〜14の場合と異なり、「リーダーモンスター増加処理」は行いません。また、「従属モンスター選択処理」において、従属モンスターは「従属モンスターテーブル」からは選択されず、リーダーモンスターと同種族のモンスターからのみ選択されます。処理の流れは以下のようになります。
「従属モンスター選択処理」は「リーダーモンスター決定処理」のそれと同じ処理です。種族モンスターレベルをもとにモンスターレベルを算出して、「地域の最低モンスターレベル」を算出して大きい方の値を選択して、最終的なその値に応じて従属モンスターが選択されます。「リーダーモンスター決定処理」と同じ処理を行っているので、ここで選択される従属モンスターはリーダーモンスターと同種族のモンスターになります。
「境界値」の計算処理は「リーダーモンスター増加処理」のそれと同じ処理です。境界値の基本値は「(従属モンスター決定時の)最終的なモンスターレベル」を16で割った余りの値となります。その値に「(最終的なモンスターレベル - 従属モンスターレベル)÷16 (値がマイナスになる場合は0)」を加えて、「現在のモンスター数」を引きます。この値が最終的な境界値となります(値がマイナスになる場合は0)。
「従属モンスター追加処理」では、「最大種類数の判定」と「最大モンスターサイズの判定」とを行い、判定に失敗した場合は追加に失敗します。
さて、冒頭で紹介したいわゆる「レアモンスター」が出現することがあるのかどうかについて、検証してみます。
まず、『モンスター出現レベルデータ(リーダーモンスター)(3)(rs3monencmain.txt)』にも『モンスター出現レベルデータ(従属モンスター)(3)(rs3monencsub.txt)』にも入っていないモンスターは、(少なくともここで解析している出現処理においては)出現する可能性はありません。よって、ヤマ・スフィンクスは出現しないことになります。
トウテツ・蒼天女・ザッハーク・ワンダーラストの4体については、『モンスター出現レベルデータ(リーダーモンスター)(3)(rs3monencmain.txt)』のテーブルの中に入っています。これらのレアモンスターがリーダーモンスターとして出現する可能性について考察してみます。
まず、「リーダーモンスター決定処理」における計算式をもとに、モンスターレベルが最高値をとる場合を考えてみます。そうすると、「種族モンスターレベルが最高値」で「パーティ人数が6人」の状態で、理想的な(都合のいい)乱数値が出た場合に、モンスターレベルが最高値になることがわかります。このように、モンスターレベルが計算上の理想値をとった場合に、そのモンスターレベルに対応する出現モンスターは以下のようになります。
系統 | 上限Lv | 修正後Lv | 対応モンスター |
---|---|---|---|
獣系 | 155 | 178 | トウテツ |
植物系 | 145 | 167 | ウエットルビー |
蛇系 | 126 | 145 | ザッハーク |
鳥系 | 153 | 176 | ワンダーラスト |
この計算結果を見る限りでは、蒼天女を除く3体のモンスターは、リーダーモンスターとして出現する可能性があるように見えます。
しかし、乱数ルーチンの処理の関係上、「理想的な乱数値」がうまく出てくれるかどうかの検証が必要です。まず、ロマンシング サ・ガ3の乱数ルーチンは乱数表から1つずつ数値を拾って乱数値を取得しているアルゴリズムであるため、乱数生成の試行は独立ではありません。つまり、3回連続で乱数値を取得した場合に3回とも理想的な乱数値が出るには、乱数表の中に理想的な乱数値が3つ並んでいる箇所がないといけないわけです。
さらに、戦闘開始時には「乱数表内の現在位置」を変更する処理があり、これが「リーダーモンスター決定処理」に大きな影響を及ぼしています。
ロマンシング サ・ガ3のメモリ内には、プレイ時間をカウントするために1/60秒ごとに1ずつ増加するカウンタが存在します。このカウンタの初期値は0で、60に達したときにプレイ時間を1秒増やし、カウンタを0に戻します。
そして、敵シンボルと接触した際に、その時点でのカウンタを16倍した値を「現在位置」の初期値とする処理が行われます。その後、戦闘開始時の処理の中で最初に乱数を使用するのは「リーダーモンスター決定処理」です。よって、出現モンスター決定処理において、乱数値の出方は60通りしかないということになります。従って、各種条件(シンボルの種類・種族モンスターレベル・従属モンスターレベル・地域ごとの最低モンスターレベル・パーティ人数)が同じ場合、とりうるモンスターパーティの編成も高々60通りしかないということになります。
そうなると、「計算上の理想値」を満たすための「理想的な乱数値」が出ることを期待するのは難しくなります。60通りの中で最高値をとった場合に出現するモンスターは以下のようになります。
系統 | 上限Lv | 修正後Lv | 対応モンスター |
---|---|---|---|
獣系 | 155 | 172 | キマイラ |
植物系 | 145 | 160 | ウエットルビー |
蛇系 | 126 | 142 | ドラゴンパンジー |
鳥系 | 153 | 170 | ブラックドラゴン |
結局のところ、トウテツ・ザッハーク・ワンダーラストの3体のモンスターも、リーダーモンスターとして出現する可能性はないようです。
参考までに、内部のモンスターレベルが上限値まで上昇している(& パーティの人数が6人いる)状態で、各ランクのモンスターがどの程度の確率で出現するかをまとめた表を以下に示します。
系統 | ランク6 | ランク7 | ランク8 | ランク9 | ランク10 | ランク11 | ランク12 |
---|---|---|---|---|---|---|---|
骸骨系 | 0 | 0 | 1/60 | 28/60 | 30/60 | 1/60 | 0 |
悪魔系 | 0 | 0 | 0 | 9/60 | 28/60 | 23/60 | 0 |
獣系 | 0 | 0 | 3/60 | 36/60 | 21/60 | 0 | 0 |
魚系系 | 0 | 0 | 0 | 9/60 | 28/60 | 23/60 | 0 |
獣人系 | 0 | 0 | 1/60 | 28/60 | 30/60 | 1/60 | 0 |
植物系 | 0 | 0 | 22/60 | 37/60 | 1/60 | 0 | 0 |
水棲系 | 0 | 19/60 | 38/60 | 3/60 | 0 | 0 | 0 |
精霊系 | 0 | 19/60 | 38/60 | 3/60 | 0 | 0 | 0 |
ゾンビ系 | 0 | 0 | 1/60 | 28/60 | 30/60 | 1/60 | 0 |
両棲系 | 0 | 0 | 1/60 | 28/60 | 30/60 | 1/60 | 0 |
蛇系 | 0 | 26/60 | 34/60 | 0 | 0 | 0 | 0 |
無機系 | 0 | 19/60 | 38/60 | 3/60 | 0 | 0 | 0 |
昆虫系 | 0 | 0 | 14/60 | 41/60 | 5/60 | 0 | 0 |
鳥系 | 0 | 0 | 8/60 | 38/60 | 14/60 | 0 | 0 |
亡霊系 | 0 | 0 | 1/60 | 28/60 | 30/60 | 1/60 | 0 |
妖精系 | 0 | 0 | 0 | 9/60 | 28/60 | 23/60 | 0 |
蒼天女・ザッハークの2体については、『モンスター出現レベルデータ(従属モンスター)(3)(rs3monencsub.txt)』のテーブルの中に入っています。これらのレアモンスターが従属モンスターとして出現する可能性について考察してみます。
蒼天女は従属モンスタータイプ6の地域で出現することになっていて、出現モンスターレベルは"182"となっています。しかし、従属モンスターレベルが最高値(158)の状態で、(乱数ルーチンの処理を無視して)理想的な乱数値が出たケースを想定しても、最終的なモンスターレベルは"179"にしかならないため、蒼天女が出現する可能性はなさそうです。
ザッハークは従属モンスタータイプ10〜13の地域で出現することになっていて、出現モンスターレベルはいずれも"160"となっています。この値は、従属モンスターレベルが高ければ達成できる値です。しかし、「従属モンスター選択・追加処理」には「最大モンスターサイズの判定」の処理があります。ザッハークのサイズは16×20であり、このサイズのモンスターが従属モンスターとして追加できるケースはありません。というわけで、ザッハークの方も出現する可能性はないようです。
この文書を作成するにあたって、実際に解析したプログラムコードを以下に示します。
;________________________________________________________________ ; ; 乱数生成 ; [入力] ; Xレジスタ: 乱数値域の下限値 ; Aレジスタ: 乱数値域の上限値 ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $97: 乱数テーブル内の現在位置(1バイト) ; $1900: 乱数テーブル(256バイト) ;________________________________________________________________ ; L038379: SEP #$10 STX $96 ; $96にX(乱数値域の下限値)を格納する CPX #$FF BNE $8383 ; X(乱数値域の下限値)が255でなければ次の処理に進む BRA $83B6 ; X(乱数値域の下限値)が255であればサブルーチンを終了する L038383: CMP #$00 BEQ $83B6 ; A(乱数値域の上限値)が0であればサブルーチンを終了する CMP $96 BEQ $83B6 ; A(乱数値域の上限値)とX(下限値)が同じ値であればサブルーチンを終了する LDX $97 ; Xに乱数位置を格納する SEC SBC $96 ; A(乱数値域の上限値)から$96の内容(下限値)を減算した値がAに入る CMP #$FF BNE $8399 ; A(乱数値域の上限値と下限値との差)が255でなければ次の処理に進む LDA $1900,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する BRA $83B6 ; サブルーチンを終了する(ジャンプ先は$83B4の方が適切では?) L038399: INC A STA $3947 ; $3947の内容(除数の下位バイト)にA(乱数値域の上限値と下限値との差 + 1)を格納する STZ $3948 ; $3948の内容(除数の上位バイト)を0クリアする LDA $1900,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する TAX ; Aの内容をXにコピーする STX $3945 ; $3945(被除数)にX(乱数値)を格納する REP #$10 JSR $8407 ; 除算サブルーチンをコールする SEP #$10 CLC LDA $394B ; Aに$394B(乱数値÷(乱数値域の上限値と下限値との差 + 1)の余)を格納する ADC $96 ; Aと$96(乱数値域の上限値)を加算した値がAに入る INC $97 ; $97(乱数位置)を1つ進める L0383B6: REP #$10 RTS ;________________________________________________________________ ; ; 除算(16ビット÷16ビット) ; [入力] ; $3945: 被除数(2バイト) ; $3947: 除数(2バイト) ; [出力] ; $3949: 商(2バイト) ; $394B: 余(2バイト) ;________________________________________________________________ ; L038407: REP #$20 STZ $3949 STZ $394B LDA $3945 BEQ $843F ; $3945(被除数)が0であればサブルーチンを終了する LDA $3947 BEQ $843F ; $3947(除数)が0であればサブルーチンを終了する CLC LDX #$0010 ; Xをループカウンタとして使用する L03841D: ROL $3945 ROL $394B SEC LDA $394B SBC $3947 STA $394B BCS $8439 LDA $394B ADC $3947 STA $394B CLC L038439: ROL $3949 DEX BNE $841D L03843F: TDC SEP #$20 RTS
;________________________________________________________________ ; ; 乱数生成(0〜98の範囲) ; [出力] ; Aレジスタ: 乱数値 ;________________________________________________________________ ; L03858B: TDC ; D(おそらく0)の内容をAにコピーする TAX ; A(0)の内容をXにコピーする LDA #$62 ; Aに98を格納する JSR $8379 ; 乱数生成サブルーチンをコールする RTS
;________________________________________________________________ ; ; 乱数生成(0〜255の範囲) ; [出力] ; Aレジスタ: 乱数値 ;________________________________________________________________ ; L038593: TDC ; D(おそらく0固定)の内容をAにコピーする TAX ; A(0)の内容をXにコピーする LDA #$FF ; Aに256を格納する JSR $8379 ; 乱数生成サブルーチンをコールする RTS
/*________________________________________________________________ * * 乱数生成 * [入力] * range_low: 乱数値域の下限値 * range_high: 乱数値域の上限値 * [出力] * ret: 乱数値 * [使用] * index: 乱数位置 * tbl_rnd: 乱数テーブル *________________________________________________________________ */ static int index = 0; /* 便宜上、初期値を0としておく */ extern const int tbl_rnd[256]; int get_rnd(int range_low, int range_high) { int ret; /* 範囲のチェック */ if (range_low >= 255) return range_low; if (range_high <= 0) return range_high; if (range_low == range_high) return range_high; /* 0〜0xffまでの範囲の乱数を求める場合 */ if (range_high - range_low == 255) return tbl_rnd[index]; /* この場合、indexが増加しない */ /* それ以外の範囲の乱数を求める場合 */ ret = range_low + (tbl_rnd[index] % (range_high - range_low + 1)); index = (index + 1) & 0xff; return ret; }
ファイナルファンタジーIVで主に使用されている乱数生成ルーチンです。乱数の上限値と下限値を設定してからこのルーチンをコールすると、その範囲内の乱数値を取得できる、というものです。
上にも書きましたが、この乱数にはけっこう偏りがあります。ある範囲内の乱数値を求める際にはよく「X mod N」という方法が使われますが、このアルゴリズムでは範囲Nに比べて乱数の最大値Xが十分に大きな値でないと乱数に偏りが出てしまいます。まあ、しょせんゲームに使う乱数ですから、乱数の質が悪くても表立って問題にはならないでしょうけど。
ちょっと気になるのが、乱数値の範囲として0〜255が指定された場合、乱数の現在位置が移動しないということです。仮に、0〜255の範囲の乱数を取得する処理が連続で行われてることがあったとしたら、乱数値として毎回同じ値が返ってくることになります(戦闘中であれば、常に「1/60秒ごとに乱数の現在位置を1ずつ増加する」処理を行っているので問題ありませんが)。プログラム中のコメントにも書きましたが、どうもジャンプ先アドレスが間違っているような気がします。
$1900以降のRAM領域には、ROMからコピーされた乱数テーブルが格納されています。乱数テーブルの初期化処理は以下のようになっています。ちなみに、この乱数テーブルはファイナルファンタジーIV〜VIのすべてで共通のものが使用されています。
;________________________________________________________________ ; ; 乱数テーブル初期化 ;________________________________________________________________ ; L15CA05: ; (略) LDX #$0000 L15CA0F: LDA $14EE00,X ; ROMから乱数表データを取得して…… STA $1900,X ; ……RAMに格納する INX CPX #$0100 BNE $CA0F RTL L14EE00: DB $07,$B6,$F0,$1F,$55,$5B,$37,$E3,$AE,$4F,$B2,$5E,$99,$F6,$77,$CB DB $60,$8F,$43,$3E,$A7,$4C,$2D,$88,$C7,$68,$D7,$D1,$C2,$F2,$C1,$DD DB $AA,$93,$16,$F7,$26,$04,$36,$A1,$46,$4E,$56,$BE,$6C,$6E,$80,$D5 DB $B5,$8E,$A4,$9E,$E7,$CA,$CE,$21,$FF,$0F,$D4,$8C,$E6,$D3,$98,$47 DB $F4,$0D,$15,$ED,$C4,$E4,$35,$78,$BA,$DA,$27,$61,$AB,$B9,$C3,$7D DB $85,$FC,$95,$6B,$30,$AD,$86,$00,$8D,$CD,$7E,$9F,$E5,$EF,$DB,$59 DB $EB,$05,$14,$C9,$24,$2C,$A0,$3C,$44,$69,$40,$71,$64,$3A,$74,$7C DB $84,$13,$94,$9C,$96,$AC,$B4,$BC,$03,$DE,$54,$DC,$C5,$D8,$0C,$B7 DB $25,$0B,$01,$1C,$23,$2B,$33,$3B,$97,$1B,$62,$2F,$B0,$E0,$73,$CC DB $02,$4A,$FE,$9B,$A3,$6D,$19,$38,$75,$BD,$66,$87,$3F,$AF,$F3,$FB DB $83,$0A,$12,$1A,$22,$53,$90,$CF,$7A,$8B,$52,$5A,$49,$6A,$72,$28 DB $58,$8A,$BF,$0E,$06,$A2,$FD,$FA,$41,$65,$D2,$4D,$E2,$5C,$1D,$45 DB $1E,$09,$11,$B3,$5F,$29,$79,$39,$2E,$2A,$51,$D9,$5D,$A6,$EA,$31 DB $81,$89,$10,$67,$F5,$A9,$42,$82,$70,$9D,$92,$57,$E1,$3D,$F1,$F9 DB $EE,$08,$91,$18,$20,$B1,$A5,$BB,$C6,$48,$50,$9A,$D6,$7F,$7B,$E9 DB $76,$DF,$32,$6F,$34,$A8,$D0,$B8,$63,$C8,$C0,$EC,$4B,$E8,$17,$F8
;________________________________________________________________ ; ; お宝入手時の処理(戦闘勝利時の処理) ;________________________________________________________________ ; L03EC72: TDC TAX STX $3591 ; STX $3593 ; 獲得経験値の合計値を0に初期化する STX $3594 ; STX $3595 ; 獲得ギルの合計値を0に初期化する STX $B5 ; 入手アイテム数を0に初期化する STX $B7 L03EC84: LDX $B7 LDA $3585,X STA $B1 BNE $EC90 ; JMP $ED68 ; モンスターを1体も倒していない場合は最後までジャンプする L03EC90: LDX $B7 LDA $358B,X STA $AF STZ $B0 ASL $AF ROL $B0 LDX $AF CLC ; LDA $0EA1C0,X ; ADC $3591 ; STA $3591 ; LDA $0EA1C1,X ; ADC $3592 ; STA $3592 ; LDA $3593 ; ADC #$00 ; STA $3593 ; 獲得経験値の合計値に加算する LDX $B7 LDA $3588,X STA $AF STZ $B0 ASL $AF ROL $B0 LDX $AF CLC ; LDA $0EA000,X ; ADC $3594 ; STA $3594 ; LDA $0EA001,X ; ADC $3595 ; STA $3595 ; LDA $3596 ; ADC #$00 ; STA $3596 ; 獲得ギルの合計値に加算する LDX $B7 LDA $358E,X STA $B3 LDA $358E,X AND #$C0 ; お宝を入手できる確率を示す2ビットの領域を取得する CMP #$C0 ; BEQ $ED0F ; "11"(3)の場合、無条件でお宝入手後の処理にジャンプする CMP #$40 BNE $ED00 LDA #$05 ; "01"(1)の場合、お宝を入手できる確率は5%となる BRA $ED06 L03ED00: CMP #$80 BNE $ED5F ; ("00"(0)の場合、無条件でお宝は入手できない) LDA #$19 ; "10"(2)の場合、お宝を入手できる確率は25%となる L03ED06: STA $B4 ; お宝を入手できる確率を$B4に格納する JSR $858B ; 乱数生成サブルーチンをコールして、0〜98の範囲の乱数値を取得する CMP $B4 ; 乱数値と$B4(お宝を入手できる確率)とを比較して…… BCS $ED5F ; ……乱数値が$B4(お宝を入手できる確率)以上であればお宝は入手できない L03ED0F: ASL $B3 ASL $B3 LDA $B3 TAX TDC TAY STY $A9 L03ED1A: LDA $0E9F00,X ; お宝として残すアイテムを読み込む STA $289C,Y INX INY INC $A9 ; LDA $A9 ; CMP #$04 ; BNE $ED1A ; アイテム4種類分繰り返す LDA $16A3 ; ADC $97 ; STA $97 ; $97(現在の乱数位置)に$16A3(16ビットのカウンタの下位8ビット)を加算する JSR $8593 ; 乱数生成サブルーチンをコールして、0〜255の範囲の乱数値を取得する CMP #$80 ; 乱数値と128とを比較して…… BCS $ED3D ; ……乱数値が128以上であれば次の処理に進む LDA #$00 ; (乱数値が128未満(0〜127)の場合)入手するアイテムは1番目となる BRA $ED4F ; アイテム選択処理を終了する L03ED3D: CMP #$D0 ; 乱数値と208とを比較して…… BCS $ED45 ; ……乱数値が208以上であれば次の処理に進む LDA #$01 ; (乱数値が208未満(128〜207)の場合)入手するアイテムは2番目となる BRA $ED4F ; アイテム選択処理を終了する L03ED45: CMP #$FC ; 乱数値と252とを比較して…… BCS $ED4D ; ……乱数値が252以上であれば次の処理に進む LDA #$02 ; (乱数値が252未満(208〜251)の場合)入手するアイテムは3番目となる BRA $ED4F ; アイテム選択処理を終了する L03ED4D: LDA #$03 ; (乱数値が252〜255の場合)入手するアイテムは4番目(「レアアイテム」)となる L03ED4F: TAX LDA $289C,X LDX $B5 ; CPX #$0008 ; BEQ $ED5F ; $B5(入手アイテム数)が8個ならば、入手アイテム欄にアイテムを追加しない STA $1804,X ; 入手アイテム欄にアイテムを追加する INC $B5 ; L03ED5F: DEC $B1 ; LDA $B1 ; BEQ $ED68 ; JMP $EC90 ; 倒したモンスターの数だけ繰り返す L03ED68: INC $B7 ; LDA $B7 ; CMP #$03 ; BEQ $ED73 ; JMP $EC84 ; モンスター3種類分繰り返す L03ED73: ; (略)
戦闘勝利時の処理からお宝入手時の処理の部分を抜き出してみました。経験値・ギル獲得の処理、そしてお宝入手関連の処理を、倒したモンスターの数だけ繰り返しています。
;________________________________________________________________ ; ; モンスター遭遇時のモンスターパーティ選択処理 ;________________________________________________________________ ; L008CF2: STA $3D STZ $3E ASL $3D ROL $3E ASL $3D ROL $3E ASL $3D ROL $3E LDA $C0 BNE $0D21 ; アラームを使ってのモンスター出現の場合はジャンプする LDA $87 TAX LDA $14EE00,X CLC ADC $17EE INC $87 BNE $0D23 LDA $17EE CLC ADC #$11 STA $17EE JMP $8D23 L008D21: LDA #$FF ; アラームを使ってのモンスター出現の場合は、乱数値のかわりに固定値$FF(255)を使う L008D23: LDX $3D CMP #$2B ; 乱数値と43とを比較して…… BCC $0D48 ; ……乱数値が43未満(0〜42)であれば、サブルーチンの最後にジャンプする INX CMP #$56 ; 乱数値と86とを比較して…… BCC $0D48 ; ……乱数値が86未満(43〜85)であれば、サブルーチンの最後にジャンプする INX CMP #$81 ; 乱数値と129とを比較して…… BCC $0D48 ; ……乱数値が129未満(86〜128)であれば、サブルーチンの最後にジャンプする INX CMP #$AC ; 乱数値と172とを比較して…… BCC $0D48 ; ……乱数値が172未満(129〜171)であれば、サブルーチンの最後にジャンプする INX CMP #$CC ; 乱数値と204とを比較して…… BCC #$0D48 ; ……乱数値が204未満(172〜203)であれば、サブルーチンの最後にジャンプする INX CMP #$EC ; 乱数値と236とを比較して…… BCC $0D48 ; ……乱数値が236未満(204〜235)であれば、サブルーチンの最後にジャンプする INX CMP #$FC ; 乱数値と252とを比較して…… BCC $0D48 ; ……乱数値が252未満(236〜251)であれば、サブルーチンの最後にジャンプする INX ; (ここに到達するのは乱数値が252〜255の場合) L008D48: STX $3D RTS
;________________________________________________________________ ; ; 「ぬすむ」処理 ;________________________________________________________________ ; L03E1CF: LDA #$17 STA $34C8 LDA #$10 STA $34C7 LDA #$F8 STA $33C6 LDA #$04 STA $33C7 JSR $85B1 LDA $CE BMI $E1ED JMP $E23C L03E1ED: JSR $858B ; 乱数生成サブルーチンをコールして、0〜98の範囲の乱数値を取得する STA $A9 CLC LDA #$32 ; $32(50)に…… ADC $2682 ; ……自分のレベルを加算して…… SEC ; SBC $272F ; ……相手の隠しパラメータ(初期値は相手のレベル + 10)を減算した値が成功確率となる BCS $E202 LDA #$01 ; 成功確率がマイナスになった場合、成功確率を1%に設定する BRA $E208 L03E202: CMP #$63 ; 成功確率と$63(99)とを比較して…… BCC $E208 LDA #$63 ; 成功確率が$63(99)を超えていた場合、成功確率を99%(必ず成功する)に設定する L03E208: CMP $A9 ; 成功確率と乱数値とを比較して…… BCS $E242 ; 成功確率が乱数値以上であれば、盗み成功処理にジャンプする JSR $858B ; 乱数生成サブルーチンをコールして、0〜98の範囲の乱数値を取得する CMP $272F ; 乱数値と相手の隠しパラメータとを比較して…… BCC $E23C ; REP #$20 LDA $2689 ; 自分の最大ヒットポイントを…… JSR $8484 ; ……16で除算する STA $A9 LDA #$0000 SEP #$20 LDA $CD JSR $CA62 ; 不明 LDA $A9 ; STA $34D4,X ; LDA $AA ; STA $34D5,X ; 受けるダメージの値を設定する JSR $CA6E ; 不明 LDA #$1C ; 表示するメッセージ番号は$1C("モンスターにみつかった") STA $34CA BRA $E241 L03E23C: LDA #$1B ; 表示するメッセージ番号は$1B("ぬすみそこなった") STA $34CA L03E241: RTS L03E242: LDA $2773 ; AND #$C0 ; CMP #$C0 ; 必ずアイテムを落とす敵の場合…… BNE $E24C ; RTS ; ……アイテムは盗めない("ぬすみそこなった"の表示も出ない) L03E24C: TDC ; INC ; STA $AA ; 盗むアイテムの個数を1に設定する LDA $2773 ; AND #$3F ; JSR $847F ; TAX ; LDA $0E9F00,X ; 盗んだアイテムの番号を取得する BEQ $E23C ; アイテム0番の場合、盗み失敗処理にジャンプ STA $A9 ; CMP #$61 ; BCS $E26D ; アイテム番号が$61(97)以上(= 矢ではない)の場合はジャンプ CMP #$54 ; BCC $E26D ; アイテム番号が$54(84)未満(= 矢ではない)の場合はジャンプ LDA #$0A ; STA $AA ; 矢の場合は盗む個数を10に設定する L03E26D: TDC TAX TXY (略) L03E2C4: JSR $E2D2 LDA $A9 STA 359A LDA #$1D ; 表示するメッセージ番号は$1D("(アイテム名) を ぬすんだ") STA $34CA RTS
;________________________________________________________________ ; ; 「うそなき」処理 ;________________________________________________________________ ; L03EAE9: LDA #$12 STA $34C8 LDA #$10 STA $34C7 LDX $A6 LDA $202F,X ; 自分の隠しパラメータ(初期値は初期レベルと同じ値)を取得して…… LSR ; ……2で割る(この値の分だけ相手の隠しパラメータを下げる) STA $A9 LDX #$0005 STX $AB L03EB00: LDX $AB LDA $3540,X BNE $EB1C TXA JSR $8489 ; 不明 LDX $A6 SEC LDA $202F,X ; 相手の隠しパラメータを取得する SBC $A9 ; 相手の隠しパラメータから$A9(自分の隠しパラメータの半分)を減算する BEQ $EB17 ; 計算結果が0になった場合、1にする BCS $EB19 L03EB17: LDA #$01 ; 計算結果が0以下の値になった場合、1にする L03EB19: STA $202F,X ; 最終結果を相手の隠しパラメータをして格納する L03EB1C: INC $AB ; LDA $AB ; CMP #$0D ; BNE $EB00 ; 敵8体分繰り返す LDA #$13 ; 表示するメッセージ番号は$1C("まものを あわてさせた") STA $34CA JMP $85A6
;________________________________________________________________ ; ; 乱数生成(モンスターが出現するかどうかを判定する処理で使用) ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $4F: 乱数テーブル内の現在位置(1バイト) ; $0B60: 乱数テーブルの値に加算する値(1バイト) ; $C0FEC0: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC0CC52: PHX INC $4F ; $4F(乱数位置)を1つ進める BNE $CC60 ; $4F(乱数位置)が0でなければ次の処理に進む LDA $0B60 ; CLC ; ADC #$11 ; STA $0B60 ; $0B60(乱数に加算する値)に17を加算する LC0CC60: LDA $4F ; TAX ; Xに乱数位置を格納する LDA $C0FEC0,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する CLC ; ADC $0B60 ; 乱数値に$0B60(乱数に加算する値)を加算した値がAに入る PLX RTS
;________________________________________________________________ ; ; 乱数生成(どのモンスターパーティが出現するかを選択する処理で使用) ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $50: 乱数テーブル内の現在位置(1バイト) ; $0B5F: 乱数テーブルの値に加算する値(1バイト) ; $C0FEC0: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC0CC6D: PHX INC $50 ; $50(乱数位置)を1つ進める BNE $CC7B ; $50(乱数位置)が0でなければ次の処理に進む LDA $0B5F ; CLC ; ADC #$17 ; STA $0B5F ; $0B5F(乱数に加算する値)に23を加算する LC0CC7B: LDA $50 ; TAX ; Xに乱数位置を格納する LDA $C0FEC0,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する CLC ; ADC $0B5F ; 乱数値に$0B5F(乱数に加算する値)を加算した値がAに入る PLX RTS
;________________________________________________________________ ; ; 乱数テーブル ;________________________________________________________________ ; LC0FEC0: DB $07,$B6,$F0,$1F,$55,$5B,$37,$E3,$AE,$4F,$B2,$5E,$99,$F6,$77,$CB DB $60,$8F,$43,$3E,$A7,$4C,$2D,$88,$C7,$68,$D7,$D1,$C2,$F2,$C1,$DD DB $AA,$93,$16,$F7,$26,$04,$36,$A1,$46,$4E,$56,$BE,$6C,$6E,$80,$D5 DB $B5,$8E,$A4,$9E,$E7,$CA,$CE,$21,$FF,$0F,$D4,$8C,$E6,$D3,$98,$47 DB $F4,$0D,$15,$ED,$C4,$E4,$35,$78,$BA,$DA,$27,$61,$AB,$B9,$C3,$7D DB $85,$FC,$95,$6B,$30,$AD,$86,$00,$8D,$CD,$7E,$9F,$E5,$EF,$DB,$59 DB $EB,$05,$14,$C9,$24,$2C,$A0,$3C,$44,$69,$40,$71,$64,$3A,$74,$7C DB $84,$13,$94,$9C,$96,$AC,$B4,$BC,$03,$DE,$54,$DC,$C5,$D8,$0C,$B7 DB $25,$0B,$01,$1C,$23,$2B,$33,$3B,$97,$1B,$62,$2F,$B0,$E0,$73,$CC DB $02,$4A,$FE,$9B,$A3,$6D,$19,$38,$75,$BD,$66,$87,$3F,$AF,$F3,$FB DB $83,$0A,$12,$1A,$22,$53,$90,$CF,$7A,$8B,$52,$5A,$49,$6A,$72,$28 DB $58,$8A,$BF,$0E,$06,$A2,$FD,$FA,$41,$65,$D2,$4D,$E2,$5C,$1D,$45 DB $1E,$09,$11,$B3,$5F,$29,$79,$39,$2E,$2A,$51,$D9,$5D,$A6,$EA,$31 DB $81,$89,$10,$67,$F5,$A9,$42,$82,$70,$9D,$92,$57,$E1,$3D,$F1,$F9 DB $EE,$08,$91,$18,$20,$B1,$A5,$BB,$C6,$48,$50,$9A,$D6,$7F,$7B,$E9 DB $76,$DF,$32,$6F,$34,$A8,$D0,$B8,$63,$C8,$C0,$EC,$4B,$E8,$17,$F8
/*________________________________________________________________ * * 乱数生成(モンスターが出現するかどうかを判定する処理で使用) * [出力] * 戻り値: 乱数値 * [使用] * index: 乱数テーブル内の現在位置 * base: 乱数テーブルの値に加算する値 * tbl_rnd: 乱数テーブル *________________________________________________________________ */ static int index = 0; /* 便宜上、初期値を0としておく */ static int base = 0; /* 便宜上、初期値を0としておく */ extern const int tbl_rnd[256]; int get_rnd(void) { index = (index + 1) & 0xff; if (index == 0) base = (base + 17) & 0xff; return (tbl_rnd[index] + base) & 0xff; }
/*________________________________________________________________ * * 乱数生成(どのモンスターパーティが出現するかを選択する処理で使用) * [出力] * 戻り値: 乱数値 * [使用] * index: 乱数テーブル内の現在位置 * base: 乱数テーブルの値に加算する値 * tbl_rnd: 乱数テーブル *________________________________________________________________ */ static int index = 0; /* 便宜上、初期値を0としておく */ static int base = 0; /* 便宜上、初期値を0としておく */ extern const int tbl_rnd[256]; int get_rnd(void) { index = (index + 1) & 0xff; if (index == 0) base = (base + 23) & 0xff; return (tbl_rnd[index] + base) & 0xff; }
ファイナルファンタジーVではプログラム中のあちこちに乱数生成ルーチンが存在していて、それぞれ別々の用途に使用されています。ここに挙げたもの以外にもいくつか乱数生成ルーチンがありますが、どれもほぼ同じアルゴリズムです。さすがに乱数テーブルだけは各ルーチンで共通のものを使っているようです。
また、ここには挙げていませんが、ファイナルファンタジーIVの乱数生成ルーチンとほとんど同じ乱数生成ルーチンも使用されています。
;________________________________________________________________ ; ; モンスター遭遇判定 ;________________________________________________________________ ; LC0CB2C: ; (略) ;________________________________________________________________ ; ; モンスターパーティ選択処理 ;________________________________________________________________ ; LC0CBCD: LDA $0AD9 AND #$E0 REP #$20 ASL STA $0D LDA $0AD8 LSR LSR AND $0038 CLC ADC $0D STA $23 LDA $0AD6 XBA ASL CLC ADC $23 CLC ADC $0F TAX LDA $D07A00,X ASL ASL ASL TAX LDA $06 SEP #$20 JSR $CC6D ; 乱数生成サブルーチンをコールして、0〜255の範囲の乱数値を取得する CMP #$5A ; 乱数値と90とを比較して…… BCC $CC11 ; ……乱数値が90未満(0〜89)であれば、サブルーチンの最後にジャンプする INX INX CMP #$B4 ; 乱数値と180とを比較して…… BCC $CC11 ; ……乱数値が180未満(90〜179)であれば、サブルーチンの最後にジャンプする INX INX CMP #$F0 ; 乱数値と240とを比較して…… BCC $CC11 ; ……乱数値が240未満(180〜239)であれば、サブルーチンの最後にジャンプする INX ; (ここに到達するのは乱数値が240〜255の場合) INX LC0CC11: REP #$20 LDA $D06800,X STA $04F0 LDA $06 SEP #$20 INC $55 LC0CC20: RTS
;________________________________________________________________ ; ; 乱数生成(モンスターが出現するかどうかを判定する処理で使用) ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $1FA1: 乱数テーブル内の現在位置(1バイト) ; $1FA4: 乱数テーブルの値に加算する値(1バイト) ; $C0FD00: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC0C3AB: PHX INC $1FA1 ; $1FA1(乱数位置)を1つ進める BNE $C3BA ; $1FA1(乱数位置)が0でなければ次の処理に進む LDA $1FA4 ; CLC ; ADC #$11 ; STA $1FA4 ; $1FA4(乱数に加算する値)に17を加算する LC0C3BA: LDA $1FA1 ; TAX ; Xに乱数位置を格納する LDA $C0FD00,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する CLC ; ADC $1FA4 ; 乱数値に$1FA4(乱数に加算する値)を加算した値がAに入る PLX RTS
;________________________________________________________________ ; ; 乱数生成(どのモンスターパーティが出現するかを選択する処理で使用) ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $1FA2: 乱数テーブル内の現在位置(1バイト) ; $1FA3: 乱数テーブルの値に加算する値(1バイト) ; $C0FD00: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC0C3C8: PHX INC $1FA2 ; $1FA2(乱数位置)を1つ進める BNE $C3D7 ; $1FA2(乱数位置)が0でなければ次の処理に進む LDA $1FA3 ; CLC ; ADC #$17 ; STA $1FA3 ; $1FA3(乱数に加算する値)に23を加算する LC0C3D7: LDA $1FA2 ; TAX ; Xに乱数位置を格納する LDA $C0FD00,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aに格納する CLC ; ADC $1FA3 ; 乱数値に$1FA3(乱数に加算する値)を加算した値がAに入る PLX RTS
;________________________________________________________________ ; ; 乱数生成(0〜1の範囲) ; [出力] ; Cフラグ: 乱数値 ;________________________________________________________________ ; LC24B3B: PHA JSR $4B42 LSR PLA RTS
;________________________________________________________________ ; ; 乱数生成(0〜255の範囲) ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $BE: 乱数テーブル内の現在位置(1バイト) ; $C0FD00: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC24B42: PHX INC $BE ; $BE(乱数位置)を1つ進める LDX $BE ; Xに乱数位置を格納する LDA $C0FD00,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aの下位8ビットに格納する PLX RTS
;________________________________________________________________ ; ; 乱数生成 ; [入力] ; Aレジスタ: 乱数値域の上限値 ; [出力] ; Aレジスタ: 乱数値 ; [使用] ; $BE: 乱数テーブル内の現在位置(1バイト) ; $C0FD00: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC24B4D: PHX PHP SEP #$30 XBA ; Aの下位8ビット(乱数値域の上限値)を上位8ビット部分と入れ替える PHA INC $BE ; $BE(乱数位置)を1つ進める LDX $BE ; Xに乱数位置を格納する LDA $C0FD00,X ; 乱数テーブルのX(乱数位置)番目から乱数値を取得して、Aの下位8ビットに格納する JSR $4769 ; Aの上位8ビット(乱数値域の上限値)と下位8ビット(乱数値)とを乗算する PLA XBA ; A(乱数値域の上限値×乱数値)の上位8ビット分を最終的な乱数値とする PLP PLX RTS ;________________________________________________________________ ; ; 乗算(8ビット×8ビット) ; [入力] ; Aレジスタ: 乗算値1 ; Bレジスタ: 乗算値2 ; [出力] ; Aレジスタ: 積 ;________________________________________________________________ ; LC24769: PHP REP $20 STA $004202 ; $4202(乗算器の乗算値1の入力レジスタ)にA(乗算値1)を、 ; $4203(乗算器の乗算値2の入力レジスタ)にB(乗算値2)をそれぞれ格納する NOP ; NOP ; NOP ; NOP ; 乗算器の計算処理を待つため、8サイクルのウェイトを入れる LDA $004216 ; $4216(乗算器の積の出力レジスタ)の内容をAに格納する PLP RTS
;________________________________________________________________ ; ; 乱数テーブル ;________________________________________________________________ ; LC0FD00: DB $07,$B6,$F0,$1F,$55,$5B,$37,$E3,$AE,$4F,$B2,$5E,$99,$F6,$77,$CB DB $60,$8F,$43,$3E,$A7,$4C,$2D,$88,$C7,$68,$D7,$D1,$C2,$F2,$C1,$DD DB $AA,$93,$16,$F7,$26,$04,$36,$A1,$46,$4E,$56,$BE,$6C,$6E,$80,$D5 DB $B5,$8E,$A4,$9E,$E7,$CA,$CE,$21,$FF,$0F,$D4,$8C,$E6,$D3,$98,$47 DB $F4,$0D,$15,$ED,$C4,$E4,$35,$78,$BA,$DA,$27,$61,$AB,$B9,$C3,$7D DB $85,$FC,$95,$6B,$30,$AD,$86,$00,$8D,$CD,$7E,$9F,$E5,$EF,$DB,$59 DB $EB,$05,$14,$C9,$24,$2C,$A0,$3C,$44,$69,$40,$71,$64,$3A,$74,$7C DB $84,$13,$94,$9C,$96,$AC,$B4,$BC,$03,$DE,$54,$DC,$C5,$D8,$0C,$B7 DB $25,$0B,$01,$1C,$23,$2B,$33,$3B,$97,$1B,$62,$2F,$B0,$E0,$73,$CC DB $02,$4A,$FE,$9B,$A3,$6D,$19,$38,$75,$BD,$66,$87,$3F,$AF,$F3,$FB DB $83,$0A,$12,$1A,$22,$53,$90,$CF,$7A,$8B,$52,$5A,$49,$6A,$72,$28 DB $58,$8A,$BF,$0E,$06,$A2,$FD,$FA,$41,$65,$D2,$4D,$E2,$5C,$1D,$45 DB $1E,$09,$11,$B3,$5F,$29,$79,$39,$2E,$2A,$51,$D9,$5D,$A6,$EA,$31 DB $81,$89,$10,$67,$F5,$A9,$42,$82,$70,$9D,$92,$57,$E1,$3D,$F1,$F9 DB $EE,$08,$91,$18,$20,$B1,$A5,$BB,$C6,$48,$50,$9A,$D6,$7F,$7B,$E9 DB $76,$DF,$32,$6F,$34,$A8,$D0,$B8,$63,$C8,$C0,$EC,$4B,$E8,$17,$F8
ファイナルファンタジーVIの乱数生成ルーチンは、ファイナルファンタジーVのそれと非常に似通っています。よって、詳しい説明は省略します。
;________________________________________________________________ ; ; 固定モンスター出現の際にどのモンスターパーティを選択するか ; [使用] ; $CF5000: 固定モンスターのモンスターパーティテーブル(1024バイト) ;________________________________________________________________ ; LC0A4DC: LDA $EB REP #$20 ASL ASL TAX TDC SEP #$20 JSR $C3C8 ; 乱数生成サブルーチンをコールして、0〜255の範囲の乱数値を取得する CMP #$C0 ; 乱数値と192とを比較して…… BCC $A4EF ; ……乱数値が192未満であればジャンプする INX ; INX ; モンスターパーティの番号を1つ(2バイト分)進める LC0A4EF: REP #$20 LDA $CF5000,X ; 出現するモンスターパーティの番号を読み込む STA $0011E0 TDC SEP #$20 LDA $EC AND #$C0 STA $078A LDA $EC AND #$3F CMP #$3F BNE $A510 LDA $0522 AND #$7F LC0A510: STA $0011E2 TDC STA $0011E3 LDA $1ED7 AND #$10 LSR STA $0011E4 LDA #$01 STA $56 RTS
;________________________________________________________________ ; ; 通常モンスター遭遇の際にどのモンスターパーティを選択するか ; [使用] ; $CF4800: 通常モンスターのモンスターパーティテーブル(2048バイト) ;________________________________________________________________ ; LC0C15D: CMP $1F6F BCS $C1AA STZ $1F6E STZ $1F6F LDA $24 CMP #$FF ; ("$FF"(255)は獣ヶ原を意味する) BNE $C171 ; "$FF"(255)でなければ通常のモンスターパーティ選択処理にジャンプする JMP $C211 ; 獣ヶ原のモンスターパーティ選択処理にジャンプする LC0C171: REP #$20 ASL ASL ASL TAX TDC SEP #$20 JSR $C3C8 ; 乱数生成サブルーチンをコールして、0〜255の範囲の乱数値を取得する CMP #$50 ; 乱数値と80とを比較して…… BCC $C18F ; ……乱数値が80未満(0〜79)であれば、サブルーチンの最後にジャンプする INX INX CMP #$A0 ; 乱数値と160とを比較して…… BCC $C18F ; ……乱数値が160未満(80〜159)であれば、サブルーチンの最後にジャンプする INX INX CMP #$F0 ; 乱数値と240とを比較して…… BCC $C18F ; ……乱数値が240未満(160〜239)であれば、サブルーチンの最後にジャンプする INX ; (ここに到達するのは乱数値が240〜255の場合) INX LC0C18F: REP #$20 LDA $CF4800,X ; 出現するモンスターパーティの番号を読み込む STA $0011E0 TDC SEP #$20 LDA $1ED7 AND #$10 LSR STA $0011E4 LDA #$01 BRA $C1AB LC0C1AA: TDC LC0C1AB: PHA JSR $0505 PLA RTL
;________________________________________________________________ ; ; 獣ヶ原での敵出現の際にどのモンスターパーティを選択するか ; [使用] ; $1DDD: モンスターパーティに遭遇したことがあるかどうかを示すフラグ?(64バイト) ; $1FA5: 現在のモンスターグループ(1バイト) ;________________________________________________________________ ; LC0C211: INC $1FA5 LDA $1FA5 AND #$3F ; 64種類のモンスターグループの中から選択される TAX LC0C21A: LDA $1DDD,X BNE $C226 ; 遭遇したことのあるモンスターパーティが含まれていればジャンプする TXA INC AND #$3F TAX BRA $C21A LC0C226: STA $1A TXA STA $1FA5 REP #$20 ASL ; ASL ; ASL ; モンスターグループの番号を8倍した値が、モンスターパーティの番号(の基本値)になる STA $1E TDC SEP #$20 JSR $C3C8 ; 乱数生成サブルーチンをコールして、0〜255の範囲の乱数値を取得する AND #$07 ; 乱数値の下位3ビットのみ使用する TAX LC0C23C: LDA $1A AND $C0BA31,X ; ($C0BA31以降の内容は、$01,$02,$04,$08,$10,$20,$40,$80) BNE $C24B ; 遭遇したことのあるモンスターパーティであればジャンプする TXA INC AND #$07 TAX BRA $C23C LC0C24B: REP #$21 TXA ADC $1E STA $0011E0 TDC SEP #$20 PHA JSR $0505 PLA LDA #$01 RTL
;________________________________________________________________ ; ; 「ぬすむ」処理 ;________________________________________________________________ ; LC2398E: LDA $05,S TAX LDA #$01 STA $3401 CPX #$08 BCS $39F9 ; モンスターが「ぬすむ」をしてきた場合は、モンスターの盗み処理にジャンプする REP #$20 LDA $3308,Y ; モンスターの所持アイテムの番号を読み込む INC SEP #$21 ; (Cフラグをセットしている) BEQ $39F1 ; アイテムを持っていない場合は、盗み失敗処理にジャンプする INC $3401 LDA $3B18,X ; Aに盗むキャラクタのレベル(以下、「自分レベル」)を読み込む ADC #$32 ; Aに50を加算する(Cフラグがセットされているのでさらに1加算される) BCS $39C8 ; A(自分レベル + 51)が256以上ならば、盗み成功処理にジャンプする SBC $3B18,Y ; A(自分レベル + 51)から盗むモンスターのレベル(以下、「相手レベル」)を減算する ; (Cフラグがリセットされているのでさらに1減算される) BCC $39F1 ; A(自分レベル + 50 - 相手レベル)が0より下ならば、盗み失敗処理にジャンプする BMI $39C8 ; A(自分レベル + 50 - 相手レベル)が128以上ならば、盗み成功処理にジャンプする STA $EE ; Aの内容を$EEに格納する(これ以降、$EEは「盗み成功確率」と解釈される) LDA $3C45,X ; 盗むキャラクタの装備品による特殊効果を読み込む LSR ; BCC $39BF ; 「とうぞくのうでわ」を装備していない場合は次の処理に進む ASL $EE ; 「とうぞくのうでわ」を装備していた場合、盗み成功確率が2倍になる LC239BF: LDA #$64 ; JSR $4B4D ; 0〜99の範囲の乱数値を取得する CMP $EE ; 乱数値と$EE(盗み成功確率)とを比較して…… BCS $39F1 ; ……乱数値が$EE(盗み成功確率)以上であれば、盗み失敗処理にジャンプする LC239C8: PHY JSR $4B42 ; 0〜256の範囲の乱数値を取得する CMP #$20 ; 乱数値と32とを比較して…… BCC $39D1 ; ……乱数値が32未満であれば、次の処理に進む INY ; 入手するアイテムは、「高確率(で盗める)アイテム」の方になる LC239D1: LDA $3308,Y ; モンスターの所持アイテムの番号を読み込む PLY CMP #$FF ; BEQ $39F1 ; アイテムを持っていない場合は、盗み失敗処理にジャンプする STA $2F35 STA $32F4,X LDA $3018,X TSB $3A8C LDA #$FF ; STA $3308,Y ; STA $3309,Y ; 一度盗みに成功したら、そのモンスターの所持アイテムはなくなる INC $3401 RTS LC239F1: SEP #$20 LDA #$00 STA $3D48,Y RTS LC239F9: ; (略)
;________________________________________________________________ ; ; ラグナロックのアイテム変化処理 ;________________________________________________________________ ; LC23A2C: CPY #$08 BCC $3A7A LDA $3C94,Y ; モンスターのアイテム変化の情報を読み込む PHA AND #$1F ; アイテム変化テーブル番号を取得する JSR $4B3B ; ROL ; JSR $4B3B ; ROL ; 4倍しながら0〜3の乱数値を加算する TAX LDA $C47F40,X ; 変化するアイテムの番号を読み込む STA $2F35 LDA #$02 STA $3A28 LDA #$1D STA $3A29 JSR $35B9 JSR $35A8 PLA ; LSR ; LSR ; LSR ; LSR ; LSR ; アイテム変化の難易度を取得する TAX JSR $4B42 ; 0〜256の範囲の乱数値を取得する CMP $C23DAD,X ; 乱数値と(アイテム変化の難易度に対応した)変化成功の境界値とを比較して…… BCS $3A7A ; ……乱数値が変化成功の境界値以上であれば、ジャンプする(変化失敗) LDA $05,S TAX LDA $2F35 STA $32F4,X LDA $3018,X TSB $3A8C LDA #$80 JMP $0E2A LC23A7A: JMP $3B03 LC23DAD: DB $FF,$C0,$80,$40,$20,$10,$08,$00 ; 成功確率のテーブル
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [出力] ; Aレジスタ: 乱数値(0〜255) ; [使用] ; $075B〜$075C: 乱数シード? ; $075D: 最終出力結果に加算する値? ;________________________________________________________________ ; L009878: PHP SEP #$20 LDA #$FF STA $10 JSR $9895 LDA #$FF STA $10 JSR $9895 INC $075D LDA $075B CLC ADC $075D PLP RTS L009895: LDA #$08 STA $11 L009899: LDA $075C EOR $10 ASL $075B ROL $075C ASL $10 ASL BCC $98B9 LDA $075B EOR #$21 STA $075B LDA $075C EOR #$10 STA $075C L0098B9: DEC $11 BNE $9899 RTS
;________________________________________________________________ ; ; 乱数生成(0〜Aの範囲) ; [入力] ; Aレジスタ: 乱数の最大値 ; [出力] ; Aレジスタ: 乱数値(0〜A) ;________________________________________________________________ ; L0098D9: PHP REP #$10 SEP #$20 PHX TAX JSR $9878 JSR $9D50 PHA PHX PLA PLA XBA PLA XBA PLX PLP RTS L009D50: PHB PHK PLB STX $10 STA $4202 LDA $10 STA $4203 NOP NOP NOP NOP LDA $4217 STA $12 LDA $4216 STA $10 LDA $11 STA $4203 NOP NOP NOP CLC LDA $4216 ADC $12 STA $11 LDA $4217 ADC #$00 STA $12 LDX $10 LDA $12 PLB RTS
;________________________________________________________________ ; ; 乱数生成(0〜Aの範囲) (他バンクからの呼び出し用) ; [入力] ; Aレジスタ: 乱数の最大値 ; [出力] ; Aレジスタ: 乱数値(0〜A) ;________________________________________________________________ ; L00953E: JSR $98D9 RTL
ドラゴンクエストVの乱数生成ルーチンです。処理の内容が今一つよくわからないのですが、おそらくCRCのアルゴリズムを使って乱数を生成しているのだろうと思います。
なお、上記プログラム内のメモリ"$075B"の内容は、約1/60秒ごとに1ずつ増加しています(プログラム"$008894"の処理)。これにより、乱数列が一定のパターンになるのを防いでいます。
;________________________________________________________________ ; ; 宝箱入手判定時の処理 ;________________________________________________________________ ; L258956: PHP REP #$30 PHA PHX PHY SEP #$30 LDA $208000 ; $208000(ROM領域)の…… AND #$20 ; ……bit5が…… BNE $89A7 ; ……セットされている場合、宝箱を入手できる(デバッグ用?) LDA $1116 CMP #$FF BNE $8970 BRL $8A86 L258970: STZ $42 BRK #$88 DB $78 LDA $46 CMP $1113 BEQ $898C INC $42 CMP #$10 BCC $8970 ADC $42 LDA $1113 STA $43 BRK #$88 DB $EE L25898C: BRK #$88 DB $74 LDA $46 ASL TAX REP #$20 LDA $278218,X ; 乱数の最大値を決める JSL $00953E ; 乱数を取得する CMP #$0000 ; 乱数値と0とを比較して…… SEP #$20 BEQ $89A7 ; ……0でない場合、ジャンプする(宝箱を入手できる) BRL $8A86 ; ジャンプする(宝箱は入手できない) L2589A7: BRK #$88 DB $75 LDA $46 CMP #$FF BNE $89B3 BRL $8A86 L2589B3: STA $10F7 LDA $42 ORA #$A0 STA $F5 L258A86: ; (略) L278218: DW $0001,$0008,$0010,$0020,$0040,$0080,$0100,$1000 ; 宝箱入手確率用のテーブル
;________________________________________________________________ ; ; モンスターが仲間になるかどうかの判定処理 ;________________________________________________________________ ; L20C1CE: PHP REP #$30 PHA PHX PHY SEP #$30 JSR $C1F6 ; 不明 BCS $C1E6 JSL $218A63 ; 不明 JSR $C2BB ; 不明 JSL $218A7A ; 不明 L20C1E6: REP #$30 PLY PLX PLA PLP RTS LDA $208000 LSR BCS $C227 BRK #$0B DB $40,$21 BEQ $C23F LDA $1010 BEQ $C23F BRK #$88 DB $A6 LDA $46 CMP #$32 BCS $C23F LDA #$00 BRK #$8A DB $DB LDA $47 BRK #$8A DB $2A LDA $100F SEC SBC $46 BCC $C227 CMP #$08 BCS $C23F L20C227: LDA $1117 CMP #$FF BEQ $C23F LDA #$00 BRK #$8A DB $78 LDX $46 CPX $1117 BEQ $C241 INC CMP #$10 BCC $C230 L20C23F: SEC ; 仲間にならない場合は、Cフラグをセットする RTS L20C241: BRK #$8A DB $76 LDX $46 STX $74 BRK #$88 DB $77 LDX $46 STX $71 LDA $7E29EA,X AND #$0F STA $72 TAX BEQ $C29E ; 同種のモンスターが1匹も仲間になっていない場合、1匹目用の判定処理にジャンプ LDA $278153,X ; 立っているビットの数を数えて、すでに仲間になっている同種のモンスターの数を取得する CMP #$02 BCC $C281 ; 同種のモンスターがすでに1匹仲間になっている場合、2匹目用の判定処理にジャンプ BNE $C23F ; 同種のモンスターがすでに3匹仲間になっている場合、ジャンプする(仲間にならない) JSL $268D0A ; 主人公が特定のアイテムを特定の順番で所持しているかチェックして…… BCS $C2B9 ; ……条件を満たしている場合、ジャンプする(仲間になる) LDA $74 ; 仲間になりやすさを取得する ASL ; 仲間になりやすさを2倍して確率用テーブルのインデックスを算出する TAX REP #$20 LDA $278182,X ; 3匹目用確率テーブルを使用して、乱数の最大値を決める JSL $00953E ; 乱数を取得する CMP #$0000 ; 乱数値と0とを比較して…… SEP #$20 BNE $C23F ; ……0でない場合、ジャンプする(仲間にならない) BEQ $C2B9 ; ジャンプする(仲間になる) L20C281: JSL $268D0A ; 主人公が特定のアイテムを特定の順番で所持しているかチェックして…… BCS $C2B9 ; ……条件を満たしている場合、ジャンプする(仲間になる) LDA $74 ; 仲間になりやすさを取得する ASL ; 仲間になりやすさを2倍して確率用テーブルのインデックスを算出する TAX REP #$20 LDA $278172,X ; 2匹目用確率テーブルを使用して、乱数の最大値を決める JSL $00953E ; 乱数を取得する CMP #$0000 ; 乱数値と0とを比較して…… SEP #$20 BNE $C23F ; ……0でない場合、ジャンプする(仲間にならない) BEQ $C2B9 ; ジャンプする(仲間になる) L20C29E: JSL $268D0A ; 主人公が特定のアイテムを特定の順番で所持しているかチェックして…… BCS $C2B9 ; ……条件を満たしている場合、ジャンプする(仲間になる) LDA $74 ; 仲間になりやすさを取得する ASL ; 仲間になりやすさを2倍して確率用テーブルのインデックスを算出する TAX REP #$20 LDA $278162,X ; 1匹目用確率テーブルを使用して、乱数の最大値を決める JSL $00953E ; 乱数を取得する CMP #$0000 ; 乱数値と0とを比較して…… SEP #$20 BNE $C23F ; ……0でない場合、ジャンプする(仲間にならない) L20C2B9: CLC ; 仲間になる場合は、Cフラグをクリアする RTS L278153: DB $00,$01,$01,$02,$01,$02,$02,$03,$01,$02,$02,$03,$02,$03,$03 L278162: DW $0000,$0100,$0040,$0020,$0010,$0004,$0002,$0002 ; 仲間になる確率用テーブル(1匹目用) L278172: DW $0000,$0400,$0080,$0040,$0040,$0040,$0040,$0020 ; 仲間になる確率用テーブル(2匹目用) L278182: DW $0000,$0400,$0100,$0080,$0040,$0040,$0040,$0010 ; 仲間になる確率用テーブル(3匹目用) L268D0A: PHP REP #$30 PHA PHX PHY SEP #$30 LDX #$00 L268D14: LDA $7E2056,X ; 特定のアイテムX番目と…… CMP $268D33,X ; ……主人公のアイテムX番目とを比較して…… BNE $8D2B ; ……異なっていた場合、ジャンプする(仲間にならない) INX CPX #$07 ; BCC $8D14 ; アイテム7つ分(終端を示す値を含む)繰り返す REP #$30 PLY PLX PLA PLP SEC ; すべて等しかった場合、Cフラグをセットする(仲間になる) RTL L268D2B: REP #$30 PLY PLX PLA PLP CLC ; 異なっていた場合、Cフラグをクリアする(仲間にならない) RTL L268D33: DB $00,$0A,$91,$02,$3C,$64,$FF ; 特定のアイテムを示すテーブル ; $00: ひのきのぼう ; $0A: とがったホネ ; $91: しあわせのぼうし ; $02: こんぼう ; $3C: のこぎりがたな ; $64: みかわしのふく ; $FF: (終端)
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [出力] ; Aレジスタ: 乱数値(0〜255) ; [使用] ; $584A〜$584D: 不明 ;________________________________________________________________ ; LC00E97: PHP PHB SEI REP #$20 PEA $7E7E PLB PLB LDA $584A ASL ASL EOR $584C ASL AND #$FF00 XBA SEP #$20 PHA LDA $584C STA $584D LDA $584B STA $584C LDA $584A STA $584B PLA STA $584A PLB PLP PHA PLA RTL
;________________________________________________________________ ; ; 乱数生成(16ビット) ; [出力] ; Aレジスタ: 乱数値(0〜65535) ;________________________________________________________________ ; LC00F6E: PHP SEI SEP #$20 JSL $C00E97 ; 乱数値の下位8ビット分を設定する PHA JSL $C00E97 ; 乱数値の上位8ビット分を設定する XBA PLA PLP PHA PLA RTL
;________________________________________________________________ ; ; 乱数生成(0〜Aの範囲) ; [入力] ; Aレジスタ: 乱数の最大値 ; [出力] ; Aレジスタ: 乱数値(0〜A) ;________________________________________________________________ ; LC00F49: PHP SEI REP #$30 PHX INC ; A(乱数の最大値)に1を加算して…… BNE $0F5A ; ……その結果が0以外(= 乱数の最大値が$FFFF未満である)場合、ジャンプする JSL $C00F6E ; 乱数の最大値が$FFFFの場合、単に16ビットの乱数値を取得するだけ PLX PLP PHA PLA RTL LC00F5A: STA $30 JSL $C00F6E ; 16ビットの乱数値を取得する LDX #$0030 ; JSL $C00D0C ; (乱数の最大値 + 1)×16ビットの乱数値の乗算を行う LDA $32 ; 計算結果(32ビット)の上位16ビットを最終結果とする PLX PLP PHA PLA RTL
;________________________________________________________________ ; ; 宝箱入手判定時の処理 ;________________________________________________________________ ; LC2B13F: PEA $2511 PEA $00FF PEA $7E00 JSL $C92965 ; モンスター番号を取得? STA $5A22 STA $2587 JSR $B1B1 ; モンスターの宝箱入手難易度を取得して、乱数の最大値を決める JSL $C00F49 ; 乱数を取得する BNE $B1B0 ; 乱数値が0でない場合、ジャンプする(宝箱は入手できない) LDX $2587 JSL $C2F136 ; 宝箱のアイテム番号を取得する DW $0174,$00FF ; (オフセット$0174(372)バイト目の全ビット($FF)を意味する) CMP #$0000 ; アイテム番号が0番の場合…… BEQ $B1B0 ; ……宝箱は入手できない STA $5A2A JSL $C02A16 ; メッセージを表示する DB $0002 ; (メッセージの番号) JSL $C02A16 ; メッセージを表示する DB $0045 ; (メッセージの番号) JSL $C1F32A ; BGM/SEを鳴らす DB $0040 ; (BGM/SEの番号) LDA #$0042 STA $2555 JSL $C02A16 ; メッセージを表示する DB $0047 ; (メッセージの番号) LDA #$0049 STA $2593 LDA $5A2A JSL $C3ED1D ; アイテムを入手する STA $5A28 BCS $B1B0 CMP #$09C8 BEQ $B1A9 LDA #$0048 STA $2593 LC2B1A9: LDA $2593 JSL $C02A29 ; メッセージを表示する LC2B1B0: RTS LC2B1B1: LDX $2587 JSL $C2F136 ; 宝箱の入手難易度を取得する DW $0163,$001C ; (オフセット$0163(355)バイト目のビット2〜4($1C)を意味する) ASL TAX LDA $C2B1C3,X ; 宝箱入手難易度をもとにして、乱数の最大値を決める RTS LC2B1C3: DW $0000,$0007,$000F,$001F,$003F,$007F,$00FF,$0FFF ; 宝箱入手確率用のテーブル
;________________________________________________________________ ; ; モンスターが仲間になるかどうかの判定処理 ;________________________________________________________________ ; LC2B320: PEA $2011 PEA $0020 PEA $7E00 JSL $C92965 ; 不明 BNE $B39B JSL $C1CEDA ; 不明 BCS $B39B JSL $C1CEB7 ; 不明 BCS $B39B JSL $C1CEF3 ; 不明 BCS $B39B JSL $C1CF02 ; 不明 BCS $B39B PEA $2512 PEA $00FF PEA $7E00 JSL $C92965 ; 不明(仲間になる可能性がある状況の場合、モンスターの番号がAに格納される?) CMP #$0000 BEQ $B39B STA $2587 JSL $C2B4AF ; モンスターの仲間になりやすさを取得して、乱数の最大値を決める CMP #$FFFF ; 乱数最大値が$FFFFの場合…… BEQ $B39B ; ……仲間にできない JSL $C9064A ; デバッグ用仲間加入判定処理 BCS $B371 ; Cフラグがセットされている場合、ジャンプする(仲間になる) JSL $C00F49 ; 乱数を取得する BNE $B39B ; 乱数値が0でない場合、ジャンプする(仲間にならない) LC2B371: JSL $C02A16 ; メッセージを表示する DW $0002 ; (メッセージの番号) LDX $2587 JSL $C489A8 BCS $B39B LDX $2587 JSL $C48D42 STA $25D5 JSR $B39C LDX $25D5 JSL $C49079 DB $2A,$97,$C4 JSL $C48FE7 LC2B39B: RTS LC2B4AF: PHP PHB REP #$30 PEA $7E7E PLB PLB LDA #$FFFF PHA JSR $B532 BEQ $B4FE INC PHA LDX $2587 JSL $C2F136 ; モンスターレベルを取得する DW $0161,$00FE ; (オフセット$0161(353)バイト目のビット1〜7($FE)を意味する) CMP $01,S PLA BCS $B4FE LDA $2587 JSL $C43D74 ; 仲間になっている同種のモンスターの数を取得する? DW $FFFF BCS $B4FE CMP #$0003 ; すでに同種のモンスターが3匹いる場合…… BCS $B4FE ; ……仲間にならない ASL ; ASL ; ASL ; 仲間になっている同種のモンスターの数を8倍する STA $01,S LDX $2587 JSL $C2F136 ; モンスターの仲間になりやすさを取得する DW $0165,$00E0 ; (オフセット$0165(357)バイト目のビット5〜7($E0)を意味する) CLC ; ADC $01,S ; 仲間になりやすさと同種のモンスターの数×8を加算して…… ASL ; ……2倍して確率用テーブルのインデックスを算出する TAX LDA $C2B502,X ; 乱数の最大値を決める STA $01,S LC2B4FE: PLA PLB PLP RTL LC2B502: DW $FFFF,$00FF,$00FF,$003F,$001F,$000F,$0003,$0001 ; 仲間になる確率用テーブル(1匹目用) DW $FFFF,$FFFF,$03FF,$03FF,$00FF,$00FF,$007F,$001F ; 仲間になる確率用テーブル(2匹目用) DW $FFFF,$FFFF,$03FF,$03FF,$00FF,$00FF,$00FF,$007F ; 仲間になる確率用テーブル(3匹目用) LC9064A: REP #$30 PEA $7E7E PLB PLB PHA LDA $C1FFFE ; ROM領域$C1FFFEの…… BPL $0663 ; ……最上位ビットが0の場合はジャンプする(仲間にならない) LDA $3D00 ; RAM領域$7E3D00の…… AND #$0010 ; ……4ビット目が…… BEQ $0663 ; ……0の場合はジャンプする(仲間にならない) PLA SEC ; Cフラグをセットする(仲間になる) RTL PC90663: PLA CLC ; Cフラグをクリアする(仲間にならない) RTL
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [出力] ; Aレジスタ: 乱数値(0〜255) ;________________________________________________________________ ; L0BA550: REP #$20 LDA $E2 PHA LDA $E0 ASL $E0 ROL $E2 CLC ADC $E0 STA $E0 PLA ADC $E2 STA $E2 LDA $E0 CLC ADC #$3549 ADC $67 STA $E0 LDA $E2 ADC #$0000 STA $E2 SEP #$20 LDA #$00 XBA LDA $E2 RTL
;________________________________________________________________ ; ; 宝箱入手判定時の処理 ;________________________________________________________________ ; L00DE40: LDA $1080 BNE $DE78 LDA $70 BPL $DE69 LDX $0F7E JSL $0B8B7A LDA $0B000D,X ; 宝箱の入手難易度を取得する STX $02 JSR $CF75 ; 5ビット右シフトする AND #$03 ; 下位2ビットのみ取得する(最終的に、宝箱の入手難易度として0〜3の値が得られる) TAY LDA $DEAB,Y ; 宝箱入手難易度をもとにして、宝箱取得判定の境界値を取得する STA $00 JSL $0BA550 ; 乱数を取得する CMP $00 ; 乱数値と宝箱取得判定の境界値とを比較して…… BCC $DE6D ; ……乱数値が宝箱取得判定の境界値未満であれば、ジャンプする(宝箱を入手できる) L00DE69: LDY #$0002 RTS L00DE6D: LDX $02 LDA $0B000F,X ; 宝箱のアイテム番号を取得する AND #$7F ; CLC ADC #$24 L00DE78: STA $E8 JSR $CA8A CPY #$0002 BNE $DE69 REP #$20 LDA $E8 AND #$00FF ASL TAX LDA $09E9FB,X STA $0C8D SEP #$20 LDY #$0000 STY $AE LDA $0C09 BNE $DEA7 INC $AE LDA $0C0A BNE $DEA7 INC $AE L00DEA7: LDY #$0000 RTS L00DEAB: DB $20,$10,$08,$02 ; 宝箱入手確率用のテーブル
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [出力] ; Aレジスタ: 乱数値(0〜255) ; [使用] ; $7F7A〜$7F7E: 不明 ;________________________________________________________________ ; LC012E3: LDA #$0001 STA $7F7E LDA $7F7A ASL ASL EOR $7F7C ASL AND #$FF00 XBA SEP #$20 PHA LDA $7F7C STA $7F7D LDA $7F7B STA $7F7C LDA $7F7A STA $7F7B PLA STA $7F7A REP #$20 STZ $7F7E RTL
;________________________________________________________________ ; ; 乱数生成(16ビット) ; [出力] ; Aレジスタ: 乱数値(0〜65535) ;________________________________________________________________ ; LC01383: PHP SEI SEP #$20 JSL $C012D1 ; 乱数値の下位8ビット分を設定する PHA JSL $C012D1 ; 乱数値の上位8ビット分を設定する XBA PLA PLP PHA PLA RTL
;________________________________________________________________ ; ; 乱数生成(0〜Aの範囲) ; [入力] ; Aレジスタ: 乱数の最大値 ; [出力] ; Aレジスタ: 乱数値(0〜A) ;________________________________________________________________ ; LC0135F: PHP REP #$30 PHX INC ; A(乱数の最大値)に1を加算して…… BNE $136F ; ……その結果が0以外(= 乱数の最大値が$FFFF未満である)場合、ジャンプする JSL $C0138 ; 乱数の最大値が$FFFFの場合、単に16ビットの乱数値を取得するだけ PLX PLP PHA PLA RTL LC0136F: STA $30 JSL $C0138 ; 16ビットの乱数値を取得する LDX #$0030 ; JSL $C0114 ; (乱数の最大値 + 1)×16ビットの乱数値の乗算を行う LDA $32 ; 計算結果(32ビット)の上位16ビットを最終結果とする PLX PLP PHA PLA RTL
;________________________________________________________________ ; ; 宝箱入手判定時の処理 ;________________________________________________________________ ; LC2A9AE: PEA $23A9 PEA $00FF PEA $7E00 JSL $C9029E ; 不明 BEQ $AA14 STA $BE71 JSR $AA15 ; モンスターの宝箱入手難易度を取得して、乱数の最大値を決める JSL $C0135F ; 乱数を取得する BNE $AA14 ; 乱数値が0でない場合、ジャンプする(宝箱は入手できない) LDA $242C ; 宝箱のアイテム番号が…… CMP #$0000 ; ……0番である場合…… BEQ $AA14 ; ……宝箱は入手できない STA $BE79 JSR $AAF3 JSL $C1A867 ; メッセージを表示する DW $0033 ; (メッセージの番号) LDA #$0042 STA $23E4 JSL $C1A867 ; メッセージを表示する DW $0034 ; (メッセージの番号) JSL $C1E32E ; BGM/SEを鳴らす DW $0040 ; (BGM/SEの番号) JSL $C1A867 ; メッセージを表示する DW $0035 ; (メッセージの番号) LDA $242C JSL $C44824 ; 不明 DW $FFFF STA $BE77 LDX #$0037 CMP #$0020 BEQ $AA0C LDX #$0036 LC2AA0C: JSR $AAF3 ; 不明 TXA JSL $C1A87A ; 不明 LC2AA14: RTS LC2AA15: PHX PHY TAY JSL $C2CC92 ; 宝箱のアイテム番号を取得する DW $000C,$00FF ; (オフセット$000C(12)バイト目の全ビット($FF)を意味する) STA $242C JSL $C2CC92 ; 宝箱の入手難易度を取得する DW $001D,$0038 ; (オフセット$001D(29)バイト目のビット3〜5($38)を意味する) ASL TAX LDA $C2AA34,X ; 宝箱入手難易度をもとにして、乱数の最大値を決める PLY PLX RTS DW $0000,$0007,$000F,$001F,$003F,$007F,$00FF,$03FF ; 宝箱入手確率用のテーブル
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [入力] ; $A0: 乱数値域の下限値 ; $A1: 乱数値域の上限値 ; [出力] ; $A0: 乱数値 ; [使用] ; $1F: 乱数テーブル内の現在位置(1バイト) ; $C2EF04: 乱数テーブル(256バイト) ;________________________________________________________________ ; LC2EE18: SEP #$20 LDA $A1 BEQ $EE22 CMP $A0 BCS $EE26 LC2EE22: STZ $A0 BRA $EE56 LC2EE26: LDA $A0 PHA LDA #$00 XBA LDA $1F TAX LDA $C2EF04,X STA $A5 STZ $A6 LDA $A1 SEC SBC $A0 INC STA $A0 JSR $ED86 PLA STA $A0 LDA $A9 CLC ADC $A0 STA $A0 LDA $1F CMP #$FF BCC $EE54 STZ $1F LC2EE54: INC $1F LC2EE56: RTS LC2EF04: DB $EA,$65,$5A,$7B,$F4,$47,$0E,$AA,$47,$9F,$39,$9B,$5A,$E3,$B1,$84 DB $8D,$EE,$72,$80,$54,$34,$33,$18,$79,$A9,$D2,$E4,$23,$19,$22,$61 DB $09,$D7,$5D,$CA,$03,$28,$DC,$2B,$5E,$B3,$C9,$E8,$27,$66,$66,$F8 DB $50,$9E,$30,$0D,$64,$F2,$EB,$DC,$2E,$84,$E7,$67,$07,$23,$7A,$BF DB $AE,$30,$93,$CE,$60,$8E,$E2,$98,$91,$0C,$61,$EC,$FC,$89,$D1,$14 DB $73,$D8,$F5,$DE,$90,$91,$93,$2B,$4D,$50,$0A,$44,$8E,$28,$0A,$2D DB $A6,$B6,$B9,$BF,$F1,$1E,$D1,$33,$F6,$60,$00,$E5,$3B,$D0,$98,$0C DB $B6,$25,$66,$1C,$14,$D2,$22,$11,$22,$42,$DF,$68,$2E,$89,$7C,$EC DB $A8,$B0,$58,$B1,$C8,$B8,$68,$56,$11,$E4,$71,$6F,$EC,$7C,$6C,$34 DB $48,$85,$EF,$46,$55,$B5,$9B,$B6,$E4,$8D,$5A,$9C,$08,$E5,$09,$DF DB $D7,$DF,$44,$94,$21,$7A,$F0,$F8,$CD,$4A,$CC,$7F,$4A,$08,$0B,$F9 DB $C0,$7A,$4E,$3C,$66,$F4,$90,$E2,$3A,$60,$38,$06,$69,$96,$73,$03 DB $43,$01,$1E,$34,$E2,$BC,$C1,$AE,$87,$BC,$F6,$6A,$B6,$2B,$BC,$E7 DB $A5,$FE,$87,$B7,$86,$86,$9D,$76,$B1,$64,$82,$A5,$CC,$31,$0A,$6E DB $65,$CD,$53,$38,$A3,$93,$BC,$27,$83,$E3,$1F,$5E,$C1,$59,$D7,$25 DB $67,$89,$EE,$CD,$20,$1E,$6A,$EE,$47,$BF,$8F,$58,$56,$87,$29,$56 ;________________________________________________________________ ; ; 除算(8ビット÷8ビット) ; [入力] ; $A5: 被除数(1バイト) ; $A0: 除数(1バイト) ; [出力] ; $A7: 商(1バイト) ; $A9: 余(1バイト) ;________________________________________________________________ ; LC2ED86: SEP #$20 LDX $A5 STX $4204 LDA $A0 STA $4206 NOP NOP NOP NOP NOP NOP NOP NOP LDX $4214 STX $A7 LDX $4216 STX $A9 RTS
ロマンシング サ・ガ2の乱数生成ルーチンです。乱数表に256個の数値があるにもかかわらず、最初の1個目が使われないため、256周期ではなくて255周期になっています。また、乱数表の数値に妙に偏りがあり、例えば$66・$B6・$BCなどは乱数表中にそれぞれ4個ずつも存在していたりします。乱数の最大値も$FFではなくて$FEが最大です。
;________________________________________________________________ ; ; 技閃き判定時の処理 ;________________________________________________________________ ; LC2C443: LDY #$0000 LDA [$9A],Y ; 閃き判定を行う技の番号 STA $6F INY LDA [$9A],Y ; 閃き判定を行う技の閃き難易度 STA $70 LDA $6F ; 技番号と…… CMP #$86 ; ……$86(でたらめ矢)とを比較して…… BNE $C460 ; ……一致しない場合(閃こうとする技がでたらめ矢以外の場合)ジャンプする(通常の閃き判定を行う) LDY #$0005 LDA [$94],Y ; キャラクタの状態が…… BIT #$10 ; ……「暗い」状態であるか判定して…… BEQ $C460 ; ……一致しない場合はジャンプする(通常の閃き判定を行う) BRA $C465 ; それ以外の場合、無条件で閃く LC2C460: JSR $CA5C ; 乱数による閃き判定を行う BEQ $C468 ; 結果が$00(技閃きに失敗したことを意味する)の場合はジャンプする LC2C465: JSR $C47A ; 武器固有技関連の判定(?)を行う LC2C468: RTS LC2CA5C: STZ $A5 ; STZ $A6 ; 技能レベルの合計値を初期化する LDY #$0018 LC2CA63: LDA [$97],Y ; 技能レベル CLC ADC $A5 STA $A5 INY CPY #$001D BNE $CA63 ; 技能レベル5種類の合計値を求める LDA #$05 ; 除数に5を設定する STA $A0 ; 被除数に技能レベルの合計値を設定する JSR $ED86 ; 除算を行う LDA $A7 ; 除算結果(技能レベルの平均値) STA $71 LDA $71 ; 技能レベルの平均値と…… CMP $70 ; ……技閃き難易度とを比較して…… BCC $CA9C ; ……技能レベルの平均値が技閃き難易度より大きければジャンプする SEC SBC $70 STA $71 ; 技能レベルの平均値と技閃き難易度の差(絶対値) LDX #$0000 LC2CA89: LDA $C2CAE5,X ; CMP $71 ; BCS $CA95 ; 差分値から境界値を選択する INX INX BRA $CA89 LC2CA95: INX LDA $C2CAE5,X ; 境界値を取得する BRA $CAB7 LC2CA9C: LDA $70 SEC SBC $71 STA $71 ; 技能レベルの平均値と技閃き難易度の差(絶対値) LDX #$0000 LC2CAA6: LDA $C2CAD1,X ; CMP $71 ; BCS $CAB2 ; 差分値から境界値を選択する INX INX BRA $CAA6 LC2CAB2: INX LDA $C2CAD1,X ; 境界値を取得する LC2CAB7: STA $71 ; 境界値を格納する LDA #$01 ; 1から…… STA $A0 ; LDA #$FF ; ……255の範囲で…… STA $A1 ; JSR $EE18 ; ……乱数値を取得する LDA $71 ; 境界値と…… CMP $A0 ; 1〜255の乱数値とを比較して…… BCS $CACE ; 境界値が乱数値以上の場合はジャンプする(技を閃く) LDA #$00 ; Aに$00を格納してサブルーチンを終了する(技閃きに失敗したことを意味する) BRA $CAD0 LC2CACE: LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(技閃きに成功したことを意味する) LC2CAD0: RTS LC2CAD1: ; 閃き確率用テーブル(-1〜-255) DB $01,$0B DB $02,$08 DB $03,$05 DB $04,$03 DB $05,$01 DB $06,$00 DB $07,$00 DB $08,$00 DB $09,$00 DB $FF,$00 LC2CAE5: ; 閃き確率用テーブル(±0〜+255) DB $00,$32 DB $01,$12 DB $02,$15 DB $03,$18 DB $04,$1A DB $05,$1C DB $06,$1D DB $07,$1E DB $08,$1E DB $09,$1E DB $FF,$1E
;________________________________________________________________ ; ; 戦闘終了後のアイテム入手時の処理 ;________________________________________________________________ ; LC2E025: LDA #$07 STA $18 STA $307F90 LDA #$1F ; STA $5D ; アイテム入手確率判定用乱数の最大値(高確率) LDX #$004F ; STX $88 ; 入手できるアイテム(高確率)の番号へのインデックス JSR $E04D ; 倒した全モンスターの中を対象に、アイテム入手判定を行う BNE $E049 ; アイテム入手判定に成功した場合、ジャンプする LC2E03B: LDA #$3F ; STA $5D ; アイテム入手確率判定用乱数の最大値(低確率) LDX #$0050 ; STX $88 ; 入手できるアイテム(低確率)の番号へのインデックス JSR $E04D ; 倒した全モンスターの中を対象に、アイテム入手判定を行う BEQ $E04C ; アイテム入手判定に失敗した場合、ジャンプする LC2E049: JSR $E10E LC2E04C: RTS LC2E04D: LDX #$A000 STX $94 LDA #$7E STA $96 LDA $22 LC2E058: PHA LDY #$0007 LDA [$94],Y BIT #$80 BEQ $E082 LDY #$0006 LDA [$94],Y BIT #$02 BNE $E082 STZ $A0 ; 0から…… LDA $5D ; ……アイテム入手確率判定用乱数の最大値の範囲で…… STA $A1 JSR $EE18 ; ……乱数値を取得する LDA $A0 BNE $E082 ; 乱数値が0以外であった場合、ジャンプする(アイテム入手判定失敗) JSR $E093 ; アイテム欄に空きがあるかどうかの判定を行う BEQ $E082 ; アイテム欄に空きがない場合、ジャンプする PLA LDA #$FF ; Aに$FFを格納して…… BRA $E092 ; ……サブルーチンを終了する(アイテム入手判定に成功したことを意味する) LC2E082: REP #$20 LDA $94 ; CLC ; ADC #$0100 ; STA $94 ; 次のモンスターの判定に進む SEP #$20 PLA DEC BNE $E058 LC2E092: RTS LC2E093: LDY $88 ; 入手アイテムの番号へのインデックス LDA [$94],Y ; 入手アイテム番号が…… CMP #$FF ; ……$FF(アイテムなし)の場合…… BEQ $E0D3 ; ……ジャンプする(アイテム入手失敗) STA $6F LDA #$20 STA $70 LDX #$0000 LC2E0A4: LDA $7EF622,X ; X番目のアイテム欄の所持数が…… BEQ $E0C2 ; ……0の場合、ループ脱出(アイテム欄に空きがある) LDA $7EF621,X ; X番目のアイテム欄のアイテム番号が…… CMP $6F ; ……入手アイテム番号と同じ場合…… BEQ $E0BA ; ……ループ脱出 INX ; INX ; アイテム欄を1つ進める DEC $70 BNE $E0A4 LC2E0BA: LDA $7EF622,X ; 入手アイテムの所持数が…… CMP #$63 ; ……99個の場合…… BEQ $E0D3 ; ……ジャンプする(アイテム入手失敗) LC2E0C2: LDA $6F STA $7EF621,X ; アイテム欄に入手アイテムのアイテム番号を格納する LDA $7EF622,X ; INC ; STA $7EF622,X ; 入手アイテムの所持数を1増加する LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(アイテム入手判定に成功したことを意味する) LC2E0D3: RTS
;________________________________________________________________ ; ; 乱数生成(8ビット) ; [入力] ; $BF: 乱数値域の下限値 ; $C0: 乱数値域の上限値 ; [出力] ; $BF: 乱数値 ; [使用] ; $14: 乱数テーブル内の現在位置(1バイト) ; $D7E63C: 乱数テーブル(1024バイト) ;________________________________________________________________ ; LC2032E: SEP #$20 LDA $BF CMP $C0 BEQ $033E LDA $C0 BEQ $033E CMP $BF BCS $0342 LC2033E: STZ $BF BRA $0378 LC20342: LDA $BF PHA LDX $14 LDA $D7E63C,X STA $C4 STZ $C5 LDA $C0 SEC SBC $BF REP #$20 AND #$00FF INC STA $C6 JSR $02A3 SEP #$20 PLA STA $BF LDA $CA CLC ADC $BF STA $BF LDX $14 INX CPX #$0400 BCC $0376 LDX #$0000 LC20376: STX $14 LC20378: RTS LD7E63C: DB $E5,$7F,$72,$82,$6C,$87,$FA,$CC,$3D,$6A,$87,$26,$A8,$E8,$DC,$E3 DB $B0,$66,$D0,$22,$39,$5D,$18,$99,$BA,$07,$A6,$A5,$50,$A0,$D8,$38 DB $20,$59,$C4,$B1,$F5,$55,$6A,$5F,$14,$D8,$A9,$19,$07,$74,$AB,$BB DB $0F,$D9,$A9,$F6,$60,$AA,$3E,$B5,$D2,$60,$30,$10,$3E,$AB,$5C,$0C DB $A0,$65,$B1,$27,$49,$EC,$E3,$5F,$20,$11,$CA,$C0,$53,$DA,$0E,$AC DB $F1,$E0,$15,$D6,$46,$72,$DB,$BA,$77,$3F,$26,$FA,$C0,$55,$AD,$73 DB $C7,$0C,$43,$6B,$62,$46,$0C,$B1,$D3,$92,$40,$15,$CC,$1B,$23,$7A DB $3F,$F0,$13,$09,$4D,$1A,$EA,$AA,$E1,$72,$94,$5D,$3C,$4A,$81,$10 DB $7F,$D2,$71,$04,$0B,$B4,$B3,$F6,$AD,$F8,$4D,$8A,$7F,$10,$B6,$25 DB $E7,$9D,$12,$50,$24,$63,$12,$C4,$D5,$62,$73,$A9,$E3,$94,$3A,$BF DB $40,$D8,$1F,$F1,$57,$68,$D6,$8C,$38,$7B,$1F,$90,$43,$70,$BF,$68 DB $69,$94,$6A,$6C,$46,$6C,$24,$83,$A3,$94,$27,$4D,$B4,$98,$65,$1C DB $0D,$5B,$9B,$B2,$28,$6D,$1F,$0A,$09,$ED,$50,$94,$BB,$CF,$E2,$3B DB $4D,$A0,$61,$18,$79,$2F,$9F,$9D,$28,$2B,$7F,$33,$F8,$17,$3C,$FA DB $F4,$32,$A1,$3F,$2B,$AB,$60,$45,$C2,$BF,$E6,$FD,$D8,$9C,$EE,$52 DB $A8,$28,$28,$0B,$D1,$02,$AE,$06,$4B,$E2,$B8,$41,$46,$2D,$24,$ED DB $14,$D2,$58,$0D,$58,$67,$1B,$4E,$14,$FB,$D4,$B0,$D6,$22,$DF,$9C DB $9C,$AD,$5B,$F7,$2D,$96,$28,$EA,$81,$95,$7A,$59,$7E,$D3,$AF,$C2 DB $90,$CC,$52,$0D,$74,$40,$7C,$72,$37,$CA,$F7,$0E,$3D,$87,$DC,$C6 DB $D6,$4F,$84,$0E,$B6,$7A,$0F,$B7,$CA,$B8,$D9,$5A,$4F,$DF,$1B,$03 DB $9E,$CC,$10,$AC,$11,$32,$5C,$39,$72,$EE,$9A,$71,$5F,$CE,$B9,$37 DB $90,$C7,$19,$79,$65,$98,$91,$92,$34,$DD,$D5,$1E,$33,$03,$51,$F4 DB $7D,$1C,$7B,$D4,$8A,$15,$BF,$E8,$18,$48,$73,$33,$DD,$58,$76,$92 DB $8F,$ED,$79,$60,$FB,$B5,$8A,$5E,$D5,$B2,$5B,$FA,$6F,$49,$67,$1A DB $78,$9D,$EB,$6B,$0A,$9D,$57,$01,$86,$52,$A3,$A7,$24,$DD,$BD,$B8 DB $24,$34,$72,$66,$09,$F4,$00,$3A,$D3,$00,$C1,$C2,$95,$1C,$9E,$AF DB $E6,$53,$A4,$D0,$04,$DA,$00,$BE,$A7,$24,$B9,$9E,$6A,$F6,$68,$44 DB $AC,$28,$3E,$AA,$68,$53,$25,$00,$5E,$2B,$CD,$C4,$84,$40,$E4,$AF DB $2C,$D8,$54,$E2,$39,$39,$41,$1B,$F2,$F1,$AF,$65,$B3,$96,$F8,$0E DB $92,$75,$81,$48,$BF,$AB,$58,$47,$32,$B6,$AF,$C9,$E4,$D6,$D3,$50 DB $37,$E6,$96,$7A,$37,$80,$D0,$C9,$69,$8A,$EB,$43,$D0,$8B,$9F,$A9 DB $C9,$61,$CC,$57,$02,$33,$24,$62,$17,$3F,$82,$19,$AD,$DC,$B0,$82 DB $83,$D1,$72,$EC,$56,$D4,$8E,$BC,$9A,$59,$40,$7C,$5D,$01,$B7,$66 DB $55,$4F,$9D,$E9,$EE,$7C,$3F,$60,$62,$7E,$4F,$74,$9F,$2B,$ED,$74 DB $19,$8A,$DA,$8B,$3B,$36,$87,$20,$9F,$66,$6A,$51,$C0,$7D,$C8,$CF DB $C1,$3D,$5D,$8F,$90,$F4,$8C,$8B,$73,$C9,$0A,$1A,$49,$F6,$28,$8F DB $8A,$9C,$2F,$23,$57,$80,$74,$DC,$20,$54,$15,$7E,$2F,$64,$86,$2E DB $28,$46,$E0,$D3,$3E,$E6,$99,$6A,$37,$13,$92,$C4,$84,$D0,$29,$F9 DB $77,$31,$B9,$FD,$E6,$EB,$39,$17,$4D,$E3,$56,$BB,$A8,$74,$50,$05 DB $2E,$20,$E8,$BD,$1A,$78,$22,$40,$26,$E6,$B4,$A9,$F6,$26,$E6,$95 DB $0B,$0D,$B2,$DF,$73,$0C,$E6,$AF,$66,$6F,$30,$3D,$78,$C8,$32,$14 DB $85,$1D,$A2,$50,$93,$2B,$0C,$8B,$C3,$EF,$29,$FB,$92,$3F,$03,$FE DB $FC,$10,$3C,$0B,$D0,$D0,$B9,$44,$35,$6E,$90,$B3,$37,$D8,$E3,$57 DB $EA,$AC,$A7,$0B,$E5,$5D,$6A,$07,$21,$72,$14,$69,$15,$42,$CA,$11 DB $90,$36,$E5,$3C,$21,$86,$1B,$AC,$10,$F4,$51,$CA,$C7,$76,$48,$85 DB $28,$D8,$FC,$69,$17,$C8,$7B,$29,$5A,$D0,$84,$9C,$08,$AE,$B7,$E0 DB $15,$99,$AA,$AC,$CF,$D4,$9E,$7D,$59,$32,$39,$2B,$DA,$51,$EE,$93 DB $92,$C7,$13,$E1,$F9,$03,$2A,$25,$97,$08,$F7,$BA,$C1,$E2,$6F,$C0 DB $65,$EC,$F1,$12,$17,$40,$06,$08,$00,$F1,$79,$78,$EB,$74,$94,$30 DB $0B,$3C,$C7,$E7,$2E,$FE,$8C,$E9,$8D,$B1,$55,$67,$E3,$96,$C3,$BD DB $69,$81,$0E,$1D,$FA,$28,$BC,$D8,$7D,$1B,$31,$53,$C2,$C6,$9F,$C7 DB $FE,$94,$E3,$6B,$9D,$0A,$E5,$21,$7C,$83,$71,$40,$DC,$DF,$B2,$A1 DB $14,$44,$BE,$7B,$4C,$C8,$DB,$B8,$D8,$31,$E9,$58,$73,$0B,$24,$02 DB $68,$4B,$1C,$D6,$00,$4E,$24,$32,$B0,$4E,$0C,$DE,$65,$AE,$E4,$F2 DB $65,$BC,$2F,$D5,$28,$3A,$A7,$2C,$24,$54,$99,$1F,$5D,$DF,$61,$42 DB $4C,$76,$12,$91,$D7,$D1,$62,$40,$82,$00,$D1,$5A,$04,$D0,$B0,$72 DB $E7,$10,$F5,$D1,$77,$EF,$91,$F3,$7D,$3F,$A2,$BB,$2D,$41,$C3,$A8 DB $BB,$49,$D2,$FE,$F3,$F5,$E6,$29,$56,$22,$D9,$C4,$8A,$EE,$17,$1E DB $33,$FD,$96,$90,$EE,$BA,$B5,$8C,$8F,$C9,$53,$3F,$D9,$85,$E1,$10 DB $D3,$12,$D6,$FD,$EE,$79,$24,$87,$1C,$5A,$29,$2C,$96,$0E,$46,$AF DB $EA,$E3,$00,$AE,$0F,$C5,$5C,$2F,$90,$67,$E4,$35,$28,$5F,$00,$91 DB $BE,$3B,$84,$E9,$AF,$F5,$39,$33,$D0,$69,$2F,$9A,$12,$0F,$17,$9D DB $3E,$B9,$8D,$C6,$A5,$19,$7A,$50,$C2,$A7,$7E,$A5,$A6,$5E,$8D,$7F DB $B1,$CA,$2B,$9A,$69,$E1,$70,$3E,$7C,$2D,$48,$96,$33,$AE,$10,$18 ;________________________________________________________________ ; ; 除算(16ビット÷16ビット) ; [入力] ; $C4: 被除数(2バイト) ; $C6: 除数(2バイト) ; [出力] ; $C8: 商(2バイト) ; $CA: 余(2バイト) ;________________________________________________________________ ; LC202A3: REP #$20 LDA $C4 PHA LDA $C6 PHA LDA #$0000 STA $C8 STA $CA LDX #$0010 LC202B5: ASL $C8 ASL $C4 ROL CMP $C6 BCC $02C2 SBC $C6 INC $C8 LC202C2: DEX BNE $02B5 STA $CA PLA STA $C6 PLA STA $C4 RTS
ロマンシング サ・ガ3の乱数生成ルーチンです。乱数表の数値の数はやや多めで1024個です。ロマンシング サ・ガ2と同様に、やはり乱数表の数値には偏りがあります。乱数の最大値も2と同じく$FFではなくて$FEが最大です。
;________________________________________________________________ ; ; 技閃き判定時の処理 ;________________________________________________________________ ; LC2C0D5: LDA $43 ; 見切り技閃き難易度 STA $83 LDA $44 ; 敵閃きレベル STA $84 LDA $45 ; $00が設定されているはず STA $85 JSL $FEE8A7 ; 閃き判定の乱数の境界値を取得する STA $8A STZ $8B STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF REP #$20 AND #$00FF INC ; 乱数値に1を加えて CMP $8A ; ……境界値と比較して…… SEP #$20 BCC $C104 ; ……「乱数値 + 1」が境界値未満であった場合、ジャンプする(見切り技を閃く) LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(見切り技閃きに失敗したことを意味する) BRA $C106 LC2C104: LDA #$00 ; Aに$00を格納してサブルーチンを終了する(見切り技閃きに成功したことを意味する) LC2C106: RTS LC2C107: LDA $143C ; 閃き成功回数が…… CMP #$08 ; ……8回の場合…… BCS $C151 ; ……ジャンプする(技を閃かない) LDA $43 ; 技閃き難易度 STA $83 LDA $44 ; 敵閃きレベル STA $84 LDA $45 ; 技の王冠があるか、最大術ポイントが0であれば$00が設定されている STA $85 JSL $FEE8A7 ; 閃き判定の乱数の境界値を取得する STA $C4 ; この境界値を被乗数とする STZ $C5 LDA #$08 ; SEC ; SBC $143C ; 「8 - 閃き成功回数」を…… STA $C6 ; ……乗数とする STZ $C7 JSR $0242 ; 乗算を行う REP #$20 LDA $C8 ; 乗算結果を…… JSR $0413 ; ……8で除算する STA $8A ; この値を最終的な境界値とする SEP #$20 STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF REP #$20 AND #$00FF INC ; 乱数値に1を加えて CMP $8A ; ……境界値と比較して…… SEP #$20 BCC $C155 ; ……「乱数値 + 1」が境界値未満であった場合、ジャンプする(技を閃く) LC2C151: LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(技閃きに失敗したことを意味する) BRA $C15A LC2C155: INC $143C ; 閃き成功回数に1を加える LDA #$00 ; Aに$00を格納してサブルーチンを終了する(技閃きに成功したことを意味する) LC2C15A: RTS LC2C15B: LDA $143C ; 閃き成功回数が…… CMP #$08 ; ……8回の場合…… BCS $C188 ; ……ジャンプする(技を閃かない) LDA $43 ; 技閃き難易度 STA $83 LDA $44 ; 敵閃きレベル STA $84 LDA $45 ; $00が設定されているはず STA $85 JSL $FEE8A7 ; 閃き判定の乱数の境界値を取得する BEQ $C188 ; 境界値が0であれば、ジャンプする(技を閃かない) STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF BNE $C188 ; 乱数値が0以外であれば、ジャンプする(技を閃かない) INC $143C ; 閃き成功回数に1を加える LDA #$00 ; Aに$00を格納してサブルーチンを終了する(技閃きに成功したことを意味する) BRA $C18A LC2C188: LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(技閃きに失敗したことを意味する) LC2C18A: RTS ;________________________________________________________________ ; ; 閃き判定の乱数の境界値を取得 ; [入力] ; $83: 技の閃き難易度 ; $84: 敵の閃きレベル ; $85: 使用するテーブル ; [出力] ; Aレジスタ: 閃き判定の乱数の境界値 ;________________________________________________________________ ; LFEE8A7: LDA #$00 XBA LDA $83 ; 技閃き難易度と…… CMP $84 ; ……敵閃きレベルとを比較して…… BCS $E8C7 ; ……技閃き難易度が敵閃きレベル以上であった場合はジャンプする LDA $84 SEC SBC $83 CMP #$0A BEQ $EABD BCC $EABD LDA #$0A LFEEABD: STA $86 LDA #$0A SEC SBC $86 TAX ; 最終的には「技閃き難易度 - 敵閃きレベル + 10(最低値は0)」がXに格納される BRA $E8D6 LFEE8C7: SEC SBC $84 CMP #$0A BEQ $E8D2 BCC $E8D2 LDA #$0A LFEE8D2: CLC ADC #$0A TAX ; 最終的には「技閃き難易度 - 敵閃きレベル + 10(最高値は20)」がXに格納される LFEE8D6: LDA $85 BNE $E8E0 ; 技の王冠がなく最大術ポイントが0でない場合、ジャンプする(閃き確率が低くなる) LDA $FE3180,X ; 閃き確率が高いテーブルを使用して、閃き判定の乱数の境界値を取得する BRA $E8E4 LFEE8E0: LDA $FE3195,X ; 閃き確率が低いテーブルを使用して、閃き判定の乱数の境界値を取得する LFEE8E4: RTL LFE3180: DB $32,$2F,$2E,$2C,$2A,$27,$25,$22,$1F,$1C,$19,$15,$12,$0F,$0C,$09,$06,$04,$02,$01,$00 ; 閃き確率用テーブル(高確率) LFE3195: DB $28,$27,$27,$25,$24,$22,$1F,$1D,$1A,$17,$14,$10,$0D,$0A,$08,$05,$03,$01,$00,$00,$00 ; 閃き確率用テーブル(低確率)
;________________________________________________________________ ; ; キャラクタ成長処理 ;________________________________________________________________ ; LC2DB7C: LDX #$153D STX $9E LDX #$2280 STX $B8 LDA $22 ; AND #$FE ; STA $22 ; 「フラグ0」をクリアする LDA #$00 LC2DB8E: PHA LDY #$0000 STA ($9E),Y LDX $B8 CPX $143E BEQ $DBBC LDY #$0007 ; LDA ($B8),Y ; キャラクタの状態を取得する BIT #$84 ; 戦闘不能であるか離脱しているかを判定する BNE $DBBC ; LDY #$0005 ; LDA ($B8),Y ; キャラクタの状態を取得する BIT #$C0 ; 気絶であるか石化しているかを判定する BNE $DBBC ; JSR $DE63 ; 最大ヒットポイントが上昇する確率を計算する JSR $DDCE ; 最大技ポイント・最大術ポイントが上昇する確率を計算する JSR $DEBE ; 各武器/術レベルが上昇する確率を計算する JSR $DF47 ; 確率低下修正値を計算する JSR $DBE6 ; 成長判定と成長処理を行う LC2DBBC: REP #$20 LDA $9E CLC ADC #$000F STA $9E LDA $B8 CLC ADC #$0080 STA $B8 SEP #$20 PLA INC CMP $2D BNE $DB8E LDA $22 ; BIT #$01 ; BNE $DBDF ; 「フラグ0」がセットされている場合、ジャンプする JSR $DF6E ; パーティの中で最大ヒットポイントが最も低いキャラクタの最大ヒットポイントを1上げる LC2DBDF: LDA $22 ; AND #$FC ; STA $22 ; 「フラグ0」と「フラグ1」をクリアする RTS ;________________________________________________________________ ; ; 最大ヒットポイントが上昇する確率の計算 ;________________________________________________________________ ; LC2DE63: REP #$20 LDY #$0016 ; LDA ($B8),Y ; 最大ヒットポイントを取得する STA $C4 ; 被乗数に最大ヒットポイントを設定する SEP #$20 LDA $FE3170 ; ($FE3170の内容は$05(5)) STA $C6 ; 乗数に5を設定する STZ $C7 JSR $0242 ; 「最大ヒットポイント×5」の計算をして、結果を$C8に格納する SEP #$20 LDX $C8 ; 「最大ヒットポイント×5」を取得して…… STX $C4 ; ……被乗数に設定する LDA $FE3171 ; ($FE3171の内容は$80(128)) STA $C6 ; 乗数に128を設定する STZ $C7 JSR $02A3 ; 「最大ヒットポイント×5÷128」の計算をして、結果を$C8に(剰余を$CAに)格納する SEP #$20 LDA $C8 ; 最終的な計算結果は「最大ヒットポイント×5÷128」となる STA $83 LDA $2032 ; リーダー(1体目)のモンスターの「レベルの上がりやすさ」を取得する STA $82 LDY #$0000 ; LDA ($B8),Y ; キャラクタ番号を取得する REP #$20 AND #$00FF JSR $0407 ; キャラクタ番号を16倍する TAX SEP #$20 LDA $FE31CE,X ; キャラクタごとの「最大ヒットポイントの上がりやすさ」を取得する CMP #$FF ; (「最大ヒットポイントの上がりやすさ」が$FFの場合、(原則的に)最大ヒットポイントは上昇しない) BNE $DEB1 LDA #$00 BRA $DEBA LC2DEB1: CLC ADC $82 ; 「最大ヒットポイントの上がりやすさ」と「リーダーのモンスターのレベルの上がりやすさ」を加算する STA $84 JSL $FEE8E5 ; 「最大ヒットポイントの上がりやすさ + リーダーのモンスターのレベルの上がりやすさ」と ; 「最大ヒットポイント×5÷128」との差から、「最大ヒットポイントが上昇する確率」を計算する LC2DEBA: STA $1498 ; 「最大ヒットポイントが上昇する確率」を格納する RTS ;________________________________________________________________ ; ; 最大技ポイント・最大術ポイントが上昇する確率の計算 ;________________________________________________________________ ; LC2DDCE: LDX $9E PHX STZ $1496 : 「最大技ポイントが上昇する確率」を0で初期化する STZ $1497 : 「最大術ポイントが上昇する確率」を0で初期化する REP #$20 LDY #$0034 ; LDA ($B8),Y ; 使用した技・術の系統を表すビット列を取得する BIT #$F800 ; 剣/大剣・斧/棍棒・槍/小剣・弓・体術のどれかに属する技(術)を使用したか判定する SEP #$20 BEQ $DE1B ; どれも使用していない場合、ジャンプする(最大技ポイントは上がらない) LDY #$0000 ; LDA ($B8),Y ; キャラクタ番号を取得する REP #$20 AND #$00FF JSR $0407 ; キャラクタ番号を16倍する TAX SEP #$20 LDA $FE31CC,X ; キャラクタごとの「最大技ポイントの上がりやすさ」を取得する(実際には値は使用されていない) CMP #$FF ; (「最大技ポイントの上がりやすさ」が$FFの場合、最大技ポイントは上昇しない) BEQ $DE1B LDY #$001B ; LDA ($B8),Y ; 最大技ポイントを取得する STA $83 LDY #$0025 ; 剣レベルから…… STY $9E ; LDY #$002A ; ……体術レベルの範囲で…… STY $A0 ; JSL $FECCA7 ; 現在の武器レベルでの基準最大技ポイントを計算する(武器レベル5種類の合計値 + 最高レベルの値×2) STA $84 JSL $FEE8E5 ; 「現在の武器レベルでの基準最大技ポイント」と ; 「現在の最大技ポイント」との差から、「最大技ポイントが上昇する確率」を計算する STA $1496 ; 「最大技ポイントが上昇する確率」を格納する LC2DE1B: REP #$20 LDY #$0034 ; LDA ($B8),Y ; 使用した技・術の系統を表すビット列を取得する BIT #$07E0 ; 蒼竜・朱鳥・白虎・玄武・太陽・月のどれかに属する術(技)を使用したか判定する SEP #$20 BEQ $DE5F ; どれも使用していない場合、ジャンプする(最大術ポイントは上がらない) LDY #$0000 ; LDA ($B8),Y ; キャラクタ番号を取得する REP #$20 AND #$00FF JSR $0407 ; キャラクタ番号を16倍する TAX SEP #$20 LDA $FE31CD,X ; キャラクタごとの「最大術ポイントの上がりやすさ」を取得する(実際には値は使用されていない) CMP #$FF ; (「最大術ポイントの上がりやすさ」が$FFの場合、最大術ポイントは上昇しない) BEQ $DE5F LDY #$001D ; LDA ($B8),Y ; 最大術ポイントを取得する STA $83 LDY #$002A ; 蒼竜術レベルから…… STY $9E ; LDY #$0030 ; ……月術レベルの範囲で…… STY $A0 ; JSL $FECCA7 ; 現在の術レベルでの基準最大術ポイントを計算する(術レベル2種類の合計値 + 最高レベルの値×2) STA $84 JSL $FEE8E5 ; 「現在の術レベルでの基準最大術ポイント」と ; 「現在の最大術ポイント」との差から、「最大術ポイントが上昇する確率」を計算する STA $1497 ; 「最大術ポイントが上昇する確率」を格納する LC2DE5F: PLX STX $9E RTS ;________________________________________________________________ ; ; 各武器/術レベルが上昇する確率の計算 ;________________________________________________________________ ; LC2DEBE: LDX $9E PHX LDA $2032 ; リーダー(1体目)のモンスターの「レベルの上がりやすさ」を取得して…… STA $82 ; ……$82に格納する LDX #$1489 STX $9E LDY #$0000 ; LDA ($B8),Y ; キャラクタ番号を取得する REP #$20 AND #$00FF JSR $0407 ; キャラクタ番号を16倍する CLC ADC #$0000 TAX LDY #$0034 ; LDA ($B8),Y ; 使用した技・術の系統を表すビット列を取得する STA $8A SEP #$20 LDY #$0025 ; 剣レベルから(月術レベルまで)順番に判定する LC2DEE9: PHX PHY REP #$20 ASL $8A ; (現在判定中の系統に属する)技・術を使用した場合、Cフラグがセットされる SEP #$20 BCC $DF10 ; (現在判定中の系統に属する)技・術を使用していない場合、ジャンプする(その武器/術レベルは上昇しない) LDA #$00 XBA LDA ($B8),Y ; (現在判定中の)武器/術レベルを取得する STA $83 LDA $FE31BF,X ; キャラクタごとの(現在判定中の)武器/術レベルの上がりやすさを取得する CMP #$FF ; (「(現在判定中の)武器/術レベルの上がりやすさ」が$FFの場合、その武器/術レベルは上昇しない) BEQ $DF10 CLC ADC $82 ; 「(現在判定中の系統の)武器/術レベルの上がりやすさ」にリーダー(1体目)のモンスターの「レベルの上がりやすさ」を加算する STA $84 JSR $DF26 ; さらに「(成長しなかった)戦闘の回数」に応じて「レベルの上がりやすさ」が修正される JSL $FEE8E5 ; 「最終的なレベルの上がりやすさ」と「(現在判定中の系統の)現在の武器/術レベル」との差から、 ; 「(現在判定中の系統の)武器/術レベルが上昇する確率」を計算する BRA $DF12 LC2DF10: LDA #$00 LC2DF12: STA ($9E) ; 「(現在判定中の系統の)武器/術レベルが上昇する確率」を格納する LDX $9E ; INX ; STX $9E ; 次の系統の武器/術レベルに進む PLY PLX INX INY CPY #$0030 ; (剣レベルから)月術レベルまで判定を繰り返す BNE $DEE9 PLX STX $9E RTS LC2DF26: LDY #$007A ; LDA ($B8),Y ; 「(成長しなかった)戦闘の回数」を取得する BEQ $DF46 ; 「(成長しなかった)戦闘の回数」が0であればジャンプする(修正なし) STA $BF ; 被乗数「(成長しなかった)戦闘の回数」を設定する LDA $FE317E ; ($FE317Eの内容は$1A(20)) STA $C0 ; 乗数に20を設定する JSR $0227 ; 「(成長しなかった)戦闘の回数×20」の計算をして、結果を$C4に格納する REP #$20 LDA $C4 ; 「(成長しなかった)戦闘の回数×20」を取得して…… JSR $040E ; ……256で割る SEP #$20 CLC ADC $84 ; STA $84 ; 「レベルの上がりやすさ」に「(成長しなかった)戦闘の回数×20÷256」を加算する LC2DF46: RTS ;________________________________________________________________ ; ; 確率低下修正値の計算 ;________________________________________________________________ ; LC2DF47: STZ $C4 ; STZ $C5 ; レベル合計値を0で初期化する LDY #$0025 ; 剣レベルから(不明レベルまで)順番に処理する LC2DF4E: LDA ($B8),Y ; (現在処理中の)武器/術レベルを取得する REP #$20 AND #$00FF CLC ADC $C4 ; STA $C4 ; レベル合計値に加算する SEP #$20 INY CPY #$0031 ; (剣レベルから)不明レベルまで順番に処理する BNE $DF4E JSR $03B1 ; レベル合計値から確率低下修正値を計算する SEP #$20 LDA $C4 LSR STA $1499 ; 確率低下修正値を格納する RTS LC203B1: REP #$20 LDA $C4 STA $C8 LDA #$0001 STA $C6 STZ $C4 BRA $03C6 LC203C0: INC $C4 INC $C6 INC $C6 LC203C6: LDA $C8 SEC SBC $C6 STA $C8 BCS $03C0 RTS ;________________________________________________________________ ; ; 成長判定 & 成長処理を行う ;________________________________________________________________ ; LC2DBE6: LDA $22 ; AND #$FD ; STA $22 ; 「フラグ1」をクリアする LDY #$0032 ; LDA ($B8),Y ; 成長タイプを取得する REP #$20 AND #$00FF JSR $0407 ; 成長タイプを16倍する CLC ADC #$34BF ; STA $B0 ; SEP #$20 ; LDA #$FE ; STA $B2 ; アドレスを生成する LDA #$10 LC2DC07: PHA LDX $B0 ; PHX ; LDA $B2 ; PHA ; アドレスを退避する LDA [$B0] ; 成長判定を行うパラメータの種類を取得する CMP #$FF BEQ $DC35 CMP #$0C ; ($0Cは対応するレベルが存在しない) BEQ $DC35 ; CMP #$0B ; BCS $DC21 ; JSR $DD54 ; 各種武器/術レベルの上昇判定と上昇処理を行う BRA $DC35 LC2DC21: CMP #$0D ; ($0Dは最大技ポイントを示す) BEQ $DC29 ; CMP #$0E ; ($0Eは最大術ポイントを示す) BNE $DC2E ; LC2DC29: JSR $DCED ; 最大技ポイント/最大術ポイントの上昇判定と上昇処理を行う BRA $DC35 LC2DC2E: CMP #$0F ; ($0Fは最大ヒットポイントを示す) BNE $DC35 ; JSR $DC53 ; 最大ヒットポイントの上昇判定と上昇処理を行う LC2DC35: PLA STA $B2 PLX INX STX $B0 PLA DEC BNE $DC07 LDA $22 BIT #$02 BNE $DC52 LDY #$007A ; LDA ($B8),Y ; 「(成長しなかった)戦闘の回数」を取得して…… CMP #$FF ; ……$FF(255)と比較して…… BEQ $DC52 ; ……「(成長しなかった)戦闘の回数」が255回であれば、ジャンプする(「(成長しなかった)戦闘の回数」は増加しない) INC ; STA ($B8),Y ; 「(成長しなかった)戦闘の回数」を1増加する LC2DC52: RTS ;________________________________________________________________ ; ; 最大ヒットポイントの上昇判定 & 上昇処理 ;________________________________________________________________ ; LC2DC53: REP #$20 LDY #$0016 ; LDA ($B8),Y ; 最大ヒットポイントを取得する CMP #$03E7 ; 最大ヒットポイントと$03E7(999)とを比較して…… BCC $DC68 ; ……最大ヒットポイントが999未満の場合、ジャンプする LDA #$03E7 ; STA ($B8),Y ; 最大ヒットポイントが999以上の場合、999にする SEP #$20 BRA $DC8A ; ジャンプする(最大ヒットポイントは上昇しない) LC2DC68: SEP #$20 LDA $1498 ; 「最大ヒットポイントが上昇する確率」を取得する BEQ $DC8A ; 「最大ヒットポイントが上昇する確率」が0の場合、ジャンプする(最大ヒットポイントは上昇しない) STA $8A STZ $8B STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF REP #$20 AND #$00FF INC ; 乱数値に1を加えて CMP $8A ; ……「最大ヒットポイントが上昇する確率」と比較して…… SEP #$20 BCC $DC8B ; ……「乱数値 + 1」が「最大ヒットポイントが上昇する確率」未満であった場合、ジャンプする LC2DC8A: RTS LC2DC8B: LDY #$007A ; LDA #$00 ; STA ($B8),Y ; 「(成長しなかった)戦闘の回数」を0に戻す LDA $22 ; ORA #$02 ; STA $22 ; 「フラグ1」をセットする JSR $DCB3 ; 最大ヒットポイントを増加する LDA $22 ; BIT #$20 ; BNE $DC8A ; 不明 LDA $22 ; ORA #$01 ; STA $22 ; 「フラグ0」をセットする JSL $FEEBC4 ; 一回転して足を上げるアニメーション JSR $CD7A ; 「(能力)がアップ!」の表示 JSR $DDB1 ; レベルアップ確率を軒並み低下させる BRA $DC8A LC2DCB3: LDY #$0021 ; LDA ($B8),Y ; 体力を取得する LSR ; LSR ; 体力の値を4で割る STA $8A STZ $8B STA $C0 ; (最高値は「体力÷4」) LDA #$01 STA $BF ; (最低値は1) JSR $032E ; 1〜「体力÷4」の範囲の乱数値を取得する LDA $BF REP #$20 AND #$00FF CLC ADC $8A ; 1〜「体力÷4」の範囲の乱数値に「体力÷4」を加算して…… ADC #$0002 ; ……さらに2を加算して…… STA $8A ; ……最終的な最大ヒットポイント増加量とする LDY #$0016 ; LDA ($B8),Y ; 最大ヒットポイントを取得して…… CLC ; ADC $8A ; ……最大ヒットポイント増加量を加算する CMP #$03E7 ; 最大ヒットポイントと$03E7(999)とを比較して…… BEQ $DCE8 ; ……最大ヒットポイントが999と等しい場合は、ジャンプする BCC $DCE8 ; ……最大ヒットポイントが999未満の場合は、ジャンプする LDA #$03E7 ; 最大ヒットポイントが999以上の場合、999にする LC2DCE8: STA ($B8),Y SEP #$20 RTS ;________________________________________________________________ ; ; 最大技ポイント/最大術ポイントの上昇判定 & 上昇処理 ;________________________________________________________________ ; LC2DCED: CMP #$0D ; ($0Dは最大技ポイントを示す) BNE $DCF9 ; LDY #$001B ; (最大技ポイントへのインデックス) LDX #$000D ; (「最大技ポイントが上昇する確率」へのインデックス) BRA $DCFF LC2DCF9: LDY #$001D ; (最大術ポイントへのインデックス) LDX #$000E ; (「最大術ポイントが上昇する確率」へのインデックス) LC2DCFF: SEC SBC #$0D STA $3D LDA ($B8),Y ; 最大技(術)ポイントを取得する CMP #$FA ; 最大技(術)ポイントと$FA(250)とを比較して…… BCC $DD10 ; ……最大技(術)ポイントが250未満の場合は、ジャンプする LDA #$FA ; STA ($B8),Y ; 最大技(術)ポイントが999以上の場合、999にする BRA $DD34 LC2DD10: SEP #$20 LDA $1489,X ; 「最大技(術)ポイントが上昇する確率」を取得する BEQ $DD34 ; 「最大技(術)ポイントが上昇する確率」が0の場合、ジャンプする(最大技(術)ポイントは上昇しない) PHY STA $8A STZ $8B STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF REP #$20 AND #$00FF INC ; 乱数値に1を加えて CMP $8A ; ……「最大技(術)ポイントが上昇する確率」と比較して…… SEP #$20 BCC $DD35 ; ……「乱数値 + 1」が「最大技(術)ポイントが上昇する確率」未満であった場合、ジャンプする PLY LC2DD34: RTS LC2DD35: PLY LDA ($B8),Y ; INC ; STA ($B8),Y ; 最大技(術)ポイントを1増加する LDY #$007A ; LDA #$00 ; STA ($B8),Y ; 「(成長しなかった)戦闘の回数」を0に戻す LDA $22 ; ORA #$03 ; STA $22 ; 「フラグ0」と「フラグ1」をセットする JSL $FEEBC4 ; 一回転して足を上げるアニメーション JSR $CDB7 ; 「(能力)がアップ!」の表示 JSR $DDB1 ; レベルアップ確率を軒並み低下させる BRA $DD34 ;________________________________________________________________ ; ; 各種武器/術レベルの上昇判定 & 上昇処理 ;________________________________________________________________ ; LC2DD54: STA $3D REP #$20 AND #$00FF TAX CLC ADC #$0025 TAY SEP #$20 LDA ($B8),Y ; (現在判定中の)武器/術レベルを取得する CMP #$32 ; (現在判定中の)武器/術レベルと$32(50)とを比較して…… BCC $DD6F ; ……(現在判定中の)武器/術レベルが50未満の場合、ジャンプする LDA #$32 ; STA ($B8),Y ; (現在判定中の)武器/術レベルが50以上の場合、50にする BRA $DD91 ; ジャンプする((現在判定中の)武器/術レベルは上昇しない) LC2DD6F: LDA $1489,X ; 「(現在判定中の)武器/術レベルが上昇する確率」を取得する BEQ $DD91 ; 「(現在判定中の)武器/術レベルが上昇する確率」が0の場合、ジャンプする((現在判定中の)武器/術レベルは上昇しない) PHY STA $8A STZ $8B STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF REP #$20 AND #$00FF INC ; 乱数値に1を足して CMP $8A ; ……「(現在判定中の)武器/術レベルが上昇する確率」と比較して…… SEP #$20 BCC $DD92 ; ……「乱数値 + 1」が「(現在判定中の)武器/術レベルが上昇する確率」未満であった場合、ジャンプする PLY LC2DD91: RTS LC2DD92: PLY LDA ($B8),Y ; INC ; STA ($B8),Y ; (現在判定中の)武器/術レベルを1増加する LDY #$007A ; LDA #$00 ; STA ($B8),Y ; 「(成長しなかった)戦闘の回数」を0に戻す LDA $22 ; ORA #$03 ; STA $22 ; 「フラグ0」と「フラグ1」をセットする JSL $FEEBC4 ; 一回転して足を上げるアニメーション JSR $CDB7 ; 「(能力)がアップ!」の表示 JSR $DDB1 ; レベルアップ確率を軒並み低下させる BRA $DD91 ;________________________________________________________________ ; ; レベルアップする確率を低下 ;________________________________________________________________ ; LC2DDB1: LDX #$0000 LC2DDB4: LDA $1489,X ; レベルアップする確率を取得して…… CMP $1499 ; ……確率低下修正値と比較して…… BCS $DDC0 ; ……レベルアップする確率が確率低下修正値以上の場合はジャンプする LDA #$00 ; レベルアップする確率を0にする BRA $DDC4 LC2DDC0: SEC SBC $1499 ; レベルアップする確率から確率低下修正値を引く LC2DDC4: STA $1489,X INX CPX #$0010 ; 全パラメータのレベルアップ確率について処理する BNE $DDB4 RTS ;________________________________________________________________ ; ; パーティの中で最大ヒットポイントが最も低いキャラクタの最大ヒットポイントを1上げる ;________________________________________________________________ ; LC2DF6E: LDX #$153D STX $9E LDX #$2280 STX $B8 LDX #$03E7 ; STX $8A ; パーティの中で最も低い最大ヒットポイント(を算出するための領域) LDA #$00 LC2DF7F: PHA LDY #$0000 STA ($9E),Y LDY #$0007 ; LDA ($B8),Y ; キャラクタの状態を取得する BIT #$84 ; 戦闘不能であるか離脱しているかを判定する BNE $DFAE ; LDY #$0005 ; LDA ($B8),Y ; キャラクタの状態を取得する BIT #$C0 ; 気絶であるか石化しているかを判定する BNE $DFAE ; REP #$20 LDY #$0016 ; LDA ($B8),Y ; 最大ヒットポイントを取得して…… CMP $8A ; ……パーティの中で最も低い最大ヒットポイントと比較して…… BCS $DFAE ; ……最大ヒットポイントがパーティの中で最も低い最大ヒットポイント以上の場合はジャンプする STA $8A SEP #$20 LDX $9E STX $A0 LDX $B8 STX $B6 LC2DFAE: REP #$20 LDA $9E CLC ADC #$000F STA $9E LDA $B8 CLC ADC #$0080 STA $B8 SEP #$20 PLA INC CMP $2D BNE $DF7F LDX $8A ; パーティの中で最も低い最大ヒットポイントと…… CPX #$03E7 ; ……$03E7(999)を比較して…… BEQ $DFEA ; ……パーティの中で最も低い最大ヒットポイントが999である場合、ジャンプする(最大ヒットポイントは上昇しない) LDX $A0 STX $9E LDX $B6 STX $B8 REP #$20 LDY #$0016 ; LDA ($B8),Y ; INC ; STA ($B8),Y ; パーティで最も最大ヒットポイントの低いキャラクタの最大ヒットポイントを1上げる SEP #$20 JSL $FEEBC4 ; 一回転して足を上げるアニメーション JSR $CD7A ; 「(能力)がアップ!」の表示 LC2DFEA: RTS ;________________________________________________________________ ; ; レベルアップ判定の乱数の境界値を取得 ; [入力] ; $83: レベルアップの難易度 ; $84: レベルの上がりやすさ ; [出力] ; Aレジスタ: レベルアップ判定の乱数の境界値 ;________________________________________________________________ ; LFEE8E5: LDA #$00 XBA LDA $83 ; レベルアップの難易度と…… CMP $84 ; ……レベルの上がりやすさを比較して…… BCS $E905 ; ……レベルアップの難易度がレベルの上がりやすさ以上であった場合はジャンプする LDA $84 SEC SBC $83 CMP #$0A BEQ $E8FB BCC $E8FB LDA #$0A LFEE8FB: STA $85 LDA #$0A SEC SBC $85 TAX ; 最終的には「レベルアップの難易度 - レベルの上がりやすさ + 10(最低値は0)」がXに格納される BRA $E914 LFEE905: SEC SBC $84 CMP #$0A BEQ $E910 BCC $E910 LDA #$0A LFEE910: CLC ADC #$0A TAX ; 最終的には「レベルアップの難易度 - レベルの上がりやすさ + 10(最低値は0)」がXに格納される LFEE914: LDA $FE31AA,X ; レベルアップ判定の乱数の境界値を取得する RTL LFE31AA: DB $64,$5A,$50,$46,$3C,$3C,$32,$28,$24,$20,$10,$08,$05,$03,$02,$01,$01,$01,$01,$00,$00 ; レベルアップ確率用テーブル
;________________________________________________________________ ; ; 戦闘終了後のアイテム入手時の処理 ;________________________________________________________________ ; LC2E0A8: STZ $0A3A ; ループ回数 LC2E0AB: LDA #$00 XBA LDA $0A3A TAX LDA $C2E0D4,X ; アイテム入手確率テーブルから、判定用乱数の最大値を決める STA $0A3B JSR $E0D8 ; アイテム入手判定を行う BEQ $E0C9 ; 判定に失敗した場合はジャンプする JSL $FEEBE4 ; アイテムの個数を調べる BNE $E0C9 ; アイテム欄に空きがない or すでに最大個数持っている場合、ジャンプする(アイテム入手判定の続き) JSR $CE72 ; アイテムを入手する BRA $E0D3 ; アイテム入手判定を途中で打ち切る LC2E0C9: INC $0A3A ; 次のアイテムの判定に移る LDA $0A3A CMP #$04 ; BNE $E0AB ; 4種類のアイテムすべてについて判定を行う LC2E0D3: RTS LC2E0D4: DB $00,$0F,$1F,$3F ; アイテム入手確率用のテーブル LC2E0D8: LDX #$2000 STX $B8 LDA $34 ; 倒したモンスターの数 LC2E0DF: PHA REP #$20 LDY #$0000 LDA ($B8),Y BIT #$2000 ; SEP #$20 BNE $E153 ; 不明 LDY #$0007 LDA ($B8),Y ; BIT #$80 ; 一撃死技(?)の判定 BEQ $E153 ; 一撃死技(?)で倒した場合はジャンプする(アイテム入手判定失敗) LDY #$0005 LDA ($B8),Y ; BIT #$04 ; 一撃死技(?)の判定 BNE $E153 ; 一撃死技(?)で倒した場合はジャンプする(アイテム入手判定失敗) LDY #$004F LDA ($B8),Y ; AND #$3F ; アイテムテーブルのインデックスを取得する STA $BF ; 被乗数にアイテムテーブルのインデックスを設定する LDA #$05 ; STA $C0 ; 乗数に5を設定する LDA $0A3A STA $8A STZ $8B JSR $0227 ; 「アイテムテーブルのインデックス×5」を計算する REP #$20 LDA $C4 CLC ADC $8A ; ループ回数(現在何番目のアイテムの入手判定をしているか)を加算する TAX SEP #$20 LDA #$00 XBA LDA $FE678B,X ; アイテムテーブルから、入手アイテム番号を取得する CMP #$FF ; 入手アイテム番号が$FF(アイテムなし)の場合…… BEQ $E153 ; ……ジャンプする(アイテム入手判定失敗) CMP #$FD ; アイテム番号が$FD未満(通常アイテム)の場合…… BCC $E13D ; ……ジャンプする SEC SBC #$FD CLC ADC #$47 TAY LDA ($B8),Y ; 装備武器1または2が入手アイテムとなる CMP #$FF ; 入手アイテム番号が$FF(アイテムなし)の場合…… BEQ $E153 ; ……ジャンプする(アイテム入手判定失敗) LC2E13D: STA $0946 STZ $BF ; 0から…… LDA $0A3B ; ……アイテム入手確率判定用乱数の最大値の範囲で…… STA $C0 JSR $032E ; ……乱数値を取得する LDA $BF BNE $E153 ; 乱数値が0以外であった場合、ジャンプする(アイテム入手判定失敗) PLA LDA #$FF ; Aに$FFを格納してサブルーチンを終了する(アイテム入手判定に成功したことを意味する) BRA $E166 LC2E153: REP #$20 LDA $B8 CLC ADC #$0080 ; 次のモンスターの判定に移る STA $B8 SEP #$20 PLA DEC BEQ $E166 JMP $E0DF ; Aに$00が格納された状態でサブルーチンを終了する(アイテム入手判定に失敗したことを意味する) LC2E166: RTS
;________________________________________________________________ ; ; モンスターレベルの上昇処理 ;________________________________________________________________ ; LC2DA6B: JSR $DB4C ; $142Fと$1430に値を設定する JSR $DA7C ; 従属モンスターレベルの上昇処理を行う LDA $1314 ; バトルの種類を取得して…… CMP #$19 ; ……$19(イベントバトル)と比較して…… BEQ $DA7B ; ……バトルの種類がイベントバトルである場合はジャンプする JSR $DAD8 ; 種族ごとのモンスターレベルの上昇処理を行う LC2DA7B: RTS ;________________________________________________________________ ; ; 従属モンスターレベルの上昇処理 ;________________________________________________________________ ; LC2DA7C: STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA #$08 ; STA $8A ; レベル上昇修正値の初期値として8を設定する STZ $8B ; LDX #$0000 LC2DA8E: LDA $FE9B7D,X ; 判定用境界値を取得する CMP $BF ; 乱数値と比較して…… BCS $DA9B ; ……境界値が乱数値以上である場合はジャンプする(ループから抜ける) INX ; 次の境界値に移動する DEC $8A ; レベル上昇修正値を1減らす BNE $DA8E ; レベル上昇値が1以上であればジャンプする(ループを繰り返す) LC2DA9B: LDA $1430 ; 種族モンスターレベル÷16と…… CMP $142F ; ……従属モンスターレベル÷16とを比較して…… BCS $DAA7 ; ……種族モンスターレベル÷16が従属モンスターレベル以上であればジャンプする LDA #$00 ; レベル上昇基本値は0とする BRA $DAAB LC2DAA7: SEC SBC $142F ; レベル上昇基本値は、「種族モンスターレベル÷16 - 従属モンスターレベル÷16」とする LC2DAAB: REP #$20 AND #$00FF ; CLC ; ADC $8A ; レベル上昇基本値と、レベル上昇修正値とを加算して…… STA $8A ; ……最終的な結果を、レベル上昇値として格納する SEP #$20 LDA $FE9C05 ; 従属モンスターレベルの上限値を取得する STA $8C STZ $8D LDA $F532 ; 現在の従属モンスターレベルと…… REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; ……レベル上昇値とを加算して…… CMP $8C ; ……レベル上限値と比較する SEP #$20 BEQ $DAD4 ; BCC $DAD4 ; 「従属モンスターレベル + レベル上昇値」がレベル上限値以下の場合はジャンプする LDA $8C ; 最終的な従属モンスターレベルは、レベル上限値になる LC2DAD4: STA $F532 ; 最終的な従属モンスターレベルを設定する RTS ;________________________________________________________________ ; ; 種族ごとのモンスターレベルの上昇処理 ;________________________________________________________________ ; LC2DAD8: STZ $BF ; 0から…… LDA #$FF ; ……255の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA #$08 ; STA $8A ; レベル上昇修正値の初期値として8を設定する STZ $8B ; REP #$20 LDA $2000 ; リーダーモンスターの番号から…… AND #$00F0 ; ……上位4ビット(種族)を取得して…… JSR $0412 ; ……16で割って…… JSR $0408 ; ……8倍する TAX SEP #$20 LDA $FE9B85,X ; 種族ごとの判定用境界値を取得する CMP $BF ; 乱数値と比較して…… BCS $DB05 ; ……境界値が乱数値以上である場合はジャンプする(ループから抜ける) INX ; 次の境界値に移動する DEC $8A ; レベル上昇修正値を1減らす BNE $DAF8 ; レベル上昇値が1以上であればジャンプする(ループを繰り返す) LC2DB05: LDA $142F ; 従属モンスターレベル÷16と…… CMP $1430 ; ……種族モンスターレベル÷16とを比較して…… BCS $DB11 ; ……従属モンスターレベル÷16が種族モンスターレベル以上であればジャンプする LDA #$00 ; レベル上昇基本値は0とする BRA $DB15 LC2DB11: SEC SBC $1430 ; レベル上昇基本値は、「従属モンスターレベル÷16 - 種族モンスターレベル÷16」とする LC2DB15: REP #$20 AND #$00FF ; CLC ; ADC $8A ; レベル上昇基本値と、レベル上昇修正値とを加算して…… STA $8A ; ……最終的な結果を、レベル上昇値として格納する LDA $2000 ; リーダーモンスターの番号から…… AND #$00F0 ; ……上位4ビット(種族)を取得して…… JSR $0412 ; ……16で割る TAX SEP #$20 LDA $FE9C06,X ; 種族ごとのモンスターレベルの上限値を取得する STA $8C STZ $8D LDA $F533,X ; 現在の種族ごとのモンスターレベルと…… REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; ……レベル上昇値とを加算して…… CMP $8C ; ……レベル上限値と比較する SEP #$20 BEQ $DB48 ; BCC $DB48 ;「種族ごとのモンスターレベル + レベル上昇値」がレベル上限値以下の場合はジャンプする LDA $8C ; 最終的な種族ごとのモンスターレベルは、レベル上限値になる LC2DB48: STA $F533,X ; 最終的な種族ごとのモンスターレベルを設定する RTS
;________________________________________________________________ ; ; 出現モンスター決定処理 ;________________________________________________________________ ; LC213EA: LDA $141B ; AND #$0F ; モンスターの種族を取得する STA $5D JSR $1411 ; 種族を元に、リーダーモンスターを決定する LDA $5A ; 最大モンスター数と…… CMP $34 ; ……現在のモンスター数とを比較して…… BEQ $1410 ; ……モンスター数が最大数に達した場合はジャンプする LDA $141B ; AND #$F0 ; 従属モンスタータイプ(16倍されている)を取得する CMP #$F0 ; BNE $1408 ; 従属モンスタータイプが$Fでない場合はジャンプする JSR $1461 ; 従属モンスター決定処理(同種族のモンスターから選択) BRA $1410 LC21408: JSR $14D0 ; リーダーモンスターを増加する BEQ $1410 ; 現在のモンスター数が最大数に達していた場合はジャンプする JSR $150C ; 従属モンスター決定処理(従属モンスターテーブルから選択) LC21410: RTS ;________________________________________________________________ ; ; リーダーモンスター決定処理 ;________________________________________________________________ ; LC21411: LDA $5D ; 種族番号を取得して…… REP #$20 AND #$00FF JSR $0406 ; ……32倍する STA $8A SEP #$20 JSR $15A0 ; 種族モンスターレベルに乱数修正をかけて、モンスターレベルを決定する LDA $56 ; 乱数修正後のモンスターレベルを取得して…… REP #$20 AND #$00FF JSR $0412 ; ……16で割って…… ASL ; ……2倍して…… CLC ADC $8A ; ……種族番号×32を加算して…… TAX ; ……テーブルのインデックスとする SEP #$20 LDA $FE77BB,X ; 乱数修正後のモンスターレベルから、リーダーモンスターの番号を取得する REP #$20 AND #$00FF STA $16D2 ; リーダーモンスターの番号を格納する LDY #$0000 ORA #$8000 STA ($B8),Y ; 1体目のモンスターの番号を設定する SEP #$20 LDA $FE77BC,X ; モンスターパーティの編成データを取得する STA $59 JSR $176F ; モンスターパーティの各種データを設定する REP #$20 LDA $B8 ; CLC ; ADC #$0080 ; STA $B8 ; 次のモンスターのアドレスに移動する SEP #$20 INC $34 ; モンスター数を1増加する RTS ;________________________________________________________________ ; ; 従属モンスター決定処理(同種族のモンスターから選択) ;________________________________________________________________ ; LC21461: LDA $5A ; モンスターの最大数から…… SEC SBC $34 ; ……現在のモンスター数を引く(ループ残り回数) LC21466: PHA ; ループ残り回数をスタックに格納する JSR $15A0 ; 種族モンスターレベルに乱数修正をかけて、モンスターレベルを決定する JSL $FED0AF ; 境界値を決定する LDA #$01 ; 1から…… STA $BF ; LDA #$0F ; ……15の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $57 ; 境界値と…… CMP $BF ; ……乱数値とを比較して…… BCC $14CB ; ……乱数値が境界値以上であった場合はジャンプする LDA $5D ; 種族番号を取得して…… REP #$20 AND #$00FF JSR $0406 ; ……32倍する STA $8A SEP #$20 LDA $56 ; 乱数修正後のモンスターレベルを取得して…… REP #$20 AND #$00FF JSR $0412 ; ……16で割って…… ASL ; ……2倍して…… CLC ADC $8A ; ……種族番号×32を加算して…… TAX ; ……テーブルのインデックスとする SEP #$20 LDA $FE77BB,X ; 乱数修正後のモンスターレベルから、種族モンスターの番号を取得する REP #$20 AND #$00FF LDY #$0000 ORA #$8000 STA ($B8),Y ; モンスターの番号を設定する SEP #$20 JSL $FED107 ; モンスターサイズの判定を行う BNE $14CB ; モンスターサイズがモンスターパーティの最大サイズを超えた場合はジャンプする JSL $FED139 ; モンスターの種類の判定を行う BNE $14CB ; モンスターパーティにすでに3種類のモンスターが存在する場合はジャンプする REP #$20 LDA $B8 ; CLC ; ADC #$0080 ; STA $B8 ; 次のモンスターのアドレスに移動する SEP #$20 INC $34 ; モンスター数を1増加する LC214CB: PLA ; ループ残り回数をスタックから取得する DEC ; ループ残り回数を1減らす BNE $1466 ; ループ残り回数が0でなければジャンプする RTS ;________________________________________________________________ ; ; リーダーモンスターを増加する ;________________________________________________________________ ; LC214D0: LDA $5A ; モンスターの最大数から…… SEC SBC $34 ; ……現在のモンスター数を引く(ループ残り回数) LC214D5: PHA ; ループ残り回数をスタックに格納する JSL $FED0AF ; 境界値を決定する LDA #$01 ; 1から…… STA $BF ; LDA #$0F ; ……15の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $57 ; 境界値と…… CMP $BF ; ……乱数値とを比較して…… BCS $14F4 ; ……乱数値が境界値未満であった場合はジャンプする LC214EB: PLA ; ループ残り回数をスタックから取得する DEC ; ループ残り回数を1減らす BNE $14D5 ; ループ残り回数が0でなければジャンプする LDA $34 ; 現在のモンスター数と…… CMP $5A ; ……モンスターの最大数を比較する RTS LC214F4: REP #$20 LDY #$0000 ; LDA $2000 ; リーダーモンスターの番号を取得する STA ($B8),Y ; モンスターの番号を設定する LDA $B8 ; CLC ; ADC #$0080 ; STA $B8 ; 次のモンスターのアドレスに移動する SEP #$20 INC $34 ; モンスター数を1増加する BRA $14EB ;________________________________________________________________ ; ; 従属モンスター決定処理(従属モンスターテーブルから選択) ;________________________________________________________________ ; LC2150C: JSL $FED0D9 ; 境界値を決定する LDA $5A ; モンスターの最大数から…… SEC SBC $34 ; ……現在のモンスター数を引く(ループ残り回数) LC21515: PHA ; ループ残り回数をスタックに格納する LDA #$01 ; 1から…… STA $BF ; LDA #$0F ; ……15の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $58 ; 境界値と…… CMP $BF ; ……乱数値とを比較して…… BCS $152C ; ……乱数値が境界値未満であった場合はジャンプする LC21527: PLA ; ループ残り回数をスタックから取得する DEC ; ループ残り回数を1減らす BNE $1515 ; ループ残り回数が0でなければジャンプする RTS LC2152C: LDA #$03 ; 内部ループ回数の初期値は3とする LC2152E: PHA ; 内部ループ残り回数をスタックに格納する JSR $15C3 ; 従属モンスターレベルに乱数修正をかけて、モンスターレベルを決定する LDA $141B ; AND #$F0 ; JSR $0412 ; 従属モンスタータイプを取得する STA $BF ; 被乗数に従属モンスタータイプを設定する LDA #$60 ; STA $C0 ; 乗数に$60(96)を設定する JSR $0227 ; 「従属モンスタータイプ×96」の計算をする REP #$20 LDA $C4 ; CLC ; ADC #$0060 ; STA $8A ; インデックスの最大値を設定する SEP #$20 LDX $C4 ; インデックスを設定する LC21551: LDA $FE79BB,X ; 従属モンスターテーブルからモンスターレベルを取得して…… CMP $56 ; ……乱数修正後のモンスターレベルと比較して…… BEQ $1563 ; BCC $1563 ; ……乱数修正後のモンスターレベルがテーブルから取得したモンスターレベル以上だった場合はジャンプする INX ; INX ; 現在のインデックスを2バイト分進めて…… CPX $8A ; ……インデックスの最大値と比較して…… BNE $1551 ; ……現在のインデックスが最大値に到達していない場合はジャンプする BRA $159A LC21563: LDA $FE79BC,X ; 従属モンスターテーブルから出現モンスターを取得する REP #$20 AND #$00FF LDY #$0000 ORA #$8000 STA ($B8),Y ; モンスターの番号を設定する SEP #$20 JSL $FED107 ; モンスターサイズの判定を行う BNE $159A ; モンスターサイズがモンスターパーティの最大サイズを超えた場合はジャンプする JSL $FED139 ; モンスターの種類の判定を行う BNE $159A ; モンスターパーティにすでに3種類のモンスターが存在する場合はジャンプする PLA ; 内部ループ回数をスタックから破棄する REP #$20 LDA $B8 ; CLC ; ADC #$0080 ; STA $B8 ; 次のモンスターのアドレスに移動する SEP #$20 INC $34 ; モンスター数を1増加する LDA $58 ; 境界値を取得して…… BEQ $159E ; ……境界値が0であればジャンプする(処理終了) (あり得ない?) DEC ; STA $58 ; 境界値を1減らす BRA $1527 LC2159A: PLA ; 内部ループ残り回数をスタックから取得する DEC ; 内部ループ残り回数を1減らす BNE $152E ; 内部ループ残り回数が0でなければジャンプする LC2159E: PLA ; ループ残り回数をスタックから破棄する RTS ;________________________________________________________________ ; ; 最終的な種族モンスターレベルを算出する ;________________________________________________________________ ; LC215A0: LDA $82 PHA LDX $8A PHX LDX $8C PHX LDX $8E PHX JSR $15E6 ; 種族モンスターレベルに乱数修正をかける JSR $16B0 ; 地域ごとに設定された最低モンスターレベルに乱数修正をかける JSL $FED097 ; 大きい方の値を最終的なモンスターレベルとして採用する PLX STX $8E PLX STX $8C PLX STX $8A PLA STA $82 RTS ;________________________________________________________________ ; ; 最終的な従属モンスターレベルを算出する ;________________________________________________________________ ; LC215C3: LDA $82 PHA LDX $8A PHX LDX $8C PHX LDX $8E PHX JSR $16F1 ; 従属モンスターレベルに乱数修正をかける JSR $16B0 ; 地域ごとに設定された最低モンスターレベルに乱数修正をかける JSL $FED097 ; 大きい方の値を最終的なモンスターレベルとして採用する PLX STX $8E PLX STX $8C PLX STX $8A PLA STA $82 RTS ;________________________________________________________________ ; ; 種族モンスターレベルに乱数修正をかける ;________________________________________________________________ ; LC215E6: LDA $141C ; 修正値を取得する BEQ $1624 ; 修正値が0の場合はジャンプする(種族モンスターレベルからモンスターレベルを算出) LDA $F532 ; 従属モンスターレベルを取得する STA $8A STZ $8B LDA $141C ; 修正値を取得する REP #$20 AND #$00FF BIT #$0080 ; 修正値の最上位ビットが…… BEQ $1614 ; ……0の場合はジャンプする(プラス修正) AND #$FF7F ; STA $8C ; 修正値の下位7ビットを格納する LDA $8A ; 従属モンスターレベルと…… CMP $8C ; ……修正値の下位7ビットとを比較して…… BCS $160F ; ……従属モンスターレベルが修正値以上の場合はジャンプする LDA #$0000 ; モンスターレベル基本値は0とする BRA $1617 LC2160F: SEC SBC $8C ; モンスターレベル基本値は「従属モンスターレベル - 修正値」とする BRA $1617 LC21614: CLC ADC $8A ; モンスターレベル基本値は「従属モンスターレベル + 修正値」とする LC21617: CMP #$00FF ; モンスターレベル基本値と$FF(255)とを比較して…… SEP #$20 BEQ $162D ; BCC $162D ; ……モンスターレベル基本値が255以下の場合はジャンプする LDA #$FF ; モンスターレベル基本値は255とする。 BRA $162D LC21624: LDA #$00 ; XBA ; LDA $5D ; TAX ; 種族番号をインデックスとする LDA $F533,X ; 種族モンスターレベルをモンスターレベル基本値とする LC2162D: STA $82 ; モンスターレベル基本値を格納する STA $BF ; 被乗数にモンスターレベル基本値を設定する LDA #$07 ; STA $C0 ; 乗数に$07(7)を設定する JSR $0227 ; 「モンスターレベル基本値×7」の計算をする REP #$20 LDA $C4 ; 「モンスターレベル基本値×7」を取得して…… JSR $0413 ; ……8で割る STA $8A ; 現時点のモンスターレベルとして格納する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA $82 ; (モンスターレベル基本値を取得して……) JSR $0413 ; (……8で割る) STA $C0 ; ……モンスターレベル基本値÷8の範囲で…… JSR $032E ; ……乱数値を取得する LDA $BF ; REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; STA $8A ; 現時点のモンスターレベルに「1〜モンスターレベル基本値÷8の乱数値」を加算する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA $82 ; (モンスターレベル基本値を取得して……) JSR $0413 ; (……8で割る) STA $C0 ; ……モンスターレベル基本値÷8の範囲で…… JSR $032E ; ……乱数値を取得する LDA $BF ; REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; STA $8A ; 現時点のモンスターレベルに「1〜モンスターレベル基本値÷8の乱数値」を加算する SEP #$20 LDA $F319 ; パーティ人数を取得して…… REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; STA $8A ; 現時点のモンスターレベルにパーティ人数を加算する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA #$08 ; ……8の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF STA $8C STZ $8D REP #$20 LDA $8A ; 現時点のモンスターレベルと…… CMP $8C ; ……1〜8の乱数値とを比較して…… BCS $16A8 ; ……現時点のモンスターレベルが1〜8の乱数値以上であればジャンプする LDA #$0000 ; モンスターレベルは0とする BRA $16AB LC216A8: SEC SBC $8C ; 現時点のモンスターレベルから1〜8の乱数値を引く LC216AB: STA $8A ; 最終的なモンスターレベルを格納する SEP #$20 RTS ;________________________________________________________________ ; ; 地域ごとに設定された最低モンスターレベルに乱数修正をかける ;________________________________________________________________ ; LC216B0: LDA $1308 ; 地域ごとに設定された最低モンスターレベルを取得する REP #$20 AND #$00F0 ; 上位4ビットをクリアする STA $8C ; 現時点のモンスターレベルとして格納する SEP #$20 LDA $F319 ; パーティ人数を取得して…… REP #$20 ; AND #$00FF ; CLC ; ADC $8C ; STA $8C ; 現時点のモンスターレベルにパーティ人数を加算する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA #$08 ; ……8の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF STA $8E STZ $8F REP #$20 LDA $8C ; 現時点のモンスターレベルと…… CMP $8E ; ……1〜8の乱数値とを比較して…… BCS $16E9 ; ……現時点のモンスターレベルが1〜8の乱数値以上であればジャンプする LDA #$0000 ; モンスターレベルは0とする BRA $16EC LC216E9: SEC SBC $8E ; 現時点のモンスターレベルから1〜8の乱数値を引く LC216EC: STA $8C ; 最終的なモンスターレベルを格納する SEP #$20 RTS ;________________________________________________________________ ; ; 従属モンスターレベルに乱数修正をかける ;________________________________________________________________ ; LC216F1: STZ $8A STZ $8B LDA $F532 ; 従属モンスターレベルを取得する STA $82 STA $BF ; 被乗数に従属モンスターレベルを設定する LDA #$07 ; STA $C0 ; 乗数に$07(7)を設定する JSR $0227 ; 「従属モンスターレベル×7」の計算をする REP #$20 LDA $C4 ; 「従属モンスターレベル×7」を取得して…… JSR $0413 ; ……8で割る STA $8A ; 現時点のモンスターレベルとして格納する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA $82 ; (従属モンスターレベルを取得して……) JSR $0413 ; (……8で割る) STA $C0 ; ……従属モンスターレベル÷8の範囲で…… JSR $032E ; ……乱数値を取得する LDA $BF ; REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; STA $8A ; 現時点のモンスターレベルに「1〜従属モンスターレベル÷8の乱数値」を加算する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA $82 ; (従属モンスターレベルを取得して……) JSR $0413 ; (……8で割る) STA $C0 ; ……従属モンスターレベル÷8の範囲で…… JSR $032E ; ……乱数値を取得する LDA $BF ; REP #$20 ; AND #$00FF ; CLC ; ADC $8A ; ADC #$0004 ; STA $8A ; 現時点のモンスターレベルに「1〜従属モンスターレベル÷8の乱数値」と「4」とを加算する SEP #$20 LDA #$01 ; 1から…… STA $BF ; LDA #$08 ; ……8の範囲で…… STA $C0 ; JSR $032E ; ……乱数値を取得する LDA $BF STA $8C STZ $8D REP #$20 LDA $8A ; 現時点のモンスターレベルと…… CMP $8C ; ……1〜8の乱数値とを比較して…… BCS $1767 ; ……現時点のモンスターレベルが1〜8の乱数値以上であればジャンプする LDA #$0000 ; モンスターレベルは0とする BRA $176A LC21767: SEC SBC $8C ; 現時点のモンスターレベルから1〜8の乱数値を引く LC2176A: STA $8A ; 最終的なモンスターレベルを格納する SEP #$20 RTS ;________________________________________________________________ ; ; リーダーモンスター増加処理における「境界値」を設定する ;________________________________________________________________ ; LFED0AF: STZ $82 ; 修正値の初期値は0とする LDA $F532 ; 従属モンスターレベルを取得して…… CMP $56 ; ……乱数修正後のモンスターレベルと比較して…… BEQ $D0C3 ; BCC $D0C3 ; ……従属モンスターレベルが乱数修正後のモンスターレベル以下だった場合はジャンプする SEC ; SBC $56 ; 従属モンスターレベルから乱数修正後のモンスターレベルを引いて…… LSR ; LSR ; LSR ; LSR ; ……16で割る STA $82 ; 修正値として格納する LFED0C3: LDA $56 ; 乱数修正後のモンスターレベルの…… AND #$0F ; ……下位4ビットを取得して…… LSR ; ……2で割って…… CLC ; ADC $82 ; ……修正値を加えたものを現時点の境界値とする CMP $34 ; 現在のモンスター数と比較する BCS $D0D3 LDA #$00 ; 境界値は0とする BRA $D0D6 LFED0D3: SEC SBC $34 ; 現時点の境界値から現在のモンスター数を引く LFED0D6: STA $57 ; 最終的な境界値を格納する RTL ;________________________________________________________________ ; ; 従属モンスター決定処理における「境界値」を設定する ;________________________________________________________________ ; LFED0D9: STZ $82 ; 修正値の初期値は0とする LDA $F532 ; 従属モンスターレベルを取得して…… CMP $56 ; ……乱数修正後のモンスターレベルと比較して…… BEQ $D0ED ; BCC $D0ED ; ……従属モンスターレベルが乱数修正後のモンスターレベル以下だった場合はジャンプする SEC ; SBC $56 ; 従属モンスターレベルから乱数修正後のモンスターレベルを引いて…… LSR ; LSR ; LSR ; LSR ; ……16で割る STA $82 ; 修正値として格納する LFED0ED: LDA $56 ; 乱数修正後のモンスターレベルの…… AND #$0F ; ……下位4ビットを取得して…… LSR ; ……2で割って…… CLC ADC $FE317A ; ……$FE317Aの内容(4)を加えて…… ADC $82 ; ……修正値を加えたものを現時点の境界値とする CMP $34 ; 現在のモンスター数と比較する BCS $D101 LDA #$00 ; 境界値は0とする BRA $D104 LFED101: SEC SBC $34 ; 現時点の境界値から現在のモンスター数を引く LFED104: STA $58 ; 最終的な境界値を格納する RTL
「予定」というよりは「調査したいことの羅列」といった方が正確かもしれません。このリストの中には、「すでに調査は終わっていて、ドキュメントを書くだけ」というものから「今の私のスキルでは無理だが、いつかはやってみたい」といったものまで含まれています。
くれぐれも、解析結果を盲信しないでください。例によって、私は解析結果の検証はほとんどやっていません。また、65816やスーパーファミコンのアーキテクチャについての知識にも乏しいため、プログラムコードの解釈に間違いがある可能性は大いにあります。誤りがあった場合はご連絡ください。