この節では、3.4.1 構造体 $7E2040
: パーティーメンバー表現 で説明した型のオブジェクトを構成するのに必要な型やオブジェクトについて述べる。
まずは仲間キャラクターのレベルアップに関係するデータ構造について述べる。 一つは、各レベルに到達する必要経験値の計算に用いられる定数と、特定のレベル群における属性値の既定値をカプセル化した型だ。 もう一つは、各レベルで習得する能力、すなわち移動時および戦闘時コマンドを定義する型だ。
さて、仲間キャラクターがレベルアップを一度もしていない状態というのも当然考慮しなければならない。 この状態を表現するデータが、人間とモンスターとで別々に定義されている。それぞれについて説明する。
さらに、仲間キャラクターの属性には、人間固有のものとモンスター固有のものとがある。 それらの属性とそれぞれが取り得る値について述べる。
最後に、仲間キャラクターの名前について簡単に説明する。
なお、この節で採り上げる特性データのうち、規模の大きいものを CSV ファイルとして 付録 B データ に収録する。 一部の変則的なデータ収納方式を採るものについては、ここで示すアドレスと異なるアドレスをファイル名に含めてあるので注意して欲しい。
アドレス $239E41
には次の表が示す、サイズ #$50
バイトのオブジェクト
#$2A
個の配列がある。本節ではこの型をレベルアップ構造体と呼ぼう:
表 3.31 構造体 $239E41: レベルアップ構造体
オフセット | 桁 | 属性 |
---|---|---|
#$00
|
レベル区間数 | |
#$01
|
レベル 2 の必要経験値 | |
#$02
|
レベル区間境界値[0] | |
#$03
|
レベル区間境界値[1] | |
#$04
|
レベル区間境界値[2] | |
#$05
|
レベル区間境界値[3] | |
#$06
|
レベル区間境界値[4] | |
#$07
|
レベル区間境界値[5] | |
#$08
|
レベル区間境界値[6] | |
#$09
|
#$07
|
経験値計算用係数[0] |
#$F8 | ||
#$0A
|
#$07
|
経験値計算用係数[1] |
#$F8 | ||
#$0B
|
#$07
|
経験値計算用係数[2] |
#$F8 | ||
#$0C
|
#$07
|
経験値計算用係数[3] |
#$F8 | ||
#$0D
|
#$07
|
経験値計算用係数[4] |
#$F8 | ||
#$0E
|
#$07
|
経験値計算用係数[5] |
#$F8 | ||
#$0F
|
#$07
|
経験値計算用係数[6] |
#$F8 | ||
#$10
|
#$07
|
経験値計算用係数[7] |
#$F8 | ||
#$11
|
さいだいHP標準値[0] | |
#$12
|
さいだいHP標準値[1] | |
#$13
|
さいだいHP標準値[2] | |
#$14
|
さいだいHP標準値[3] | |
#$15
|
さいだいHP標準値[4] | |
#$16
|
さいだいHP標準値[5] | |
#$17
|
さいだいHP標準値[6] | |
#$18
|
さいだいHP標準値[7] | |
#$19
|
さいだいHP標準値[8] | |
#$1A
|
さいだいMP標準値[0] | |
#$1B
|
さいだいMP標準値[1] | |
#$1C
|
さいだいMP標準値[2] | |
#$1D
|
さいだいMP標準値[3] | |
#$1E
|
さいだいMP標準値[4] | |
#$1F
|
さいだいMP標準値[5] | |
#$20
|
さいだいMP標準値[6] | |
#$21
|
さいだいMP標準値[7] | |
#$22
|
さいだいMP標準値[8] | |
#$23
|
ちから標準値[0] | |
#$24
|
ちから標準値[1] | |
#$25
|
ちから標準値[2] | |
#$26
|
ちから標準値[3] | |
#$27
|
ちから標準値[4] | |
#$28
|
ちから標準値[5] | |
#$29
|
ちから標準値[6] | |
#$2A
|
ちから標準値[7] | |
#$2B
|
ちから標準値[8] | |
#$2C
|
すばやさ標準値[0] | |
#$2D
|
すばやさ標準値[1] | |
#$2E
|
すばやさ標準値[2] | |
#$2F
|
すばやさ標準値[3] | |
#$30
|
すばやさ標準値[4] | |
#$31
|
すばやさ標準値[5] | |
#$32
|
すばやさ標準値[6] | |
#$33
|
すばやさ標準値[7] | |
#$34
|
すばやさ標準値[8] | |
#$35
|
かしこさ標準値[0] | |
#$36
|
かしこさ標準値[1] | |
#$37
|
かしこさ標準値[2] | |
#$38
|
かしこさ標準値[3] | |
#$39
|
かしこさ標準値[4] | |
#$3A
|
かしこさ標準値[5] | |
#$3B
|
かしこさ標準値[6] | |
#$3C
|
かしこさ標準値[7] | |
#$3D
|
かしこさ標準値[8] | |
#$3E
|
うんのよさ標準値[0] | |
#$3F
|
うんのよさ標準値[1] | |
#$40
|
うんのよさ標準値[2] | |
#$41
|
うんのよさ標準値[3] | |
#$42
|
うんのよさ標準値[4] | |
#$43
|
うんのよさ標準値[5] | |
#$44
|
うんのよさ標準値[6] | |
#$45
|
うんのよさ標準値[7] | |
#$46
|
うんのよさ標準値[8] | |
#$47
|
みのまもり標準値[0] | |
#$48
|
みのまもり標準値[1] | |
#$49
|
みのまもり標準値[2] | |
#$4A
|
みのまもり標準値[3] | |
#$4B
|
みのまもり標準値[4] | |
#$4C
|
みのまもり標準値[5] | |
#$4D
|
みのまもり標準値[6] | |
#$4E
|
みのまもり標準値[7] | |
#$4F
|
みのまもり標準値[8] |
レベル区間数とは、このオブジェクトに定義されているレベル区間の個数のことだ。 例えば主人公のデータではこの値は 7 に定義されている。 論理的には、レベル区間の個数はこの値よりも 1 多いと考えるべきだ。
レベル 2 の必要経験値とは、このオブジェクトに対応するキャラクターが、 そのレベルが 2 であるときの経験値の下限値を意味する。 例えば主人公のデータではこの値は 19 だ。
対応キャラクターのレベル最小値が 2 でなくても、この属性には意味があるので注意。
この配列型データメンバーは、レベル区間を自然な順序で昇順に並べて、 それぞれの区間の上限だけを列挙したものだ。
以下、例えば記号 [1, 5)
はレベルの集合 {1, 2, 3, 4}
を意味するものとする。
論理的には最初のレベル区間は [2, レベル区間境界値[0])
と考える。
同様に、最後のレベル区間は [レベル区間境界値[レベル区間数 - 1], レベル最大値]
と考える。
例として主人公のレベル区間データを説明する。この配列の値は物理的には次のように定義されている。
{5, 10, 15, 20, 30, 45, 60}
論理的には次の区間列が定義されているものと考える:
// index // 0 1 2 3 4 5 6 7 {[1, 5), [5, 10), [10, 15), [15, 20), [20, 30), [30, 45), [45, 60), [60, 100)}
例えば主人公のレベルが 25 に上がるときには、
20 <= 25 < 30
であるので、係数配列のインデックスを 4 とする。
このメンバーデータは、対象レベルに対する必要経験値や属性値の、 前レベルに対するそれらからの増加率を決定するために参照される(前者についてはのちほど説明する)。
当メンバーデータおよび後続の各メンバーデータに対して、インデックス i の有効範囲はすべて 0, 1, ..., レベル区間数 - 1 だ。以降、このことをいちいち断らない。
この配列メンバーデータは、到達レベルに対する必要経験値を算出するアルゴリズムが必要とする二種類の係数のペアを要素とする。 計算の詳細についてはのちほど説明する。
例えば主人公に関する経験値計算用係数配列は次のように与えられている。
便宜上、後述する説明で用いられる記号 a, b
を添えておく。
ROM データでは a
と b
の要素が交互に詰められているが、
ここではわかりやすさのために分けた:
// index // 0 1 2 3 4 5 6 7 a = {2, 1, 1, 1, 1, 1, 1, 1}; b = {2, 18, 10, 6, 4, 3, 0, 0};
さいだいHP標準値[i] およびさいだいMP標準値[i]とは、対応するキャラクターのレベルがレベル区間境界値[i]に等しいときの、 そのキャラクターのさいだいHPおよびさいだいMPそれぞれの標準値を意味する属性だ。 ただし、実際にはその半分の値を保持していて、計算部で倍にする。
本項で言う「標準」とは、「キャラクターの装備が裸であり、 かつ種や実のたぐいをいっさい与えないでレベルが上がる場合に取る値」 くらいの意味に解釈して欲しい。「既定値」と解釈してもよいかもしれない。 以下、特に断らない限りこの仮定を置くものとする。
各種標準値[i] とは、対応するキャラクターのレベルがレベル区間境界値[i] の値に等しいときの、そのキャラクターの対応属性値の標準値を表すものだ。 さいだいHPやさいだいMPとは異なり、実際の値を表現している。
例として、主人公のデータにおける標準値配列を次に挙げる:
{11, 23, 38, 49, 65, 105, 149, 185, 255}
各要素は先に例示した主人公のレベル区間データの値と対応しており、主人公がレベル 1, 5, 10, 15, 20, 30, 45, 60, 99 であるときには、ちからの値はそれぞれ 11, 23, 38, 49, 65, 105, 149, 185, 255 が標準的であるということだ。 この配列に含まれない値のレベルに上がるときには、新しいちからの値はレベル 区間の隣り合う端点に対応するちから標準値同士から線形補間で得られる。
例。レベル 25 の主人公のちからの値を計算しよう。対応区間は
[20, 30)
だ。区間端点それぞれのレベルにおけるちから標準値は
それぞれ 65, 105 であるので、その差分 105 - 65 == 40
を
30 - 20 == 10
で割る。商は 4 なので、左端点の標準値 65 と
4 * (25 - 20) == 20
との和を取る。その結果 85 を得る。
これがレベル 25 におけるちから標準値となる。
なお、実際のレベルアップ処理では、現在のレベルと新しいレベルそれぞれの標準値を計算し、 この増分値(上の例でいうところの商 4 のこと)を対象キャラクターの現在の属性値に加算しても許せるか否かを試験するという工程を含む。 この仮新属性値が標準値の 1.5 倍以上であると、今までの計算値を破棄して、 増分値をランダムで高々 1 に抑える役割も果たす(下記コード片)。
23/EC76: C220 REP #$20 23/EC78: B99709 LDA $0997,Y ; 属性 X 基準値 23/EC7B: 4A LSR A 23/EC7C: 18 CLC 23/EC7D: 799709 ADC $0997,Y 23/EC80: 999709 STA $0997,Y ; 上限値 := 属性 X 基準値の 150% 23/EC83: C8 INY 23/EC84: C8 INY 23/EC85: A546 LDA $46 23/EC87: F012 BEQ $EC9B if(現在の属性値){ 23/EC89: D99509 CMP $0995,Y 23/EC8C: 900D BCC $EC9B if(現在の属性値 >= 上限値){ 23/EC8E: E220 SEP #$20 23/EC90: A900 LDA #$00 23/EC92: 6E9509 ROR $0995 23/EC95: 6900 ADC #$00 23/EC97: 9500 STA $00,X $00,X = rand(1) ; 属性 X の増分を高々 1 とする 23/EC99: 8022 BRA $ECBD ; 次の属性へ } } ; 以下通常処理を経て次の属性へ
ほしふるうでわを装備しているキャラクターや、仲間ゴーレムなど、 先述の仮定が成り立たない条件下でのすばやさの成長が、このロジックが適用される例であることが知られている。 仲間ゴーレムの例では、仲間になったレベル 5 の時点ですばやさが 51 ある。 すなわち、序盤レベル帯でのすばやさ標準値 32 の 1.5 倍である 48 以上であることによる。
以上でレベルアップ構造体の説明を終わる。
ここで必要経験値の計算アルゴリズムについて簡単に述べる。 以下、対象キャラクターを主人公に固定する。 また、説明用に記号を次のようにおく:
n
レベルを表す値。1 <= n <= 99
が成り立つ。
E[n]
レベル n
に必要な経験値。
必要経験値の計算アルゴリズムとは、キャラクタークラスとレベル n
を与えて E[n]
を計算するものだ。
先述の「レベル 2 の必要経験値」とは E[2]
が定義されているメンバーデータに他ならない。
例えば主人公については E[2] == 19
.
なお、全キャラクター共通で E[1] == 0
と規定されている。
F[n]
E[n]
を計算するときに用いる調整のための値。
アルゴリズムで帰納的に決定される値。
a, b
経験値計算用係数配列の各 1 バイト長要素の #$07, #$F8
それぞれの桁に対応する値をそれぞれ
a, b
と呼びたい。
これらに付けるインデックスは経験値計算用係数配列に対するインデックスではないことに注意して欲しい。
次項で述べる。
index
経験値計算用係数配列 a, b
用インデックス。
例えば主人公ならば n == 3
のとき、これを含むレベル区間は
[1, 5)
であると説明した。このインデックスは 0 であるので
index = 0
とする。
したがって、a[index] == 2, b[index] == 2
となる。
次に E[n]
を計算するアルゴリズムの概略を記す。
この値はループを用いて帰納的に計算される。
初期ステップは前述のように E[1] = 0
であり、
E[2]
はメンバーデータ「レベル 2 の必要経験値」の値を代入する。
一般ステップのみを説明すれば十分だろう。
// // ...レベル n が次のレベル区間に移行するタイミングで index の値をインクリメントする処理がここに入る... // // 以降がアルゴリズムの核心部 D[n] = E[n - 1] - E[n - 2]; F = F[n - 1] + 32 - F[n - 2]; if(F >= 32){ F -= 32; } else if(D[n] != 0){ --D[n]; } else{ D[n] = 0xFFFFFFFF; } p1 = D[n] * a[index]; p2 = D[n] * b[index]; p3 = F * a[index]; p4 = F * b[index]; sum = p2 + p3 + p4; D = p1 + (sum >> 5); E[n] = E[n - 1] + D; F[n] = F[n - 1] + (sum & 31); if(F[n] >= 32){ ++E[n]; F[n] -= 32; } if(E[n] > 10000000){ E[n] = 10000000; F[n] = 0; }
例えば、主人公のレベル 3, 4, 5 に必要な経験値はそれぞれ 58, 139, 269 だ。 研究熱心な読者には、このアルゴリズムが本当にこれらの値を出力することを確認してもらいたい。
n
レベルを表す値。1 <= n <= 99
が成り立つ。
E[n]
レベル n
に必要な経験値。
必要経験値の計算アルゴリズムとは、キャラクタークラスとレベル n
を与えて E[n]
を計算するものだ。
先述の「レベル 2 の必要経験値」とは E[2]
が定義されているメンバーデータに他ならない。
例えば主人公については E[2] == 19
.
なお、全キャラクター共通で E[1] == 0
と規定されている。
F[n]
E[n]
を計算するときに用いる調整のための値。
アルゴリズムで帰納的に決定される値。
a[n], b[n]
経験値計算用係数配列を検索して得られる、対象レベルに対応する各係数。
この配列の各 1 バイト長要素の #$07, #$F8
それぞれの桁に対応する値をそれぞれ
a, b
と呼びたい。
これらに付けるインデックスは経験値計算用係数配列に対するインデックスではないことに注意して欲しい。
例えば主人公ならば n == 3
のとき、経験値計算用係数配列に適用されるインデックスは 0 である。
したがって、a[3] == 2, b[3] == 2
となる。
次に E[n]
を計算するアルゴリズムの概略を記す。
この値はループを用いて帰納的に計算される。
初期ステップは前述のように E[1] = 0
であり、
E[2]
はメンバーデータ「レベル 2 の必要経験値」の値を代入する。
一般ステップのみを説明すれば十分だろう。
D[n] = E[n - 1] - E[n - 2]; F = F[n - 1] + 32 - F[n - 2]; if(F >= 32){ F -= 32; } p1 = D[n] * a[n]; p2 = D[n] * b[n]; p3 = F * a[n]; p4 = F * b[n]; sum = p2 + p3 + p4; D = p1 + (sum >> 5); E[n] = E[n - 1] + D; F[n] = F[n - 1] + (s & 31); if(F[n] >= 32){ ++E[n]; F[n] -= 32; }
習得能力構造体と銘打ってしまったが、「レベルに応じて習得する能力」の表現に関するデータが実は複数 ROM 中にある。 便宜上、それらを統合した概念を習得能力構造体と呼んでしまうことにする。 一方は習得レベルと対応する習得能力の組の集合であり、他方は習得可能な能力の配列だ。
前者は具体的には次のような不定サイズのデータで表現されている:
表 3.32 構造体 $23B819: 習得レベル定義
オフセット | 意味 |
---|---|
#$00
|
要素数 |
#$01
|
移動時呪文習得フラグ |
#$02
|
戦闘時コマンド習得フラグ |
#$03
|
|
(以下、要素数分だけフラグ組を繰り返す) |
要素数とは、データ内に存在するフラグ組の個数のことだ。 ゼロの場合にはフラグ組定義がない、つまり特殊技能のないキャラクターであることを意味する。
移動時呪文習得フラグとは、仲間キャラクター構造体 $7E2040
のオフセット
#$23
に作用するビットマスクの値だ。
このビットがオンになることで、対応する呪文を使用することが許される。
戦闘時コマンド習得フラグとは、仲間キャラクター構造体 $7E2040
のオフセット
#$24..#$25
に作用するビットマスクの値だ。
このビットがオンになることで、対応する呪文または特技を使用することが許される。
習得可能な能力の集合のほうは不定サイズのコマンド ID の配列の形式で定義されている:
コマンド ID とは、3.9.3 一般のコマンド の意味でのコマンドテーブルの行番号とみなせる。 この配列と先の構造体との関係は、フラグの桁とコマンド ID のある配列添字とが対応するということだ。
仲間キャラクターの初期状態とは、それを仲間にした直後における $7E2040
オブジェクトの「値」を意味する。
仲間キャラクターが人間であれば、この値は配列 $23C014
に直接定義されている。
一方、モンスターの場合には次の型からなるオブジェクトの配列 $23AED1
に定義されている値を用いる。
これは変則的な扱いなのだが、ベビーパンサーとキラーパンサーについては人間方式で初期化を行う。
このプログラムは同じ役割を果たす型を、人間キャラクターとモンスターキャラクターとで別々に定義することがよくあるのだが、 初期状態においてもその手法を採っている。 モンスターの仲間では、初期状態における所持品と装備品とを考慮せずに済むので、その分のメモリーが節約できるということだろう。
構造体 $23C014
については 3.4.1 構造体 $7E2040
: パーティーメンバー表現 で述べたものと同型だ。
構造体 $23AED1
は #$09
バイト長の、次の表に示すような構成だ。
このオブジェクトが #$2A
個配列されている。
表 3.34 構造体 $23AED1: 仲間モンスター初期状態
オフセット | 桁 | 属性 |
---|---|---|
#$00 |
初期レベル | |
#$01 |
最大レベル | |
#$02 |
ちから | |
#$03 |
みのまもり | |
#$04 |
かしこさ | |
#$05 |
うんのよさ | |
#$06 |
さいだいHP | |
#$07 |
さいだいMP | |
#$08
|
#$0F
|
装備クラス |
#$30 | さいだいHP | |
#$C0 | (unknown) |
初期レベルとは、このモンスターを仲間にした時点におけるそのレベルの値を意味する。
最大レベルとは、このモンスターが到達し得るレベルの値の最大値を意味する。
これらの属性は、このモンスターを仲間にした時点におけるそれぞれの値を意味する。
さいだいHPは物理的に二箇所で定義されているが、これはデータ長が 1 バイトに収まらないからそうしている。
この属性だけ初期状態と関係がない概念だ。
$7E2040
オブジェクトの属性値とはならない。
装備クラスとは、このモンスターが何を装備できるのかを決定する数値だ。
データサイズが 4 ビットなので 16 種類のクラスがある。
この値が 3.13.9 配列 $23B669
: 装備許可フラグ(モンスター) で示した順序と対応する。
戦闘中のメダパニ状態における振る舞いを決定するときには、
プログラムは仲間キャラクターを「おとこ」「おんな」「???」の三つに分類する。
まずキャラクターがモンスターであるかどうかを調べ、それから性別配列 $23C236
を参照する。
短い配列なので、ここで全データを引用する:
23/C236: 00 ; 主人公 23/C237: 00 ; クーパー 23/C238: 01 ; アニー 23/C239: 01 ; ビアンカ 23/C23A: 01 ; ビアンカ 23/C23B: 00 ; サンチョ 23/C23C: 00 ; ピピン 23/C23D: 01 ; フローラ 23/C23E: 00 ; パパス 23/C23F: 00 ; ヘンリー 23/C240: 01 ; ベラ
人間とモンスターとで異なる属性といえば、移動中の「つよさ」ウィンドウで「せいべつ」欄の真上に表示される部分だ。
人間の場合は文字列配列 $23C5F9
のインデックスを属性値として取る。
この配列の具体的な内容については付録の文字列テーブルを参照して欲しい。
一方、仲間がモンスターならば、属性値としてはそのモンスター ID を取る。
一応断っておくと、モンスター ID とはモンスターデータテーブル $238000
の行番号を意味する。
仲間モンスター ID からモンスター ID への対応は配列 $23D72D
で定義されている:
23/D72D: 0E ; ベビーパンサー 23/D72E: 59 ; キラーパンサー 23/D72F: 00 ; スライム 23/D730: 03 ; ドラキー ... 23/D755: C5 ; ヘルバトラー 23/D756: D7 ; ネーレウス
通常、仲間キャラクターは名前で呼ぶ。
仲間キャラクターが人間であれば、主人公とその子供らはプレイヤーが名前を文字列の入力によって設定する。
それ以外は既定の文字列を参照するだけであり、その配列はアドレス $23C5CE
にある。
モンスターの仲間キャラクターについては、モンスター ID と仲間順位情報の両方を用いて文字列配列
$23C242
の対応要素を参照する。
主人公、息子、娘の名前を示す文字列はアドレス $7E2001
, $7E2009
, $7E2011
に格納される。
ただし、その既定値はアドレス $23D6ED
にある、ある種の初期化データの一部で与えられている。
23/D6ED: 01 ; 00: 23/D6EE: 01 ; 01: 23/D6EF: 01 ; 02: 23/D6F0: 01 ; 03: 23/D6F1: 01 ; 04: 23/D6F2: 01 ; 05: 23/D6F3: 01 ; 06: 23/D6F4: 01 ; 07: 23/D6F5: 01 ; 08: 23/D6F6: 49 ; 09: ク 23/D6F7: 78 ; 0A: ー 23/D6F8: 83 ; 0B: ゜ 23/D6F9: 5B ; 0C: ハ 23/D6FA: 78 ; 0D: ー 23/D6FB: 01 ; 0E: 23/D6FC: 01 ; 0F: 23/D6FD: 01 ; 10: 23/D6FE: 42 ; 11: ア 23/D6FF: 57 ; 12: ニ 23/D700: 78 ; 13: ー 23/D701: 01 ; 14: 23/D702: 01 ; 15: 23/D703: 01 ; 16: 23/D704: 01 ; 17: 23/D705: 01 ; 18:
文字列配列 $23C5CE
および $23C242
については、
付録の文字列テーブルを参照して欲しい。