This is the document on the Quake 2 MAP file format. It describes the concepts, the file syntax, how to parse MAP files, how to create them, and what special flags and attributes tell the processing tools.
There is a top level document describing the Q2DP that is mandatory to be distributed along with this document, which is part of the Q2DP collection. See the Q2DP main document for all details on Authors and Contributors, Contacts, Corrections, Submissions, Trademarks and Acknowledgments, Distribution Policy and Copyright. If the top level document is missing, please contact the current maintainer of the Q2DP project, Bernd Kreimeier ( bk@gamers.org), as soon as possible.
You will find the most recent version of this document at www.gamers.org/dEngine/quake2/Q2DP/.
See the ChangeLog for most recent changes to the Q2DP collection, and the Q2DP Supplement for the latest contributions and errata that have not yet been processed.
We rely on you, the reader and fellow developer, to make this collection useful. If you have any suggestions, corrections, or comments, please send them to the current maintainer, Bernd Kreimeier ( bk@gamers.org). For details please read the section on how to submit a contribution in the Q2DP main document.
Quake uses a standard right-handed (X,Y,Z) coordinate system (RHS). If you are not familiar with the reference of "right-handed" to a coordinate system, it basically provides an intuitive visual description of the system: point your fingers towards the positive x-axis and bend your fingers so that your knuckles face the positive y-axis, your thumb will point towards the positive z-axis:
^ z+ | | | | |------------> y+ / / / / < x+
Some entities also need to have an angle that tells the direction it is facing within the xy plane. The units are degrees, the values possible are thus 0-359, which zero pointing east and 90 pointing ???north???, that is, we are using a counterclockwise (CCW) system. Some angles have special values:
Brushes are one of the two primary components of a MAP file. Each brush defines a solid region. Brushes define this region as the intersection of four or more planes. Each plane is defined by three noncolinear vertices. These vertices must go in a clockwise oriented sequence:
| | ---v1--v2------- | | v3 | |
The Quake brushes are meant to be 3D volumes, thus four planes are needed. If you share as many vertices as possible, you will need four vertices to define all four planes (each combination of three vertices defining one plane). In this case, the lines connecting two vertices would also be the edges of the faces, and the vertices would be the vertices of the brush, sharing a (more or less distorted) tetrahedron.
Each brush statement looks like this:
// A Brush. { ( 128 0 0 ) ( 128 1 0 ) ( 128 0 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 256 0 0 ) ( 256 0 1 ) ( 256 1 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 128 0 ) ( 0 128 1 ) ( 1 128 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 384 0 ) ( 1 384 0 ) ( 0 384 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 64 ) ( 1 0 64 ) ( 0 1 64 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 128 ) ( 0 1 128 ) ( 1 0 128 ) GROUND1_6 0 0 0 1.0 1.0 }
( 128 0 0 ) ( 128 1 0 ) ( 128 0 1 ) GROUND1_6 0 0 0 1.0 1.0 // 1st Point 2nd Point 3rd Point Texture
x_off - Texture x-offset (must be multiple of 16) y_off - Texture y-offset (must be multiple of 16) rot_angle - floating point value indicating texture rotation x_scale - scales x-dimension of texture (negative value to flip) y_scale - scales y-dimension of texture (negative value to flip)
The actual MAP file format is quite simple. It is simply a text file. All Quake editing tools should support both UNIX or DOS text formats, as id's tools do - that is, handle both CR/LF and LF.
MAP files are the development format for Quake levels. It is preferred that all editors work with MAP files, as all tools released by id Software also use the MAP file format. A map file is basically un-'compiled' level data. The actual MAP file is totally unusable by Quake itself until it is coverted to a BSP file by a BSP builder such as id Software's QBSP.
???MOVE: For a MAP file's lightmaps to be calculated by Quake, the level must also be run through a light builder, such as LIGHT. Otherwise, everything will be displayed at full brightness. Finally, to speed up the level and ensure proper display, visibility lists should be calculated by a program such as VIS. The process for building levels in Quake is quite time-consuming compared to building Doom levels, even on a fast computer.
The grammar of a MAP file is roughly as follows:
{ entity { brush (optional) } } ...
Many entity/brush combinations can be put into a map file. All MAP files must contain with a worldspawn entity, usually as the first entry. This entry defines all of the normal brushes that make up the structure of the level. There should be only one worldspawn entity per MAP file. Here's the syntax of a worldspawn entity:
"classname" "worldspawn" // Tells Quake to spawn the world "wad" "DIRPATH" // tells what graphics (texture) WAD2 file to use. "message" "TITLE" // The title of the level "worldtype" "#" // Describes time of environment // (changes appearance/name of keys) // 0 == Medieval (medieval) // 1 == Runic (metal) // 2 == Present (base) "sounds" "#" // Tells the CD player which track to play. "light" "#" // Default light level
A complete, minimal, simple map file would look like this:
{ "sounds" "1" "classname" "worldspawn" "wad" "/gfx/base.wad" "worldtype" "0" { ( 128 0 0 ) ( 128 1 0 ) ( 128 0 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 256 0 0 ) ( 256 0 1 ) ( 256 1 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 128 0 ) ( 0 128 1 ) ( 1 128 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 384 0 ) ( 1 384 0 ) ( 0 384 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 64 ) ( 1 0 64 ) ( 0 1 64 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 128 ) ( 0 1 128 ) ( 1 0 128 ) GROUND1_6 0 0 0 1.0 1.0 } } { "classname" "info_player_start" "origin" "256 384 160" }
As you can see, all brushes are contained in entities, even those that make up the main level. The most complex part of MAP files are the entities. They are what the rest of this document are about.
Entities are the second major component of Quake MAP files. An entity is basically a bit like a thing, but they also function as triggers and as pathmarkers. A entity statement looks like this:
{ "classname" "light" "origin" "0 128 64" "light" "255" }
This is what is called a general entity statement. It is called a general statement because it does not attach to a brush. An attached entity statement looks like this:
{ "classname" "func_door" "angle" "0" "speed" "16" "targetname" "t1" "sounds" "1" "wait" "16" { ( 128 0 0 ) ( 128 1 0 ) ( 128 0 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 256 0 0 ) ( 256 0 1 ) ( 256 1 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 128 0 ) ( 0 128 1 ) ( 1 128 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 384 0 ) ( 1 384 0 ) ( 0 384 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 64 ) ( 1 0 64 ) ( 0 1 64 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 128 ) ( 0 1 128 ) ( 1 0 128 ) GROUND1_6 0 0 0 1.0 1.0 } }
Attached entity brushes can have an "origin" tag. It can be used to offset where they appear in the level.
For the rest of the document, when I give you frameworks for a structure, the individual entries can be in any order, and lots are optional. I try to mark if an entry is optional, although this has not yet been rigorously tested.
In a "" block, your choices for that block are delimited by commas.
I believe that anything with an origin tag can have an optional 'spawnflags' tag. This is not confirmed, however. Tags can be combined by addition or bitwise or (really the same thing).
General: 256 - Normal Skill or higher 512 - Hard Skill or higher 1792 - Appears in deathmatch only item_health: 1 - Larger Health/Larger Ammo 2 - Megahealth monster_zombie: 1 - Crucified Zombie
The entities define the monsters, things, but also the positions in space where something must happen. So they are the Quake equivalent of both the THINGS and the LINEDEF types from DOOM.
The entity definitions are made up of a series of specific details
that define what each is, where it starts, when it appears etc. Each
specific is followed by a modifier that arguments it. All definitions
have the classname
specific that identifies that entity. The
classname
specifics relate intimately with the code lump
and are the names of functions written in
Quake C.
I have chosen the terms ``specific'' and ``arg'' for the two different parts of each detail of the definition. These terms may or may not suit but, they will have to do until we learn what id calls them.
Specifics Args Description ------------------------------------------------------------------------ "classname" "name" // Type of entity to be defined (mandatory) "origin" "X Y Z" // Coordinates of where it starts in space. "angle" "#" // Direction it faces or moves (sometimes in degrees) "light" "#" // Used with the light classname. "target" "t#" // Matches a targetname. "targetname" "t#" // Like a linedef tag. "killtarget" "#" // Removes target when triggered? "spawnflags" "#" // Used to flag/describe an entity that is not default. "style" "#" // Used to flag/describe an entity that is not default. "message" "string" // Message displayed when triggered (\n for linebreaks) "mangle" "X Y Z" // Point where the intermission camera looks at {BRUSH INFO} // In entities that describe triggers/doors/platforms, etc, // the brush info is inserted into the entity brackets, // delimited by another set of brackets. specifics/args present only in models: "speed" "#" // How fast the model is moved. "wait" "#" // How long a pause between completion of movement or // return to original position (in seconds or 10ths) "lip" "#" // Seems to be a means of adjusting the starting position. "dmg" "#" // How much damage the model causes when it shuts on you? "health" "#" // How much damage the model takes before it triggers "delay" "#" // Time before event is triggered "sounds" "#" // How much damage the model causes when it shuts on you? "wad" "wadfile" // The wad2 graphics file used by the world for textures. "height" "#" // How high a platform will raise ------------------------------------------------------------------------- { "specific1" "arg1" // The first descriptors (usually classname) "specific2" "arg2" // The second ... // Etc... { <INSERT BRUSH INFO HERE> // for entities like doors/triggers/platforms/etc } }
Note: The term model refers to a combination of a brush and an entity. One or more brushes are bound to an entity, which controls the behavior of the brushes. All brushes are contained within models.
The model numbers in the compiled BSP (* x
) comes
from the order in which the models are stored in the
models structure. These numbers
are originally derived from the order of the models in the
source MAP file.
The worldspawn model is a bounding box that defines the extents of the whole world.
The models are defined by a bounding box of the max
and
min(x,y,z)
. Therefore they are always parallel to the
horizontal planes. This bounding box is simply used for speeding
up collision detection and will not affect the movement of the
models themselves.
air_bubbles : Rising bubbles ambient_drip : Dripping sound ambient_drone : Engine/machinery sound ambient_comp_hum : Computer background sounds ambient_flouro_buzz : Flourescent buzzing sound ambient_light_buzz : Buzzing sound from light ambient_suck_wind : Wind sound ambient_swamp1 : Frogs croaking ambient_swamp2 : Slightly different sounding frogs croaking ambient_thunder : Thunder sound event_lightning : Lightning (Used to kill Cthon, shareware boss) func_door : Door func_door_secret : A door that is triggered to open func_wall : A moving wall? func_button : A button func_train : A platform (moves along a "train") func_plat : A lift/elevator func_dm_only : A teleporter that only appears in deathmatch func_illusionary : Creates brush that appears solid, but isn't. info_null : Used as a placeholder (removes itself) info_notnull : Used as a placeholder (does not remove itself) info_intermission : Cameras positioning for intermission (?) info_player_start : Main player starting point (only one allowed) info_player_deathmatch : A deathmatch start (more than one allowed) info_player_coop : A coop player start (more than one allowed) info_player_start2 : Return point from episode info_teleport_destination : Gives coords for a teleport destination using a targetname All item_ tags may have a target tag. It triggers the event when the item is picked up. item_cells : Ammo for the Thunderbolt item_rockets : Ammo for Rocket/Grenade Launcher item_shells : Ammo for both Shotgun and SuperShotgun item_spikes : Ammo for Perforator and Super Perforator item_weapon : Generic weapon class item_health : Medkit item_artifact_envirosuit : Environmental Protection Suit item_artifact_super_damage : Quad Damage item_artifact_invulnerability : Pentagram of Protection item_artifact_invisibility : Ring of Shadows (Invisibility) item_armorInv : Red armor item_armor2 : Yellow armor item_armor1 : Green armor item_key1 : Silver Key item_key2 : Gold Key item_sigil : Sigil (a rune) light : A projected light. No visible lightsource. light_torch_small_walltorch : Small wall torch (gives off light) light_flame_large_yellow : Large yellow fire (gives off light) light_flame_small_yellow : Small yellow fire (gives off light) light_flame_small_white : Small white fire (gives off light) light_fluoro : Fluorescent light? (Gives off light, humming sound?) light_fluorospark : Fluorescent light? (Gives off light, makes sparking sound) light_globe : Light that appears as a globe sprite monster_army : Grunt monster_dog : Attack dog monster_ogre : Ogre monster_ogre_marksman : Ogre (synonymous with monster_ogre) monster_knight : Knight monster_zombie : Zombie monster_wizard : Scragg (Wizard) monster_demon1 : Fiend (Demon) monster_shambler : Shambler monster_boss : Cthon (Boss of Shareware Quake) monster_enforcer : Enforcer monster_hell_knight : Hell Knight monster_shalrath : Shalrath monster_tarbaby : Slime monster_fish : Fish monster_oldone : Shubb-Niggurath (requires a misc_teleportrain and a info_intermission) misc_fireball : Small fireball (gives off light, harms player) misc_explobox : Large Nuclear Container misc_explobox2 : Small Nuclear Container misc_teleporttrain : Spiked ball needed to telefrag monster_oldone path_corner : Used to define path of func_train platforms trap_spikeshooter : Shoots spikes (nails) trap_shooter : Fires nails without needing to be triggered. trigger_teleport : Teleport (all trigger_ tags are triggered by walkover) trigger_changelevel : Changes to another level trigger_setskill : Changes skill level trigger_counter : Triggers action after it has been triggered count times. trigger_once : Triggers action only once trigger_multiple : Triggers action (can be retriggered) trigger_onlyregistered : Triggers only if game is registered (registered == 1) trigger_secret : Triggers action and awards secret credit. trigger_monsterjump : Causes triggering monster to jump in a direction trigger_relay : Allows delayed/multiple actions from one trigger trigger_push : Pushes a player in a direction (like a windtunnel) trigger_hurt : Hurts whatever touches the trigger weapon_supershotgun : Super Shotgun weapon_nailgun : Perforator weapon_supernailgun : Super Perforator weapon_grenadelauncher : Grenade Launcher weapon_rocketlauncher : Rocket Launcher weapon_lightning : Lightning Gun
For all light-emmitting entities, spawnflags and style have special meanings:
Spawnflags: 0 - Light starts on. Switches off when triggered. 1 - Light starts off. Switches on when triggered. Style: 0 - normal 1 - flicker (first variety) 2 - slow strong pulse 3 - candle (first variety) 4 - fast strobe 5 - gentle pulse 6 - flicker (second variety) 7 - candle (second variety) 8 - candle (third variety) 9 - slow strobe 10 - flourescent flicker 11 - slow pulse, not fading to black styles 32-62 are assigned by the light program for switchable lights 63 - testing Regular Light: { "classname" "light" "origin" "X Y Z" // Tells where the light is "light" "#" // Tells how bright the light is (optional - default 200) "style" "#" // How the light appears "spawnflags" "#" // State light starts in "targetname" "#" // Target id of the light } Fluorescent Light: { "classname" "light_fluoro" "origin" "X Y Z" // Tells where the light is "light" "#" // Tells how bright the light is (optional - default 200) "style" "#" // How the light appears "spawnflags" "#" // State light starts in "targetname" "#" // Target id of the light } Fluorescent Light (makes sparking sound): { "classname" "light_fluorospark" "origin" "X Y Z" // Tells where the light is "light" "#" // Tells how bright the light is (optional - default 200) "style" "#" // How the light appears "spawnflags" "#" // State light starts in "targetname" "#" // Target id of the light } Torches: { "classname" "light_torch_small_walltorch" "origin" "X Y Z" // Tells where the light is "light" "#" // Tells how bright the light is (optional - default 200) "style" "#" // How the light appears "spawnflags" "#" // State light starts in "targetname" "#" // Target id of the light } Fire: { "classname" "light_flame_large_yellow, light_flame_small_yellow, light_flame_small_white" "light" "#" // Tells how bright the light is (optional) "origin" "X Y Z" }
{ "classname" "trigger_changelevel" "map" "mapname" // Map to change to on trigger (e.g. e1m8) "spawnflags" "#" // Flags describing the object (optional) { <INSERT BRUSH INFO HERE> } }
{ "classname" "trigger_teleport" "target" "t#" // Teleport destination name to teleport to. "targetname" "t#" // Trigger name (optional) - only teleports once activated if targetname is present. { <INSERT BRUSH INFO HERE> { }
{ "classname" "info_teleport_destination" "origin" "X Y Z" "angle" "#" // angle the player will face upon leaving teleport "targetname" "t#" // Teleport's trigger name }
{ "classname" "func_door, func_door_secret" "angle" "#" // angle it faces "speed" "#" // speed of movement "targetname" "#" // Door's trigger name "sounds" "#" // sound it makes "wait" "#" // delay before closing "spawnflags "#" // Flags describing the object (optional) "lip" "#" // some kind of offset (optional) { <INSERT BRUSH INFO HERE> } }
Spawns on certain skill levels (controlled by spawnflags), can be removed by a trigger. (attaches to brush):
{ "classname" "func_wall" "spawnflags" "#" // flags for something (optional I'd guess) }
{ "classname" "func_plat" "height" "#" // height it rises? (optional) "sounds" "#" // sound it makes (optional) { <INSERT BRUSH INFO HERE> } }
{ "classname" "func_train" "sounds" "#" // Sound it makes when activated "speed" "#" // Speed at which it moves (optional) "target" "t#" // Trigger name of its first path_corner destination "targetname" "t#" // Its trigger name "dmg" "#" // Damage done on crush { <INSERT BRUSH INFO HERE> } }
{ "classname" "path_corner" "origin" "X Y Z" "target" "t#" // Trigger name of next train destination. "targetname" "t#" // It's trigger name. }
{ "classname" "func_button" "angle" "#" // Angle button moves? "speed" "#" // Speed it moves in? "target" "t#" // Trigger name of target entity "health" "#" // If there is health, button is shootable "sounds" "#" // Sound it makes when activated // 1 == Steam Metal // 2 == Wooden Clunk // 3 == Metallic Click // 4 == In-Out "wait" "#" // Wait until retrigger? (-1 stays pressed) "delay" "#" // Delay before action is triggered { <INSERT BRUSH INFO HERE> } }
{ "classname" "trigger_once, trigger_multiple, trigger_onlyregistered, trigger_secret" "style" "#" // 32 works "killtarget" "t#" // Kills something [for triggering monster events] (optional) "target" "t#" // Trigger name of target "sounds" "#" // Sound made when triggered // 1 == Secret Sound // 2 == Beep Beep // 3 == Large Switch // 4 == Set "message" to text string "wait" "#" // Delay before retrigger. some classes only. (optional) "delay" "#" // Delay before action is triggered { <INSERT BRUSH INFO HERE> } }
Triggers target
after it is triggered count
times:
{ "classname" "trigger_counter" "targetname" "t#" // Its trigger name "target" "t#" // Trigger name of its target "count" "#" // Decrements on each trigger. When 0 activates target. "wait" "#" // Required delay before retrigger "delay" "#" // Delay before action is triggered { <INSERT BRUSH INFO HERE> } }
{ "classname" "trigger_relay" "origin" "X Y Z" // Where it is located "killtarget" "#" // Removes targeted entity (optional) "targetname" "t#" // Its trigger name "target" "t#" // Trigger name of its target "delay" "#" // Delay before action is triggered }
{ "classname" "trigger_monsterjump" "speed" "#" // Forward velocity of the monster jump "height" "#" // How high the monster jumps "angle" "#" // Angle towards which the monster jumps { <INSERT BRUSH INFO HERE> } }
{ "classname" "func_illusionary" { <INSERT BRUSH INFO HERE> } }
{ "classname" "trap_spikeshooter" "origin" "X Y Z" "angle" "#" // Angle the trap fires at "targetname" "t#" // Trap's trigger name "spawnflags" "#" // ??? 1024 works }
{ "classname" "trap_shooter" "origin" "X Y Z" "angle" "#" // Angle the trap fires at "spawnflags" "#" // ??? 1024 works "wait" "#" // Time between shots }
{ "classname" "misc_fireball" "origin" "X Y Z" "speed" "#" // Tells how fast the fireball moves }
Pushes the player in a direction.
{ "classname" "trigger_push" "origin" "X Y Z" // Where it is located "speed" "#" // Force of the push "angle" "#" // Direction player is pushed towards (-1=up -2=down, other=normal) }
{ "classname" "info_intermission" "origin" "X Y Z" // location of camera "mangle" "X Y Z" // location the camera looks at "angle" "#" // angle of the camera }
{ "classname" "trigger_setskill" "message" "#" // Skill level to change to. { <INSERT BRUSH INFO HERE> } }
I'll figure out how to use it later... I pretty much know how, I just want to test it before I put it here :). From the testing I've done, the lightning produced will not damage a player, however. It probably triggers a script to give the boss character damage.
{ "classname" "event_lightning" "origin" "X Y Z" // location of lightning (origin?) "targetname" "t#" // It's trigger name }
Creating a moving platform isn't that difficult. First, you must define the brush that will do the moving. Here's an example:
{ "classname" "func_train" "sounds" "1" "speed" "128" "target" "t1dest1" "targetname" "t1" { ( -768 0 0 ) ( -768 1 0 ) ( -768 0 1 ) GROUND1_6 0 0 0 1.0 1.0 ( -640 0 0 ) ( -640 0 1 ) ( -640 1 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 -384 0 ) ( 0 -384 1 ) ( 1 -384 0 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 -256 0 ) ( 1 -256 0 ) ( 0 -256 1 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 -256 ) ( 1 0 -256 ) ( 0 1 -256 ) GROUND1_6 0 0 0 1.0 1.0 ( 0 0 -128 ) ( 0 1 -128 ) ( 1 0 -128 ) GROUND1_6 0 0 0 1.0 1.0 } }
{ "classname" "path_corner" "origin" "0 0 0" "targetname" "t1dest1" "target"" "t1dest2" } { "classname" "path_corner" "origin" "0 128 0" "targetname" "t1dest2" "target"" "t1dest1" }
Have you been wondering how you can get events to trigger when a monster dies, as first seen in E1M2 with the demons? Well, it's not too difficult. When you attach a target tag to a monster, the monster's death will trigger the event. I believe (not tested) that if other monsters have a targetname tag the same as a monster with the target tag, the target event will only occur when all monsters with a matching targetname tag are dead. The monster with the target tag need not have the targetname tag.
The trigger_count class is quite an interesting trigger. You know of the area in E1M1 where you have to hit the three switches to open the door? Well, that's done using a trigger_counter. Each of the buttons you hit has its target property set so it points to a trigger_counter. The trigger_counter has its count tag set to three. Each time a switch is hit, the trigger_counter's count property will decrement by one. When it reaches zero, it will open the door. Each button can only be triggered once as it has a wait of -1. Here's an example:
{ "classname" "func_door" "targetname" "door2" "target" "light1" "angle" "-1" "wait" "-1" "sounds" "4" "message" "press all buttons" { ( -10 120 0 ) ( -10 80 0 ) ( 10 120 0 ) *slime0 2 0 0 1.000000 1.000000 ( 10 80 0 ) ( 0 80 100 ) ( -10 120 0 ) *slime0 2 0 0 1.000000 1.000000 ( 0 80 100 ) ( -10 80 0 ) ( 0 120 100 ) *slime0 2 0 0 1.000000 1.000000 ( -10 80 0 ) ( 10 80 100 ) ( 10 80 0 ) *slime0 2 0 0 1.000000 1.000000 } } { "classname" "trigger_counter" "count" "3" "targetname" "door1" "target" "door2" "wait" "-1" } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "door1" { ( 180 -200 50 ) ( 180 -180 50 ) ( 200 -180 50 ) tlight11 16 0 0 1.000000 1.000000 ( 180 -200 30 ) ( 200 -200 30 ) ( 200 -180 30 ) tlight11 16 0 0 1.000000 1.000000 ( 200 -180 50 ) ( 180 -180 50 ) ( 200 -180 30 ) tlight11 16 0 0 1.000000 1.000000 ( 180 -180 50 ) ( 180 -200 50 ) ( 180 -180 30 ) tlight11 16 0 0 1.000000 1.000000 ( 180 -200 50 ) ( 200 -200 50 ) ( 180 -200 30 ) tlight11 16 0 0 1.000000 1.000000 ( 200 -200 50 ) ( 200 -180 50 ) ( 200 -200 30 ) tlight11 16 0 0 1.000000 1.000000 } } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "door1" { ( 180 -10 50 ) ( 180 10 50 ) ( 200 10 50 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 -10 30 ) ( 200 -10 30 ) ( 200 10 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 200 10 50 ) ( 180 10 50 ) ( 200 10 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 10 50 ) ( 180 -10 50 ) ( 180 10 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 -10 50 ) ( 200 -10 50 ) ( 180 -10 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 200 -10 50 ) ( 200 10 50 ) ( 200 -10 30 ) +0basebtn 0 0 0 1.000000 1.000000 } } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "door1" { ( 180 180 50 ) ( 180 200 50 ) ( 200 200 50 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 180 30 ) ( 200 180 30 ) ( 200 200 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 200 200 50 ) ( 180 200 50 ) ( 200 200 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 200 50 ) ( 180 180 50 ) ( 180 200 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 180 180 50 ) ( 200 180 50 ) ( 180 180 30 ) +0basebtn 0 0 0 1.000000 1.000000 ( 200 180 50 ) ( 200 200 50 ) ( 200 180 30 ) +0basebtn 0 0 0 1.000000 1.000000 } }
Unlike in Doom-Engine games, you can precisely teleport monsters into new locations in Quake. To do so, you must first create a out of reach area for the monsters to reside in. Give this area a trigger_teleport tag and assign a targetname tag to it. Create a teleport_destination where you want the monster to appear. Now, you must create a trigger whose target property points to the trigger_teleport's targetname. When this trigger is activated, the monster in the room will teleport to the teleport_destination. Make sure that there is only one monster per room and one room per teleport_destination. Otherwise, when the teleport is triggered, all the monsters will telefrag each other (like what happens in E1M7 when you win).
The behavior of buttons can be altered in many ways. By default, buttons are activated by pressing them (moving near them). Buttons can be made shootable by giving them a health tag. Unless you want to have to shoot the button tons of times, set the health tag to "1". If you want to have the button flash when you shoot it, you must include all of the button animation textures in the level. You can just put them on brushes outside the level.
Textures named "skip" and "mon". These are a part of the set of "no-draw" textures that you never see: clip brushes, triggers and origin points. "Skip" is designed to be used with in conjunction with the "hint" texture. One uses the hint texture on a brush to suggest a place for the BSP to start a new leaf. If you apply "hint" to the entire brush, every surface of the brush that is exposed to the interior space of a level will create a potential leaf break. If you make the brush "Skip" texture first, then mark only one side of the brush with "Hint," only one plane will be created. The program ignores the other sides of the brush. We've found that when using "Hint," it is often best to move the hint surface so that it is not flush with adjacent surfaces. That is to say, move it in 8 pixels from the opening of a hallway rather than sealing off the end of the hall.
The "Mon" texture is essentially a subset of the clip brush texture, call it the "Monster Clip". A "standard" clip brush will block the movement of both player and monster entities. The monster clip texture only has the property of blocking the movement of monsters. We used it in places where we wanted monsters kept out (the "airlocks" between levels) or to create surfaces that the monsters would be willing to walk on (their AI tries to keep them from falling off ledges or walking out over open spaces).
John Carmack mentioned hint brushes and SURF_NODRAW for
the first time 18.11.1997. In the Q2 utility README
from 17.9.1997, he writes with respect to SURF_NODRAW
:
"A hint to the game that this surface's texture
will never actually be used to draw anything.
Origin brushes, clip brushes, trigger brushes,
sky brushes, etc. should all have this set so a texture
is not created at runtime for them." At that time, the
feature was not implemented.
A public introduction was given by Christian Antkow on 19.1.1998. He wrote: Q2 Hint Brushes (a brush with a texture name of "hint") are a way of telling the viser to treat the contents of the brush as a seperate volume. This is particularily usefull in forcing the viser to break the PVS in certain cases.
Enable a feature of Quake II that the designers
at id use frequently, gl_showtris 1
.
This tells Quake 2 to draw all the triangles which
make up a scene. You will probably need a GL card
with a full OpenGL driver implementation in order
to use this feature. This does not work on 3Dfx Voodoo.
This feature is particularily usefull because you can
see where the PVS breaks (if it breaks at all) in any
given area, as well as showing you the complexity of a
scene. By turning on r_speeds 1
and
gl_showtris 1
for the scene in our sample map,
we can see the speed and we can see the
triangles which make up the scene.
In a complex area, you could stick e.g. an 8 unit wide hint brush into the scene. This takes a bit of knowledge of how the vis process works and you have to sort of second-guess the engine as to what it might see from any given spot. There is no "correct" way to do this, you sort of have to go on your gut feeling as to where to place the hint brushes. Christian Antkow provided a small tutorial with screenshots that should be in any decent QE4 or editor manual.
After re-bsping the map with hint brushes in place, we can see how many polygons we have shaved off from a particular view.
According to an explanation by Yahn Bernier, you apply 'hint' to one face (not a whole brush) of a brush that you want to be used as a split plane by QBSP. Apply 'nodraw' to the other faces of the brush. When QBSP looks for split planes, it avoids splitting along the line of the hint plane. Often, the hint plane itself is thus used as a splitter. Placing them all over the place in a map will not do much good. QBSP tries to find the plane that
The place you would probably put a hint brush is covering the opening into a room from a hallway. QBSP does not like any plane that is "almost" parallel to another one for splitting purposes. It will do almost anything to find a better plane.
It grabs all planes in the world and picks one of them to split the whole world with. Then it does the same to each half. You do that until the set of planes you are looking at is a "convex hull." That is a leaf in the Quake bsp tree.
So the hint attribute just tries to tell QBSP not to pick any split plane that will chop up the plane with the hint attribute. Of course, a hint can split another hint, no problem. When QBSP searches for a split plane, it assigns a "value" to each candidate based on whether its a hint plane, an epsilon plane (the parallel thing I mentioned), splits a lot of other planes, or leads to an uneven tree. There is a formula to weight these and find the "best" one.
It is not possible to screw up the compiling procedure by 'incorrectly' placing hint planes. QBSP will not generate a bad output, it only "disfavors" those planes from being split. You will get a slightly different tree, but not an invalid result.
Area portals are ???brushes that notify the qbsp preprocessor that this given part of the map might seal of two adjacent areas completely. Doors are a prime example. This is used to aid the floodfilling used to determine sound propagation and PHS clipping at runtime. Area portals are set manually, during map editng, by ???.
Do area portals also aid PVS processing???
Be frugal with textures, use as few as possible when designing your levels. When your basic architecture is done, go back and add texture accents and other special textures. If it's not a part of the outer shell of your level, or something that is intended to block views, mark everything that juts out into your rooms as Detail (pillars, lights, railings, computers, furniture, etc.). Isolate areas of your map with area portals in doors (remembering that a portalled area has to be completely isolated from other areas ... it can't be portalled at one door and touch the rest of the level unportalled at another). If you have lights or pilasters, or whatever sticking out of your walls, "plaster over" them with clip brushes to make a smooth wall. This will keep players from getting stuck when they try to slide along the walls. If making single player maps, don't put every monster in the map (they take up space in memory). If you have large areas (like the Big Valley in City 1), limit the number of different kinds of entities that you place in that area of the map. When making spot lights, use the info_null as the target (it becomes a fire and forget target ... not remembered by the game). Keep moving brush models to a minimum and make them relatively simple. They add three times their face count in polygons to your views.