4.16. タイルエンカウント

4.16.1. 解析
4.16.1.1. $C68E4A - タイルエンカウント構造体へのポインタ配列
4.16.1.2. JSL $C92C2D によるデータ取得
4.16.1.3. JSL $C92C39 によるデータ取得
4.16.1.4. $C6843C - タイルエンカウント構造体
4.16.2. TODO リスト

ここでは、$C6843C に定義されている構造体、 タイルエンカウント構造体の完全解析を試みる (敵側グループの決定アルゴリズムの説明はここでは行わない)。 「タイルエンカウント」という用語は、 PS 版ドラクエ 7 のデバッグモードの同名デバッグメニューから拝借した。 これは、フィールドやダンジョンにおける戦闘モード開始時における 敵側グループの構成を決めるためのデータである。

タイルエンカウント構造体のアドレスを特定する方法は調査しない。 既に Shingo Endo 氏解析資料 [URL1] 等によって解析済である。 後発の解析であるため、それを再利用させてもらう。

4.16.1. 解析

これまで見てきた各種構造体 (モンスター仲間キャラクターアイテム行動構造体等) と同じく、データ構造体配列と、そのデータへのポインタ配列の二本立てである。

プログラムからデータを参照する手段は、サブルーチン $C92C2D および $C92C39 のみを利用するようだ。

4.16.1.1. $C68E4A - タイルエンカウント構造体へのポインタ配列

格納されている値は、タイルエンカウント ID に対応するデータが格納されているアドレスである。 ただし、この値に #$C60000 を加算したものが絶対アドレスとなる。

C6/8E4A:    C6    // DB
C6/8E4B:    3C84  // 00: タイルエンカウント[00] は $C6843C に格納(以下同様)
C6/8E4D:    5284  // 01
C6/8E4F:    6884  // 02
...
C6/8F33:    348E  // 74

4.16.1.2. JSL $C92C2D によるデータ取得

サブルーチン $C92C2D は、汎用的なデータ取得ルーチンを実装するものである。 具体的に言うと、 「ある構造体データの ID = Y の、あるメンバー」を取得するルーチンである。

このサブルーチンを呼び出す際には、以下のものを「実引数」として引き渡す:

  • (1 byte) 取得する値をどこにしまうか

  • (2 byte) 対象となる構造体のサイズ

  • (3 byte) 構造体データのアドレス配列の絶対アドレス

  • (2 byte) 欲しいデータのある箇所が、何バイト目からあるのか

  • (3 byte) ビットマスク

プログラムがタイルエンカウンド構造体データ ID = Y の、 モンスターID [00] というメンバー(後述)の値を取得するために、 以下のようにして $C92C2D を呼び出す。

C3/C2A4:    222D2CC9    JSR $C92C2D         ■(RTL+B) a = [arg[03-05]]arg[06-07],@y & arg[08-0A]
            01
            1600    // タイルエンカウント構造体データのサイズ
            4A8EC6  // タイルエンカウント構造体データへのポインタ配列先頭アドレス
            0300    // 3 バイト目から
            E01F00  // & 001FE0h した値
C3/C2B3:    60          RTS

4.16.1.3. JSL $C92C39 によるデータ取得

JSL $C92C2DX バージョンである。すなわち、 ある構造体データの ID = X の、あるメンバー」を取得するルーチンである。

4.16.1.4. $C6843C - タイルエンカウント構造体

構造体一個あたりのデータ格納レイアウトを以下に示す (記号「←」は、ビット列が次のバイトの下位ビット列と一体化して、 一つのメンバーを構成することを示す)。

表 4.36 $C6843C タイルエンカウント構造体 メモリレイアウト

Byte:Bit 80 40 20 10 08 04 02 01
00 レベル
01 For $7E2011 ID for $C3015C 先制攻撃率定義構造体 ID
02 For $7E3022 For $7E3020 For $7E301E
03 JMP ($F5CE,X)[00] For $7E301E
04 For $7E3020 モンスター ID[00]
05 JMP ($F5CE,X)[01]
06 For $7E3022 モンスター ID[01]
07 JMP ($F5CE,X)[02]
08 JMP ($F5CE,X)[03] For $7E3024 モンスター ID[02]
09 モンスター ID[03]
0A JMP ($F5CE,X)[04] For $7E3026
0B For $7E3028 モンスター ID[04]
0C JMP ($F862,X) テーブル ID
0D For $7E302A モンスター ID[05]
0E JMP ($F862,X) テーブル ID
0F JMP ($F862,X) テーブル ID For $7E302C モンスター ID[06]
10 モンスター ID[07]
11 JMP ($F862,X) テーブル ID For $7E302E
12 For $7E3030 モンスター ID[08]
13 JMP ($F862,X) テーブル ID
14 For $7E3032 モンスター ID[09]
15 未使用 モンスター ID[0A]

以下、各メンバーの説明をする。

レベル

トヘロス・せいすい・しのびあし状態における移動中、 戦闘突入チェックを回避するか否かを決める値である。 主人公のレベルが、この値 +5 以上であれば、戦闘には突入しない。

この 1 バイトの値は戦闘時に $7E2010 にセットされる。 「にげる」の成否は、このセットした値と主人公のレベルに対して 同じ +5 による大小判定チェックを行う。

先制攻撃率定義構造体 ID

先制攻撃率定義構造体配列 $C3F9AE のインデックスを格納している。

// [00-01]: 整数区間の下限。
// [02-03]: 整数区間の上限を下限 + d で表すときの d の値。
C3/F9AE:     08000800
C3/F9B2:     08000000
C3/F9B6:     20000800
C3/F9BA:     20002000

この値からサブルーチン $C2326F 呼び出しにより乱数を生成し、$7E2011 & 03h をセットする。 すなわち、戦闘突入時点における味方側・敵側の先制攻撃の発生有無を決める値である。

ちなみに、戦闘開始直後の先制攻撃決定アルゴリズムのいい加減な説明は次のようになる。 まず、エンカウント ID に対応する先制攻撃率定義構造体が決まる。 このとき、先制攻撃テストに利用するふたつの閾値 L, U が決まる。 例えば $C3F9B6 が適用されるケースでは、 閾値はそれぞれ L := 0x0020 および U := L + d == 0x0020 + 0x0008 となる。

  1. サブルーチン $C00E97 による乱数 R を取得する。

  2. R < L ならば、こちら側の先制攻撃となる。

  3. そうではなく、R < U ならば、敵側の先制攻撃となる。

  4. そのいずれでもなければ、先制攻撃は敵・味方のどちら側にも発生しない。

もう一つ注意したい点がある。 実は「しのびあし」状態にあるとき、d の値を 1/4 にする処理が追加される。 敵側の先制攻撃確率がかなり減ることになる。

ID for $C3015C

戦闘突入か否かを決めるデータ配列のインデックスである。 パーティが一歩歩くたびに、 $7E3B8F が減る方向に更新される。 これが 0 を切ると、戦闘が発生する。 この一歩において、どれだけこのカウンターを減らすかを決める値を決めるデータ配列のインデックスである。

$C3015C2 バイト値 2 個からなる型の配列である。 前の 2 バイト値は乗算に用い、後ろの 2 バイト値は除算に用いる。 カウンター $7E3B8F を、(おそらく)乱数由来の $70 に乗算・除算をしたもので減じていく。 ちなみに、しのびあし状態のときは、乗算・除算後の値を 4 で割ったもので減じる。

C3/015C:    01000200   // $7E3B8F -= ($70 * 1 / 2)
C3/0160:    02000300   // $7E3B8F -= ($70 * 2 / 3)
C3/0164:    03000400   // $7E3B8F -= ($70 * 3 / 4)
C3/0168:    01000100   // $7E3B8F -= ($70 * 1 / 1)
C3/016C:    05000400   // $7E3B8F -= ($70 * 5 / 4)
C3/0170:    03000200   // $7E3B8F -= ($70 * 3 / 2)
C3/0174:    07000400   // $7E3B8F -= ($70 * 7 / 4)
C3/0178:    02000100   // $7E3B8F -= ($70 * 2 / 1)
For $7E2011

戦闘突入時に、以下の各ビットにセットする値である:

  • $7E2011 & 20h

  • $7E2011 & 04h

  • $7E2011 & 08h

  • $7E2011 & 10h

メンバー名が適切ではないのだが、 このバイトの中身が解析できていない現時点では他に呼びようがない。 情報求む。

For $7E301E-$7E3032

このデータが定義するモンスターのうち、どれが出現し易いかを表現する数値である。 具体的な説明は次の機会にする。

JMP ($F5CE,X)

モンスターグループの匹数を決定するルーチン (ジャンプテーブルが $C3F5CE にある)で参照されるメンバーである。 この値をもとに乱数を生成して、それが $7E2008,Y すなわち、 当該グループの匹数となる。

JMP ($F862,X) テーブル ID

モンスターグループの匹数を決定するルーチン (ジャンプテーブルが $C3F862 にある)のジャンプテーブルのインデックスである。 ジャンプテーブルは以下の通りである。 各ルーチンの実装で、コメントのようにアキュームレータに値をセットする。 値が複数書いてあるものは、乱数生成を行うことを意味する。

C3/F862:    72F8 // a = 1
C3/F864:    76F8 // a = 2
C3/F866:    7AF8 // a = 3
C3/F868:    7EF8 // a = 1, 2
C3/F86A:    87F8 // a = 2, 3
C3/F86C:    93F8 // a = 3, 4
C3/F86E:    9FF8 // a = 4, 5, 6, 7
C3/F870:    ABF8 // a = 8
モンスター ID [00-0A]

その戦闘に最初に登場している可能性のあるモンスターのモンスター ID である。 [0A] のモンスターは特殊な条件が成り立っていないと、 登場し得ない。

未使用

すべてのタイルエンカウントデータに対して、ゼロで埋められている。

4.16.2. TODO リスト

  • モンスターグループのセットルーチンを説明