Manifest and command reference¶
Use this page after How a ContentTool mod is made. It records the accepted file shapes and author-facing console commands. Route recipes explain when to use them.
meta.json¶
Phoenix Point discovers a mod only when a top-level folder contains meta.json. A content-only mod
uses this complete shape:
{
"ID": "yourname.mymod",
"AssemblyName": "",
"Version": "1.0.0",
"Author": [
{ "Key": "English", "Value": "Your Name" }
],
"Name": [
{ "Key": "English", "Value": "My Mod" }
],
"Description": [
{ "Key": "English", "Value": "A Phoenix Point content mod." }
],
"Dependencies": [
"com.morgott.ContentTool"
]
}
ID is required. Dependencies must contain com.morgott.ContentTool; that is what makes the mod
manager enable the engine before your mod. AssemblyName is optional. Leave it empty or omit it for
a content-only mod. If it names a DLL, that DLL must exist when you package.
Keep ID equal to ppcontent.json's id. Phoenix Point and ContentTool use them for different
identity systems, and the current packager does not catch a mismatch.
ppcontent.json: top level¶
This complete file shows every top-level section together. The values are illustrative; use the route pages before copying it into a real project.
{
"id": "yourname.mymod",
"bundle": "MyMod.bundle",
"scale": 0.008,
"play": "Spider_Idle",
"loop": "Spider_Idle, Spider_Walk",
"replace": [
{
"bundle": "aln_acidworm_assets_all.bundle",
"asset": "acidworm_low_albedo",
"texture": "acid_skin"
}
],
"publish": [
{
"key": "yourname.mymod/models/field_scanner",
"asset": "models/field_scanner",
"type": "GameObject",
"deps": "defaultlocalgroup_unitybuiltinshaders.bundle"
}
],
"sounds": [
{
"media": 18839791,
"file": "my_click.mp3"
}
],
"creature": {
"clips": {
"Spider_Walk": "walk",
"Spider_Idle": "idle",
"Spider_Idle_long": "",
"Spider_Damage": "reaction",
"Spider_Attack_1": "attack",
"Spider_Attack_2": "ranged",
"Spider_Death": "death"
},
"events": {
"walk": "SwarmerStep_EventDef 0.15, SwarmerStep_EventDef 0.65",
"attack": "ActionDo 0.4054, ShootShot 0.4865, ActionEnd 0.8378",
"ranged": "ActionDo 0.2286, ShootShot 0.5429, ActionEnd 0.9143",
"death": "Ragdoll 0.9"
},
"name": "Spider",
"model": "cyborg_spider",
"donor": "Swarmer_TacCharacterDef",
"ranged": "Crabman_Head_Spitter_WeaponDef",
"up": "0,1,0",
"lift": 2.1372,
"health": 40,
"will": 10,
"speed": 16,
"volume": 1,
"climbPitch": 90
},
"weapons": [
{
"id": "YourName_FieldRifle_WeaponDef",
"clone": "PX_AssaultRifle_WeaponDef",
"guid": "replace-this-with-your-own-dashed-uuid",
"name": "Field Rifle",
"model": "yourname.mymod/models/field_scanner",
"fit": "auto",
"offset": "0,-0.07,0",
"projectile": "Crabman_Head_Spitter_WeaponDef",
"tint": "#4CFF5A",
"trail": "0.6",
"damage": 40,
"spread": 2.5,
"count": 1,
"clips": 3
}
]
}
| Key | Type | Default and use |
|---|---|---|
id |
string | Required. Names the mod's bundle namespace, sound identities, video keys and private patched-copy cache. |
bundle |
string | Required declaration of the bundle filename this mod may own under Dist; it need not match the project folder name, and routes with nothing to bake need not produce the file. |
scale |
number | 1 when absent or zero; a negative value is refused. Uniform scale for an added rig/model and its root-motion conversion. |
play |
string | First bakeable clip when absent. The clip placed on the imported model's Animator. |
loop |
comma-separated string | No clips loop when absent. Names imported clips that must cycle. Creature walk and idle must loop. |
replace |
array | Shipped texture, material, mesh, clip-curve or video changes. |
publish |
array | New Addressables keys. Existing game-catalog keys are refused. |
sounds |
array | Replacements for shipped Wwise media. |
creature |
object | Opts into creature scaffolding and build data. |
weapons |
array | Weapon defs to clone and add. |
A mod may contain at most one .bundle, but a manifest-only payload is valid. There is no
one-route-per-mod rule. Every top-level section is parsed independently. Array rows must be flat
objects: do not put a nested object inside a replace, publish, sounds or weapons row. A
declared array with no complete row is refused. Literal \uXXXX escapes are not decoded, so write
ordinary characters.
replace[]¶
Each row must contain exactly one route key: texture, material, mesh, clip or video.
bundle and asset are required only when the row is not a video. Every video row is exempt from
that pair: omit bundle; include asset to replace that catalog row, or omit asset to add a row.
| Route key | Value | Source or effect |
|---|---|---|
texture |
source stem | Content\Textures\<stem>.png, .jpg or .jpeg replaces a Texture2D. |
material |
_Property=value |
Sets one float serialized on a shipped Material. |
mesh |
source stem | Content\Meshes\<stem>.obj or .glb replaces a Mesh; a rigged GLB can also replace skin data. |
clip |
position*k or scale*k |
Multiplies one channel of a shipped AnimationClip. rotation*k is refused. |
video |
source stem | Content\Videos\<stem>.webm, .mp4 or .mov; asset present means replace, absent means add. |
asset is the exact, case-sensitive serialized name found by ct_list assets, except for video.
A video value may be either the entire streaming path or only its filename; matching uses exact,
case-insensitive equality after normalizing slashes. ContentTool refuses zero matches or any value
that matches more than one row. Prefer the full path printed by ct_video defs, such as
StreamableCopiedAssets/Videos/Factions/Phoenix/PP_Intro.webm.
publish[]¶
| Key | Required | Meaning |
|---|---|---|
key |
yes | New Addressables address the game or your DLL will request. Namespace it with the mod ID, for example yourname.mymod/models/field_scanner. |
asset |
yes | Lowercase path inside your bundle, such as models/sniper. |
type |
yes | Resource type such as GameObject. |
deps |
no | Semicolon-separated shipped bundles containing external references the asset needs. |
The key must be non-empty and new. A key already present in the game's catalog is refused by name, and two enabled mods cannot publish the same key. ContentTool cannot override a shipped key through this route.
sounds[]¶
Each row requires media, the unsigned decimal ID of replaceable shipped media, and file, a file
under Content\Audio\Replace. Quoted and unquoted media IDs are accepted. The filename shortcut
Content\Audio\Replace\18839791.mp3 produces the same replacement without a sounds row. Two
sources aimed at one media ID are refused.
creature¶
| Key | Default | Meaning |
|---|---|---|
clips |
none | Imported clip name to role. Required roles: walk, idle, attack, death. Optional: jump, reaction, ranged, climb. |
events |
none | Role to ordered EventName fraction entries. Fractions are clamped to 0–1. |
name |
donor name | Roster display name. |
model |
the only GLB | Stem under Content\Models; set it when more than one model exists. |
donor |
Swarmer_TacCharacterDef |
Shipped creature definition whose component and combat structure is cloned. |
up |
0,1,0 |
Model's imported up axis. |
lift |
0 |
File-unit distance from origin down to the lowest vertex. |
health |
donor value | Starting strength; zero keeps the donor value. |
will, speed, volume |
0 |
Base will, Action Points and volume. |
pace |
shipped pace | Tiles per second. Explicit 0 preserves the source clip's timing. |
climbPitch |
0 |
Degrees nose-up while synthesized climbing clips play. The spider example uses 90; 0 is honest for a biped. |
ranged |
none | Shipped WeaponDef cloned as a second attack. |
aiAction |
MoveAndShoot_AIActionDef |
AI action added for the ranged attack. |
shootBone |
measured | Bone used for the synthesized shoot point. |
accuracy |
donor value | Accuracy for the ranged body part; zero keeps the donor value. |
colliders |
on | The value off disables synthesized hit/hover colliders. |
aim |
measured/root | Bone used for the aim marker. |
hitRadius |
measured | Radius for per-bone sphere colliders. |
hitBones |
empty | Comma-separated bones that receive sphere colliders instead of one box. |
The first creature bake writes every discovered clip into clips with empty roles. Map the four
required roles by hand and rerun it. See A new creature and the
animation contract.
ranged is intentionally used in two slots: a value inside creature.clips is the ranged clip
role, while creature-level ranged names a shipped WeaponDef to clone for the attack.
weapons[]¶
| Key | Required | Default and meaning |
|---|---|---|
id |
yes | New weapon def name. |
clone |
yes | Shipped WeaponDef to clone. Choose the same weapon class and hold pose. |
guid |
yes | Author-chosen stable def identity; no command generates it. See the weapon GUID rules. |
name, blurb |
no | Display text. |
icon |
no | Relative PNG path such as Icons\rifle.png. |
model |
no | Published Addressables key. Absent keeps the donor's art. |
fit |
no | With a model and no explicit shoot, must be auto. |
shoot, aim, shell |
no | Socket coordinates as x,y,z. Zero is a legal coordinate. |
flip |
no | true reverses which fitted end is treated as the muzzle. |
scale |
no | Positive float; explicit uniform mesh scale, overrides the fit solver. Zero or negative does not override. |
rotate |
no | "x,y,z" euler degrees; explicit mesh rotation, overrides auto rotation + flip. |
offset |
no | "x,y,z" metres; local-position nudge added after the auto-fit solve. It preserves the solved rotation/scale/centre and moves derived sockets with the mesh. Without auto-fit, adds to the baked mesh child's position. |
projectile |
no | Name of a WeaponDef (takes its DamagePayload.ProjectileVisuals) or a ProjectileDef name directly. |
flash |
no | Name of a WeaponDef; takes its VisualEffects (EquipmentVisualEffectsDef: Flash/Smoke/Shell). |
tint |
no | #RRGGBB; clones the ProjectileDef + private prefab copy, recolours TrailRenderer + ParticleSystem colours. No #RGB, no alpha. |
trail |
no | Positive float seconds; TrailRenderer.time on the private prefab copy = beam length. Implies a private clone like tint; zero or negative does not override. |
damage, spread |
no | Zero keeps the cloned values. |
count, clips |
no | Starting-storage weapon and magazine quantities; zero adds none. |
damagetype |
no | DamageTypeBaseEffectDef name. |
keywords |
no | Semicolon-separated DamageKeywordDef=value entries. |
Weapon VFX parsing and application are in WeaponBuild.cs:373-502; fit overrides and offset
composition are in WeaponBuild.cs:767-840 and :906-920.
Source folders and identifiers¶
Content\Textures\ .png .jpg .jpeg
Content\Meshes\ .obj .glb
Content\Models\ .glb
Content\Videos\ .webm .mp4 .mov
Content\Audio\ .wav .ogg .mp3
Content\Audio\Replace\ .wav .ogg .mp3
Icons\ .png
The lowercased file stem is the identifier. Two same-stem files in one folder are refused.
name.stream.wav creates a streamed added sound named name; other added sounds are embedded.
There is no top-level models or videos array: added files are discovered from these folders.
Icons\ is top-level, beside Content\, and is included by the packager. An icon value is a path
relative to the mod folder and must name a PNG that Unity can decode. In JSON,
"Icons\\rifle.png" represents the real path Icons\rifle.png; the doubled slash is JSON escaping,
not a second directory separator.
Author-facing console commands¶
Discover and inspect¶
ct_version
ct_dump <ClassName> [depth=3]
ct_list bundles [nameFilter]
ct_list assets <bundleFile> [typeFilter] [nameFilter]
ct_list videos [nameFilter]
ct_list audio [nameFilter]
ct_list defs <nameFilter> [typeFilter]
ct_list bones <bundleFile> <meshName> [nameFilter]
ct_list props <bundleFile> <materialName>
ct_list clip <bundleFile> <clipName>
ct_extract tex <bundleFile> <assetName>
ct_extract mesh <bundleFile> <assetName>
ct_extract video <name>
ct_extract audio <wemName>
ct_voices watch [seconds]
ct_music probe [waitSeconds]
ct_sound status <mediaId>
ct_sound probe <mediaId>
ct_sound probe event <eventId>
ct_video defs
ct_creature list
The three exact-name inspectors—bones, props, and clip—refuse duplicate names rather than
choosing a path ID. Phoenix Point really ships duplicates, so narrow the target or choose a unique
asset instead of assuming the first match.
ct_list REFUSED - 2 Meshs are named 'ALN_Fireworm' (pathIds ...) - refusing to guess which one to use
Preview, bake and apply¶
ct_project <project>
ct_sound bake <project>
ct_route7 apply <project>
ct_route7 status
ct_catalog apply <project>
ct_catalog verify
ct_catalog status
ct_video live <project>
ct_video status
ct_video resolve <runtimeKey>
ct_video open <runtimeKey>
ct_video play <defName>
ct_video quit
ct_dev on <project>
ct_dev off
ct_dev status
ct_dev sets
ct_dev set <name>
ct_dev next
ct_dev reload
ct_bench [open|close|reset]
ct_fit [show]
ct_fit <weapon> [<dx,dy,dz>|save|reload]
ct_fit <weapon> move|turn|pos|rot <x,y,z>
ct_fit <weapon> scale <value>
ct_replace <targetPath> <file-or-value>
ct_revert
ct_scan on|off|status
ct_project writes Dist\<bundle> when the project has bundle content and may rewrite a creature
manifest with its discovered clips. Video-only projects write no bundle. ct_sound bake writes
Dist\Sounds\<mediaId>.bnk; neither invokes the other. ct_route7,
ct_catalog, and ct_video are author previews, not player or release
steps. A player only enables the mod.
ct_bench opens the in-game weapon fit workbench, closes
it, or resets its view. With no argument it toggles. A fully loaded geoscape campaign is required.
ct_fit controls the same live fit service from the console. A bare x,y,z or move adds a
position delta in metres; turn adds euler degrees; pos, rot and scale set absolute values.
save writes the three fit fields into the originating manifest, while reload discards live
changes and re-reads it. Every adjusting form updates matching live instances and prints the final
scale, rotate and offset values.
Package¶
This stages a publishable folder under
%USERPROFILE%\AppData\LocalLow\Snapshot Games Inc\Phoenix Point\ContentTool\Packaged\. It neither
bakes nor compiles. A refusal deletes the incomplete staged folder. On success, zip the folder
itself so the archive contains <project>\meta.json.
Engine diagnostics¶
Commands such as ct_bake, ct_audio, ct_texswap, ct_meshswap, ct_liveswap, ct_fmt,
ct_seamprobe, ct_mission, ct_creature gate, ct_extract gate, ct_sound selftest,
ct_sound shapec, ct_scan gate, ct_music gate and ct_outtest are ContentTool's regression and
instrumentation gates. They are not authoring steps. A guide may mention one only when diagnosing
the advanced live-preview target path.