この節では戦闘コマンドについて述べる。
戦闘コマンドとは、戦闘中の一ターン内において、敵や味方それぞれに対する命令をカプセル化した概念だと定義する。
プログラムコードの観点では、戦闘中に配列 $1073
などに格納される 1 バイトの値を ID とする何らかの配列要素を戦闘コマンドと表現していると解釈する。
本節では最初に実際のプログラムコードに沿う形で戦闘コマンドの分類を行う。 戦闘コマンドと関連性のあるデータについても併せて見ていく。 それから、戦闘コマンド分類のそれぞれについて、付随するモデルの物理的構造やコードの挙動を検討していく。 最後に、コードを分析して得た戦闘コマンドデータを CSV 形式のテキストファイルで提供する。
戦闘コマンドを理解するには、配列 $1037
と配列 $1073
について理解する必要がある。
まずはこれらの配列について説明する。
配列 $1037
は次の構造の 1 バイト型データを要素とするものだ:
本節では、戦闘コマンド情報構造体の下位 2 ビットの値で戦闘コマンドを分類する。 戦闘処理のコードを解析すると、次のように分類するのが妥当なように思える:
さらに、この戦闘コマンド情報配列と一対一対応する配列がアドレス $1073
にあり、
こちらには戦闘コマンドを示す 1 バイトの値を格納する。
戦闘コマンド情報配列と戦闘コマンド配列は両方とも、プレイヤー側馬車外のキャラクター群と
敵側パーティーのキャラクター群の関連情報を保持するのに十分な長さがある。
例えば、自陣側の馬車外メンバーが主人公、スラリンの 2 名で、敵陣がメタルスライム 3 匹 だとしよう。 このときのキャラクター、コマンド情報のアドレス、そしてコマンドのアドレスの対応関係は次のようになる:
主人公
|
$1037
|
$1073
|
スラリン
|
$1038
|
$1074
|
メタルスライムA
|
$1039
|
$1075
|
メタルスライムB
|
$103A
|
$1076
|
メタルスライムC
|
$103B
|
$1077
|
実は本節での配列の説明は、戦闘コマンド自身を理解し易くするためにかなり簡略化してある。 配列の有効性やコマンド連続実行時の挙動等、言及しなかった事実があることを断っておく。
種別が 0 の戦闘コマンドは、一般コマンドの意味で防御系のコマンドをとることが必要のようだ:
20/8862: 20809F JSR $9F80 ; $1073,X に戦闘コマンドをセット 20/8865: C900 CMP #$00 20/8867: F039 BEQ $88A2 20/8869: C971 CMP #$71 ; みをまもる1 20/886B: F02F BEQ $889C 20/886D: C972 CMP #$72 ; みをまもる2 20/886F: F02B BEQ $889C 20/8871: C9AC CMP #$AC ; 防御中に同種の仲間が登場 20/8873: F027 BEQ $889C 20/889C: A900 LDA #$00 ; 戦闘コマンド情報構造体 20/889E: 206B9F JSR $9F6B ; $1037,X に戦闘コマンド情報をセット 20/88A1: 60 RTS
種別が 1 の戦闘コマンドには次に挙げる性質、属性がある:
名前
数値の変化量を評価するコード
実行時に表示するメッセージ
必要 MP
有効範囲
目標が敵側に作用するものかどうかのフラグ
実行コード
一般の戦闘コマンドの大半は、ウィンドウのテキスト表示処理に利用できる名前文字列と対応付けられている。
実体はアドレス $228000
にある文字列だが、
3.3.3.1 機能表 のキー #$2B
の参照機能を引き起こす
BRK 命令を実行することで、戦闘コマンドの名前にアクセスする。
何らかのパラメーターを増加または減少するコマンドは、その変化量を評価するためのサブルーチンを必要とする。
そのサブルーチンのアドレスはジャンプテーブル $20B7F9
で定義されていて、
対応するサブルーチンアドレスがあるテーブル列の ID は、戦闘コマンドのそれと一致している。
変化評価を必要としないような戦闘コマンドには、空サブルーチンのアドレスが割り当てられている。
戦闘コマンドの実行直後には、画面中央下のメッセージウィンドウに臨場感豊かな文言が出力される。
各戦闘コマンドはそのような戦闘メッセージの ID を一つ有する。
このメッセージ ID は配列 $268565
の要素として定義されていて、
配列のインデックスとコマンドの ID が対応している。
戦闘コマンドを実行するために、実行者の MP を消費する必要があるものが多い。
この情報が配列 $268765
で定義されている。
これは 1 バイトの値を要素とする配列なのだが、MP 情報は #$1F
部分のビットで定義されている。
そして、配列のインデックスはコマンドの ID と対応している。
さらに、特殊な MP 消費をするコマンドのために、一部はプログラムで消費量を上書きする:
26/83A1: BF658726 LDA $268765,X 26/83A5: 291F AND #$1F ; 必要 MP 26/83A7: C91F CMP #$1F 26/83A9: F01D BEQ $83C8 if(MP != 0x1F){ 26/83AB: C91E CMP #$1E 26/83AD: D004 BNE $83B3 if(MP == 0x1E){ 26/83AF: A924 LDA #$24 MP = 36d 26/83B1: 8006 BRA $83B9 } 26/83B3: C91D CMP #$1D 26/83B5: D002 BNE $83B9 else if(AL == 0x1D){ 26/83B7: A9FF LDA #$FF MP = 255d } 26/83B9: 2C1610 BIT $1016 26/83BC: C210 REP #$10 26/83BE: 5004 BVC $83C4 ; このあたりの処理は 26/83C0: 0A ASL A ; パルプンテのはやぶさ効果を 26/83C1: 9001 BCC $83C4 ; 考慮するものゆえ 26/83C3: 6A ROR A ; 今は議論しない 26/83C4: FA PLX } 26/83C8: A900 LDA #$00 ; 消費なし
このコードの解析によると、マジックナンバーがかなり多いことがわかる。
全ビットがオン、つまり MP が 31 ならばそれは呪文コマンドではないという取り扱いになるようだ。
ベホマズンに対応する消費 MP #$1E
は 36 ポイントに変更する。
さらにメガザルに対応する消費 MP #$1D
は 255 ポイントに変更する。
ここには表れていないが、値 #$FF
もまた「現在の MP の残り全て」を表現するマジックナンバーだろう。
戦闘コマンドの有効範囲とは、次の表に示す区分のことだ。
この情報も配列 $268765
で定義されている。
各バイトの #$60
部分のビットを占める。
例えば「メラゾーマ」は敵の誰かに作用するコマンドであるが、「ザオラル」はそうではない。
この情報も配列 $268765
で定義されている。
各バイトの #$80
部分のビットを占める。
種別が 2 の戦闘コマンドとは、プレイヤーの通常の入力操作において次の条件を満たす、味方側キャラクター限定のものをいう:
指定キャラクターのコマンド選択ウィンドウで「どうぐ」を選択した
どうぐウィンドウで選択した物のアイテム種別が武器である
指定キャラクターがその武器を装備することが可能である
「つかう・そうび」ウィンドウで「そうび」を選択した
言い換えると、キャラクターが武器の装備を変更して、さらに直接攻撃をするという流れを生じるコマンドを意味する。 この定義から、武器でないアイテムが関わるどのようなコマンドも、ここで議論するものには該当しないことを意味する。 例えば「ほしふるうでわ」を選択すると、そのターンで実際にそれを装備するが、これは種別 2 のコマンドではない。 後述する種別 3 のコマンドに分類される。
実際には、戦闘コマンド情報を評価するタイミングで次のような操作を施す:
現在装備している武器があれば、それを外す。 これに伴って、対象キャラクターの攻撃力と守備力を再評価する。
選択したアイテムを装備する。 やはり対象キャラクターの攻撃力と守備力を再評価する。
戦闘コマンドとコマンド種別を「こうげき」コマンドと一般コマンドにそれぞれ置き換える。 この際、装備を変更したことを示すメッセージを表示する(下記コード片参照)。
20/8583: A973 LDA #$73 ; 「こうげき」 20/8585: 8DF710 STA $10F7 ; 戦闘コマンド ID 20/8588: 20809F JSR $9F80 ; コマンドキャラクターの戦闘コマンド ID を設定 20/858B: 20419F JSR $9F41 ; コマンドキャラクターの戦闘コマンド情報を参照 20/858E: 29FC AND #$FC 20/8590: 0901 ORA #$01 ; 種別 1: 一般コマンド 20/8592: 206B9F JSR $9F6B ; コマンドキャラクターの戦闘コマンド情報を設定 20/8595: 20DF9E JSR $9EDF ; コマンドキャラクターを参照 20/8598: 85F5 STA $F5 20/859A: 85F7 STA $F7 20/859C: 00A1 BRK #$A1 ; message #$0028: [F7]は ぶきを [F5]にもちかえた! 20/859E: 28 20/859F: 20D69D JSR $9DD6 ; 待ち 20/85A2: ADF710 LDA $10F7 ; 戦闘コマンド ID 20/85A5: 82A6FE BRL $844E ; (戦闘コマンド ID による分岐)
なお、戦闘コマンド ID を収める変数には、
そこに「こうげき」を示す値 #$73
が入るまでは新しく装備する武器のアイテム ID が収められていた。
種別 3 の戦闘コマンドとは、味方側キャラクターに限定された、道具に関する戦闘コマンドを意味する。 先に述べたように、実行時のメッセージに「そうびした!」とあっても、実質的にはこちらの種別になるコマンドが普通にある。
種別が 3 の戦闘コマンドには次に挙げる性質、属性がある:
アイテム
実行コード
戦闘コマンド ID はアイテム ID でもあるので、処理コードはアイテムの属性をすべて参照することが可能だ。 例えば、コマンド処理中にアイテムが消耗品であるかどうかを知る必要がある。 アイテムについては 3.13 アイテム で議論する。
実行コードはサブルーチンで実装されている。ジャンプテーブルはアドレス $20C162
にあるのだが、
このテーブルの行番号は戦闘コマンド ID すなわちアイテム ID とは一致していない。
アイテム ID から行番号への対応表がアドレス $27819A
にあり、これを用いて変換する。
たいていのアイテムは「しかし なにも おこらなかった」というメッセージ表示しか引き起こさないので、
それ以外の戦闘コマンドに対してしか意味のある実行コードを用意しなくて済むようにこのような構造になっている。
使用コマンドが何らかの固有処理を有する場合には、その処理コードを特定した直後に、 そのアイテムを消費するかどうかを判定し、消耗品であれば実際にキャラクターの持ち物袋からそれを一つだけ削除する。 その後に固有処理をするサブルーチンを呼び出す。
戦闘コマンドデータを CSV で表現したファイルを 付録 B データ から参照できる。 本節で述べた複数の配列とテーブルを水平に結合したり、コメント欄を付加してあったりするので利用者は注意して欲しい。