個別処理
以下の話題をまとめています。
乱数管理
概要
乱数は、0x7e1ece~0x7e1d05の8バイトで管理しており、場面に応じて連続する2バイト(ワード)を使い分けます。
乱数計算自体は、15次M系列によるLFSRのビット列更新で賄うため規則そのものは単純ですが、各フレームで更新が何回走るかは次フレーム移行までに充てられる処理時間に依存するため、予測困難となっています。
乱数計算自体は、15次M系列によるLFSRのビット列更新で賄うため規則そのものは単純ですが、各フレームで更新が何回走るかは次フレーム移行までに充てられる処理時間に依存するため、予測困難となっています。
乱数更新
乱数更新は次図の通り、8バイト64ビットを1ビットずつずらしつつ、0x7e1eceのMSBを新たに計算するように行います。


これは、b[n]=b[n-7]+b[n-15] mod 2 の漸化式に基づく15次M系列LFSRを実装したものです。そのため、周期32767での繰り返しになっています。
実際は、乱数はワード(2バイト)単位で使用するため、ワードの値として次の関係式があると見なすことができます。なお、初期値は電源投入/リセット直後に W[0]=1 が設定されます。
実際は、乱数はワード(2バイト)単位で使用するため、ワードの値として次の関係式があると見なすことができます。なお、初期値は電源投入/リセット直後に W[0]=1 が設定されます。
- ワード漸化式: W[n]=f(W[n-1]), f(w)=((w&1)<<15) | ((w&0xfeff)>>1) | ( ((w&0x200)>>2)^((w&2)<<6) )
※リトルエンディアンのため、ビット順が一部ねじれているように見えることに注意 - 保持しているワード: 偶数アドレス開始だと順にW[n],W[n-16],W[n-32],W[n-48]の4ワードですが、奇数アドレス開始の場合もあり、その場合 W[n-8],W[n-24]等、中間の世代の値になります。

乱数用途
- 揺れの間隔のランダム補正
揺れの次の周期までのフレーム数(ワード) 0x7e034c を 0x82abfa からのルーチンで計算する際の補正値として、0x7e01d0 の乱数ワードを用います。
乱数の下位9bit(0~511)をxとした場合、xが奇数の場合 3600-x、偶数の場合 3600+x を新しい値として決定します。 - 傾きパターンA/Bの決定
10秒間の揺れが終わって次の周期に移行する際、傾きがAパターン(一段階傾き変化して終わり)、Bパターン(一段階変化直後に元の傾きに戻る)の判断を行うときに、0x7e01ceの乱数ワードを用います。
乱数ワードの下位3bitが7であればBパターン、それ以外はAパターンになります。そのため、Bパターンになる確率は単純には 1/8 と言えます。
なお、具体的な判断の箇所は 0x82991a のルーチンの 0x829bfd の処理からです。 - 傾き方向の決定
揺れの周期が次に移るときに傾きが限界でない場合 (例えばボイラー爆発前で反時計+22.5°の場合は時計回りしかないので除外)、乱数0x7e01ceの偶奇で傾きの回転方向の判定を行います。
偶数の場合は時計回り、奇数なら反時計となります。
上記傾きパターンA/Bの決定がAだった場合、同一フレームでA/B判定と共通の乱数で判定を行うため、反時計の方が割をくって起こりにくくなっています。逆にBだった場合は次のフレームまで待って乱数が更新されてから判定を行うため、確率は純粋に半々です。つまり、4/8の確率でA+時計回り、3/8の確率でA+反時計回り、B+時計回り/反時計回りがそれぞれ1/16の確率ということです。
なお、具体的な判断箇所は、Aの場合は 0x82991aのルーチンの0x829c6aから、Bの場合は 0x829dabのルーチンの 0x829df8からです。 - 悲鳴SEの選択
揺れの周期で傾きが変化する時、演出効果として悲鳴のSEが0x829b34からのサブルーチン0x809745のコールによって、小さな音で鳴らされます。
この際、まず演出が入るかどうかに 0x7e01ce の乱数を用います。(確率1/4で鳴らす)。
そして演出が入る場合、7種類の悲鳴SE(34~40)のどれを選ぶかに、0x7e01cf の乱数を使用します。(40番のみ2/8、他は1/8ずつの確率) - 雷鳴演出
おそらくプロローグの場面で、演出効果として SE ID1の雷鳴(音量200)を 0x82aabf からのサブルーチン 0x809745 で鳴らすかどうかを、0x7e01ce の乱数で判定します。( 確率1/512 )
低確率に見えますが、判定自体は毎bf行うと考えられるため、実際は平均17秒間隔で雷鳴がとどろくような演出になります。 - 爆発演出
裏エリアの吹き抜けで、爆発の演出のため 0x82c1f2~ の SE 12,13 を鳴らす等の処理を行うかの判定を、0x7e01ceの乱数で行います。( 確率1/128 ) - 愚痴メッセージ表示
下の「メッセージ処理」で解説している、カテゴリ6の「愚痴」に相当するメッセージ表示時に、0x7e01ce の乱数ワードを使用します。( 確率1/512 )
判定はキャラ毎ですが、同一タイミングなら乱数も共通です。ただし、キャラの順番に応じて 2,4,6,… の補正値を加算して判定に用いるため、結果が共通になるとは限りません。
コード
乱数更新は、0x808012からのルーチンで行います。
;乱数更新(A:8bit,X:16bit)
L808012: ldx.w #$01ce ; a2 ce 01
808015: lda.b $00, x ; b5 00
808017: eor.b $01, x ; 55 01
808019: and.b #$02 ; 29 02
80801b: clc ; 18
L80801c: beq L80801f ; f0 01
80801e: sec ; 38
L80801f: ror.b $00, x ; 76 00
808021: ror.b $01, x ; 76 01
808023: ror.b $02, x ; 76 02
808025: ror.b $03, x ; 76 03
808027: ror.b $04, x ; 76 04
808029: ror.b $05, x ; 76 05
80802b: ror.b $06, x ; 76 06
80802d: ror.b $07, x ; 76 07
80802f: rts ; 60
上記乱数更新ルーチンを呼び出すのは、0x808030からのルーチンのうち、以下の部分です。
;見かけ上無限ループ
808061: inc.b $0180 ; ee 80 01
L808064: jsr L808012 ; 20 12 80
808067: lda.b $0180 ; ad 80 01
L80806a: bne L808064 ; d0 f8
上記処理は、フラグバイト 0x7e0180 が一旦設定された後無限ループになるように見えますが、実際はNMI(V-Blank)による 0x8090b3 からの割り込みルーチンの以下の箇所でゼロクリアされるため、無限ではありません。
しかしこのことにより、処理内のループ回数が処理時間依存になっており、乱数変化の事前予測が非常に困難となっています。
※フレームによっては乱数更新の 0x7e0180 フラグ設定に至ってない状況も想定されているようで、フラグ未設定時の分岐もあります。おそらくフレーム内にメインの処理が完結したかどうかのフラグを兼ねていると推測できます。
しかしこのことにより、処理内のループ回数が処理時間依存になっており、乱数変化の事前予測が非常に困難となっています。
※フレームによっては乱数更新の 0x7e0180 フラグ設定に至ってない状況も想定されているようで、フラグ未設定時の分岐もあります。おそらくフレーム内にメインの処理が完結したかどうかのフラグを兼ねていると推測できます。
;フラグクリア箇所
8090c7: lda.b $0180 ; ad 80 01
L8090ca: beq L8090db ; f0 0f
8090cc: stz.b $0180 ; 9c 80 01
メッセージ処理
概要
ここで言うメッセージ処理は、アクション領域の下部の様々なメッセージが表示される際の処理を指します。
※各キャラのセリフだけでなく、OPデモの英文和訳やEDでの後日談エピソード表示、またキャラと会話する際のプロンプト表示にも関係します。
※各キャラのセリフだけでなく、OPデモの英文和訳やEDでの後日談エピソード表示、またキャラと会話する際のプロンプト表示にも関係します。
メッセージ処理は4系統(内1系統はおそらく不使用)ありますが、いずれも 0x7e024c~の16文字2行分のバッファを更新することで、最終的に画面表示に反映させます。
※実際は16文字目の分は画面に収まりきらないので、最大でも15文字以内で行が構成されます。
メッセージの内容を識別する数値を、ここではメッセージIDとします。しかし、同じメッセージIDでも状況によって異なる内容になります。これは、メッセージの参照先が複数あるからですが、これを区別するものをカテゴリと呼ぶことにします。
メッセージは特定の値を終端とする不定長のバイト列で管理され、ここから表示される文字が決定されるのですが、この時に使用される文字コード表も場面に応じて複数使い分けられています。
※実際は16文字目の分は画面に収まりきらないので、最大でも15文字以内で行が構成されます。
メッセージの内容を識別する数値を、ここではメッセージIDとします。しかし、同じメッセージIDでも状況によって異なる内容になります。これは、メッセージの参照先が複数あるからですが、これを区別するものをカテゴリと呼ぶことにします。
メッセージは特定の値を終端とする不定長のバイト列で管理され、ここから表示される文字が決定されるのですが、この時に使用される文字コード表も場面に応じて複数使い分けられています。
具体的なメッセージの一覧については、wikiのメニューの「メッセージ一覧」から、各カテゴリに応じたページをご覧ください。
処理の系統
前述の4系統は次の通りです
- 汎用メッセージ表示処理
会話イベント、ストーリー表示等で汎用的に使われる処理です。 - プロンプト表示
ANSWER? や COMMUNICATION? という2種類のプロンプト表示の処理です。 - 操作状況に対応したメッセージの表示処理
イベント以外のキャラ操作・アクション状況に応じて表示されるメッセージに関わる処理です。
具体的には、ロスト状態のキャラを呼んだときの「まるできこえていないな」といったメッセージや、体力を消耗したキャラが「わたしすこしやすみたいわ」と訴えるようなものが該当します。
また、案内板(船内各所に設置された地図)の説明文表示も、同じ処理で対応しています。 - 合成メッセージ処理※不使用と思われる処理
こちらは、キャラの名前とその他のパーツを合成して、それを表示するという二段階になっている処理です。
合成したデータを保持するために0x7e149e~の領域を使用します。
おそらく、誰がアクションを行っているかを明示するために名前を表示するような想定で設けられた機能かと思いますが、実際に使われている場面に遭遇することはないため、没になった機能と思われます。
メッセージのカテゴリ
以下のように7種類のカテゴリに分けられます。カテゴリの番号については適当に割り振ったものです。
- カテゴリ1: OP/ED用メッセージ
OPデモ、キャラセレクト・ゲーム開始時と、エンディングに関わるメッセージをまとめて管理するカテゴリです。 - カテゴリ2: 生存者氏名
エンディングで生存者がリストアップされる時の氏名を管理するカテゴリです。 - カテゴリ3: プロローグ時共通メッセージ
プロローグで各NPCがしゃべるセリフの中で、主人公キャラの選択に依らないものを管理するカテゴリです。 - カテゴリ4: 地図説明文
船内の案内板(地図)を見た時の、場所の説明文を管理するカテゴリです。 - カテゴリ5: メッセージパーツ
前述の処理系統のうち「合成メッセージ処理」に使う、パーツを管理するカテゴリです。キャラ名を保持する5yとそれ以外の5zのサブカテゴリに分かれます。ただし、実際には使われないものと思われます。 - カテゴリ6: 操作に対応するメッセージ
前述の処理系統のうち「操作状況に対応したメッセージの表示機能」で用いるメッセージを管理するカテゴリです。これは更に、誰を主人公にするかで4つのサブカテゴリに分かれます。ここではキ/レ/ル/ジに応じて 6c,6r,6l,6j としています。 - カテゴリ7: 一般のメッセージ
その他一般のメッセージ(各種イベントやプロローグ・エピローグのストーリー含む)を管理するカテゴリで、大量のメッセージ情報を含みます。こちらも誰を主人公にするかで4つのサブカテゴリに分かれるため、ここでは 7c,7r,7l,7j としています。
コード表
各文字は1バイト(0~255)のデータとして管理されます。
このうち、254は行終端、255は文字列終端として特別な意味を持つため、実際に表示される文字の対応はありませんが、残りの254種類の値にはそれぞれ表示用のフォントデータが対応しています。
※メッセージの中には終端の255の1バイトのみのものもあります。これは使われないメッセージあるいは空メッセージを意味します。
このフォントデータ(コード表)は5種類存在し、場面に応じて使い分けられます。
このうち、254は行終端、255は文字列終端として特別な意味を持つため、実際に表示される文字の対応はありませんが、残りの254種類の値にはそれぞれ表示用のフォントデータが対応しています。
※メッセージの中には終端の255の1バイトのみのものもあります。これは使われないメッセージあるいは空メッセージを意味します。
このフォントデータ(コード表)は5種類存在し、場面に応じて使い分けられます。
- コード表1: OPデモで流れるストーリーの和訳の表示用
- コード表2: キャラ選択と決定時の説明文の表示用
- コード表3: プロローグ~エピローグまで、メインで使用するもの
- コード表4: エンディング(生存者リストアップまで)での後日談ストーリーの表示用
- コード表5: ベストエンディングでの追加ストーリー分の表示用
コード表の前半はどれもほぼ共通の内容となっており、後半の漢字データを更新するような形で切り替えられていきます。なお、文字データは基本的に仮名や漢字1文字ずつに対応するものですが、中には特殊なフォントや、異なるピッチで文章を表示するために複数文字をまとめて分割したものも含まれます。





メッセージデータ管理詳細
各メッセージは、同一カテゴリのデータは同一のバンク(SFC/SNES空間を16bitずつに区切った単位)のROM領域で管理されます。
※異なるカテゴリで同一のバンクになるものもあります。
※異なるカテゴリで同一のバンクになるものもあります。
メッセージデータは不定長であるため、メッセージIDに応じた先頭アドレス(16bit)を管理する配列領域と、各メッセージデータをつなげた領域の2段階での管理となります。両者は同一バンクに存在します。
例えばカテゴリ4の場合、バンク0x9cのアドレス0x8002(0x9c8002)に、メッセージID 1番のアドレス0x8048(0x9c8048)が載っていて、0x8055にあるバイト255までで一区切り、アドレス0x8004(0x9c8004)にはメッセージ2番のアドレス0x8056が載っていて、これは1つ前のメッセージの次の領域になっている…といった配置になっています。
なお、ROM上の管理はバイト単位ですが、実際に2行分のメッセージデータバッファ 0x7e024c~に保存する際にはワードデータに変換します。
変換規則は、バイトデータ x に対して、0x1c00|((x&f8)<<2)|((x&7)<<1) です。特にデータ 0 は 0x1c00 に変換され、これは空白による表示位置調整に使用されます。
以下、カテゴリ毎のデータ配置の詳細です。
例えばカテゴリ4の場合、バンク0x9cのアドレス0x8002(0x9c8002)に、メッセージID 1番のアドレス0x8048(0x9c8048)が載っていて、0x8055にあるバイト255までで一区切り、アドレス0x8004(0x9c8004)にはメッセージ2番のアドレス0x8056が載っていて、これは1つ前のメッセージの次の領域になっている…といった配置になっています。
なお、ROM上の管理はバイト単位ですが、実際に2行分のメッセージデータバッファ 0x7e024c~に保存する際にはワードデータに変換します。
変換規則は、バイトデータ x に対して、0x1c00|((x&f8)<<2)|((x&7)<<1) です。特にデータ 0 は 0x1c00 に変換され、これは空白による表示位置調整に使用されます。
以下、カテゴリ毎のデータ配置の詳細です。
カテゴリ | バンク | 管理配列 ベースアドレス |
メッセージID 範囲 |
メッセージ 保存範囲 |
備考 |
---|---|---|---|---|---|
1 | 0x84 | 0x8000 | 0~0x48 | 0x809e~0x8568 | このカテゴリのみ、適用するコード表が 1,2,4,5と複数存在します |
2 | 0x87 | 0x8000 | 0~0x3e | 0x807e~0x8210 | 生存者となりうるキャラの氏名で構成され、適用するコード表は4です。 中に「ブライアン=ディクスター」という氏名もあります。 おそらく没キャラです。 |
3 | 0x9b | 0x8348 | 0~0xc6 | 0x84d6~0x8f1f | 主人公依存と思われるネルソン君との会話も含まれてますが使用されません。 ※カテゴリ7j収録のメッセージが使われます。 |
4 | 0x9c | 0x8000 | 0~0x23 | 0x8048~0x821c | |
5y | 0x86 | 0x8000 | 0~0x3e | 0x8073~0x817c | |
5z | 0x85 | 0x8000 | 0~0x18 | 0x8032~0x812a | |
6c | 0x8b | 0x82d0 | 1~0x85 | 0x83dc~0x8af9 | |
6r | 0x8b | 0x8afa | 1~0x7b | 0x8bf2~0x9282 | |
6l | 0x8b | 0x9283 | 1~0x76 | 0x9371~0x9a04 | |
6j | 0x8b | 0x9a05 | 1~0x70 | 0x9ae7~0xa132 | |
7c | 0x90 | 0x8000 | 0~0x815 | 0x902c~0xd3a8 | メッセージID 0,1 は全主人公共通の「プロンプト表示」用です。 |
7r | 0x9d | 0x8000 | 0~0x80a | 0x9016~0xdd28 | |
7l | 0x83 | 0x8000 | 0~0x884 | 0x910a~0xdf9d | |
7j | 0x88 | 0x8000 | 0~0x81b | 0x9038~0xe1ea |
各系統処理概要
- 汎用メッセージ表示処理
- 0x80cd2cのルーチンで処理を行います、予め $024a にメッセージIDをセットしておきます。
- $02ecが非ゼロの場合は、プロローグの共通メッセージと判断し、カテゴリ3のメッセージをロードします。
なお、$02ec は場面 (部屋や表エリアの各ブロック) に入る毎に切り替わります。つまり、主人公固有のキャラがいる領域ならゼロが、共通のキャラがいる領域なら非ゼロがセットされるということです。 - 進行度$0354が特定の値の場合、対応したカテゴリのメッセージをロードします。
進行度5(OPデモ~ゲームスタート)、-2(エンディング共通)、-3(ベストエンディング)ならカテゴリ1、進行度10(エンディングでの生存者表示)ならカテゴリ2のメッセージをロードします。 - その他のケース(プロローグ~エピローグ)では、$02c8の示す主人公に応じて7c,7r,7l,7jのカテゴリのメッセージをロードします。
- プロンプト表示
- 0x80ce98のルーチンで処理を行います。メッセージIDを保存するのは $024a ではなく $02a0 です。
- 対応するメッセージIDは0,1のみですが、$02a0には+1した値として1,2を予め指定します。
- 主人公によらず、カテゴリ7cのメッセージをロードします。0,1に対してそれぞれ"ANSWER?","COMMUNICATION?"のプロンプトが対応します。
- カテゴリ7r,7l,7jのメッセージID0,1にも同様のデータが保存されていますが、こちらは使いません。
- 他のメッセージ処理と異なり、このプロンプトは左詰めで表示されます。
- 操作状況に対応したメッセージの表示処理
- 0x80e129のルーチンで処理を行います。予め $024a にメッセージIDをセットしておきます。
- 案内板(船内各所に配置されている地図)を見る場合、$0390に非ゼロがセットされているのを検出し、カテゴリ4のメッセージをロードします。
- $0390がゼロの場合、主人公または同行者のメッセージを、$02c8の示す主人公に応じて6c,6r,6l,6jのカテゴリから選んでロードします。
- 合成メッセージ処理
- 0x82d29fのルーチンからそれぞれ呼び出される、0x82d748, 0x82d748の各ルーチンでパーツをロードし、合成して$149e~のバッファに格納します。
- 最初にロードするのがカテゴリ5yのキャラクター名、続いてロードするのがカテゴリ5zのその他のパーツです。
- $149e~のバッファから0x7e024c~のバッファへ反映するのは、別途0x80e23fのルーチンが対応します。
メッセージチャンク
※外部サイト紹介に挙げているサイト運営者のたままーさんに頂いた解析資料により詳細が判明しました。この場を借りてお礼申し上げます!
イベントにおいては、上で整理したメッセージは個別に扱われるのではなく、ある程度のまとまりで管理されています。これをここでは(メッセージ)チャンクと呼ぶことにします。
チャンクには1~のIDが割り振られており、別々の主人公キャラでIDは重複します。また、主人公に依存しないプロローグの共通イベントにも同様にIDが割り振られています。
チャンクの一覧については、メニューの「メッセージチャンク一覧」からご参照ください。
チャンクには1~のIDが割り振られており、別々の主人公キャラでIDは重複します。また、主人公に依存しないプロローグの共通イベントにも同様にIDが割り振られています。
チャンクの一覧については、メニューの「メッセージチャンク一覧」からご参照ください。
例えばキャラと会話してメッセージが表示されると、会話が収まるまで関連するメッセージが自動で流れるのが分かります。この「自動で流れる一連のメッセージ」がチャンクに相当します。
メッセージの一覧をご覧いただくと、関連するメッセージが固まって存在することにお気付きかと思いますが、チャンクは連続するIDのメッセージから構成されます。
また、"ANSWER?"とプロンプトが出るような会話のケースの場合、応答次第で分岐してさらに自動でメッセージが流れます。この遷移もチャンクの管理データとして保持しています。( プロンプトが出ない場合の自動遷移もあります )
以下、管理データの概要です。
メッセージの一覧をご覧いただくと、関連するメッセージが固まって存在することにお気付きかと思いますが、チャンクは連続するIDのメッセージから構成されます。
また、"ANSWER?"とプロンプトが出るような会話のケースの場合、応答次第で分岐してさらに自動でメッセージが流れます。この遷移もチャンクの管理データとして保持しています。( プロンプトが出ない場合の自動遷移もあります )
以下、管理データの概要です。
- 主人公別チャンク管理データ
- バンク0x9aの0x8000から、チャンクあたり5バイトのデータの配列として管理されます。
- キ/レ/ル/ジのそれぞれチャンクID1から順に、522, 524, 539, 522 のチャンクが収録されています。
- チャンク内の5バイトを16進数として AB,CD,EF,GH,IJ とした場合、以下の構成になっています。
- DAB … 開始メッセージIDから1引いた値
- C … チャンクに含まれるメッセージ数 ( 1~15 )
- G … 8以上の場合、チャンク終了時に"ANSWER?"のプロンプトが出ることを示します
- GHE&0x7ff … "ANSWER?"のプロンプトが出るケースで、応答した場合の遷移先のチャンクIDです。
- FIJ … "ANSWER?"のプロンプトが出るケースで、無視した場合の遷移先のチャンクIDです。
遷移がなく一旦会話が途切れる場合は 0 が設定されています。
- 例えばレドウィンのチャンクID 5 は、それ以前のキャプリスの分も併せると 527番目の 9a8a46 ( 9a8a00+5*526 ) に格納されており、 1a,20,20,80,05 というバイトの並びから、開始メッセージ27、メッセージ数2、"ANSWER?"のプロンプトあり、応答すると次のチャンクは2、無視すると次のチャンクは5(同じチャンクの繰り返し)であることが分かります。
- プロローグ時共通メッセージ用
- バンク0x9bの0x8000から、チャンクあたり10バイトのデータの配列として管理されます。
- チャンク内は2バイトワードデータ×5で以下のような構成になっています。
- オフセット0 … 開始メッセージIDから1引いた値
- オフセット2 … チャンクに含まれるメッセージ数 ( 1~ )
- オフセット4 … 4 もしくは 0 で、4の場合はチャンク終了時に"ANSWER?"のプロンプトが出ることを示します。
- オフセット6 … "ANSWER?"のプロンプトが出るケースで、応答した場合の遷移先のチャンクIDです。
- オフセット8 … "ANSWER?"のプロンプトが出るケースで無視した場合、あるいはプロンプトが出ないケースの遷移先のチャンクIDです。
遷移がなく一旦会話が途切れる場合は 0 が設定されています。
「操作状況に対応したメッセージ」に関してはチャンクという管理にはなっていませんが、複数メッセージを連続で流すケースも想定して、先頭メッセージ・連続メッセージ数のペアで管理している部分もあります。
- 82d172~82d1a5
- 地図の案内図ID(1~26)に応じた、カテゴリ4の先頭メッセージIDと連続メッセージ数のペアが保持されています
- ペアの選択は、案内図ID($03ca: 1~26)を配列のインデクス(1開始)として行います
- 82cba3~82cbb2
- 単発の主人公メッセージとして、カテゴリ6のメッセージIDが保持されています
- メッセージIDの選択は、$03c8を配列のインデクス(0開始)として、サブルーチン0x82cbf3で行います
- メッセージIDそのものは、各主人公で共通です。( 参照するメッセージの内容は異なります )
- 選択されたメッセージIDが7以下の場合、50%のランダムで+1の補正が入ります。
つまり、同じシチュエーションに2つのメッセージが半々で現れることになります。 - 実際に使われるのは、施錠されているドア、同行者のミス、自身の体力枯渇の予兆、ロスト状態のキャラをL/Rで呼んだ時の4パターンのみです。
- 82cbb3~82cbf2
- 連続した複数の主人公メッセージとして、カテゴリ6の先頭メッセージIDとメッセージ数の組が保持されています
- メッセージIDの選択は、$03c8-16を配列のインデクス(0開始)として、サブルーチン0x82cbf3で行います
※$03c8が16未満の場合は、前項の単発メッセージと解釈されます - このテーブルは主人公毎に分かれています
- 実際に使われるのは、インデクス0,1の、転覆直前/直後の主人公の独白のみです
- その他
- 同行者が場面に応じて発する単発のメッセージは直接メッセージIDを計算します
- 対象のキャラに応じて同一内容でも8種類(0~7)のバリエーションがあり、メッセージIDに対する補正値として効果を表します。
その補正値は82d0c3~82d102にあります。モブキャラ用の値は0xffで「メッセージ表示なし」という扱いですが、なぜか救出可能キャラの中で唯一、クレイバーにもこの0xffが割り当てられています。 - メッセージIDは、主人公毎のオフセット値(82d103~82d106にある)+内容に応じた値(0~7)×8+キャラ毎の補正値(0~7)で決定されます。
- 内容に応じた値の内訳は次の通りです
- 0: 一画面引き離し
画面切り替えを司るサブルーチン 0x81b516 内で値 0 がセットされます。対象となるキャラは同行者の先頭(最後に救出したキャラ)です。 - 1: 再合流
イベント処理のトップのサブルーチン 0x81c440 内で値 1 がセットされます。対象となるキャラは、ロスト者の末尾(残存している中で最初に救出したキャラ)です。 - 3: 体力消耗
キャラ個別処理を行うサブルーチン 0x81be02 から呼び出されるサブルーチン 0x81a8b9 内で値 3 がセットされます。対象は条件を満たした同行者キャラです。
契機は2酒類あり、非連れ歩き状態で体力32を切った場合と (強制休憩)、マーカー到着時に体力64を切っている場合です。 - 4: 体力枯渇ミス
キャラ個別処理を行うサブルーチン 0x81be02 から呼び出されるサブルーチン 0x81a8b9 内で値 4 がセットされます。対象は条件を満たした同行者キャラです。
条件は、マーカー到着時に体力枯渇でミスになることで、通常は強制休憩により起こらないため、再合流直後マーカーに到着した時に体力枯渇している状況限定というレアなメッセージになります。 - 6: 愚痴
キャラ個別処理を行うサブルーチン 0x81be02 から呼び出されるサブルーチン 0x81a8b9 内で値 6 がセットされます。
対象キャラはマーカーに到着した疲労状態にない(体力64以上の)キャラ全てで、確率1/512です。
同一タイミングで判定する場合、乱数が共通なので確率が人数分とはいきませんが、補正値を加算しての判定になるので全員揃って条件を満たす/満たさないと決まるわけではありません。
- 0: 一画面引き離し
エンディング選択
ボイラー室の脱出口に入った時の、エンディング選択処理についてまとめます。
エンディング選択は、最終的な救助者の確定と、ポイント計算によるエンディング分岐の2段階で行われます。
エンディング選択は、最終的な救助者の確定と、ポイント計算によるエンディング分岐の2段階で行われます。
最終的な救助者の確定
同行者それぞれに判定を行って、最終的な救助者を確定します。
生存エンディングの際に、エンディングで生存者として表示されるキャラの情報の決定でもあります。
生存エンディングの際に、エンディングで生存者として表示されるキャラの情報の決定でもあります。
- 処理箇所
0x81b516からのルーチンの、0x81b534からのループが対象です - 対象者
$021aで示される人数から-1した、主人公以外の同行者分が対象です。
ロスト状態で再合流していないキャラはこの人数に含まれないため、対象外となります。 - 判定対象データ
$04d0~のキャラ管理データAの中で、ユーザ毎の32バイトのブロックの中のオフセット22バイトのワード(マップ内のy座標)により位置を判定します。 - 判定基準
y座標が0x9000未満(上の方が座標が小さい)の場合が対象者となります。0x9000以上の場合は0x8183f1からのルーチンによってキャラ管理データAからデータが削除され、$021aの人数もデクリメントされます。
ゲームの流れで取り上げている、「高台2つ前の足場」のy座標が0x8ff0で、ここがギリギリ範囲内となっています。
ポイント計算
エンディング分岐は、上記処理で残ったキャラのポイントで行われます。重要時運物関連の判定もポイントだけで行われます。
- 処理箇所
場面切り替わり時のイベントID判断を行う0x9feaaeからのルーチンで、場所ID R-63 の場合の特別処理として、エンディング分岐判断も行います。 - ポイント計算
- 0x9feafcからのループで、$021a-1の示す人数(主人公以外の同行者)分、ポイント合計を計算します。
- $04d0~のキャラ管理データAの中で、ユーザ毎の32バイトブロックの中のオフセット8バイトのワード(キャラID)を元に、キャラポイントテーブルからポイントを参照します。
- キャラポイントテーブルは予め主人公$02c8を元に先頭アドレスをc8ee,c96e,c9ee,c96eのいずれかから決定しておきます
- 重要人物の場合は、ポイントの上位バイトに特定の値が設定されているため、後の判断の際の識別情報となります。
エイミー、イスメイ、アデラの場合は0x8000を加算したポイントになっていますが、ステラ、ハリー、ジャックは0x8000を分割して設定されており、全員分揃って「重要人物救出」という判定になるよう調整されています。 - ポイント計算と同時に、各キャラに対してルーチン0x82ea30を呼び、$14d4~の領域へのキャラIDの追加と、$14d2の救出者人数インクリメントも行われます。
※主人公分はまた別の箇所で処理されます。
- エンディング分岐
- ポイント計算で得られた合計値に従い、エンディングIDのベースを決定します。
- そもそも同行者ゼロ($021aの値が1)の場合は、上記ポイント計算なしに、ベース0が決定します。
- ポイント合計が負(重要人物分の0x8000が加算されていると、内部的に負の値と判定されます)の場合、重要人物ありEDと判定されます。
- ポイント合計の下位バイトが25以上の場合エンディングIDのベースが4となります。
- ポイント合計の下位バイトが25未満の場合8がベースとなります。
- ポイント合計が非負の場合(重要人物分の0x8000が揃っていない場合)、重要人物なし/不足のEDと判定されます。
- $0400の値を見て、負の場合「重要人物を説得したが救助できなかった」と判定し、エンディングIDのベースが20となります。
※$0400は、説得完了した生存者のポイントを単純に合計していた値です。
※レドウィンの場合、ステラ、ハリー、ジャック全員を説得した上で、誰か1人でも途中で欠けた場合にこの条件が成立します。 - $0400が非負で、ポイント合計の下位バイトが25以上の場合、エンディングIDのベースが12となります。
- $0400が非負で、ポイント合計の下位バイトが25未満の場合、エンディングIDのベースが16となります。
- $0400の値を見て、負の場合「重要人物を説得したが救助できなかった」と判定し、エンディングIDのベースが20となります。
- 最終的に、主人公を示す$02c8の値(0~3)を足して、エンディングID 0~23 の24通りが決定されます。
※実際には異なるIDで同一エンディングとなるものがあり、エンディングは実質19通りです。 - なお、この時計算したポイント合計値は$0400に反映されます。(スタッフロール後のポイント合計表示に使われます)
揺れの周期と傾き変化
周期的な揺れと、それに伴う傾きの変化は、操作・ルート進行上大きく影響のある、かつ運の絡む要素になっています。
時間の管理
- ワード0x7e0344がフレームカウント(単位はf、1bfに2増加)となっており、各種周期の判定に使われます。
※60分の時間制限の判定のためのカウントとは別です - フレームカウントの一番の利用目的は、進行度更新に関わる周期の蓄積の判断です。その際、3種類の上限値が使われます。
- 周期フレーム上限1(0x7e034c)
傾きの変化が発生する時間を表します。
周期の始まりに、3600fを基準として-511~+510の幅でランダムに変動します。
※正確には、0~511の乱数を引いて、奇数だった場合はマイナスに、偶数だった場合はプラスに補正します。 - 周期フレーム上限2(0x7e034e)
揺れが始まる時間を表します。
上記周期フレーム上限1より常に600f小さい値が設定されます。 - 周期フレーム上限3(0x7e14ec)
揺れに連動して特定の場所で障害物が落下し始める時間を表します。
上記周期フレーム上限1より常に480f小さい値が設定されます。
- 周期フレーム上限1(0x7e034c)
- このフレームカウントは、周期判定以外にも使われることがあります。
- 転覆までの時間
プロローグで自動進行部分(レドウィンの場合は子供とのやりとり含む)が終わるか、プロローグスキップした時点から、2900f (ルークのみ3150f) で転覆直前の予兆の揺れが始まります。
転覆の発生はルークも含め 3600f後です。
この判定に0x7e0344のフレームカウントが使用されます。 - 多段階の傾き
傾きの変化が1段階で収まらない場合、0x7e0344が一旦ゼロクリアされ、次の段階までの上限240fあるいは300fで判定が行われます。
これは、後述するパターンBの傾き変化、+90°をまたいだ傾き変化、ボイラー爆発時の傾き変化が該当します。
※300fはボイラー爆発の場合です
- 転覆までの時間
- 制限時間超過の判定は、経過時間の分を保存する0x7f03e2によって行います。
60分経過ですぐにゲームオーバーになるわけではなく、そこから0x7e14deが最終カウントダウンのタイマーとして使用されます。
まずは揺れが始まり200bf(400f)で沈没の画像のカットイン、その後1000bf(2000f)でボイラー室に到達できなければゲームオーバーとなります。
周期的な傾き変化
周期フレーム上限1に達したところで、傾きの変化が始まります。
この変化にはパターンA,Bの2酒類があり、挙動が変わってきます。
この変化にはパターンA,Bの2酒類があり、挙動が変わってきます。
ただ、どちらの場合も3種類のパラメータ管理が関わってきます。
- 現在の傾き(0x7e0212)
0~512で傾きを管理しており、転覆前の正常時が傾き0、転覆直後の反転状態が傾き256、時計回りに増加 ( 511から増えると0に戻る ) という数値です。
ただし、基本は32単位の16段階となっており、中間的な傾きは傾き変化時限定です。( 傾き変化が終わる時に32単位に丸められる ) - 傾き変化量(0x7e0214)
変化が始まる時点で±32の値が設定されます。+32は反時計回り、-32は時計回りです。毎bfこの値が0に近づくと共に、同じ分だけ上記0x7e0212が更新され、一段階で22.5°の変化が発生します。 - 変化後予定値(0x7e14e0)
傾きの変化が終わった後の予定値を管理するものです。
大抵は、上の2つの値の差と等しいのですが、そうでないことがあり、想定外の動きにつながる場合があります。
敢えて冗長的なこの値を設けているのは、場面切り替えをした時の傾き反映のためと思われます。
そして、パターンA,Bについては、次のように決定されます。
- サブルーチン0x82991aの処理0x829bfd からパターンA/B判断が始まり、乱数ワード 0x7e01|ce の下位3bitが7ならパターンB (確率1/8)、それ以外はパターンAとなります。
- パターンAの場合
- 同一フレームの同サブルーチンの処理 0x829c6a からどっち向きに回転するかを判断し、傾き変化量、変化後予定値に反映します。。
- 乱数ワード 0x7e01ce が奇数なら反時計回り、偶数なら時計回りです。
※乱数が、パターンA/Bの時の使い回しになるため3:4に頻度が偏ります - 変化後に傾きが反時計+90°( 内部の値として 128 ) になる場合、変化後予定値はもう一段階進めた値が設定されます。
- パターンAが決定したフレームで次の周期が計算されますが、反時計+90°を超えて二段階変化する場合、二段階目の変化用に傾き変化量が設定されるフレームで、周期の再計算とフレームカウントのリセットが行われます。
- 傾き変化中 (二段階変化の途中含む) に画面切り替えを行った場合、即座に変化後予定値への切り替わりが起こり、傾き変化処理が終了します。
放っておけば二段階変化する状況であっても、画面切り替えで処理が終了した場合は、周期の再計算・フレームカウントリセットが発せしません。
- パターンBの場合
- 変数 0x7e14e2 をパターンB中の管理用に使用します。各パラメータの決定は、サブルーチン0x829dabにおいて複数フレームに渡って行います。
- パターンBが決定されたフレーム:
0x7e14e2 に-2をセットするだけで、次のフレームに処理を持ち越します。 - 次のフレームの処理 ( 0x7e14e2の値-2 ):
どっち向きに回転するか判断し、0x7e14e2 を -3 に更新します。
判断の際使用する乱数および偶奇での判定はパターンAの時と同じですが、次のフレームで乱数更新されているため、確率は半々となります。
また、二段階目のタイミングを計るため、ここでフレームカウントがリセットされます。
なお、変化後予定値は現在の傾きと同じ値が設定されます。( 揺り戻しがあるため ) - 揺り戻しの処理 ( 0x7e14e2の値-3 ):
前段の傾き変化が終わったら、効果音6を鳴らし、再び微振動を発生させます。
フレームのカウントが240f(4秒)を超えたら、逆向きの傾き変化量をセットして 0x7e14e2 に -4 をセットします。 - 揺り戻し完了 ( 0x7e14e2の値-4 ):
揺り戻しの傾き変化が終わったら、次の周期を計算しパターンBの処理を終えます。 ( 0x7e14e2 もゼロクリアされます ) - 前段の傾き変化中に画面切り替えを行った場合、傾き変化処理は完了せずに、変化後予定値に一旦傾きがリセットされます。この次の後段の傾きは時計回りに固定されます。
なお、傾き変化の方向の判断には、限界との比較があります。
- 傾きが許容範囲限界の場合は、回転方向のランダム判定は行われません。時計回りの向きの限界は0x730352、反時計回りの向きの限界は0x730353のバイトの値を0x10倍して用います。これらの限界値は進行度が進むたびに更新されます。
- つまり、時計回り上限に達している(超過含む)場合は反時計回り処理確定、それ以外で反時計回り上限に達している(超過含む)場合は時計回り処理確定、そうでなければ上記のように乱数で判断という区分になります。
- なお、反時計+180°(転覆前の水平状態)は、必ず反時計回り上限抵触と判断されるため、時計回り処理が確定します。
周期の蓄積とボイラー爆発
前項パターンA/B決定時に、周期 0x7e0348 が加算されます。パターンAの場合に限り、この周期が12になると進行度が進み周期がリセットされます。
※これにより進行度が2→1と変化すると傾き変化の代わりにボイラー爆発となります。
それ以外にも、主人公ミスでも周期+4の加算があり、12以上となると進行度が進みます。
なお、周期が12を超えていても次の進行度は周期0から始まります。超過分を持ち越すことはありません。
※これにより進行度が2→1と変化すると傾き変化の代わりにボイラー爆発となります。
それ以外にも、主人公ミスでも周期+4の加算があり、12以上となると進行度が進みます。
なお、周期が12を超えていても次の進行度は周期0から始まります。超過分を持ち越すことはありません。
なお、イベントのある部屋 ( イベントがあっても終了していると除く ) だと、周期上限2に達した時点で次の周期に進みます。パターンA/Bの判定も傾き変化もありません。なお12以上で進行度が進むのは同様です。
ボイラー爆発時は、傾きが反時計+67.5°( 内部数値 160 ) になるまで、傾きが断続的に変わっていきます。
その際、フレームカウントが300fになると変化量が+32 ( 反時計回り )、変化後予定値が現在の傾きから-32 になって更に傾きが変わります。反時計+67.5°に到達すると、初めて次の周期に移ります。
その際、フレームカウントが300fになると変化量が+32 ( 反時計回り )、変化後予定値が現在の傾きから-32 になって更に傾きが変わります。反時計+67.5°に到達すると、初めて次の周期に移ります。
なお、イベントのある部屋にいる場合、ボイラー爆発時の傾き変化も保留されます。しかし、300fおきのパラメータ設定自体は継続します。そのため、イベント終了のタイミングによっては変化後予定値が+67.5°を少し上回ることがあります。
具体的には、イベント終了→傾き変化再開→300fおきのパラメータ設定(変化中の傾きからの差分で予定値が中途半端になる)→画面切り替えで中途半端な傾きが設定される、という手順です。
そうすると、傾き反時計+67.5°になるまでほぼ1回転するという「大回転」という現象が発生します。
具体的には、イベント終了→傾き変化再開→300fおきのパラメータ設定(変化中の傾きからの差分で予定値が中途半端になる)→画面切り替えで中途半端な傾きが設定される、という手順です。
そうすると、傾き反時計+67.5°になるまでほぼ1回転するという「大回転」という現象が発生します。