A DISCUSSION ON THE STRUCTURE OF THE QUAKE.FGD FILE IN WORLDCRAFT

by Imaginos


Credit where credit is due dept.

First off, thanks to Ben Morris, creator of Worldcraft http://www.acdsystems.com/worldcraft/, for making this outstanding editor!

And thanks too, to Chris Bokitch master of The Official Worldcraft Editing Site (www.planetquake.com/worldcraft).


History dept.

The following is an attempt to explain the structure of the quake.fgd (game definition file) that comes with Worldcraft.

One of the great things about Worldcraft is its ability to be customized. I'm sure as time goes by, that Ben will add even more customizing features to this great Quake level editor.

Not long ago, I became interested in creating a TeamFortress level with Worldcraft and it was this interest that led me to decode the quake.fgd file.

TeamFortress, like CTF (Capture The Flag), has several unique entities not found in regular Quake. For this reason, Ben supplied the CTF entities in a recent release of Worldcraft. But, that didn't do anything for me and my desire to have TeamFortress entity support in Worldcraft :(

So, one night on IRC, I happened to run into one of the guys who created TeamFortress and told him what I was trying to do. Not a good idea he says. Because, certain TF entities (most notably info_tfgoal), could have many different configurations depending on what the goal entity was to be used for. Those familiar with this aspect of TF will understand what I'm talking about. He suggested using EntEd - the TF Entity Editor, instead.

Well, OK.

But, in reading the EntEd documentation, I found that I had to do the following:

Create an existing entity, such as info_null, and rename it to info_tfgoal (or info_tfwhatever) and enter a new key for it called 'netname'. Then, type in something that described the entity (like Team1 backpack or whatever).

Save the file in .map format and then load it into EntEd. EntEd would find the TF entities I had created in my .map file and show me the names I had assigned to them. Then, I could edit their properties and resave the map.

Well, I did that, but I thought it would be a whole lot easier if the entities already existed in Worldcraft and if they had the 'netname' key option too.

Not long after this, my son, realistik, was talking to Ben Morris one morning on IRC and he told Ben what I was trying to do.

No problem, says Ben. Just tell him to edit the quake.fgd file.

Well, I tried to do that, but one look at that file and I grew weak in the knees. No simple .ini file, this!

After several unsuccessful tries, I finally figured out how to enter the data I wanted in the .fgd file and it worked.

When I sent the file out to Chris Bokitch, who runs The Official Worldcraft Editing Site (www.planetquake.com/worldcraft), I sent the entire quake.fgd file. But Chris had a little idea of his own - i.e. just release the new code as a patch that could be inserted into the .fgd file. This way, others could release patches of their own to add in.

Well, that was (and is, Thanks, Chris!) a good idea. But, how would they go about it if they didn't know the structure of the file?

And the answer to that question is the reason you're reading this now...

Programming 101 dept.

The quake.fgd file looks intimidating at first glance, but it's not hard to understand if you know what it's telling you. Now, I'm not a real programmer, by any stretch of the imagination, but this file appears to be written in some variant of the C programming language - possibly Visual C++. Real programmers reading this will probably get a good laugh out of some of the things I'm about to present here but, hey - it works, dammmit :)

One thing that you want to keep in mind is that some of the data in the .fgd file is internal - that is, it's used only in the editor itself; while other data are used in the creation of the .map file.

A good example of this are the 'size' and 'color' variables used for some entities. 'Size', and the numbers in parentheses after it refer to how the entity or item will be displayed on the screen grid in Worldcraft. The same goes for 'color'.

A player start, when placed on the map, for instance will be displayed as a cube 32 units square by 48 units tall. It's colour, in RGB format (0 255 0) will be bright, light-green.

But other entries, such as 'light', 'sounds', 'dmg' and so on, are statements that will be placed in the .map file when it's created.

The variables, in parentheses, which follow these entries, hold the data which will accompany them in the .map file.

Take 'sounds', for example. A typical quake.fgd entry (or statement, if you prefer) looks like this:

sounds(choices) : "Sound" : 1 = 
	[
		1: "Stone"
		2: "Machine"
		3: "Stone Chain"
		4: "Screechy Metal"
	]

Here, the variable (choices) will contain either 1,2,3 or 4. The statement ': "Sound" : 1 =' means that sound #1 (Stone) is the default sound for this item.

We'll go into more detail on the structure of the statements in a minute, but for now, just remember that some of the variables are internal (for displaying things on the screen or generating text in the context menus) and others are used in creating the .map file.

OK, let's get into it! The first statements we'll look at are:

Palette: "wc.pal"
    DefaultClass: "func_door"

These are right near the top of the quake.fgd file. If you haven't already printed out the file, now would be a good time to do it as we'll be referring to some of the code in it from now on.

The Palette statement is self-explanitory; the 'wc.pal' file in the Worldcraft root directory is the default Palette (the Quake texture colours) that Worldcraft uses to display the textures in the Browser and in the 3D Textured View window. If you want to use a different palette to display your textures in Worldcraft, then this is where you would change it. Suppose you have some custom textures made with a custom palette that you want to use in Worldcraft. Just change "wc.pal" to "mystuff.pal" and save the quake.fgd file (always being sure that 1) mystuff.pal exists  2) that it is in the root directory of Worldcraft and 3) that the palette file is in Microsoft format.

The second statement, DefaultClass: "func_door" means that when you choose to make a brush an entity, that func_door will be the first choice displayed in the Object Properties dialog.

Since 'func_door' is a member of the SolidClass (more on this later), other choices for the DefaultClass: statment would be func_plat, fund_train, func_button and so on.

Moving right along, you'll notice that each section has a heading that looks like this:

//
// text
//

The two forward slashes are comments, much like the 'rem' statement in an old DOS batch file. Use these whenever you want to make notes to yourself (or others) in the .fgd file and as section headers when you add a new section to the file.

The first section is 'worldspawn'. I'm not going to go into what worldspawn is - but if you really want to know, get the Quake Map Specs at http://www.infi.net/~nichd/qmapspec.html and look it up.

The first statement is:

@SolidClass = worldspawn : "World entity"

Now, you're going to see this @whateverClass thing a lot. What this statement does (ok, what it APPARENTLY does) is put 'worldspawn' in the class of Solids.

What are solids? Any brush that you create in Worldcraft and any brush you see in Quake is a solid. Alright?

But, sometimes you want to make a brush move - you want it to be active - to do something besides sit there and look good. A prime example of this is when you select a brush and make a door out of it. If you page down (or leaf thru) the quake.fgd file, you'll see that 'func_door' is a member in good standing of the SolidClass, along with 'func_plat', 'func_button' and many more.

The reason for classes (as I see it) is twofold:

1) that when you choose certain kinds of entities, so that you can modify their properties to suit your desires, only entities of a certain Class should be available.

For instance, if you select a brush and Tie (it) to (an) Entity (such as func_door), ONLY those entities that can be assigned to brushes (solids) should appear in the list of choices. That is, you couldn't select a brush and turn it into a 'light' entity. But, if 'light' were one of the choices, and you chose it, then you would get an error when the map was compiled.

2) that certain items, for screen display purposes, such as their size and colour on the map grid in Worldcraft, can be grouped in the same class. We'll see more of this in a few minutes when we get to the PlayerClass section.

For now, let's go back to our statement:

@SolidClass = worldspawn : "World entity"

The = sign assigns 'worldspawn' to the Class of Solids.

'worldspawn' itself, is what will appear in the .map output file. Beneath this statement are several more:

[
	message(string) : "Text on entering the world"
	worldtype(choices) : "Ambience" : 0 =
	[
		0 : "Medieval"
		1 : "Runic (metal)"
		2 : "Present (base)"
	]
	sounds(integer) : "CD track to play" : 1
	light(integer) : "Default light level"
]

The structure is this:

worldspawn (main entity)
message (option)
worldtype (option)
		  "Ambience" : 0 =

		0 : "Medieval"
		1 : "Runic (metal)"         (sub-options of worldtype)
		2 : "Present (base)"
	
sounds (option)
light  (option)

In other words, there is a main entity and a list of options or flags that can be set for that entity. Note too, that some options (worldtype) can have sub-options of their own, and that some (sounds) have a default value assigned to them while others (message) do not.

You'll notice that there is some text in quotes at the end of each item. The format here is:

data to be placed in the current variable : text to be displayed in the Object Properties dialog boxes.

In other words, 0 : "Medieval"
		1 : "Runic (metal)"
		2 : "Present (base)"

the values 0,1 and 2 are to be placed in the (choices) variable while "Medieval", "Runic (metal)", and "Present (base)" will be displayed in the Object Properties Class Info dialog box in Worldcraft.

When the .map file is created, it would look like this if we elected to have a message that says "my first Quake level", worldtype: Runic, Play CD track 4 and an ambient light level of 400 (not that setting the ambient light level DOES anything that I've ever seen):

{
"classname" "worldspawn"
"message" "my first Quake level"
"worldtype" "1"
"sounds" "4"
"light" "400"
}

But what about "World entity"? where is that displayed or used?

Truth is, I don't know. I can only assume that it is used by the editor or program that Ben used to create the .fgd file, since it appears nowhere in Worldcraft itself. If it does, I haven't found it...

Alright, let's move on to the next section:

//
// base marker definitions
//

@baseclass = Appearflags [
	spawnflags(Flags) =
	[
		256 : "Not in Easy" : 0
		512 : "Not in Normal" : 0
		1024 : "Not in Hard" : 0
		2048 : "Not in Deathmatch" : 0
	]
]

@baseclass = Targetname [ targetname(target_source) : "Name" ]
@baseclass = Target [ target(target_destination) : "Target" ]

Whoa, D00d! What's all this mess?

Well, first of all, look at the header, 'base marker definitions'. What this appears to be is the definition of a group of global variables that will be used throughout the rest of the .fgd file.

Let's break it down...

The first statement is:

@baseclass = Appearflags [
	spawnflags(Flags) =
	[
		256 : "Not in Easy" : 0
		512 : "Not in Normal" : 0
		1024 : "Not in Hard" : 0
		2048 : "Not in Deathmatch" : 0
	]
]

Hey! I've seen this before. Yeah! In the Object Properties/Flags folder.

Roger that, Space Cadet! That's exactly where this goes.

The @baseclass = statement creates a temporary/global variable. In this case 'Appearflags' can be considered a global variable since it appears over and over throughout the .fgd file. Spawnflags contains several choices beneath is, all with a default value of 0, the results of which will be contained in the variable (Flags).

Appearflags is a Worldcraft variable; while spawnflags and the data contained in (Flags) will be outputted to the .map file when it's generated. The text in quotes ("Not in Easy", etc.) will be displayed in the Object Properties dialog in the Flags folder. Putting a checkmark in the"Not in Deathmatch" box, for instance, will put the value 2048 in the (Flags) variable.

So, whenever you see the Appearflags variable in a statement later on in the .fgd file, it means that that entity or item can have these flags set for it. An example would be if you had a secret room that you had put in the level where you could go and load up with health and ammo. If you didn't want the door leading to that room to appear in Normal play (as opposed to Coop or Deathmatch play), you would set the "Not in Normal" flag for that door. This is why certain things do not appear in the standard levels when you are in Normal play, but do appear (or don't appear - like monsters) when you play them in Deathmatch mode. Ah ha!

Let's look at the other two statments before we move on.

@baseclass = Targetname [ targetname(target_source) : "Name" ]
@baseclass = Target [ target(target_destination) : "Target" ]

Here, the variables Targetname and Target are being defined. The text inside the brackets is the same as before - data to be placed in the current variable : text to be displayed in the Object Properties dialog boxes.

Targetname and Target are Worldcraft variables; while targetname and target are statements that will be put in the .map file when it's generated. The values for targetname and target come from the Worldcraft variables (target_source) and (target_destination).

"Name" and "Target" will be displayed in the Object Properties dialog in the Class Info folder.

With that out of the way, let's move on to the next (and final) section:

//
// player starts, deathmatch, coop, teleport
//

The first statment here is:

@baseclass base(Appearflags) size(-16 -16 -24, 16 16 24) 
	color(0 255 0) = PlayerClass []

Something new here, gang. OK. @baseclass base(  - all the data between here and the = sign will be assigned to the base(PlayerClass) variable in the lines that follow. The [] symbols show that this is the end of this definition. That goes for all definitions, by the way. They all start with [ and end with ]. Sometimes you will see several sets of these, one inside the other. This is a 'nested' statement - that is, the statements/definitions made inside other [] statements are part of the statement/definition that began it all. Confused? Good! Find one of your programmer pals and ask him to explain it ;)

Now, 'size' - we talked about this earlier. The numbers inside the parentheses (-16 -16 -24, 16 16 24), define a cube 32 units square by 48 units tall. The colour of this cube that will be displayed in Worldcraft is (0 255 0) which is bright light-green in RGB coding. Many drawing programs have a spectrum screen that you can move your mouse around in and click to make a custom colour and some of them show the RGB (Red Green and Blue) values of the colour you selected. If you want to change this colour, you'll need to know the RGB values of the colour you want to use.

Note that in addition to defining the size and colour of PlayerClass entities, that the Appearflags variable has been included so that the various classes of player starts can be included/excluded from certain levels of play (Normal/Deathmatch/Hard, etc.).

Now, let's examine PointClasses. PointClass 'points' to a unique entity definition such as a Player1 start or a Deathmatch Player start.

@PointClass base(PlayerClass) = info_player_start : "Player 1 start" []

See? There's base(PlayerClass). Therefore all the data contained in the base(PlayerClass) definition will apply to the info_player_start entity.

As always, the data following the = sign means

data to be placed in the current variable : text to be displayed in the Object Properties dialog boxes and the [] symbols end the definition of this entity.

That's pretty much all there is to it.

Using these guidelines, I added the following lines to the end of the quake.fgd file to add TeamFortress entities to Worldcraft:

//
// TeamFortress Entities
//

@baseclass base(Appearflags) size(0 0 0, 24 24 24) color(255 128 0) = TfClass
[
  netname(string) : "netname"
]

@PointClass base(TfClass) = info_tfdetect : "TF Detection Entity" []
@PointClass base(TfClass) = info_tfgoal : "TF Goal Entity" []
@PointClass base(TfClass) = info_tfgoal_timer : "TF Timer Goal" []
@PointClass base(TfClass) = item_tfgoal : "TF Goal Item" []
@PointClass size(-16 -16 -24, 16 15 24) color(255 255 0) 
base(Appearflags) = info_player_teamspawn : "TF Player Start"
[

You'll notice that I didn't end the first statement with [], but added this instead:

[
  netname(string) : "netname"
]

The reason I did that is because I wanted the netname variable to appear in the Object Properties Class Info dialog. It is included in the   base(TfClass) variable definition and applies to all entities that use that variable.

Because I wanted to change the colour of the info_player_teamspawn entity, I didn't include it in the PlayerClass entities. The colour of the info_tfxxx entities is bright orange, which makes them stand out on the screen from the regular Worldcraft entities. The TF Player Start entity is bright yellow, but I may change it - and so can you, now that you know how!

Note too that in defining the TF Player Start entity, I did not start the statement with @PointClass base(TfClass). Why? Because I wanted this entity to be a different size and colour than the other TF entities. The size is the same as the PlayerClass entities, because they're easy to spot that way.

One other thing, and maybe someone reading this (Ben, are you there?) may be able to explain it. The following statement appears at the end of the PlayerClass entities section:

@PointClass = info_null : "info_null (spotlight target)"
[
	targetname(target_source) : "Name" 
]

I understand the statement whose purpose is to define the info_null entity, but, what size and colour is it supposed to be? There is no size and colour information in it. It is 16 units square by 16 units tall but I find no 'default' size for entities anywhere in quake.fgd. Nor is it a member of any particular class. If anyone can explain this, I'd really like to know.

Well, kiddies, that's all I have to say about that...

Address any questions/comments/hosannas/flames to

imaginos@tyler.net

Go ye therefore and HACK!