PS版FF8 調査

2021/04/19
ゼルカード目押しツモ補助スクリプト(English)及びFC01版(暫定)(English)にgame_fpsオプションを追加。Windows7(61fpsで動く)に対応したかも。
スクリプトの類をgithubへ引っ越し。
2021/03/08
ゼルカード目押しツモ補助スクリプト(English)及びFC01版(暫定)(English)のレアカードタイマーが動作しなくなっていた(str2patternメソッドがエラーを吐く)のを修正。
2021/01/24
ゼルカード目押しツモ補助スクリプト(English)にFC01版(暫定)(English)を追加したついでにちょっとだけ変更。
カード乱数の漸化式を修正(0x10dc0→0x10dcd)。
2017/09/01
フィールドマップの歩数エンカウント内容の決定方法に英語版(適当)を追加。
2017/08/30
フィールドマップの歩数エンカウント内容の決定方法を追加。
2017/05/12
ゼルカード目押しツモゼルカード1戦目押し用資料にX-ATM092(撃破)を追加。
2017/03/06
敵のAIスクリプト解析にSteam版(only in English)を追加など。
2016/12/17
ゼルカード目押しツモ補助スクリプトのオプションの説明を追加。
2016/10/27
ゼルカード目押しツモ補助スクリプト致命的なバグを修正
2016/10/26
ゼルカード目押しツモの3人アップ視点一覧をゼルカード1戦目押し用資料に統合。補助スクリプトを更新・ようやっとEnglishに対応。
アルティミシア戦(ほぼ)ロスなし初期パーティ固定法補助スクリプトを更新・Englishに対応。
2016/10/23
ゼルカード目押しツモ全1人アップ・3人アップ視点一覧を追加。
2016/10/20
ゼルカード目押しツモにちょくちょく追記。
英語版(まだ見出しとカードだけ)を作成。
2016/10/18
ゼルカード目押しツモディアボロスカードなしゼルママ戦詰め手順を追加。
2016/10/15
ゼルカード目押しツモそして1戦ツモへ…を追加。補助スクリプトを更新、ゼルカード1戦目押し用資料を追加。
2016/09/27
戦闘からの逃走を追加。
自動回復・パーティ変更ポイント一覧を追加。
2016/09/26
敵のAIスクリプト解析に「RTA中主要な敵のAI」シートを追加など。
2016/09/25
アルティミシア戦(ほぼ)ロスなし初期パーティ固定法も〜っと!参考動画を追加。
2016/09/01
魔法消滅シミュレータをURLクエリによって魔法リスト指定可能にした。
2016/07/10
ゼルカード2戦目押しツモを追加。あと初期パーティもゼルカードもJavaScript版は作らないと思います。
2016/07/02
敵のAIスクリプト解析をそこそこまともなものに更新。
2016/06/28
敵の魔法消去行動の偏りを追加。
2016/06/24
アルティミシア戦(ほぼ)ロスなし初期パーティ固定法を追加。長い…
HP割合とCrisis LevelのグラフのCL 1~4を、256ではなく「CL 1..4」(ややこしいな)を分母とする割合で示すよう変更。ついでにグラフに説明を追加。
2016/06/17
HP割合とCrisis Levelプロパゲーターのドロップを追加。
2016/06/04
カーウェイ邸番号探索スクリプトJavaScript版を追加。
2016/06/02
カーウェイ邸番号探索スクリプトを追加。
2016/05/27
モンスターAIスクリプト解析を追加。

フィールドマップの歩数エンカウント内容の決定方法

歩数エンカウント内容は、以下の3要素によって決定される。

乱数テーブルには以下のものが用いられる。なお、これはDanger Limitテーブルと同一である。

  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

疑似コード:

$prev_step_encounter_id = 0
$step_encounter_number = 0

def step_encounter
  # 歩数エンカウント回数の加算は内容決定前
  $step_encounter_number = ($step_encounter_number + 1) & 0xff
  rnd = RND_TABLE[$step_encounter_number]

  if rnd < 128 && current_map_encounter_ids[0] != $prev_step_encounter_id
    id = current_map_encounter_ids[0] # 128/256 = 50%
  elsif rnd < 192 && current_map_encounter_ids[1] != $prev_step_encounter_id
    id = current_map_encounter_ids[1] # 64/256 = 25%
  elsif rnd < 240 && current_map_encounter_ids[2] != $prev_step_encounter_id
    id = current_map_encounter_ids[2] # 48/256 = 18.75%
  else
    id = current_map_encounter_ids[3] # 16/256 = 6.25%
  end

  $prev_step_encounter_id = id
  return id
end
歩数エンカウント回数(1byte)
0x8005de23
前回の歩数エンカウントのEncounter ID(2byte)
0x8005de40
エンカウント内容決定用乱数テーブル[256]
0x800c54e8-800c55e7
現在いるフィールドマップに設定されているエンカウント内容[4]
0x800e3018-0x800e301b
実行アドレス(だいたい)
0x800a7e48-0x800a7ef4
乱数のサブルーチン(called from 0x800a7e48)
0x800a7c64-0x800a7c8c

自動回復・パーティ変更等のポイント一覧

回復・蘇生・治療・GF解除・パーティ変更・パーティ編成画面。


戦闘からの逃走

戦闘の種類しきい値逃走に掛かる時間備考
期待値最悪値
バックアタック・先制攻撃2551.00sX-ATM092(修復中)
通常エンカウント1281.98s10sルナパン通過戦闘・トライエッジ・ティアマト
被バックアタック・被先制攻撃1611.54s41s

戦闘乱数について

 99   6 240  35 248 229 168   1 193 174 127  72 123 177 220   9
 34 109 125 238 157  88 213  85  36  57 122 223 142  84 108  27
192  11 208  67 216 154  71  93  33   2  23  75 219  17 175 112
205  77  52  73 114 145  45  98 151  89  69 247 110  70 170  10
163 200  49 146  56 250 212 230 203 243 222 107 187 241  28  60
214 173 178 169 221  87  66 149  12 121  37  31 188 231 172  91
131  40 118 242  24 218 135 161  97 111 190  90  94  81 239 176
201  21 116 137 189 209 162 117 215 153 133  76  79 210 191  74
 32   8  86 160  80  58 103  38  65  51 183 186 251  48 207 124
132  44  50 233  29  22 130 120 164 128 101  95  14  39 185  25
195 167 182   0  59 252 136 225 198 147 254 139 217 184  19 105
 47 100  18  55 253 119 226 181   4 224  26 140 143 180 204 249
 96 235  41 227 144 165 104  61 129 115  63 171 126 179  15 206
196  53 148 150 134 113 211  42 228 159 156 236  78  20 245 234
 64 166 246   3 152 197   7 244  43 194  62 232 155  54  83  46
141  13  82  16 102  30 237 138  68 158   5 255  92 199 106 202

参考文献

Final Fantasy VIII Battle Mechanics FAQ
10.3 Running away に逃走についての記述あり。

ゼルカード目押しツモ

カード乱数の状態はセーブデータに保存されているのでゼルカードツモを行う直前のセーブデータから練習できます。

そして1戦ツモへ…(2016/10/15追加)

以前ワンチャンあるかもと書いていた戦闘中のカード乱数消費回数の正確なカウントによる1戦目の目押しにある程度目処が立った。カード乱数の戦闘中の使用箇所を把握していることが前提だが、PS版FFシリーズ特有の連射休憩ポイントの多さを利用してプレイ中の録画を見返しながらカウントを行えば、プレイ中の負担はさほどでもないはずだ。

ディアボロス撃破〜ゼルママ間はディアボロス戦におけるカウントを見直す余裕があまりないので、ディアボロス戦はゼルママの後に回した方がもいいかもしれない。その場合は「魔法のランプ入手→ゼルママ→ディアボロス撃破」という経路になる。

2戦ツモと比較して約40sの短縮になるが、面倒臭さは「先キスカ固定のパターン」と「1戦目開幕状況」の入力だけで済む2戦ツモとは比較にならないだろう。カード乱数消費回数のカウントが1ずれるだけでも1戦目押しは失敗する(一応2戦目でリカバリできるようにはしているが)。

ディアボロスカードなしゼルママ戦詰め手順

一発ツモ想定ということでゼルママのゼルカード所持前提かつドローパターンなし。ゼルママの手札になりうるカードのうち右下隅のハウリザードを左から返すものはないことと、左上隅のゲイラを下から返すのはグラットのみであることを利用している。

ゼルママがゼルカードを所持していない場合は先手番でのゼルママの初手が変わるため適用できない。ディアボロスカードがないため、後手番の戦術の勝率は恐らく99%以上だが100%ではない。また、置き場所を誤ったときのリカバリがしにくく、最悪負けまであるかもしれない。

プレイヤーの手札
ハウリザード・レッドマウス・ゲイラ・イフリート・キスティス
手順先手番後手番
勝ち勝ち上に置かれたカードの強さが右1の場合
1手目レッドマウス右下ゼル左上
2手目ゼルハウリザード右下
3手目ハウリザード
4手目中央イフリート
5手目イフリート左下
6手目左上レッドマウス右上ゲイラ
7手目ゲイラ中央or左下
8手目キスティス中央キスティス中央>右上
9手目キスティス右上左下左下or右上

カード乱数について

カード乱数は以下のようなものに関わる。

カード乱数生成

カード乱数は、カード勝負だけではなく戦闘中(他もあるかも)にも消費されることと、それ以上にカード勝負開始画面で乱数の状態が毎フレーム1加算されることがその特定を困難にしている。カード乱数は以下の性質を持っている。

カード乱数生成のサブルーチン
0x80023ce0
カード乱数の状態のインクリメント
0x800a4894
カード乱数の状態の保存先(4byte)
0x800773f4

ちなみに先キスカ固定法はゲーム開始直後に30連で「勝負する」を選択するとカード乱数の状態を1+10=11と1+11=12の2通りに絞り込めることが利用されている。以下、この節でいう「乱数」は全て「カード乱数」を指すものとする。

ゼルカード2戦目押しツモ

ゼルママ1戦目の開幕状況から乱数の状態を特定し、それを元に2戦目のレアカードフレームを目押ししてゼルカを出させることにより、2戦でゼルカをツモることができる。

ゲーム開始または先キスカ固定法によるキスカツモからゼルママ戦の間に一度もカード勝負画面を開かず、かつゼルママ戦1戦目で「勝負する」を先キスカ固定法のように連射(最速である必要はない)で選択すれば乱数の状態を現実的な探索範囲内に収められる。そしてその開幕状況(ゼルママの手札5枚と先後)から乱数の状態を特定することができる。ゼルママはLv1,2,4,5と幅広い範囲のカードを繰り出してくるため、ゼルママがゼルカを出さないときのカード勝負開幕状況は、コヨコヨ以外の43枚から重複なし5枚 * 先後 = 43P5 * 2 = 2億3102万3520通りも存在し、乱数の状態はまずひとつに絞り込める。

ゼルママがレアカードを出す確率は10%であり、カード勝負開始画面でレアカードを出すフレームは190f周期で19f連続する。現在の乱数の状態が特定できていればこの「レアカードフレーム」もわかるため、理論上は19f(約0.32s)の目押しができれば必ずゼルママにゼルカを出させることができる。現実は個人環境差やCD-ROM読み込み時間のブレがあるため、実際の目押し猶予はそれより厳しい。とはいえ目押し失敗なり配置ミスによるドローなりでゼルカを取れなかった場合も、その失敗したカード勝負の開幕状況から乱数の状態を特定し次のカード勝負でレアカードフレームを……と繰り返すことによってリカバリを無限に続けることができる。

理論上はゼルママ戦までの戦闘中(他にもあるかも)のカード乱数消費回数を正確にカウントできれば、乱数特定のためのワンクッションを置かずに1戦目でいきなり目押ししてゼルカを出させることができる。しかしゲーム開始からディアボロス撃破までカード乱数は500~600回程度も消費されるため、もし乱数消費ポイントが全て判明したとしてもそれを全てカウントせねばならない1戦目押しはさすがに非現実的と思われる。もしかするとワンチャンあるのかもしれないけれど。

ゼルママ戦詰め手順

ゼルママの手札が何であろうがこの手順通りに置けば確実に勝てる。ドローパターンはたまに勝ってしまうが高確率でドローにできる。乱数特定用の1戦目をドローにすれば約2秒の短縮になるが、相手手札の入力が必要なことを考えると最後に相手手札一覧を確認できずに終わるドローは好ましくないかもしれない。

ゼルママとはディアボロス入手前のドール実地試験直後にも戦えるが、そのタイミングで戦うことの「リセゲータイミングが早まる」というメリットは目押しツモによって消えると思う。それもあってディアボロスカードがある前提の詰め手順にしている。

プレイヤーの手札
フンゴオンゴ・ハウリザード(何でも可)・キスティス・ディアボロス・イフリート
手順先手番後手番
勝ちドロー勝ちドロー
1手目フンゴオンゴ左上左上
2手目上 or 左フンゴオンゴ
3手目ディアボロス左 or 上上 or 中央 or 左下
4手目右上イフリート右下
5手目イフリート右下上 or 中央 or 左下
6手目中央ディアボロス左下 or 中央
7手目キスティスキスティス右上
8手目左下 or 下右 or 左下キスティスハウリザードディアボロスの右
9手目ハウリザード下 or 左下ハウリザード左下 or 右右 or 下

キスティスカード回収との兼ね合い

ゼルカード2戦目押しツモではゼルママ1戦目で「乱数の状態の特定」を、そしてそれを元に2戦目で「レアカードフレームの目押し」を行っているが、2戦目でも「乱数の状態の特定」を行えば、戦闘を間に挟まない限り他のカードプレイヤーとのカード勝負でいきなり「レアカードフレームの目押し」ができる。そしてそれは「ゼルママ→他」だけでなく「他→ゼルママ」の順でも行える。これを利用してキスカの入手に一工夫加えられないか考えてみたが、

ゼルママ→FC01
戦闘を挟まずにカード勝負できない。
(2016/10/20追記)戦闘中のカード乱数消費箇所が明らかになったため可能にはなった。しかし先キスカ固定使用時と比較して、 といった理由から、採用する価値はないように思われる。と同時に先キスカ固定の偉大さを実感する。あと、スクリプトもこんな使い方にはまだ対応していない。
FC01→ゼルママ
戦闘を挟まずにカード勝負できる。しかしFC01はカード乱数を消費して手筋を決定するタイプのプレイヤーで、このタイプは先キスカ固定法のような完全パターンにでもハメない限りカード勝負後の乱数の状態を特定できない(少なくとも自分には考え付かない)。
ゼルママ→FC02,03
FC02,03→ゼルママ
ともに戦闘を挟まずカード勝負できるが、寄り道ロスが先キスカ固定法使用時とほぼ等しいためわざわざそうするメリットがない。

と、どのパターンも噛み合わなかった。結論としてキスカとゼルカを両方回収する場合、キスカは先キスカ固定法で、ゼルカはディアボロス入手後に目押しツモで取るのが最善ではないかと思う。

参考文献

カードプレイヤーリスト - アルティマニア補遺
各カードプレイヤーについて
Deling
カードプレイヤーの定義
Card Rule Spreading/Abolishing Mechanics and other RNG goodness - Final Fantasy VIII Message Board for PlayStation - Page 6 - GameFAQs
6年前のカード乱数解析OMG

敵の魔法消去行動の偏り

以下の敵の行動は、魔法を1種類以上所持しているキャラからランダムに1人を選択し、更にその所持魔法からランダムに1種類を選択し、ドリンクマジックとドローは1個、魔法消滅はその全てを消去する。

しかしその魔法選択率は所持魔法のバトル配置次第で大変な偏り方をみせることがある。魔法選択処理はだいたいこんな感じ。所持魔法の走査に用いるindex(0-based)の初期値が「0~所持魔法の種類の数-1」の範囲内の値しかとらないことがポイントで、空欄の存在が選択率に大きく影響するアルゴリズムになっている。

def select_spell
  index = rand(0..255) % kinds_of_spell
  while true
    spell = spell_inventory[index]
    return spell if spell != :blank
    
    index += 1
  end
end

n種類の魔法を所持しているとき選択率の分布が一様(256分率だけど)になるのは、1ページ目の1段目から順にn-1番目の欄まで空欄が存在しない、またはn=1の場合のみ。それ以外の場合は全て偏りが発生し、少なくとも一番下にある魔法が選択から除外される。最も極端な例を挙げると、一番上にある魔法よりも上にn-1個以上の空欄が存在する場合、一番上にある魔法が必ず選択されるなんてことがある。しかしこの仕様を利用すれば、ラスボス戦において僅かな種類のダミー魔法でトリプルをはじめとする重要魔法を確実に守れるはずだ。ダミーa種>ダミー以外b種の順に配置する場合、一番上のダミーよりも上にb個以上の空欄があれば、ダミーが全て消滅するまでダミー以外が選択されることはない。

具体的な挙動はシミュレータを弄って確認してみてください。


アルティミシア戦(ほぼ)ロスなし初期パーティ固定法

かなりめんどくさいっす。

フィールド乱数について

FF8では乱数が用途別にいくつも存在し、そのひとつにフィールド上での雑多なものごとに関わるものがある。その乱数を「フィールド乱数」と呼ぶ。フィールド乱数が関わるものごとには例えば以下のようなものがある。

以下、この節でいう「乱数」は全て「フィールド乱数」を指すものとする。アルティミシア戦の初期パーティ決定にはフィールド乱数以外の種類の乱数は関わらないので。

フィールド乱数生成(物好き用)

フィールド乱数の元になる「乱数の状態」は、乱数の状態をXとおいてその初期値を1とする漸化式「Xn+1 = (Xn * 0x41c64e6d + 0x3039) mod 0x100000000」で計算される。そしてこの乱数の状態Xから「((X / 0x10000) mod 0x8000) mod 0x100」として計算された0~255の値がフィールド乱数として用いられる。これは乱数の状態Xの上位9~16bitにあたる。

擬似Rubyコードで表すとこんな感じになる。ちなみに乱数の状態を求める漸化式は初期値の決め方以外PS版DQ4やDQ7と全く同じもの。ただ単にメジャーかつシンプルなアルゴリズムだから被っただけなんだろうけどね。

$rng_state = 0x0000_0001
def field_rng()
  $rng_state = ($rng_state * 0x41c6_4e6d + 0x3039) & 0xffff_ffff
  return ($rng_state >> 16) & 0x7fff & 0xff
  # 厳密にいうと & 0xff は返り値を受け取った呼び出し元で行っているが
end

以下は日本語版でのそれぞれのアドレス。乱数の状態はFF8 - Caraway Code retro-engineeringでSequenceと呼ばれている値と恐らく同じものだが、言語が異なるためアドレスも異なる。

乱数生成のサブルーチン
0x800382e8
乱数の状態の保存先(4byte)
0x8005510c
フィールド乱数として用いられた値(1byte)
0x8005510e

自分の確認した限り、乱数の状態が「乱数の消費」と「ハードリセット(初期値の1に戻る)」以外の要素で変更されることはない。つまり、現在の乱数の位置を特定できれば以降の乱数は全て予測できる。……というか、既にそれはカーウェイ邸番号探索でされているけれど。

最終マップで行われていること

ラスボス直前の最終マップにいる間(視点切り替え後含む)は0.5秒経過するたび乱数が1個消費される。これを「空回し」と呼ぶ。ただただ消費されているだけで、なにかに使われるわけではない。

# map#:583, label:91 から一部抜粋

while true
  wait(15/30s)
  rnd()
end

アルティミシア戦の初期パーティは、開いた扉が静止した直後に乱数を1個消費して決定される。これを「最終パーティ選定」と呼ぶ。6C3の全20通りからランダムに選択されるが、「セリキ」のみ9/256≒3.5%で他は13/256≒5.1%である。

# map#:583, label:70 から一部抜粋&簡略化

temp = rnd()
# tempの範囲は0..255
if temp < 13
  setparty(ゼスア)
elsif temp < 26
  setparty(ゼスリ)
elsif temp < 39
  setparty(ゼスセ)
elsif temp < 52
  setparty(ゼスキ)
elsif temp < 65
  setparty(アスリ)
elsif temp < 78
  setparty(アスセ)
elsif temp < 91
  setparty(アスキ)
elsif temp < 104
  setparty(リスセ)
elsif temp < 117
  setparty(リスキ)
elsif temp < 130
  setparty(セスキ)
elsif temp < 143
  setparty(アゼリ)
elsif temp < 156
  setparty(アゼセ)
elsif temp < 169
  setparty(アゼキ)
elsif temp < 182
  setparty(リゼセ)
elsif temp < 195
  setparty(リゼキ)
elsif temp < 208
  setparty(セゼキ)
elsif temp < 221
  setparty(リアセ)
elsif temp < 234
  setparty(リアキ)
elsif temp < 247
  setparty(セアキ)
else
  setparty(セリキ)
end

ハードリセット後にアルティミシア城前のセーブデータから再開し、最後の道の移動と選択肢を最速で進行した場合のパーティは「ゼスキ」または「アスリ」になる。「ゼスキ」は最終パーティ選定に44個目の乱数が使用されたことを、「アスリ」は45個目が使用されたことを意味する。つまり44個目が空回しされるかされないかのタイミングで最終パーティ選定が実行されるというわけで、このことから最終マップを最速で進行した場合、マップinから最終パーティ選定までの空回し継続時間は0.5*44=約22.0sと考えられる。確実に45個目の乱数を適用したいときは空回しタイミングのちょうど中間をみて、滞在時間を約0.25s伸ばすといいだろう。45+n個目を適用したいなら0.5n+0.25s伸ばす。後述する方法でパーティを固定させるときもこの0.25sは必須である。

エミュレータ(ARBEX100606)と実機(PS版FF8日本語版・SCPH-90000(高速モード))では空回し継続時間に差があるようだったためこれは実機で調査した。なお、扉のテキストを送る最中も空回しは続いているため、テキスト長の異なる他言語版に22.0sをそのまま適用することはできない。

メニュー操作を最終マップで行うとき、メニュー開閉n回につき空回し時間が(n-1)*2fだけ伸びる。ただし扉に近すぎると伸び幅が大きい(超接近して1回開閉しただけで空回しが12f(0.2s)伸びたのを確認)。メニュー開閉を何度も繰り返すことなんて普通はないから、扉のすぐ傍でしない限りは最終マップでメニュー操作を行っても空回しへの影響はほぼないということになる。扉のテキスト送りが遅れるかもしれないが(□連射?)。

アデル戦後ムービー中の乱数消費

アデル戦後の時間圧縮ムービーが流れている最中、スコールはコントローラの入力次第で様々な動きを見せる。このときアニメーションが開始されるタイミングで△○×□↑↓←→のどのボタンも押されていなければ、乱数が1個消費され↑↓←→いずれかの動きがランダムに行われる。この乱数消費は画面に空が映った直後に開始され、途中鳥の大群に覆われてスコールが見えなくなった後も続き、画面がホワイトアウトする直前を最後に終了する。ムービー中終始無入力状態のときに消費される乱数の数は45個で、その周期は平均101f≒約1.68sである。「平均」と表現しているのは、乱数消費によるアニメーションの場合「→」が他の方向より4f長くなるからだ(方向キー入力時の動きの周期は常に100fなので、無入力時は動き1回につき平均1f遅れる計算になる)。

ボタン×
アニメーションのフレーム数by 手動100100100100108116108116
by 乱数104100100100----
無入力時 rnd() & 33210----
# map#:351, label:8 から一部抜粋&簡略化
while true
  animation = 0

  # 押下されているボタンによってアニメーションが決まる
  # 下にいくほど優先順位高
  animation = 5 if keyscan(△)
  animation = 6 if keyscan(×)
  animation = 7 if keyscan(□)
  animation = 8 if keyscan(○)
  animation = 1 if keyscan(↑)
  animation = 2 if keyscan(↓)
  animation = 3 if keyscan(←)
  animation = 4 if keyscan(→)

  # △×□○↑↓←→どれも押されていない場合、
  # フィールド乱数を消費して↑↓←→のうちどれかのアニメーションが選択される
  animation = 1 + (rnd() & 3) if animation == 0

  if animation == 5
    animekeep(6)
  elsif animation == 6
    animekeep(7)
  elsif animation == 7
    animekeep(8)
  elsif animation == 8
    animekeep(9)
  elsif animation == 1
    animekeep(10)
  elsif animation == 2
    animekeep(11)
  elsif animation == 3
    animekeep(12)
  elsif animation == 4
    animekeep(13)
  end
end

(ほぼ)ロスなし初期パーティ固定法

自分が考えているロスなしパーティ固定法とは、ムービー中のスコールの5~16回目の動きをみて現在の乱数の位置を特定し、20から45個目までの0~26の範囲で任意の数だけ乱数を消費することで目当てのパーティを選定させようというもの。うまく行けばロスは最終マップでの0.25sだけで済む。具体的にとる行動は以下の通り。

常時無入力時の乱数の番号ムービー中のスコールパーティ固定のためのプレイヤーの行動
1~3視認できない。次に備えて何も入力しないようにする。オート連射も当然オフにする。テキストを送らなくてもタイムは落ちない。
4~15視認できるようになる。無入力状態を維持しつつ、乱数特定用の12回分の動きを記録する。12回目の動きを確認したらすぐ十字キーorアナログスティックをどの方向でもいいから入れ続ける
16~19ゼルが邪魔になったりして視認しにくくなる。引き続き方向キーを入れ続けて乱数消費を阻害しつつ、記録した動きから乱数の特定作業を行う。
20~33画面が海中に切り替わったあと、また視認できるようになる。スムーズに特定できたなら、スコールが海に飛び込むムービー後に画面が海中へ切り替わる瞬間に合わせてコントローラから指を離し、無入力状態を「1.68*乱数を進めたい数 s」持続して乱数を消費させる
特定に手間取ってそのタイミングに間に合わなかった場合は、スコールの動きをみて乱数消費の中間の辺りを見計らいコントローラから指を離す。
必要なだけ消費させたらまたボタンを押し続け(このときは△○×□でもよい)、乱数を動かさないようにする。
34~44鳥の大群に覆われる。これ以降は全く視認できない。
45画面がホワイトアウトする直前。ムービー中最後の乱数消費ポイント。ここを過ぎてもまだ必要なだけ乱数を消費できていない場合、残りの回数はドローポイントまたは最終マップで消費する。
ムービー終了後
ドローポイント or 最終マップ残りのn回を消費する。ドローポイントをn回調べる(1回/0.6s)か、最終マップで0.5*n s待機する。
最終マップ待機なり斜め移動なりして滞在時間を0.25s伸ばす

ドローポイントを調べれば、パーティにドロー要員がいなかろうがアルティミシア城内でドローが封印されていようが既にそこからドロー済みであろうが、乱数が必ず1個だけ消費される。カウントにだけ集中できるので特定後の乱数消費手段の中では最も安全な方法といえる。調べるドローポイントはSeeD達の傍のトリプルか中庭のスロウがいいだろう。

乱数の範囲を1500~3500とかなり広くとって動きの重複を探索してみても一組も見つからなかった。これはつまり、RTA中の乱数の現在位置は、スコールの連続する12回の動きを見れば確実に特定できるということを意味する(はず)。さすが4^12通りだ、誕生日攻撃を喰らっても問題ないぜ

しかしこの方法には手間が掛かる以外にも大きな問題があって、それは目当てのパーティに固定するのに手頃な乱数消費数で済むとは限らないというものだ。ハードリセットを伴うパーティ固定法ならば乱数が常に同じ位置にあることが保証されているからその心配はないのだが。……Disc3終了時のセーブ&リセットで乱数を初期化してアデル後ムービーで乱数を0~45個消費することでディスク交換ロスを吸収しつつパーティを全20パターンから選択できる、というセーブリセ再現の改善案みたいなボツ案もあった。

それはさておき、固定する目標に単一ではなく複数のパーティを設定し、その中で乱数の消費数が最も少ないパーティを採用することでこの問題の解消を試みる。左のグラフは乱数の範囲0~9999で目標パーティ数1~4のとき必要となる乱数消費の累積分布だ。パーティ数1のときの乱数消費の多さが目につく。ここではアーヴァイン+低Lvキャラの組み合わせとなるパーティを目標に設定しているが、唯一確率の低い「セリキ」を選択しない限りは他のパーティでもだいたい同じ結果になるはずだ。

速やかに乱数特定を終えムービーが終わるまでに乱数を26個消費できる場合、乱数消費を後に持ち越すことなくムービー中に済ませられる確率は目標パーティ数1では74.2%しかないが、2なら94.7%。3と4なら99%超でほぼ間違いなくロスなく済ませられる。また、特定に多少手間取って20個しか消費できない場合も目標パーティ数3なら97.2%、4なら99.0%である。

乱数消費アゼセor アゼキor セアキor アゼリ
0~20個65.4%89.6%97.2%99.0%
0~26個74.2%94.7%99.1%99.7%

戦略レベルに影響が及ぶかもしれないが、手頃な乱数消費でパーティを引けない問題は目標パーティを3個以上設定すれば解消できる、と考えていいのではないだろうか。他にエンカウント歩数への影響とか実際のプレイでの乱数の位置はどこじゃだとかいろいろあるかもしれないけれど、とりあえずこんなところで。

参考文献

Deling
神。オプコード「0e8 - RND」を検索すれば、ゲームのスクリプト中の乱数の使用箇所がわかる。最初に挙げた例もドローポイント以外はDelingで確認できるもの。調べれば更に面白いものが見つかるかもしれない。ただしpseudo-codeを読む際はなぜか二項演算子の左辺と右辺が反転している点に注意。オプコードの意味はFF8/Field/Script/Opcodes - QhimmWikiに載っている。
PS版DQ4 2章エンドールメタルの乱数調整について:きほんはまる - ブロマガ
乱数の仕組みの解説について。

HP割合とCrisis Level(ピンチ度)

Crisis Level = ピンチ度でした。


プロパゲーターのドロップ


カーウェイ邸番号探索スクリプト

日本語のPS版FF8でしか確認してないっす。以下特徴。

参考文献

FF8 - Caraway Code retro-engineering
乱数の解析・デリングシティNPCによる番号特定方法
ハードリセ無パスワード固定法について:ギルのブロマガ - ブロマガ
上記の日本語による解説
Bienvenue ! - Hyperbolic World
電柱による番号特定方法

敵のAIスクリプト解析

psff8-enemy-ai.zip

2017/03/06
Steam版(only in English)を追加。元のPS日本語版AIのディレクトリには”psjp-”を頭に付けた。
見落としていたアルティミシア(ドローポイント)を追加。この影響でミシア3などでのID指定のズレが直った。
ゴーマニ・ドロマニ・バイセージの個体変数を命名(Steam版用)。
イフリートのカウンターについての説明を修正。
2016/09/26
OPコード24が「fill_ATB」とわかったのでAIを更新。
それに伴いアルティミシア2のAI解説を少し変更。
readme.odsに「RTA中主要な敵のAI」シートを追加。ただしややこしいのは省いてます。
変数名の「recieve」の綴りを「receive」に修正。
2016/07/02
更新履歴を書ききれないくらい一新。
readme、OPコード・変数名・敵の行動・魔法一覧、アルティミシア戦詳細を添付。
不明なOPコードは18個から9個にまで減りました。
2016/05/27
見切り発車的な公開。不明なOPコードは51個中18個。

参考文献

ff8 monsters : .dat files analysis
random_npcさんによるモンスターAIスクリプト解析(5年前!)
FF8 - QhimmWiki
FileFormat, Encounter Codes
Deling
ソースコードより、バイナリからテキストへの変換方法
CzarDragon's Den
敵のアビリティ名(英語)一覧
Final Fantasy VIII - Final Fantasy Wiki - Wikia
英日対訳表など
ファイナルファンタジー用語辞典 Wiki*
敵の行動パターンなど
アルティマニア補遺 - ラストバトル
やり込み in FF - FF8ラスボスデーター
アルティミシア戦に関する情報

ヒレ


味方の能力値


そういや何かあればtwitterまで。