Seaside Laboratory

Articles

KFX マニアックス

今や希少種となってしまった KFX ユーザー向けに作られた「重箱の隅つつく」系の解析資料。ネタが見つかり次第更新していく予定。

プログラム解析系のネタを書くときには飛龍++さんの逆アセソースを参考にしています。

最終更新日 2023-02-04

voice.lst

使用可能な WAV ファイルの数

voice.lst はマニュアル上に詳細な説明がなく、motion.lst では 9 番が無効値として予約されていたことから、使用可能な WAV ファイルの数は 0 ~ 8 番までの 9 個だと思われていた。

しかし、プログラムを解析したところボイスを格納している配列は 20 要素確保されていることが判明したため、実際は 0 ~ 19 番から 9 番を除いた 19 個を使うことができる。WAV ファイルの数が多くないのであれば、無理してマイナスのボイス番号を使う必要はない。

マイナスのボイス番号

使用可能な WAV ファイルの数は決まっているため、範囲外のボイス番号を使用するとメモリが破壊されてしまう。

マイナスのボイス番号を使った場合、ボイス (DirectSoundBuffer) 領域の手前にあるスプライト (DirectDrawSurface) 領域から順に破壊され、特に残像用のスプライトは近くに配置されているので、早い段階で破壊されてしまう。

スプライト領域には余分に確保された未使用データが存在するので、軽度の破壊であれば耐えることができる。

motion.lst

行番号が持つ意味

キャラクター作成マニュアルには、以下のような記述がある。

必殺技等で使えるのは、1000 ~ 1299 行までです。

単なるガイドラインのようなものかと思いきや、

Num のスタートは (技の始まりは) COMMAND.LST の CmdNum を 10 倍した数値を指定するのがベストです
COMMAND.LST が 0 ~ 99 まで技が固定してあるように 0 ~ 999 までは 10 倍の数値で始まり
基本動作系のモーションは 10 以上はみ出ることは避けて下さい。

と書かれているように、command.lst と motion.lst の行番号には暗黙の変換ルールが存在する。KFX は motion.lst の Num から command.lst の CmdNum を逆算し、この逆算した CmdNum を使って状態判別を行うことがある。

  • 空中に居る相手に CL が 0 か 1 の攻撃を当てたとき、それが基本技なのか非基本技なのかを判定。基本技の場合はダウンせず復帰する。
  • 浮いている相手に追撃したとき、その前の攻撃で使われた CL が 4 または 5 なのかを判定。

モーションの詰め込みなどで関係のない行番号を使用すると状態判別が正常に行われなくなる。

Sp カウンターの仕組み

キャラクターにはモーションの経過時間を保持するためのカウンター変数があり、以下の動作を繰り返している。

  1. カウンターに 1 を足す。
  2. カウンターが Sp 未満の場合は最初に戻る。
  3. カウンターが Sp 以上になったら 0 でリセットして次の行へ遷移。

ところが、何故かキャンセルしたときだけはカウンターが -1 でリセットされてしまうため、結果的に遷移先の行が 1 フレーム伸びてしまう。

Ci ってなんぞや

リストに使われているパラメーター名には英単語の略称のようなものが使われており、例えば Sp なら Speed、De なら Depth といったように簡単に推測できるものがほとんどだが、Ci だけは何の略称なのか不明である。

キャラクターの属性情報を雑多にまとめたものなので Character Information の略かと思ったが、hitcheck 上では「CIRCO」という表記になっていて謎は深まるばかり…。

ジャンプ開始行の Ci を 0 にする

コマンドに上方向の入力が含まれる必殺技 (サマーソルトキック等) は、入力が遅いと先にジャンプしてしまうため、その後にコマンドが完成しても無視されてしまう。このシビアな入力を緩和すべく「ジャンプ開始行の Ci を 0 にして先にジャンプが出たとしてもキャンセルで必殺技に遷移させる」というテクニックが考案された。

このテクニック、よくよく考えてみると実に奇妙なのである。Ci が 0 ということは、ジャンプモーションとはいえプログラム上の認識はニュートラルでしかなく、ニュートラルはジャンプに遷移可能なので、理論上は上を押し続けている限りジャンプ→ニュートラル→ジャンプ…と無限にループすることになる。

実際そうならないのは「モーションの行番号が変化しないと遷移しない」という仕様に因るもので、同じモーションへの遷移は変化がなかったと見なされ、結果的に無限ループが抑制される。

遅延する音声

新たなモーションへ遷移したときにだけ音声が再生されるよう、再生の条件は「Sp カウンターが 0 のとき」となっている。「Sp カウンターの仕組み」に書いた通り、キャンセル時は Sp カウンターが -1 でリセットされるため、音声が再生されるまでに 1 フレームの遅延が発生してしまう。

Sp カウンターはヒットストップとともに進行が停止するため、ヒットキャンセル時はさらに遅延することになるが、KFX はヒットストップが短いのであまり気になることはない。ただ、キャンセルして「チョーすごい技」を出すと暗転による長時間の停止が発生するため、体感できるレベルの遅延になる。

大気圏離脱現象

ゲームをプレイしていると極稀にプレイヤーが空へ飛んで行ってしまい、しばらく落ちてこないことがある。

プレイヤーは攻撃を受けると、屈み中であれば屈み食らい、空中であれば吹き飛んだ後に復帰して着地といったように、状態に応じた食らいモーションが適用されるが、CL が 6 の攻撃はどのような状態であっても「ふらふら食らい」が適用される。

オフセット移動 (位置を一定量ズラすことを目的とした移動) などで Y ベクトルに大きな値を設定したキャラクターが、移動中に運悪く「ふらふら食らい」の攻撃を受けてしまうと Y ベクトルがリセットされず、もの凄い勢いで空へ飛んで行ってしまう。

行頭で発射されない飛び道具

行頭に飛び道具発射フラグを設定すると処理がスキップされてしまい発射されなくなる、という有名なバグが存在する。

KFX は以下のような順番で処理を行っているため、コマンド経由で特定のモーションに遷移すると飛び道具発射時の Sp カウンターが必ず 1 になってしまう、という構造的な欠陥を抱えている。

  1. コマンド発動
  2. フレーム進行 (Sp カウンター増加)
  3. 飛び道具発射

飛び道具が発射される条件は「遅延する音声」に書かれているものと同じなため、Sp カウンターが 1 になると発射されなくなってしまう。キャンセル時は Sp カウンターがズレるため、1 フレーム遅れる形で発射される。

何でもプレイヤー基準

KFX はプレイヤーと飛び道具の処理をごっちゃにしているので、所々で不具合が発生している。有名なものとしては以下のようなものがある。

  • 飛び道具がヒットしたときにプレイヤー側のヒット分岐が有効になる。
  • 曙フィニッシュかどうかはプレイヤー側の Ci で決まる。

この他にも攻撃結果がプレイヤーの座標によって変わってしまうというマイナーなバグも存在する。飛び道具による攻撃であってもプレイヤー側の状態を参照するため、飛び道具が当たった瞬間にジャンプすると空中からの攻撃と判定され、仰け反り時間が大幅に増加する。

command.lst

移動コマンドの謎

移動コマンドは通常のコマンドとは違い専用のプログラムで処理されているので、コマンド参照に必要な CmdNum とモーション参照に必要な AmnNum 以外は全て無視される。CmdNum はプログラム側からの番号指定があるため、実質カスタマイズ可能なのは AmnNum だけとなる。

CmdNum が変更できなかったり、Std が 000 でも動作してしまうといった不可解な挙動は、全て特殊処理が引き起こした現象である。

Std が 000 のコマンド

Std に 000 を設定した場合、キャラクターの状態と Std が一致することがないため、コマンドが発動しないと考えるのが自然であるが、実際は command.lst を読み込んだ時点で Command が無効化されている。

KFA には Std が 000 のキャンセルコマンドを設定することで、ゲージを使ったガードキャンセル技を作るといった様々なテクニックが存在するが、KFX だと Command 自体が無効化されるのでこのテクニックを使うことはできない。

多ゲージ消費技の仕組み

同じコマンドを記述することで多ゲージ消費技が作れるというのは、意図して用意された仕様や応用テクニックではなく、単にバグである。

command.lst は小さい番号から順に読み込まれ、コマンドの有無にかかわらず全コマンド (0 ~ 129) がチェックされる。条件を満たすコマンドが見つかる度に 1 ゲージ消費され、発動可能と判定された一番最後のコマンドが採用される。

enemy.lst

距離認識バグ

エネミーは正常に動作しているのか検証するのが難しいため、プログラムの解析が行われるまで気付かれなかったマイナーバグが存在する。

  • 相手との距離を保持した変数を確率値で上書き破壊する。
  • 各行の条件チェックを行う度に確率値を計算し直しているので不正確になる。

あとはバグというより、仕様的なもの。

  • 使える行数は 40 行。
  • 使える文字は "12346789pPkKnN" だけ。"5" など対象外の値を設定したときの挙動は未定義。
  • 先頭にあるボタン入力は無視される。

enemy.lst はレバーとボタンを組み合わせたコマンドを前提としているので、先頭にボタンがあると正しく読み取ることができない。

stage.bmp

ステージ画像の折り返し

ステージのサイズはゲーム上では 640x240 となっているが、画像ファイル上では 320x480 となっていて、横長のステージを途中で折り返したような形で格納されている。これはおそらく DirectDraw の制約に因るもので、DirectX 3 SDK のドキュメントには以下のように書かれている。

DirectDraw does not permit the creation of display memory surfaces wider than the primary surface.

DirectDraw はプライマリサーフェスよりも大きなディスプレイメモリサーフェスを作成することができません。

DirectDraw は画面 (プライマリサーフェス) のサイズを超えるスプライト (ディスプレイメモリサーフェス) を扱うことができないので、ステージ画像はこの制約に引っかかってしまう。制約は「画像を複数のパーツに分ける」という単純な方法で回避可能だが、ラスター構成のビットマップ画像を左右で分割しようとすると座標計算が面倒になるので、上下に分離するという方法がとられたと思われる。

ただ、実際は一度にまとめて読み込んでいるので、この特殊な形状は古い DirectDraw に対応していた時代の名残のようなものと言える。KFX の前身にあたる KOKFX は DirectX 3 を使っているため、ステージ画像は STAGE0.BMP と STAGE1.BMP という 2 つのファイルに分かれている。

この仕様は KFA に引き継がれる際に「左右半分に割ったものを縦に配置」と解釈されることになったが、正しくは「画面サイズに分割して縦に配置」なので、もし KFX が大きなステージに対応していたら、サイズ 1280x240 のステージは 640x480 ではなく 320x960 になっていた可能性がある。

その他

ガードゲージの秘密

KFX の逆アセソースを調べていたら「ガードゲージが増えると防御力も増える」というコードを発見して関心していたら、

「ガードしすぎて腕がしびれてきた」ゲージが多いほど防御量は上がります。

何気なくヘルプに書かれていた。

リソースデータのカスタマイズ

マニュアルには記載されていないが、下記ファイル名と同じファイルを KFX のディレクトリに配置することで、リソースデータの一部をカスタマイズすることができる。

ファイル名 デフォルト 用途
moji.bmp リソースの NUMBITMAP ビットマップフォント
load1.bmp load.bmp OPTION 画面背景
load2.bmp load.bmp INFORMATION 画面背景
load3.bmp load.bmp CHARACTER SELECT 背景背景
load4.bmp load.bmp VS 画面背景
load5.bmp load.bmp 起動時とエンディング背景
kfx.wav なし 起動時の効果音

moji.bmp のみ KFX 1.31 から対応 (といっても最終版) で、ディレクトリも system ではなく kfx.exe と同じ場所に配置する必要がある。

マニュアルの間違い

KFX 付属マニュアルに書かれている悲しい間違い。

是非ご覧下さい
CMAKER フォルダにはキャラクター作成キット、解説テキストを用意しました。

キャラクター作成キットは KFX 1.00 にしか収録されなかったので、キャラクターを作る気満々になっても作成キットが見つからず挫折すること間違いなし。本体とは別に、kfxcm100.lzh という名前で単体配布されていた時期もあった。

作った技をゲーム中で見る場合、HOME CLR キーで判定を表示しておいて、ROLL UP と ROLL DOWN でスピード調節をすることができるんですが、それを使用するとどのように技が出ているかわかりやすいでしょう。

スピード調節機能は存在しない。便利そうな機能だけに無かったときの落胆は大きい。

エンディングの間違い

エンディングが始まると「CONGLATULATIONS」という文字が上に向かって流れていくが、正しい表記は「CONGRATULATIONS」。