Hexen/Modding Tips
Map Design
Notes here are supplemental/errata to the Official Hexen Specs; consult that for additional information.
Sector Specials
Light sequences
Automatic light sequences flow towards the sector tagged with special 2, passing through alternating sectors of type 3 and 4. Beware that attempting to implement forked paths with the automatic specials won’t work in vanilla Hexen, and if the pattern assignment breaks then the game will crash when a player touches one of the unassigned sectors. If you need a light sequence that branches or is otherwise more complex, you’ll have to use the Light_Phased special or an ACS script instead.
Polyobjects
Polyobjects can take some time to fully understand. You may want to start out with a close study of how the Hexen IWAD levels set them up.
A polyobject is formed by creating solid walls (one-sided linedefs) inside a dummy sector which I’ll refer to as the polyobject cage. The properties of this sector don’t matter, only the properties of the linedefs that are to become your polyobject.
While it’s reasonable to allow for some space to grow the map, try to keep the polyobject cage somewhat close to the actual map sectors. The game might experience strange problems if it’s stuck too far out into the void.
Each polyobject is associated with an anchor and a start spot. These are numbered by a special case use of the thing angle property, rather than by the TID system.
The anchor points are placed in the polyobject cage area, often within the polyobject’s void space. The polyobject’s anchor point determines the point that will be placed on the polyobject start spot, and the point that the polyobject will rotate around when rotation specials are used.
The start spots are placed in the actual map space and determine where the polyobject will appear during play. To minimize the possibility of errors, every polyobject start location should be contained in its own rectangular sector, separate from others and encompassing the entirety of the space that the polyobject in question must move within. This is particularly important for pairs of doors. Polyobjects cannot share a subsector, and the map will not load in game if it finds that they do.
Polyobjects don’t have to be convex shapes, though it’s simpler if they are since you can then use Polyobj_StartLine (trigger type 1) and expect it to work without incident. The Polyobj_ExplicitLine (trigger type 5) special exists for the purpose of making non-convex polyobject constructions render correctly; you may have to use a little trial-and-error on the line ordering if you’re making a complex shape, but a general rule of thumb is that the lines on the inside of a concave shape should generally have a higher order number than the lines on the outside.
Polyobject movement
Rotation specials make a polyobject rotate around its anchor point. Rotate right means rotate clockwise, and rotate left means rotate counter-clockwise. Since map special arguments have a maximum value of 255, "byte angles" are used for the angle values. A byte angle of 0 will make the polyobject do a full 360-degree revolution. 64 will result in a 90-degree quarter-turn, 128 results in a half-turn. Finer values also work as expected based on this.
The door swing special is somewhat similar, but it rotates the polyobject counter-clockwise, pauses for the specified delay, then rotates the polyobject clockwise back to its starting position. The maximum valid angle for a swinging door is 128, to do a 180-degree turn. The door swing speed is treated as a signed value, so if you use a value higher than 127 it will actually go clockwise instead of moving any faster. This is a little tricky to arrange since you have to subtract your intended speed value from 256 and use the result for clockwise rotations. If your doors are paired, it’s easier to just move the one you want moving counter-clockwise normally, and let the other one mirror the movement. To do this, set the clockwise-moving door as a mirror of the counter-clockwise-moving door, and have all trigger lines that operate the doors set to activate the counter-clockwise-moving door.
For swinging doors, the polyobject’s anchors should be placed inside the polyobject approximately where hinges would be, near the side of the door adjacent to the wall. A centrally placed anchor will result in a door that rotates like a revolving door instead. When an anchor is placed outside the polyobject itself, the rotation specials will cause it to "orbit" its start spot.
Special Boss Setup
Some of the bosses require special settings to function properly.
Heresiarch
There is just one gotcha when setting up a Heresiarch. Instead of the normal action special with arguments, special actions to be performed upon his death should instead be invoked by putting a script number where you would normally put the number of the action special, and leaving the arguments at zero. This script will be run when the Heresiarch is killed.
The technical reason for this exception to normal thing specials is because the Heresiarch’s internal AI code highjacks the argument slots for its own use.
Death Wyvern
The death wyvern is the most finicky boss to set up. If you do it improperly, you can end up with the wyvern being stuck in place and emitting a continuous, annoying scream, or even worse, the game could hang.
Death wyvern essentials:
- The death wyvern itself. It requires a TID and can be given any action special you want.
- A thing with an identical TID to the wyvern, requiring arguments containing the TID(s) at least one waypoint. It need not be a map spot (Hypostyle uses an ettin).
- Map spots that serve as waypoints. Each should have a unique TID and arguments containing the TID(s) of at least one other waypoint.
A very basic setup would be:
- Death Wyvern: TID = 1, Special/Arguments can be any reasonable action to execute upon wyvern’s death
- Map Spot: TID = 1, Special = 0, Arg1 = 2, Arg2 = 3
- Map Spot: TID = 2, Special = 0, Arg1 = 3
- Map Spot: TID = 3, Special = 0, Arg1 = 2
With this setup, the wyvern will behave as follows: upon waking, fly to the map spot with TID 1 (since that one matches the Wyvern’s own). According to the args on this map spot, randomly choose between the map spot with TID 2 or the one with TID 3 and fly there. The wyvern will then continuously patrol back and forth between map spot 2 and map spot 3 until slain.
The wyvern swoops around in arcs when changing direction but will try to head for the exact position of its target map spot, taking into account its Z height setting as well, you can control its general altitude along the path. Another interesting thing to note: you can make a waypoint map spot reference its own TID as one of the arguments. When following this destination, the wyvern will circle around and back to that spot (may only work for spots that reference solely themselves, not 100% sure).
In addition to thing setup, the death wyvern requires some careful testing and preparation of its area to ensure that it will not get stuck, as it has no awareness of walls in its navigation. Make sure that the path between any two linked waypoints is clear with enough space to spare to account for the swooping movements.
NB: The official specs, and by extension map editing utilities based on them, may erroneously lead you to believe that the map spot destination numbers should be placed in the arguments of the wyvern itself. That is a mistake which will at best result in the arguments being ignored (if you gave the wyvern a TID and a first destination with matching TID) and at worst leave you with a stuck wyvern.
Korax
Korax expects there to be a few special scripts and map settings as well, but unlike the death wyvern he doesn’t necessarily break down totally without them. However, it is theoretically possible for missing Korax scripts to cause a crash under certain circumstances and Chocolate Hexen currently treats missing Korax scripts as a fatal error, although vanilla generally continues running despite this. Lack of the scripts will at best cause error messages to appear at the top of the screen and make things look sloppy to the player, so it behooves you to provide them (plus, it offers many opportunities to give the battle more "oomph", though if you really don’t want them you can always provide dummy scripts in those slots just to prevent the undesirable consequences of missing scripts).
The script numbers used are:
- 249: Run when Korax is injured to below 50% HP.
- 250: Randomly invoked battle script.
- 251: Randomly invoked battle script.
- 252: Randomly invoked battle script.
- 253: Randomly invoked battle script.
- 254: Randomly invoked battle script used only after Korax is below half health.
- 255: Run when Korax is slain.
As with ACS in general, the battle scripts can be used for a variety of effects; the Dark Crucible is set up for such things as setting off traps, calling minions, and altering the battle field. When Korax performs the "attack" where he raises his fist and sends a lightning bolt to the ceiling, a battle script will be invoked. Unfortunately, a well-equipped and skilled player allowed to get near to Korax can often kill him before he gets a chance to do much with these scripts.
Korax is also capable of teleporting himself. There are no adverse effects to not setting this up, but again, it can help add challenge and interest to the fight. Korax will only teleport once his health is below 50%. Once at this point, he can randomly choose from any map spots given a TID of 249 and teleport to one of them.
ACS Scripts
Be aware that vanilla Hexen is not compatible with ZDoom ACS. If you intend to make your mod work with non-ZDoom ports, make sure you compile your scripts to the original ACS format. This can be achieved by using the original ACC released by Raven, or using the separate "compile to Hexen bytecode" button if you’re compiling your scripts from SLADE.
Inserting dummy scripts
Vanilla Hexen maps cannot run without a proper BEHAVIOR lump (a 0-length entry named BEHAVIOR is not enough). If you really do not want to include scripts in your map, you need to run tests on it before scripts have been added, or you want to override a special script requirement (e.g. Korax scripts), a dummy script which performs no action but satisfies the requirement that a script exist may be written like this:
script 1 (void) { }
Displaying Strings
Strings to be displayed by Print or PrintBold in vanilla Hexen should be given in ALL CAPS (the Hexen font uses lowercase letters only, so the in-game message will appear in lowercase.) Strings that contain lowercase letters may be displayed as gibberish.
Spawnables
The following objects can be spawned by script and by the destruction of certain items. DEFS.ACS (included via COMMON.ACS) gives names to them for when you are spawning via script, but for appearing out of breakable objects you can only refer to them by number. The names are generally meant to be easy mnemonics for the object spawned, but some of them are a bit obscure.
Spawned objects in vanilla Hexen cannot be assigned TIDs. This is particularly significant for the "thrust spike" objects which can be spawned, but not subsequently activated/deactivated.
In scripts, these things can be created using the Thing_Spawn, Thing_SpawnNoFog, Thing_Projectile and Thing_ProjectileGravity functions. The number of a particular type of thing currently on the map can also be retrieved using the thingcount function.
You may also place any of these spawnable objects inside some types of breakable scenery. To have an item spawn out of a breakable object (pots, decorative armor), you should leave the breakable object's special at 0 but set its first argument to the spawn number of the item you want it to contain. Certain objects do not behave logically when spawned this way; for instance, projectiles (such as those listed here in the "trap" category), will hang inertly in place and cannot cause damage. Objects that have a significant angle property (e.g. monsters) will spawn facing east.
The pairs 66/97 and 67/99 (permanent small flames and permanent large flames) are redundant spawn numbers. 66 and 97 both spawn the same permanent small flame, while 67 and 99 both spawn the same permanent large flame. Use whichever one you prefer, but ideally pick one or the other and be consistent about it.
The pair of sapphire planets, and the pair of emerald planets, are not interchangeable despite looking identical, and there are actually separate sprite entries for each of them. Puzzle slots will demand a particular sapphire or emerald and won't accept the other one.
The phantom "mash" monsters are special versions of certain normal monsters that are translucent and do not leave corpses when killed. In the core levels, they appear only while fighting Korax, and are spawned by one of his battle scripts.
Using out-of-bounds spawn numbers is not recommended. It might spawn something, but is unlikely to be consistent across varying ports (even vanilla vs. chocolate). The defined numbers range from 0 to 108. Things that do not appear in this table cannot be spawned without port extensions; notably, the Firestorm and Arc of Death weapons and the Brown Chaos Serpent enemy do not have spawn numbers, and many monster projectiles cannot be fired from scripted traps.
Table of spawnable things
Thing | DEFS name | Number | Category |
---|---|---|---|
Nothing | T_NONE | 0 | Nothing |
Centaur | T_CENTAUR | 1 | Enemy |
Slaughtaur | T_CENTAURLEADER | 2 | Enemy |
Green Chaos Serpent | T_DEMON | 3 | Enemy |
Ettin | T_ETTIN | 4 | Enemy |
Afrit | T_FIREGARGOYLE | 5 | Enemy |
Stalker | T_WATERLURKER | 6 | Enemy |
Stalker Boss | T_WATERLURKERLEADER | 7 | Enemy |
Reiver | T_WRAITH | 8 | Enemy |
Reiver (buried) | T_WRAITHBURIED | 9 | Enemy |
Lava ball projectile | T_FIREBALL1 | 10 | Trap |
Blue mana | T_MANA1 | 11 | Item |
Green mana | T_MANA2 | 12 | Item |
Boots of Speed | T_ITEMBOOTS | 13 | Item |
Porkalator | T_ITEMEGG | 14 | Item |
Wings of Wrath | T_ITEMFLIGHT | 15 | Item |
Dark Servant | T_ITEMSUMMON | 16 | Item |
Banishment Device | T_ITEMTPORTOTHER | 17 | Item |
Chaos Device | T_ITEMTELEPORT | 18 | Item |
Dark Bishop | T_BISHOP | 19 | Enemy |
Wendigo | T_ICEGOLEM | 20 | Enemy |
Magic Bridge | T_BRIDGE | 21 | Platform |
Dragonskin Bracers | T_DRAGONSKINBRACERS | 22 | Item |
Crystal Vial | T_ITEMHEALTHPOTION | 23 | Item |
Quartz Flask | T_ITEMHEALTHFLASK | 24 | Item |
Mystic Urn | T_ITEMHEALTHFULL | 25 | Item |
Krater of Might | T_ITEMBOOSTMANA | 26 | Item |
Timon's Axe | T_FIGHTERAXE | 27 | Item |
Hammer of Retribution | T_FIGHTERHAMMER | 28 | Item |
Segment of Quietus (1) | T_FIGHTERSWORD1 | 29 | Item |
Segment of Quietus (2) | T_FIGHTERSWORD2 | 30 | Item |
Segment of Quietus (3) | T_FIGHTERSWORD3 | 31 | Item |
Serpent Staff | T_CLERICSTAFF | 32 | Item |
Segment of Wraithverge (1) | T_CLERICHOLY1 | 33 | Item |
Segment of Wraithverge (2) | T_CLERICHOLY2 | 34 | Item |
Segment of Wraithverge (3) | T_CLERICHOLY3 | 35 | Item |
Frost Shards | T_MAGESHARDS | 36 | Item |
Segment of Bloodscourge (1) | T_MAGESTAFF1 | 37 | Item |
Segment of Bloodscourge (2) | T_MAGESTAFF2 | 38 | Item |
Segment of Bloodscourge (3) | T_MAGESTAFF3 | 39 | Item |
Porkalator trap projectile | T_MORPHBLAST | 40 | Trap |
Grey rubble (larger) | T_ROCK1 | 41 | Effects |
Grey rubble (medium) | T_ROCK2 | 42 | Effects |
Grey rubble (smaller) | T_ROCK3 | 43 | Effects |
Brown clod with green specks (1) | T_DIRT1 | 44 | Effects |
Brown clod with green specks (2) | T_DIRT2 | 45 | Effects |
Brown clod with green specks (3) | T_DIRT3 | 46 | Effects |
Small grey pebble | T_DIRT4 | 47 | Effects |
Green particle | T_DIRT5 | 48 | Effects |
Brown particle | T_DIRT6 | 49 | Effects |
Arrow trap projectile | T_ARROW | 50 | Trap |
Dart trap projectile | T_DART | 51 | Trap |
Dart trap projectile (poisoned) | T_POISONDART | 52 | Trap |
Spiked ball projectile | T_RIPPERBALL | 53 | Trap |
Blue glass shard | T_STAINEDGLASS1 | 54 | Effects |
Yellow glass shard | T_STAINEDGLASS2 | 55 | Effects |
Purple glass shard | T_STAINEDGLASS3 | 56 | Effects |
Green glass shard | T_STAINEDGLASS4 | 57 | Effects |
Gold glass shard | T_STAINEDGLASS5 | 58 | Effects |
Small blue glass shard | T_STAINEDGLASS6 | 59 | Effects |
Small yellow glass shard (1) | T_STAINEDGLASS7 | 60 | Effects |
Small yellow glass shard (2) | T_STAINEDGLASS8 | 61 | Effects |
Small purple glass shard | T_STAINEDGLASS9 | 62 | Effects |
Small green glass shard | T_STAINEDGLASS0 | 63 | Effects |
Serrated blade projectile | T_BLADE | 64 | Trap |
Frost shard projectile | T_ICESHARD | 65 | Trap |
Small flame on ground | T_FLAME_SMALL | 66 | Effects |
Large flame on ground | T_FLAME_LARGE | 67 | Effects |
Mesh Armor | T_MESHARMOR | 68 | Item |
Falcon Shield | T_FALCONSHIELD | 69 | Item |
Platinum Helm | T_PLATINUMHELM | 70 | Item |
Amulet of Warding | T_AMULETOFWARDING | 71 | Item |
Flechette | T_ITEMFLECHETTE | 72 | Item |
Torch (item) | T_ITEMTORCH | 73 | Item |
Disc of Repulsion | T_ITEMREPULSION | 74 | Item |
Combined Mana | T_MANA3 | 75 | Item |
Yorick's Skull | T_PUZZSKULL | 76 | Puzzle item |
Heart of D'sparil | T_PUZZGEMBIG | 77 | Puzzle item |
Ruby Planet | T_PUZZGEMRED | 78 | Puzzle item |
Emerald Planet (1) | T_PUZZGEMGREEN1 | 79 | Puzzle item |
Emerald Planet (2) | T_PUZZGEMGREEN2 | 80 | Puzzle item |
Sapphire Planet (1) | T_PUZZGEMBLUE1 | 81 | Puzzle item |
Sapphire Planet (2) | T_PUZZGEMBLUE2 | 82 | Puzzle item |
Daemon Codex ("O" book) | T_PUZZBOOK1 | 83 | Puzzle item |
Liber Oscura ("A" book) | T_PUZZBOOK2 | 84 | Puzzle item |
Steel Key | T_METALKEY | 85 | Key item |
Cave Key | T_SMALLMETALKEY | 86 | Key item |
Axe Key | T_AXEKEY | 87 | Key item |
Fire Key | T_FIREKEY | 88 | Key item |
Emerald Key | T_GREENKEY | 89 | Key item |
Dungeon Key | T_MACEKEY | 90 | Key item |
Silver Key | T_SILVERKEY | 91 | Key item |
Rusted Key | T_RUSTYKEY | 92 | Key item |
Horn Key | T_HORNKEY | 93 | Key item |
Swamp Key | T_SERPENTKEY | 94 | Key item |
Drop of water | T_WATERDRIP | 95 | Effects |
Temporary small flame | T_TEMPSMALLFLAME | 96 | Effects |
Permanent small flame | T_PERMSMALLFLAME | 97 | Effects |
Temporary large flame | T_TEMPLARGEFLAME | 98 | Effects |
Permanent large flame | T_PERMLARGEFLAME | 99 | Effects |
Phantom Green Chaos Serpent | T_DEMON_MASH | 100 | Enemy |
Phantom Brown Chaos Serpent | T_DEMON2_MASH | 101 | Enemy |
Phantom Ettin | T_ETTIN_MASH | 102 | Enemy |
Phantom Centaur | T_CENTAUR_MASH | 103 | Enemy |
Giant spike (up) | T_THRUSTSPIKEUP | 104 | Scenery |
Giant spike (buried) | T_THRUSTSPIKEDOWN | 105 | Scenery |
Reiver skin drip | T_FLESH_DRIP1 | 106 | Effects |
Reiver chainmail drip | T_FLESH_DRIP2 | 107 | Effects |
Reiver spark drip | T_SPARK_DRIP | 108 | Effects |
Music
There are two ways that Hexen can play background music: from MUS tracks in a WAD or from the Hexen CD. The majority of people (especially nowadays) probaby use the MUS soundtrack, but there are a few things that should be noted regarding the CD. Particularly, only about half of the MUS tracks have CD equivalents, and several of the CD tracks were taken by the storyline/intermission tracks (which are also used in a few levels when playing CD audio).
There are two different methods for assigning the two different types of music. CD tracks are assigned in the MAPINFO lump with a cdtrack # key for each map where # is the CD track number. MUS tracks are assigned in the SNDINFO lump with a $MAP # track key, where # is the map number and track is the name of the MUS lump to play.
For example, to play the Winnowing Hall music on MAP01 in the IWAD, MAPINFO includes:
cdtrack 13
and SNDINFO includes:
$MAP 1 Winnowr
If you wish to include a custom MUS track in a PWAD for vanilla Hexen, bear in mind that it must really be in MUS format; autoconversion of MIDI lumps was a feature added to the Doom engine after the Heretic/Hexen codebase was forked from it.
CD Tracks
Here are the CD tracks and their equivalent MUS name. Track 1 is the data track and should not be played as audio. There is an oddity in Deathkings, in that tracks are sometimes substituted even if the MUS track is available on the CD.
Table of Hexen CD track MUS equivalents
Track Number | MUS name | Deathkings Substitution |
---|---|---|
2 | Jachr | Matches |
3 | Blechr | Matches |
4 | Hexen | |
5 | Orb | |
6 | Hall | |
7 | Chess | |
8 | Cryptr | Matches |
9 | Falconr | 18 (Chap_4r), 10 (Octor) |
10 | Octor | 5 (Orb) |
11 | Rithmr | 19 (Fantar) |
12 | Sixater | 21 (Levelr) |
13 | Winnowr | Matches |
14 | Swampr | 16 (Bonesr) |
15 | Wutzitr | 20 (Foojar) |
16 | Bonesr | 8 (Cryptr) |
17 | Chap_1r | 14 (Swampr) |
18 | Chap_4r | |
19 | Fantar | |
20 | Foojar | 17 (Chap_1r) |
21 | Levelr | |
22 | Simonr | 6 (Hall) |
CD Substitutions
The omitted tracks on the CD leave fifteen MUS tracks that do not have a CD track equivalent, so you may have to select a next-best track out of the ones available on the CD. Both the original campaign and Deathkings substitute CD tracks for these missing pieces in their MAPINFO, but Deathkings substitutes them differently from the main game. "Chartr" exists in the Hexen IWAD but goes unused in both campaigns.
Table of Hexen CD missing track substitutions
MUS name | Beyond Heretic substitution | Deathkings substitution |
---|---|---|
Borkr | 17 (Chap_1r) | |
Chap_2r | 6 (Hall) | |
Chap_3r | 10 (Octor) | 15 (Wutzitr) |
Chartr | ||
Chippyr | 22 (Simonr) | |
Crucibr | 2 (Jachr) | 15 (Wutzitr) |
Deepr | 20 (Foojar) | 15 (Wutzitr) |
Fortr | 16 (Bonesr) | 13 (Winnowr) |
Fubasr | 6 (Hall) | 9 (Falconr) |
Grover | 5 (Orb) | 7 (Chess) |
Percr | 21 (Levelr) | 9 (Falconr) |
Secretr | 14 (Swampr) | |
Stalkr | 9 (Falconr) | 16 (Bonesr) |
Voidr | 19 (Fantar) | 22 (Simonr) |
Wobabyr | 15 (Wutzitr) | 9 (Falconr) |