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.
Why use FGDs?
The reasoning behind FGDs becomes obvious the moment you turn off SmartEdit:
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:
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.
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.
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:
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:
- FGDs can only define entities and their properties, not create new ones.
- 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). - A keyvalue can display as either an
integer
, astring
,choices
, orflags
, 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.