MOD制作と解析でよくある落とし穴
kbotの形式ドキュメントのあちこちに散らばっている、特によく引っかかる注意点を1ページにまとめた早見表です。どうにも理屈に合わないバグに当たったら、まずここを見てください。各項目から、該当する形式のページにある詳しい説明へリンクしています。
Tip
「MODが読み込まれない」を探していますか? 「沈黙したまま壊れる」という報告の原因としては、TDFのセミコロンの規則(#tdf--fbi--otaの最初の項目)が、ほかのすべての注意点を合わせたよりも多くを占めます。
HPI / UFO / CCX / GP3
- インストール先のルートに置いた単体のファイルは、どのアーカイブよりも優先されます。
units/MYUNIT.fbiをディスクに置くと、エンジンはtotala1.hpiの中にあるどのファイルよりもそちらを使います。優先順位の全体はhpi.mdのレイヤー構造の図を参照してください。 - アーカイブ同士では最初にマウントされたものが勝ち、
*.hpiは10個までしかマウントされません。TA 3.1cはrev31.gp3、次に*.ccx、*.ufo、そして開くことのできた最初の10個の*.hpiをマウントします。どのグループも、ASCIIの大文字に揃えた名前の順です。zz_mod.hpiとして配布したMODは製品版のどのアーカイブよりも順位が低く、それより前に並ぶ*.hpiがすでに10個あるインストールでは、ディスクのスキャンを通じてしかマウントされず、すべてのものより下になります。MODは.ufoとして配布してください。 - HPI v1のポインターはファイルの先頭からの絶対位置で、ディレクトリの先頭からではありません。何も引く必要はありません。定番のパーサーでは、
DirectorySizeバイトのバッファーを確保し、そのオフセットStartの位置にディレクトリを復号することで、ポインターがそのままバッファーを指すようにしています。ディレクトリツリーを参照してください。 - HPI v1の暗号のシードはファイル内の位置とともに進みます。バッファーだけを切り離して復号することはできず、そのバッファーがファイルのどの絶対オフセットから来たかを知っている必要があります。暗号鍵の導出を参照してください。
DecryptKey == 0なら、XOR暗号は一切適用しないでください。独自に作られたアーカイブには平文のものもあり、それでも暗号を適用すると、すでに読める内容がめちゃくちゃになります。- SQSHチャンクのチェックサムは暗号化されたバイトに対して計算されます。暗号を解く前に検証してください。圧縮チャンク(SQSH)を参照してください。
- TA: KingdomsはHPI v2(
Version == 0x00020000)を使い、レイアウトが異なります。XORはなく、SQSHチャンクはファイルごとに1つです。TAKのアーカイブにv1のパーサーを試す前に、TA: KingdomsのHPI v2の節を読んでください。 BANKマーカーのセーブデータはまったくの別物です。HPIの外枠を流用していますが、中身はファイルシステムではなく、シリアライズされたゲームの状態です。kbotはこれをデコードしません。.gp3にはrev3.1パッチが必要です。中身はやはりHPIですが、パッチを当てていないTAはこれをマウントしません。
TDF / FBI / OTA
- すべての値は
;で終わらなければなりません。セミコロンが抜けていると、何の警告もなく複数行の値がつながり、とんでもなく間違ったステータスになります。「ユニットのHPがおかしい/説明がない/コストが違う」の原因の第1位がこれです。 - キーは大文字と小文字を区別しませんが、値は区別します。
Side=armはたいていの参照では機能しますが、Sideを文字列の一致で比べるMODのコードでは動かなくなります。 - リストの値はスペース区切りです(
Category=COMMANDER MOBILE WEAPON)。カンマ区切りではありません。例外はOTAのnumplayers=2,4,6,8,10で、こちらはカンマを使います。 - 真偽値は整数の
1/0です。true/falseではありません。エンジンは整数の形しか受け付けません。 - MODでユニットを追加するときは、決して
sidedata.tdfを編集しないでください。代わりにdownload/<UNITNAME>.tdfの[MENUENTRY]で登録します。Core Contingencyと同じ仕組みです。TDF:[MENUENTRY]とMOD制作のチュートリアルを参照してください。 Bmcode=0はユニットを建設メニューから隠します。ほかがすべて正しくても同じです。ほとんどの場合、必要なのはBmcode=1です。- どの建設ユニットの下にも載っていないユニットは建設できません。FBIがほかの点で正しくても同じです。「新しいユニットは存在するのに建設できない」というバグで最もよくある原因です。MOD制作のステップ5を参照してください。
- 入れ子のセクションの深さは慣習であって、仕様ではありません。2段階を前提にしているツールもありますが、武器のTDFは3段階になります(
[WEAPON][DAMAGE][category])。
TNT / SCT — マップの地形
- TNTの
WidthとHeightは16ピクセルの属性セル単位です。タイル数でもピクセル数でもありません。タイル数にするには2で割り、ピクセルの寸法にするには16を掛けます。TNTのヘッダーを参照してください。 SeaLevel == 0は「水がない」という意味ではありません。取りうる最小の値がちょうど境界にあるため、水面下になるものが何もない、という意味です。本当に水のないマップにはSeaLevel = 1を使ってください。- TNTの地形物ワードが地形物を配置するのは、地形物テーブルの件数より小さい場合だけです。
0xFFFCはボイド(void)です。0xFFFE(Lava Run、AC02)、0xFFFD、そしてテーブルの件数以上のワードは何も配置せず、そのセルは建設可能なままです。 - TNTのミニマップの余白にはパレットインデックス
0x64が使われます。マップの領域は余白を走査して見つけるのではなく、マップから寸法を決めます(長辺が252で、見えている部分のマップの縦横比に合わせる)。ゲームが格納されたミニマップを読むのは、ヘッダーの0x2cにあるワードのビット0が立っている場合だけです。TNTを参照してください。 - TA: Kingdomsの
.tntはIDVersion == 0x4000を使います。TAは0x2000です(TAは古い0x1020のレイアウトも読みます)。TAKのTNTにkbot tnt imageを使うとでたらめな出力になります。タイルのデコーダーがTA専用だからです。TA:Kのマップを参照してください。 - SCTのセクションは、TAEで読み込むには縦横どちらも4の倍数でなければなりません。ディスク上の形式としてはほかのサイズも許されますが、エディターは「section misaligned」としてそれを拒否します。
- SCT V2の高さのセルは8バイトで、V3は4バイトです。kbotはどちらも受け付けますが、エディター向けに作られたMODにはV3しか出てきません。
3DO — モデル
- 上方向はZではなくYです。軸を入れ替えずに3DOをZが上のエンジン(Blender、最近のUnreal)に取り込むと、どのユニットも横倒しになります。
- テクスチャのないプリミティブは、
IsColored != 0でない限り見えません。疑似コードvisible = (texture != "") || (IsColored != 0)で両方の場合を拾えます。3DOの描画で最もよくあるバグです。 OffsetToSelectionPrimが意味を持つのはルートのオブジェクトだけです。子オブジェクトではこれを-1(0xFFFFFFFF)にしなければなりません。- 固定小数点のスケールは
1 / 65536です。0x00010000と書かれた頂点は1.0ワールド単位で、下位16ビットが小数部です。 - 頂点はオブジェクトのローカル座標です。グローバル座標ではありません。2つのオブジェクトが同じ頂点の配列を持つことがありますが、それは鏡像のジオメトリであって、データを共有しているわけではありません。
COB / BOS — スクリプト
call-scriptのオペコードは0x10062000です。3.1より前の一部のドキュメントにある0x10063000ではありません。0x10063000を使う古い逆コンパイラーは意味のない出力を生成します。オペコードのリファレンスを参照してください。- オペコード中のピース番号はピース名テーブルへのインデックスです。文字列ではありません。COBは名前を文字列プールとして持ち、ランタイムは速度のためにインデックスを使います。
SET_SIGNAL_MASKは関心を登録するだけで、何も止めません。マスクにXが立っているスクリプトを止めるのは、signal Xを送ることです。signal-never-signalledというlintルールは、誰もシグナルを送らないマスクを設定しているスクリプトを検出します。- CavedogのScriptorは
CURRENT_SPEEDをリテラルの29に展開してしまいます。そのためラウンドトリップの後、MoveWatcherのようなスクリプトが動かなくなります。kbotの逆コンパイラーは記号名を復元します。どうしてもScriptorでラウンドトリップする必要があるなら、(get CURRENT_SPEED) > 5のように括弧で囲んでください。 - ジャンプのオフセットは
OffsetToScriptCodeからのバイトオフセットです。現在の命令からの相対位置ではありません。JUMP 0x4Cは「OffsetToScriptCode + 0x4Cへジャンプ」という意味です。 GET_UNIT_VALUEのポート番号はTAとTA:Kで異なります。TAの一覧(kbotの逆コンパイラーがラベルを付けるのはポート1〜20)は付録Bにまとめてあります。
GAF — スプライトアニメーション
- シーケンスがループするのは、+2のワードの下位バイトが立っている場合だけです。0なら1回再生して止まります。標準のシーケンスはどれも1を格納しているので、0を書き込むツールはアニメーション付きの地形物を止めてしまいます。
textures/*.gafとanims/vismasks.gafのフレームは非圧縮でなければなりません。ゲームはこれらを単純なピクセル配列として読みます。- フレームの表示時間が
0のシーケンスもあります。ゲームはそのようなフレームを1ティックだけ表示します。既定値に「直さない」でください。 - パレットインデックス0は、それだけでは透過になりません。透過になるのは、非圧縮フレームのキーと、圧縮フレームのスキップのランだけです。
- 圧縮されたピクセルの行は
Widthより短いことがあります。ゲームはそのまま次のバイトを読み進めますが、kbotは行を透過のピクセルで埋めて警告を出します。標準のファイルにはそのような行はありません。 - レイヤーの原点はホットスポットです。合成フレームの各レイヤーは、その原点がフレームの原点に重なるように配置されます。
anims/terrain.gafとanims/vismasks.gafは、ヘッダーのVersion == 0です。ゲームはバージョンのワードを無視します。- TA: Kingdomsの
.tafには2つの種類があります。パレット付きの.tafはGAFそのものです。ディスク上の形式は同じで、違うのはTAKが共通のTAパレットではなく陣営ごとの.pcxからパレットを取ることだけです。トゥルーカラーの.tafは同じコンテナを流用しますが、16ビットのARGBピクセルを格納し、パレットを必要としません。正式な判断材料になるのは拡張子ではなく、フレームの形式バイトです(0/1ならパレット付き、4/5ならトゥルーカラー)。TAF / TSFを参照してください。
TAF / TSF — トゥルーカラーのアニメーション
.tafは2つの意味で使われています。パレット付き(GAFとして読む)とトゥルーカラー(kbot tafで読む)です。TAKのトゥルーカラーのファイルの多くは慣習として_4444/_1555という接尾辞を持っていますが、判断するときは必ずフレームの形式バイトを見て、ファイル名を当てにしないでください。- GIFへの書き出しでは情報が失われます。GIFが持てるのは1ビットの切り抜きと255色だけなので、ARGB4444のなめらかなアルファは硬い輪郭になります。本来のアルファチャンネルを残すにはAPNG(
kbot taf export --format apng)を使ってください。 - フレーム名のフィールドと
0x0Bのフラグバイトには、ゴミが入っていることがあります。バイト単位で一致するラウンドトリップのためには、そのまま保持してください。正規化しないでください。 0のDelay/Durationは正当な値です。GAFと同じく、0ティックのフレームはイベント駆動です(静止したメニューの背景がこれを使います)。「直さない」でください。
PCX — 画像
- TAは
BytesPerLineを無視します。一般的なリーダーはBytesPerLine > Widthのとき余白を捨てますが、TA 3.1cは1行につきちょうどWidthバイトをデコードするので、余白のあるファイルはゲーム内でずれます。BytesPerLine == Widthで書き出し、kbot pcx describeで確認してください。 - TAが読み込むのはバージョン5だけです。また、どのファイルも8ビットの単一プレーンのデータとしてデコードします。
- 上位2ビットが立っているリテラルのバイトは、RLEでエスケープしなければなりません。値が
0xC0–0xFFのバイトは、そのままではリテラルとして不正です。長さ1のランとして出力してください(0xFFが1つなら0xC1, 0xFF)。扱いを誤ると、1行進んだ時点でデコードがずれてしまいます。 - ゲームは、
0x0Cのマーカーの有無にかかわらず、末尾の768バイトをパレットとして扱います。一般的なリーダーはマーカーを必要とします。
PAL / ALP / LHT / SHD — パレットとルックアップテーブル
- 各エントリの4バイト目はアルファではありません。Cavedogが常に
0x00にしていた、使われない埋め草のバイトです。これをアルファとして扱うオープンソースのパレットツールは、パレットを描画できません(alpha=0 → すべて透過)。 - インデックス0は黒で、透過ではありません。地形、ミニマップ、背景はこれを不透明で描きます。スプライトは独自の透過キーを選びます。
palette.palが空か存在しない場合は、palette.pcx(その末尾の768バイト)で置き換えられます。1,024バイトより長い.palは1,024バイト目まで読まれます。.alp/.lht/.shdはRGBのパレットではありません。.alpは256×256、.shd/.lhtは32×256の、パレットインデックスのテーブルです。ゲームはこれ以外のサイズのテーブルを無視し(そして作り直し)ます。kbot pal describeで描画するとでたらめな表示になるので、代わりにkbot pal lookupを使ってください。- TAのパレットには、重複したRGBの3つ組が13個あります。エディターが読み込み時に重複を除くと(そうするものもあります)、エントリが失われ、パレットは知らないうちに243エントリになります。
FNT — ビットマップフォント
Offsets[c] == 0は「グリフなし」という意味で、「オフセット0にあるグリフ」ではありません。オフセット0はヘッダーの途中に当たるので、Cavedogは0を番兵値として使い回しています。- ビットの順序は、各バイトの中でMSBファースト(上位ビットから)です。最近のビットマップフォントではLSBファーストのほうが一般的なので、思い込まないでください。
- ビットストリームは行の間で途切れません。各走査線の終わりに埋め草はありません。
AIプロファイル
- TA: KingdomsのAIファイルには
planディレクティブがありません。weight/limitの行が並ぶだけです。kbotのパーサーは暗黙のdefaultプランを開くので、リーダーはブロックがないことを許容する必要があります。TA:K — planのないプロファイルを参照してください。 - どこにも検証はありません。ユニット名の綴りの間違い、壊れた重みの値、
planの外にある行は、どれも何も知らされずに無視されます。プロファイルが壊れていることに最初に気づくのは「AIの動きがおかしい」と感じたときで、ログにメッセージは出ません。 - カテゴリの別名(
PLANT、LEVEL3)が「存在する」のは、少なくとも1つのユニットがCategory=でそれを宣言している場合だけです。LEVL3と綴りを間違えると、ディレクティブは何も知らせずに、何の効果も持ちません。
サウンド
- WAVが見つからなくても、エラーにはならず無音になるだけです。
select1=cmsdel1(文字が入れ替わっている)と綴りを間違えると、エンジンは何も再生しません。ログもなく、代わりの音も鳴りません。 - TDFの値では
.wavの拡張子は暗黙に付きます。決して書かないでください。select1=cmdsel1.wavはsounds/cmdsel1.wav.wavになり、何も見つかりません。
TA: Kingdoms固有の注意点
- TAKで改造したファイルの大半を読み込むには、コマンドライン引数
-disablecavedogverificationが必要です。これがないと、エンジンはハッシュが許可リストにないものをすべて拒否します。 - TAKのデータには陣営コードが4つではなく8つあります。プレイできる4つの陣営のARA / TAR / VER / ZONに加えて、CRE(Creon、Iron Plague)、MON(モンスター)、LIF(野生生物)、NPC(キャンペーンの登場人物)があります。ユニット一覧の絞り込みでは、これらすべてを扱う必要があります。
- TAKの武器は、各FBIの中に
[WEAPON1]〜[WEAPON3]のサブセクションとして直接書かれています。独立したweapons/*.tdfのディレクトリにはありません。 - TAKの建設メニューは2×3ではなく、一列に並びます。各建設ユニットには
canbuild/<builder>/<unit>.tdfというディレクトリがあり、それぞれに[Menu] { Priority = N; }のブロックが1つだけあります。
関連項目
- MOD制作のチュートリアル:TAのユニットMODを最初から最後まで作る手順。
- 用語集:上で触れた、複数の形式にまたがる用語の定義。
- クイックスタート:最初に試したい5つのkbotコマンド。