|
|
|
|
|
|
|
|
| Links |
|
|
|
How to Edit an FGD Entity List - By Raeven0 |
|
This tutorial was last updated on Feb 26, 2006.
Introduction
<plug>Download Raeven0's custom HL2 FGDs now!</plug>
What is an FGD?
An FGD is essentially a list of every entity Hammer is allowed to create. It contains the entities themselves, all the properties of the entities, the help text associated with the entities, and even the appearance of the entities in the editor.
Why is it called an FGD?
Every source says FGD stands for Forge Game Data. The file contains entities (hence "game data"), and Hammer's original name was The Forge (hence "Forge").
If I want to add a new entity to HL2DM, can I just put it in the FGD and use it?
No, no, no, a thousand times NO! An FGD is only a list of entities supported by the game. If you want to add a new entity, you'll need to recode the game completely!
Can you teach me how to modify Hammer's FGDs so I can make changes to them myself?
Yes.
Will you teach me how to modify Hammer's FGDs so I can make changes to them myself?
Yes.
Please teach me how to modify Hammer's FGDs so I can make changes to them myself.
Oh, you meant right now? Well, let's get started!
Before you go any further, you should be highly proficient with entities. If you can't distinguish a brush entity from a point entity, you won't understand FGDs at all ;)
Editing an FGD
A basic entity contains many parts. Red items are required; green items are optional; black items are optional within optional (they may or may not be included even if the text around them is).
class helpers = classname : "The text to appear in Hammer's help for this entity."
[
property(datatype) readonly : "The text to appear in SmartEdit" : "default value" : "The text to appear in Hammer's help for this property."
spawnflags(flags) =
[
flagnum : "Text to appear in the Flags tab for this flag." : defaultvalue
]
input Inputname(datatype) : "The text to appear in the entity's help for this input."
output Outputname(datatype) : "The text to appear in the entity's help for this output."
]
I shall now explain what that means.
Class
An entity's class is the first declaration in its entry in the FGD. Class determines whether the entity is a point entity, a brush entity, a keyframe, a mover, an NPC, or simply a base. The classes are:
@BaseClass
@PointClass
@SolidClass
@KeyframeClass
@MoveClass
@NPCClass
A base is made the same way a normal entity is made, except that a base isn't really an entity; it's merely a list of properties that other entities can apply to themselves. (It's much more efficient to have an entity apply the Targetname base to itself, for example, than to put a targetname property in every last entity. With bases, too, you can simply edit the base, and every entity that calls on it will inherit the changes.)
A base is made and named exactly like an entity, except that instead of a classname, it has a name by which you refer to it.
The bases are:
- NameBase - only in my FGDs and those based off of them, NameBase contains the targetname(target_source) property.
- Targetname - references NameBase, above, and contains the inputs Kill, AddOutput, etc.
- Parentname - contains a Parent property and parent-related inputs
- Angles - contains angles(angle) property, for pitch/yaw/roll
- Origin - contains origin(origin) property
- Shadow - used of entities that can cast shadows, contains inputs and properties for shadow-casting
- Studiomodel - contains model- and skin-related properties
- BasePlat - used of platforms, contains the inputs GoUp(void), GoDown(void), and Toggle(void)
- BaseBrush - used of brush entities, contains inputs for HL1 ports
- EnableDisable - used of on/off entities, contains inputs and properties for enabling or disabling
- RenderFxChoices - used of lights and entities that can fade in and out; contains fade-in and fade-out presets
- RenderFields - contains Render Mode, FX Amount, and FX Color
- DxLevelChoice - contains minimum and maximum DX level properties
- Inputfilter - contains a property to control inputs an entity will accept
- Global - contains globalname(string) for level transitions
- DamageFilter - contains the damage filter property and input
- ResponseContext - contains inputs and properties for response contexts
- Breakable - contains properties, inputs, and outputs for breakable objects
- BreakableBrush - contains properties, inputs, and outputs for breakable brushes
- BreakableProp - contains properties, inputs, and outputs for breakable props
- BaseNPC - contains properties, inputs, and outputs for NPCs
- BaseNPCMaker - contains properties, inputs, and outputs for NPC-spawning entities
- BaseHelicopter - contains properties and inputs for flying helicopter-like objects
- PlayerClass - contains properties and inputs for spawn points
- Light - contains properties and inputs for lights
- Node - contains properties for node entities
- HintNode - contains properties and inputs for hint-node entities
- TriggerOnce - contains properties, inputs, and outputs for the trigger_once entity
- Trigger - contains properties, inputs, and outputs for every other trigger entity
- KeyFrame - contains properties for keyframe entities
- Mover - contains PositionInterpolator(choices) property for mover entities
- Button - contains inputs, outputs, and properties for buttons
- Door - contains inputs, outputs, and properties for doors
- BaseFilter - contains filter data, such as the TestActivator(void) input
- PlatSounds - move sounds for trains and such
- WeaponItemBase - only in my FGDs, contains inputs, outputs, and properties for weapons and items
- Weapon - contains inputs, outputs, and properties specific to weapons
- Item - contains inputs, outputs, and properties specific to items
- BaseVehicle - contains vehicle inputs, outputs, and properties
- BaseDriveableVehicle - contains driveable vehicle inputs, outputs, and properties
There are many more bases, but most will never be used except for their respective entities. Often these bases will reference each other to save space, so watch for redundancy when you list bases for an entity.
Helpers
Helpers do many things. They control the size and color of point-entities; they control which bases (see above) an entity calls on; they control those annoying spheres that you see around prop_physics and other entities; etc.
The color(r g b) helper, for instance, changes the color of a point-entity. The size(-x -y -z, x y z) helper controls the size of the point-entity's cube; the sphere(property) helper allows you to connect the value of a certain property to a sphere around the entity.
So, for a point-entity that's blue and is a 32-by-32-by-32 cube with the center of the entity corresponding to the center of the cube:
@PointClass color(0 0 255) size(-16 -16 -16, 16 16 16)
But wait! What about those bases?
Well, suppose we want our entity to have a name and accept all the inputs associated with having a name. There's a base for just that purpose: it's called Targetname. So, let's edit our entity to call on the Targetname base:
@PointClass base(Targetname) color(0 0 255) size(-16 -16 -16, 16 16 16)
Hmmm. Well, y'know what, I think our entity should also have a Parent. The base associated with Parent is Parentname, so let's add that one:
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16)
What we have here now is a point-entity with Name and Parent properties (because it calls on those bases) that will be blue and cubic. Fun!
An entity that calls on a base will inherit all helpers from the base except sphere(property). The helpers are:
- base(base1, base2, base3, ...) - lists bases called by the entity
- color(r g b) - controls the color of a point-entity
- size(-x -y -z, x y z) - controls the size of an entity by declaring lower-left and upper-right corners relative to the entity's center
- sphere(property) - allows you to create a sphere around the entity whose radius is property
- line(r g b, property1, property2) - draws a line of color r g b (color will be overridden by entity color) from the entity specified by property1 to the entity specified by property2
- iconsprite("path/sprite.vmt") - allows you to display a sprite for the entity instead of a box; path is relative to the materials directory, for instance "editor/fog_controller.vmt"
- studioprop("path/model.mdl") - allows you to display a model for the entity instead of a box; path is relative to the mod directory, for instance "models/editor/spot_cone.mdl"
- studio() - causes the entity to show the model specified by its model property, unless you specify a model in parentheses, in which case acts exactly like studioprop()
- sprite() - causes the entity to show the sprite specified by its model property, unless you specify a sprite in parentheses, in which case acts exactly like studiosprite()
- sidelist(property) - draws a line from the entity to the faces specified by property
- halfgridsnap - whereas entities usually snap to the grid, this helper forces the entity to snap to a half-grid
- wirebox(property1, property2) - draws a wireframe box around the entity, with one corner at (entity's center) - (property1) and the opposite corner at (entity's center) + (property2)
There are also a few entity-specific helpers, and of course some helpers that don't work...
Classname
Classname refers to the actual "name" of the entity: point_teleport, trigger_hurt, info_player_start, etc. The only restrictions on the classname: don't use strange characters and don't use a classname that's already in the FGD somewhere. If you use duplicate classnames, only the first one (the one closest to the top in the FGD) will be read by Hammer.
Now, with a classname, we have:
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document."
The classname won't change anything about the entity. At all. Naming mine point_tutorial, for instance, will make it no different than if I had named it weapon_pumpkingun or func_chimp. Classname is very important, but it means next to nothing.
property(datatype)
This is where it gets fun. Sadistic, evil fun.
You can't have a base for everything, because often you need a certain property for only one entity, or you need to change the property slightly for the one entity.
If you take any entity and turn off Hammer's SmartEdit (by clicking the SmartEdit button), you'll see a list of keys and values. Each key, such as targetname and origin, is one of the entity's properties. The datatype in parentheses determines what sort of data is allowed for the value. Valid datatypes are:
- property(target_destination) - used of target properties; lets you choose another entity in the map for the value
- property(target_name_or_class) - used of target properties; lets you choose another entity or a classname
- property(npcclass) - used of NPC targets; lets you choose an NPC
- property(filterclass) - used of filter targets; creates a drop-down menu with a list of every filter in the map
- property(target_source) - used only of Name; lets you add this entity to the map's entity list
- property(angle) - used of directions and pitch/yaw/roll; lets you declare three numbers (pitch/yaw/roll) and gives you the little compass for them
- property(origin) - used only of Origin; lets you position the little blue sphere, the entity's origin
- property(studio) - used of models and sprites; if the entity has a studio() helper, then the property marked studio will control how the entity looks in the 3D view
- property(integer) - used of numbers; denotes a value that should be an integer
- property(float) - used of numbers; denotes a value that should be a float, that is, a rational number
- property(vector) - used of coordinates, directions, etc.; denotes an XY or XYZ vector
- property(color255) - used of color properties; allows the user to pick a color with a "Pick Color" button
- property(sound) - used of sounds; allows the user to choose a sound from a list
- property(sidelist) - used of side lists; allows the user to pick faces
- property(choices) - used of any property; the syntax of this one is different, but it shows a drop-down menu and changes the value based on the item chosen from the menu
- property(string) - used of any property; allows the value to be anything
The format for (choices) is:
property(choices) : "SmartEdit text" : defaultvalue : "Help" =
[
"value1" : "Drop-down menu option 1"
"value2" : "Drop-down menu option 2"
"value3" : "Drop-down menu option 3"
]
When drop-down menu option 1 is chosen, the property key's value will be set to value1, and so on.
However, many of the datatypes seem exactly the same, for no known reason. There is no real difference in float, integer, and string, for example. Datatypes are mostly used to cause the appropriate buttons (such as Pick Color or Pick Sound) to show up.
So, let's add a few custom properties to our point_tutorial... I will open the properties with a square bracket and close them with another square bracket.
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document."
[
radius(float) : "Radius" : "1024" : "The radius of the tutorial."
message(string) : "Message to show" : "" : "The message to show."
starton(choices) : "Start On?" : "1" : "Whether to start on." =
[
"0" : "No"
"1" : "Yes"
]
]
Why is there so much blank space?
All of that blank space is whitespace. It doesn't serve any purpose but to make the code easier to read. In the above, you can clearly see that all the properties belong to point_tutorial and that the Yes and No belong to starton(choices). Whitespace is used to organize code in the same way written English is organized into paragraphs.
The above sample could have been written thus:
@PointClass base(Targetname,Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document." [
radius(float):"Radius":"1024":"The radius of the tutorial."
message(string):"Message to show":"":"The message to show."
starton(choices):"Start On?":"1":"Whether to start on."=[
"0" : "No"
"1" : "Yes" ]
]
However, this would have made the code much more difficult to read.
What's with all the quotation marks and colons?
Quotation marks set off a string in the entry. Everything within the quotation marks will be used. Quotation marks are often not necessary and only used for organization; however, if you want a string to be empty (contain no data), you have to use "". The message(string) property above, for instance, would default to having no value (which is not the same as 0); if you turn off SmartEdit, you won't see that key, because keys are only listed if they have a value.
This workaround was used in the game_player_equip entry of my FGD. A bug with the entity causes the thing to give a gun even if the gun's value is set to 0; however, by using a "" instead of a 0 for the No option, I stopped the key from appearing at all, correcting the bug.
The colons, equal signs, and square brackets serve only to tell Hammer where each section of the entry begins and ends. The helpers are separated from the classname by an equal sign, properties are separated from one another by newlines, etc. (In this case, the properties themselves are said to be newline-delimited, meaning the properties are separated from one another by newlines. If the properties were semicolon-delimited or whitespace-delimited, then semicolons or whitespace would be used to separate them.)
There's one more thing about properties to know. You may have noticed in info_node that you cannot edit the "Node ID." This is because the Node ID is read-only; that is, you can read its value, but you cannot change its value.
You may find it necessary to make a property read-only. To do this, just interpose the word readonly between property(datatype) and the first colon, like so:
READ/WRITE - property(datatype) : "Property" : "value" : "Property help"
READ-ONLY - property(datatype) readonly : "Property" : "value" : "Property help"
The latter property would be read-only.
spawnflags(flags)
The spawnflags(flags) property controls the flags that appear in an entity's Flags tab. The syxtax of the flags is different from any other property:
spawnflags(flags) =
[
flagnum : "Text in Flags tab" : default
]
Where flagnum is the number of the flag (flag 1, flag 2, flag 4, flag 8, etc.) and default is either 1 or 0--on or off.
The flagnum must be a power of 2, and if you turn off SmartEdit, you'll see why. The flags you've checked control the value of the spawnflags key. If flags 1, 16, and 128 are checked, then spawnflags would be 145, the sum of the flagnums. There is only one combination of powers of 2 that equals 145; indeed, there is only one combination of powers of 2 for any number. That's why flags must be numbered in powers of 2: so that the game can take the spawnflags value and know every flagnum that must have been used to make it.
Let's add flags 128 and 256 to our entity, and let's make them both default to ON.
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document."
[
radius(float) : "Radius" : "1024" : "The radius of the tutorial."
message(string) : "Message to show" : "" : "The message to show."
starton(choices) : "Start On?" : "1" : "Whether to start on." =
[
"0" : "No"
"1" : "Yes"
]
spawnflags(flags) =
[
128 : "Do you like cake?" : 1
256 : "Look, a woman!" : 1
]
]
Inputs and outputs
You can add inputs and outputs to an entity in much the same way as adding a property:
input Inputname(datatype) : "Help"
output Outputname(datatype) : "Help"
The datatype for inputs determines valid Override Parameters for the input. Unfortunately, this functionality seems unfinished (as with the properties datatypes), as all the datatypes are exactly the same. Obviously, choices does not work. If you want to disable the override parameters, use the void datatype:
input Kill(void)
I have no idea why outputs need a datatype.
Let's add an input and an output to point_tutorial.
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document."
[
radius(float) : "Radius" : "1024" : "The radius of the tutorial."
message(string) : "Message to show" : "" : "The message to show."
starton(choices) : "Start On?" : "1" : "Whether to start on." =
[
"0" : "No"
"1" : "Yes"
]
spawnflags(flags) =
[
128 : "Do you like cake?" : 1
256 : "Look, a woman!" : 1
]
input Display(void) : "Displays the message declared by the message(string) property."
output OnDisplay(void) : "Fired when the point_tutorial displays its message."
]
End
So there you have it--the syntax of the FGD file format, laid out in bold print with an occasional joke thrown in to keep your interest. Our finished entity entry would be thus:
@PointClass base(Targetname, Parentname) color(0 0 255) size(-16 -16 -16, 16 16 16) = point_tutorial : "A test entity. Its classname is point_tutorial and this text will appear in the entity's help document."
[
radius(float) : "Radius" : "1024" : "The radius of the tutorial."
message(string) : "Message to show" : "" : "The message to show."
starton(choices) : "Start On?" : "1" : "Whether to start on." =
[
"0" : "No"
"1" : "Yes"
]
spawnflags(flags) =
[
128 : "Do you like cake?" : 1
256 : "Look, a woman!" : 1
]
input Display(void) : "Displays the message declared by the message(string) property."
output OnDisplay(void) : "Fired when the point_tutorial displays its message."
]
However, since no game supports the entity point_tutorial, it would do nothing!
Ahhh, but there's one last cool thing you can do to an FGD...
Comments
A comment is a bunch of text preceded by two backslashes (//) that will not be considered a part of the FGD. For instance:
// This line would not even be read
@PointClass base(Targetname) = info_null [] // Hammer would see info_null,
// but it wouldn't read this line because it saw two backslashes
@SolidClass base(Parentname) = info_chimp [] // So you can use comments
// like these to put special developer data in your entity entries
// without affecting the entity itself.
Hammer would see:
@PointClass base(Targetname) = info_null []
@SolidClass base(Parentname) = info_chimp []
Very useful. Look at the top of any up-to-date FGD and you're likely to see a massive changelog set off by double-backslashes.
Real End
Still need help? Ask your questions in one of our HELP FORUMS
|
|
|
| Comments |
| Arraggatis - Apr 6, 2009 |
| i have a question for the CS:S fgd one. Are the prop_vehicle_jeep entities supposed to show up on the map bcause when i try to make it show up on the map it doesnt work. |
| JeffMOD - Aug 19, 2008 |
| OK. How about a Tutorial on Making Custom FGDs work in HL/ HL2. I tried making a custom FGD for HL, and I could place my new zombie (It was a second one with a new model, so I could have two kinds) in hammer, but it wouldnt spawn in HL. |
|
|
|
|