Valve Developer Union

Anatomy of an FGD (and How to Write Your Own)

(January 7, 2018)


Since the beginning, Worldcraft has featured a "SmartEdit" mode that makes setting the properties of entities and items easier. The functions available in the SmartEdit mode are made available to Worldcraft through what it calls "Forge Game Data" files, or FGDs. (Remember that Worldcraft's original name during development was The Forge.) All Worldcraft-like editors, plus editors like TrenchBroom, support these for defining entity properties.

An FGD is simply a plain text file that defines the keyvalues and spawnflags of a particular entity. It looks complicated, but it's actually quite simple when you get down to it.

Reminder

FGDs are descriptive, not prescriptive

Just because an entity appears in an FGD does not mean it'll magically work in-game. They can define properties of an already-existing entity, but they can't define new ones. Outside of the editor, FGDs are meaningless.

Why use FGDs?

The reasoning behind FGDs becomes obvious the moment you turn off SmartEdit:

light keyvalues with SmartEdit off
light keyvalues with SmartEdit off

Older editors simply display a list of keyvalues. Keyvalue names tend to be terse and more than a bit ambiguous; for this light entity, the style keyvalue sets blinking patterns through certain number values, but which numbers mean which almost certainly requires checking a reference guide. For spawnflags, the process involves even more arbitrary numbers.

Compare with SmartEdit on:

light keyvalues with SmartEdit on
light keyvalues with SmartEdit on

FGDs serve a valuable function in shielding the end user from having to put in strange, arbitrary values, but why learn to write your own? After all, custom FGDs can commonly be found on the internet. If someone else has already done that work, why bother learning it yourself?

Consider two scenarios: that of a mapper on a modding team and that of someone using custom map compilers. A custom FGD defining a mod's new entities should be written to make it as easy as possible for end users (and teammates) to make maps for that mod. Similarly, if a custom compiler features new functions for lighting, visibility optimization, or other improvements, a custom FGD makes taking advantage of those new features as simple as using the built-in features.

Basic formatting

FGDs define entities in one of three classes: @PointClass for point entities, @SolidClass for brush entities, and @BaseClass for a generic class that the other two can inherit values from. @BaseClass entities don't appear on their own, but they work the same. (Source Hammer supports a few other classes as well, including @NPCClass, @KeyFrameClass, @MoveClass, and @FilterClass. For brevity and clarity, as well as not confusing anyone writing an FGD for Quake or GoldSrc, we'll disregard these.)

Let's take a look at the definition for worldspawn, the basic entity that all non-entity brushes belong to, as seen in halflife.fgd:

@SolidClass = worldspawn : "World entity"
[
	message(string) : "Map Description / Title"
	skyname(string) : "environment map (cl_skyname)"
	sounds(integer) : "CD track to play" : 1
	light(integer) : "Default light level"
	WaveHeight(string) : "Default Wave Height"
	MaxRange(string) : "Max viewable distance" : "4096"
	chaptertitle(string) : "Chapter Title Message"
	startdark(choices) : "Level Fade In" : 0 =
	[	
		0 : "No"
		1 : "Yes"
	]
	gametitle(choices) : "Display game title" : 0 = 
	[	
		0 : "No"
		1 : "Yes"
	]
	newunit(choices) : "New Level Unit" : 0 = 
	[
		0 : "No, keep current"
		1 : "Yes, clear previous levels"
	]
	mapteams(string) : "Map Team List"
	defaultteam(choices) : "Default Team" : 0 = 
	[
		0 : "Fewest Players"
		1 : "First Team"
	]
]

Entity declarations

The first (and main) part of this is the entity declaration, which defines its class, name, and a short description:

@SolidClass = worldspawn : "World entity" []

This defines worldspawn as a brush entity, with a description of "World entity". You can also specify one or more base classes with a base() declaration. Any properties (keyvalues and spawnflags) in the base class will also appear in the classes that cite it as a base.

@SolidClass base(Targetname) = func_monsterclip : "Monster clip brush" []

This defines func_monsterclip as a brush entity that inherits the properties of the Targetname class, which looks like this:

@BaseClass = Targetname
[ 
	targetname(target_source) : "Name"
]

You define the entity's properties between the brackets on the end of the entity declaration. If your entity has no properties, you can leave the brackets empty. (Worldcraft's FGD parser ignores whitespace, so feel free to put as many blank lines as you want between them and their properties for ease of reading.)

Property declarations

Property declarations come next. They have a very similar format to the entity declaration, with the keyvalue, a property type, and a description written in that order:

message(string) : "Map Description / Title"

In this case, message is the keyvalue, (string) is the way the property will display in Worldcraft, and "Map Description / Title" is the description that'll display in Worldcraft. The colon (:) character is used as a separator in FGD files.

worldspawn's message keyvalue, displaying as a string field
worldspawn's message keyvalue, displaying as a string field

As you can see, the "Map Description / Title" field in Worldcraft can have text entered into it. This corresponds to the message keyvalue in worldspawn. There's four main types of property types: string, integer, choices, and flags. integer is functionally identical to string, but is reserved for numbers rather than letters. (Some editors, notably J.A.C.K., support float for values with decimals as well as integer, but Worldcraft doesn't support this. If you're looking for backwards compatibility, use integer.)

Keyvalues with the choices property type require a separate set of brackets. This is perhaps the most useful of the property types, as the end user can select from clearly-labeled options rather than arbitrary numbers.

@BaseClass = RenderFxChoices
[
	renderfx(choices) :"Render FX" : 0 =
	[
		0: "Normal"
		1: "Slow Pulse"
		2: "Fast Pulse"
		3: "Slow Wide Pulse"
		4: "Fast Wide Pulse"
		9: "Slow Strobe"
		10: "Fast Strobe"
		11: "Faster Strobe"
		12: "Slow Flicker"
		13: "Fast Flicker"
		5: "Slow Fade Away"
		6: "Fast Fade Away"
		7: "Slow Become Solid"
		8: "Fast Become Solid"
		14: "Constant Glow"
		15: "Distort"
		16: "Hologram (Distort + fade)"
	]
]

This defines a base class named RenderFxChoices with a single property: a list of choices for the renderfx keyvalue. It's important to remember the equals (=) sign when defining a list of choices.

The choices for renderfx values
The choices for renderfx values

Finally, the flags property lets you define spawnflags in the same way as you define choices. These will appear in the "Flags" tab in Worldcraft. Be sure to use this only with the spawnflags keyvalue, however; otherwise, it won't have the desired results.

Here are the spawnflags for the Monster base class:

spawnflags(Flags) = 
	[
		1 : "WaitTillSeen" : 0
		2 : "Gag" : 0
		4 : "MonsterClip" : 0
		16: "Prisoner" : 0
		128: "WaitForScript" : 0
		256: "Pre-Disaster" : 0
		512: "Fade Corpse" : 0
	]

And the resulting output:

monster_barney's spawnflags
monster_barney's spawnflags

Remember what we said earlier about the colon (:) being used as a separator? You can define a default value for any keyvalue by appending a colon and then the value, in quotes if necessary.

wait(integer) : "delay before close, -1 stay open " : 4

This keyvalue, from the Door base class, will automatically have a value of 4 when the end user ties this entity to a brush. Oftentimes, you'll see 0 being used as a placeholder value in spawnflags, so they don't start activated.

Summarizing FGD files

To summarize, let's go over a few basic functions of and found in FGD files:

  1. FGDs can only define entities and their properties, not create new ones.
  2. You define an entity as either a @BaseClass (whose values can be included in another entity), a @PointClass (for point entities), or a @SolidClass (for brush entities).
  3. A keyvalue can display as either an integer, a string, choices, or flags, which affect how Worldcraft displays the property.

The best way to learn the structure of an FGD is to peek through the ones included with the game, or the ones you can find on the internet. They look a lot more complicated than they really are.