Skip to main content

FGD files

An FGD (Forge Game Data) file is a plain text file that tells Hammer which entities exist, what properties they take, and what inputs and outputs they have. It is the reason the entity list in Hammer knows which settings entities like light_omni2 have.

info

FGDs are editor metadata only. The game never reads them. An entity works because it exists in code, described by the schema; the FGD only describes it to the tools. Adding a class to an FGD does not create an entity, and a wrong FGD produces a map that compiles but behaves differently than the editor implied.

GitHub iconsledge-formats can be used to parse modern FGD files.

Where they live

They are loose text files in the mounted game directories, for example in Counter-Strike 2:

game/core/base.fgd entities common to all Source 2 games
game/core/lights.fgd lighting entities
game/core/models_base.fgd prop entities
game/core/markup_volumes.fgd nav and markup volumes
game/csgo/csgo.fgd CS2's own entities
game/csgo_core/models_gamedata.fgd extra prop data

Hammer picks them up from the search paths, so the available files depend on which directories the game mounts. See Content mounting.

Syntax

Every example below is the same entity, logic_relay, as Counter-Strike 2 ships it in base.fgd. It is short enough to quote almost whole:

@PointClass base(Targetname, EnableDisable) iconsprite("editor/logic_relay.vmt") = logic_relay :
"A message forwarder. Fires an OnTrigger output when triggered, and " +
"can be disabled to prevent forwarding outputs."
[
spawnflags(flags) =
[
1: "Only trigger once" : 0
2: "Allow fast retrigger" : 0
]

input Trigger(void) : "Trigger the relay, causing its OnTrigger output to fire if it is enabled."
input Toggle(void) : "Toggle the relay between enabled and disabled."

output OnSpawn(void) : "Fired when the relay is spawned."
output OnTrigger(void) : "Fired when the relay is triggered."
]

Reading the first line left to right:

PieceMeaning
@PointClassan entity placed as a point. @SolidClass is tied to geometry, @BaseClass is inherited from and never placed
base(Targetname, EnableDisable)inherit the properties, inputs and outputs of these base classes
iconsprite(...)a helper: what the editor draws for it. Others include studioprop(), size() and line()
= logic_relaythe classname, which is what actually ends up in the map
: "..."the description shown in Hammer, split over several strings with +
[ ... ]the property, input and output list

Inputs and outputs are declared in that same list and take a type, void where there is no parameter. A flags property carries its bits inline, as spawnflags does above; a choices property has the same shape with a value and label per line.

What it inherits

logic_relay declares no properties of its own. Everything a mapper sets on it comes from the two base classes it names, and those are ordinary definitions in the same file:

@BaseClass = EnableDisable
[
StartDisabled(boolean) : "Start Disabled" : 0

input Enable(void) : "Enable this entity."
input Disable(void) : "Disable this entity."
]

A property line is keyname(type) : "Label" : default : "Help text". StartDisabled shows the short form with no help text. Targetname, the other base class, shows the rest of the shape:

targetname(target_source) { sort_priority = 90 } : "Name" : : "The name that other entities refer to this entity by."

The braces are metadata for the editor, here sorting the key to the top of the property panel, and the empty default between the two colons is exactly that, no default.

Targetname is also where Kill, AddOutput and RunScriptCode come from, which is why nearly every entity accepts them without declaring them.

Editor configuration

They also carry editor configuration, most visibly auto visgroup rules:

@VisGroupFilter { filter_type = "toolsMaterial" material = "toolsclip.vmat" group = "Tool Brushes/Clip" }
@VisGroupFilter { filter_type = "entityTag" tag = "Lighting" group = "Entities/Lighting" }

These are what populate Hammer's automatic visibility groups, either by the tool material a mesh uses or by a tag on the entity class.

Technical details

Parsing one

An FGD is plain text, so a small parser is easy enough, but the syntax is surprisingly complex: base class inheritance, remove_key, inline choices and flags blocks, helpers with arbitrary arguments, and Source 2's own additions.

GitHub iconsledge-formats already handles it. Its Sledge.Formats.GameData package is an MIT licensed C# FGD reader and writer, published on NuGet, and the wider library covers the other Valve editor formats alongside it.

Property types

The type in keyname(type) decides the editor widget and, for name-like types, whether the value gets rewritten during prefab flattening:

TypeMeaning
float, integer, boolean, stringplain values
target_destinationthe name of another entity, rewritten by prefab fixup
choicesfixed list, defined inline
flagsbitfield of named flags
sounda soundevent name
remove_keyremoves an inherited key from a base class
vectorthree floats
color255RGB in 0 to 255
filterclassthe name of a filter entity
studioa model path
angle, vecline, local_pointvalues with a viewport handle
target_name_or_classmatches by entity name or by classname
model_attachment, model_breakpiecepicked from the referenced model
instance_variablea value supplied by the placement
target_sourcea name this entity provides
sprite, sequence, material, materialgroup, particlesystemasset references, browsable in the editor

The name-like family is the one to watch, because it is exactly the set that prefab name fixup rewrites.

Entity I/O target types

Connections are not always name to name. The target of a connection carries a type:

enum EntityIOTargetType {
ClassName = 0, ClassNameDerivesFrom = 1, EntityName = 2,
ContainsComponent = 3, SpecialActivator = 4, SpecialCaller = 5,
EHandle = 6, EntityNameOrClassName = 7,
};

EntityNameOrClassName is why firing at weapon_ak47 hits every AK in the map: map I/O resolves a target against names and classnames. See EntityIOTargetType.cs and the matching EntityIOTargetType_t in the Schema Explorer.