=========================================================================== The Unofficial Specs for Duke Nukem 3D V1.1 By Vincent Nguyen Quang Do E-Mail : 101366.1734@compuserve.com Compuserve: 101366,1734 =========================================================================== What's new in this version: - Simply the list of all the keywords and their use. I hadn't the time to finish this part when I uploaded the first version. =========================================================================== These specs are highly preliminary, and are far from complete. It requires a certain programming knowledge to understand what I have written, but fortunately, not much. It is not a complete documentation with every aspect of the script language. You have to examine the .CON files for yourself using what you have learned from reading this text. First, with the usual stuff: as stated in the .CON files, Apogee and 3D Realms do not support experiments with these files. To be more precise, here is the official policy : " 3D Realms encourages you to experiment freely with the parameters contained in this file, and to share your discoveries with the rest of the world. However, BE ADVISED that 3D Realms does NOT offer any technical support on how to use this file or other user-modifiable features of this game. Do NOT call 3D Realms or Apogee Software for advice or help with this subject. " Next thing : I cannot be held responsible for any damage to your softwares, hard-drive, computer and anything related to you modifying the .CON files. The bottom line is : MAKE BACKUP COPIES FIRST. Although I don't think it can be damaging to your hard drive to modify these files, who knows ? At last, you can distribute this file around you, unless someone from 3D Realms or Apogee tells you you can't anymore (they didn't tell me I was violating some kind of copyrights, so I assume I can write this specs). Finally, I am french, so please forgive me for any bad written english. No let's start with the fun part. =========================================================================== PART I : The Basics As many of you probably noticed, when you start Duke Nukem 3D, the game starts a compiler that takes as paramater the GAME.CON file in the game directory. The very beginning of this GAME.CON file is the following : include defs.con include user.con These files are processed first. The defs.con contains all the " define " for the game. The user.con contains all the parameters that can be changed in the game (damage for each weapon, hitpoints for each monster and so on...). So, we're going to start with the DEFS.CON file. This file is simply a bunch of define. Five types of objects are defined in this manner : the sprites (more on this later), the basic actions, the weapons, the different player actions, the inventory action and the sound effects. Ex : define FIRSTGUNSPRITE 21 define CHAINGUNSPRITE 22 define RPGSPRITE 23 define FLAMETHROWERSPRITE 24 <= The sprite for the flamethrower . . . define face_player 1 <= The basic actions used in the AI code define geth 2 define getv 4 . . . define KNEE_WEAPON 0 <= Weapon definition define PISTOL_WEAPON 1 define SHOTGUN_WEAPON 2 define CHAINGUN_WEAPON 3 define RPG_WEAPON 4 define HANDBOMB_WEAPON 5 define SHRINKER_WEAPON 6 define FLAMETHROWER_WEAPON 7 define TRIPBOMB_WEAPON 8 define pstanding 1 <= Stands for Player Standing define pwalking 2 define prunning 4 define pducking 8 define pfalling 16 . . . define GET_STEROIDS 0 <= Inventory Actions define GET_SHIELD 1 define GET_SCUBA 2 define GET_HOLODUKE 3 . . . define KICK_HIT 0 <= Sound Effects define PISTOL_RICOCHET 1 define PISTOL_BODYHIT 2 define PISTOL_FIRE 3 define EJECT_CLIP 4 define INSERT_CLIP 5 The USER.CON file for itself contains all the user modifiable stuff and is not very interesting in itself (unless someone tells me he doesn't understand anything in the file, I won't write anything more about this one). Now for the hard part, and what you've all been waiting for: the GAME.CON file This file is primarly used by DN3D for it's monsters' AI. Modifying this file allows for VERY interesting effects, such as creating new monsters ; modifying weapons effects, monsters' behavior and so on... Let's start with the basic notion of the game.con file : the " actor ". action RUBCANDENT 1 1 1 1 1 action RUBCAN actor RUBBERCAN WEAK ifaction RUBCANDENT { ifactioncount 16 { strength 0 action RUBCAN break } } else ifhitweapon { ifwasweapon THROWFLAME { state rats ifrnd 48 spawn BURNING debris SCRAP3 4 /* spawn EXPLOSION2 */ killit } else ifwasweapon RADIUSEXPLOSION { state rats ifrnd 48 spawn BURNING debris SCRAP3 12 /* spawn EXPLOSION2 */ killit } else action RUBCANDENT } enda Every object, monster, in short anything Duke can deal with is an actor. This actor is referenced in the map by a number. This number is related to the bunch of define in the DEFS.CON file. For example, the basic trooper is referenced by the LIZTROOP keyword, which in itself is defined as the integer 1680 (see the DEFS.CON file). Not only is this number used for identification purpose, but also for display purpose. You'll understand more about this later. So, let's suppose you have the actor RUBBERCAN in the map. The above definition shows that this RUBBERCAN has 5 hitpoints (keyword WEAK). The code within the " actor " and the " enda " keywords defines how the RUBBERCAN is handled, in this particular case when it is hit by a weapon. This code is always running as long as the actor is " alive ", namely it hasn't been killed with the " killit " keyword. Now, what can we do with this actor, and how are the sprites displayed ? If you take a look at the code above, you can see the keyword " ifaction ". Still above, there are two action definitions. In fact, an " action " in the .CON files is simply a sequence of sprite. As long as the " actor " is performing an action, these sprites are displayed in sequence. This speed as well as the different sprites of this sequence are determined by the numbers after the action name. I'll explain this later with a more interesting actor. We have here the basic notions of the GAME.CON file. Every object, monster, is an ACTOR which is performing a certain ACTION. These ACTION are related to the sprites displayed. With the " ifaction " and the " action " keywords, the game knows what action the actor is performing, and can change this action. On the above example, the script performs the following: If the RUBBERCAN is performing the RUBBERCANDENT action, then it counts how many times (" ifactioncount ") this action has already been processed. If this number is below 16, it does nothing and runs again. If this number is 16, then it calls the action RUBCAN. The action RUBBERCANDENT is associated to the sprites showing the RUBBERCAN with a small dent. The action RUBCAN is the standard RUBBERCAN. If the action is not RUBBERCANDENT, then the script tests if the RUBBERCAN was hit by a weapon, and what kind of weapon. If this is an explosion or a flamethrower, then the RUBBERCAN is destroyed. Else, the scripts performs the RUBBERCANDENT action. This the very first example of what can be done with the programming language. One could for example spawn an ammo if the rubbercan is destroyed. Or you could generate a particular sound. Or spawn rats. Or anything else comes to mind (almost...) Let's take a deeper look into the action keyword (you can find the following line in the game.con file) : action ATROOPWALKING 0 4 5 1 12 As stated before, each monster is referenced by a number. For the basic trooper, this number is 1680, for the PIGCOP it is 2000. The keyword action allows to define what frames are to be displayed when an actor is performing a certain action. The first number is the RELATIVE first frame number. Why relative ? Simply because to obtain the real sprite number, you have to add the actor number, here 1680. The second number is the number of frames to be displayed, the third is how many frames you actually skip between each frame you actually display, the last is a multiplier (allowing to cycle through the frames backward), and the last is a tempo. Hence, the above action would display the relative frames 0,5,10 and 15 (add 1680 to have the absolute frame). Note that this is a 3D Sprite, so if you turn around the monster, you can see different orientation, without changing anything in the script. action ATROOPWALKINGBACK 15 4 5 -1 12 This action would display the frames 15,10,5,0. The other important keywords are " move ", " ai " and " state ". move allows to define a speed : " move TROOPWALKVELS 72 " ai allows to associate an action with a subroutine and a basic action : ai AITROOPSEEKENEMY ATROOPWALKING TROOPWALKVELS seekplayer ATROOPWALKING is the action TROOPWALKVELS is the speed seekplayer is a basic action, directly performed by the program, without the help of the script. It allows the actor to "move" toward the player without having to program everything. As for the "state" keyword, we have to examine more closely the actor : actor LIZTROOP TROOPSTRENGTH ATROOPSTAND state troopcode enda LIZTROOP is the actor number (1680), TROOPSTRENGTH is the hitpoints, ATROOPSTAND is the first action performed by this actor. The keyword "state" calls a kind of sub-routine, defined in the scripts between the "state" and "ends" keywords. Here is the code for this state : state troopcode fall <= drop an actor to the floor ifinwater ifrnd 1 spawn WATERBUBBLE ifaction ATROOPSTAND { ifrnd 192 ai AITROOPSHOOTING else ai AITROOPSEEKPLAYER } If the action is ATROOPSTAND, then there is one 3 chances on 4 (192 out of 256) that the actor will perform the AI subroutine AITROOPSHOOTING. Otherwise it performs the AI subroutine AITROOPSEEKPLAYER. ifinwater ifrnd 4 spawn WATERBUBBLE else ifaction ATROOPPLAYDEAD { ifhitweapon { ifwasweapon RADIUSEXPLOSION { sound SQUISHED state troop_body_jibs state standard_jibs state delete_enemy } break } else state checksquished ifcount PLAYDEADTIME { addkills -1 soundonce PRED_ROAM cstat 257 strength 1 ai AITROOPSHOOTING } else ifp pfacing resetcount break } If the trooper is playing dead, we still have to check wether the lying body is destroyed by an explosion or is squished. If the PLAYDEADTIME is over, then trooper rises again (with a strength of 1) and tries to shoot the player. else ifaction ATROOPDEAD { strength 0 ifrespawn ifcount RESPAWNTIME { spawn TRANSPORTERSTAR cstat 257 strength TROOPSTRENGTH ai AITROOPSEEKENEMY } ifhitweapon { ifwasweapon RADIUSEXPLOSION { sound SQUISHED state troop_body_jibs state standard_jibs state delete_enemy } break } else state checksquished break } If the trooper is dead, then it"s strength is zero. The program checks if the respawn flag is set, and if so let the trooper rise again. If the body is hit by an explosion, then it is definitely "killed" else ifaction ATROOPSUFFERDEAD { ifactioncount 2 { ifrnd 64 { resetcount action ATROOPPLAYDEAD } else { soundonce PRED_DYING action ATROOPDEAD } } } This action is performed when the trooper is shot and is on the ground in pain. It has 3 chances out of 4 to die, and one chance to play dead (see above) else ifaction ATROOPDYING { state troopdying break } else ifaction ATROOPSUFFERING { state troopsufferingstate ifhitweapon state checktroophit break } else ifaction ATROOPFLINTCH { ifactioncount 4 ai AITROOPSEEKENEMY } else { ifai AITROOPSEEKPLAYER state troopseekstate else ifai AITROOPJETPACK { state troopjetpackstate soundonce DUKE_JETPACK_IDLE } else ifai AITROOPSEEKENEMY state troopseekstate else ifai AITROOPSHOOTING state troopshootstate else ifai AITROOPFLEEING state troopfleestate else ifai AITROOPFLEEINGBACK state troopfleestate else ifai AITROOPDODGE state troopseekstate else ifai AITROOPDUCKING state troopduckstate else ifai AITROOPSHRUNK state troopshrunkstate else ifai AITROOPHIDE { state troophidestate break } } ifhitweapon state checktroophit else state checksquished ends The rest is simple and deals with the other action the trooper can perform. What you must remember : An action is simply a list of frames. You can make the actor do something based upon this action, but it doesn’t act on itself. You have to explicitely use the "ifaction" keyword. On the other hand, an AI seems to allow a certain degree of freedom for the actor : it does act on it's own... You can however have the actor perform something more difficult by using the "ifai" keyword. Note that this is purely an hypothesis. Now, let's go back to the frames. The following action is performed when the trooper is effectively dead. action ATROOPDEAD 54 The relative frame number number 54 (add 1680 for absolute). What happens if you change this number ? Well, for example, try 320. Instead of a dead body, you have a standing pigcop. Remember the PIGCOP number (2000) ? And 2000-1680 happens to be 320... So every sprite can be accessed with this method. In fact, remember that even if a body is lying on the ground, it still an actor with it's own action and script. I think that the game deletes old actor when there are too much actor in the level. You could also replace the above with action ATROOPDEAD 0 4 5 1 12 You would have a walking corpse ! What I can tell you is now to experiment. As you can see, it isn’t a very hard language. A few comments though : it seems there are no variables, the "ifpdistl" keyword stands for ifplayerdistancelower, and the "ifpdistg" stands for iplayerdistancegreater (took a while to figure that one out !) =========================================================================== Part II : Keywords Now that you understand the basic concepts of the script language used in Duke Nukem, here is a list off all the keywords with their associated effects (at least, those I understood). I hope I won't forget any. If you find a mistake, do not hesitate to contact me ! This list is the result of many trials and errors, and as such is prone to mistakes. I do not claim to have the supreme knowledge. That said, let's go back to our list. When necessary, I used the <...> and [...] symbols. What's between the first symbols seem to required, while what's between the second symbols is optional. action ... Define an action and it's associated sprites (see above). You can also use this one within a code to change the actor's current action. actor [actioname] [ai ainame] [statename] If you define more the ai and/or the state performed, you must end your list with the keyword "enda" addinventory Guess what ? The list of available objects is in the defs.con file. addphealth can be negative. This allows to substract health from the player (used in the Octabrain code) break Forces the program to exit the current function. This allows to bypass some code if certain requirements are met. cactor Call an actor. In fact, it only calls the actor code not the actor definition (action, ai, and so on) This can be used for example when creating multiple monsters with the same basic design: you create the actors which in turn call the basic actor. To understand this better, I suggest you take a look at the alien trooper definition. cstat Change the actor status. I don't have all the numbers but here is what I know: 32768 hides the sprite (not sure) 257 ressurects the actor once more 0 kills it debris Spawns flying debris. The kind of debris is defined by debris_name, the number by... endoflevel This is used when you want the script to end the level. Usually when you kill a boss. The number following it is unknown to me however. Maybe a tempo ? else I think this one is quite clear. fall Apparently, this instantly drops the actor to the floor. However, I am not quite sure about this one. globalsound What's the difference with "sound" and "soundonce" ? I don't know. guts Spawns flying guts. The difference with the above is not obvious. hitradius This generates an explosion. Damage to monsters and other players depends of the distance between the explosion and these actors. Damages range from strength1 to strength4. ifai Checks whether the actor's current ai is ai_name ifaction Same thing for the action ifactioncount When called from within an action, counts the number of times this action has been processed and then checks whether it is greater than the number. If so, it performs the following code. ifcansee Checks whether the actor can see another actor. ifcanseetarget Apparently used for inventory items. This checks whether the player can see the actor. ifcanshoottarget Checks whether the actor can shoot the player. This usually used with the "ifcansee" keyword. ifceilingdistl Stands for "If Ceiling Distance Lower Than" ifcount This one was quite hard to figure out. Apparently, it checks whether the time is greater than the number. It allows to check whether an actor was kept in a certain action. Ex: state genericshrunkcode ifcount 32 { ifpdistl RETRIEVEDISTANCE pstomp } ... When a monster is shrunk, it can be squished by the player if he steps on it. Raising the number allows the monster to survive longer, as the player must stay longer within RETRIEVEDDISTANCE to stomp it. ifdead Checks whether the actor has been killed. iffloordistl Stands for "If Floor Distance Lower Than" ifgapzl I wasn't able to figure that one out... I initially thought it was "if gap in z coordinate is lower than" but it doesn't fit with the main game.con file. Any hint would be appreciated... ifhitspace Checks whether the player hit the spacebar. Allows to run a special code in some instances. Ex: when there is a water fountain, Duke can drink and gain 1 health point. This is performed in the game.con file ifhitweapon If the actor was hit by a weapon, then performs the following code ifinwater Checks whether the actor is underwater ifmove Checks against what is the speed of the current actor. ifonwater Checks whether the actor is swimming at the surface ifp Checks something on the player ifpdistl Stands for "If PLayer Distance Lower than" ifpdistg Do you guess that one ? ifphealthl Stands for "If Player Health Lower Than" ifrespawn Checks whether the respawn flag is set. ifrnd Generates a random number and checks whether it is lower to the following number. If so, it executes the following action. Ex: ifrnd 192 ai AILIZGETENEMY If the random number is lower than 192, then the actor changes it's ai state to AILIZGETENEMY. ifspawnedby When you use the "spawn" keyword within an actor, the spawned actor keeps in mind what spawned it (!!!). You could use this to set up traps for players (such as having the ammo dropped by a trooper detonate if a player tries to pick it up...) ifspritepal Checks whether the sprite palette is ifsquished Apparently, this checks whether the actor is squished. However, since I don't know what squishing means in the game... ifwasweapon I think you already guessed that one ? Available weapon_type are: RPG THROWFLAME SHRINKSPARK RADIUSEXPLOSION FIREEXT (??) KNEE FIRELASER COOLEXPLOSION1 (Octabrain Shot) SHOTSPARK1 Maybe some more, but they were not used in the main script. You could also try the following: SHOTGUN FIRSTGUN ??? include As stuff said, includes another file killit Erase the current actor from the game. The script for this actor no longer runs. move [no] Define a speed. Is mainly used with the "ai" keyword. However, you can also use it within a state function. no is a number that defines a special action performed by the actor. Ex: move TURRVEL face_player This moves the actor at speed TURRVEL so that it faces the player. The list of possible action is in the defs.con file. operate I don't know what this one is used for. It probably allows an actor to operate a switch, a door or an elevator. However, I've never seen a monster performs such a thing. Maybe because when they don't see the player, they are as dumb as a wall. palfrom I must say I don't know how this one works... palive Checks whether the player is alive. It must be used in cunjunction with the "ifp" keyword. The other options are: pstanding, pwalking, ... See in the defs.con file pstomp This one is funny. It is used in the genericshrunk code (see above). It shows the player stomping on a monster. You can usually use it with very small monsters, although it can be also done on big ones, if you can manage to climb on one (g). resetactioncount Reset to Zero the number of times this action has been processed. resetcount Reset to Zero the time check used by "ifcount" shadeto This in fact dim or brighten the actor, depending on the value of . -127 renders (temporarly) the actor black, while 127 renders it white. However, I must confess I didn't very good results. It seems there is a palette involved, and I couldn't determine the relationship. The best example is in the Octabrain code (JELLYFISH in the script). shoot The actor shoots with a weapon. Weapon types include the following: FIRELASER RPG FLAMETHROWER MORTER CHAINGUN SHOTGUN There might be some others... sizeto This one is fun. It shrinks the actor at nx/256 and ny/256 it's original size. So, if you take 512, you would double the size of the actor. However, there are a few things to keep in mind when using this. First, the shrinking is not instantaneous. It takes a while until the corresponding code is performed. If you want to shrink an actor, you must always use the "ifaction" keyword in order to wait before it does anything else. Try it and you'll understand. Second, it seems you must periodically resize the actor. If you use this keyword just once, then it will revert back to it's original size a while after. sound Generates a sound. The list of available is in the defs.con file (I think) soundonce Also generates a sound, but only once (?) spawn Spawns an actor. Note that not every sprite is spawnable. It must somehow have been defined as spawnable... So you can only spawn existing actor and cannot define new ones (using a number above 4000 for example, as might be hinted by looking at the defs.con file) spritepal Change the palette sprite to the palette no. Usually, an actor is always referenced by a number. But there is also a palette that can be used for multiple monster definition. For example, the trooper captain (with the teleporter) is a standard trooper with a different palette. You can test what palette the current actor has with the "ifspritepal" keyword. state ... ends Can be used in two manner: Defining the function and it's code Calling the function. It must have been defined before You don't have to use the "ends" keyword when calling a state. strength Defines the actor's strength. Note that you must also change the status of the actor using the "cstat" keyword if you want to ressurect a monster (see below) quote This displays the quote number .See in the defs.con file for more information. Well, I think that's all. I hope I didn't forget anything. If so, please forgive me and give me a call so I can correct my mistake. Any information on any of the missing features would be greatly appreciated. =========================================================================== Part III : Some Examples Why not start with some examples ? Well, you noticed that sometimes a trooper drops a pistol ammo. Here is the code : state checktroophit ifaction ATROOPSUFFERING { stopsound LIZARD_BEG sound PRED_DYING cstat 0 strength 0 action ATROOPSUFFERDEAD break } ifdead { state drop_ammo <= Here. It calls the drop_ammo state state random_wall_jibs addkills 1 Well, you could have it drop a TRIPBOMB, no ? Just add the line "spawn TRIPBOMBSPRITE" and there you go, you can pick up a trip bomb. Unfortunately, there is no sprite for it in the shareware version, so don’t be surprised if you don't see it, it's normal. Furthermore, you can use it only as long as you don't switch to another weapon, because I didn't find a way to reselect it. You can try all the weapons in this manner (SHRINKERSPRITE and FLAMETHROWERSPRITE). You could also have it respawn another monster... such as a PIGCOP, or a JELLYFISH (Octabrain), or... a BOSS1 (aaaaargh...). Better still. You can replace all the basic trooper in the game with a PIGCOP. All you have to do is define the PIGCOP ("define PIGCOP 2000" in the DEFS.CON file) as number 1680. Of course, you still get the frames for the trooper, but it would act like a PIGCOP, and shoot with a shotgun... If you want to have the correct frames, you have to modify the pigcop's actions and add 320 to each basic frame. But remember that this only deals with the standard standing trooper. Ducking trooper and flying trooper would remain the same. Until they hit the ground... There are tons of things to experiment. If I have the time, I'll upload a few of our creations (a friend and me). What I need know is FEEDBACK as well as some information on what I didn't quite figure out in the language. So if you think you discovered something that would be of interest, feel free to write me a little E-mail. If you have any question, I can be reached at the following E-Mail adress : 101366.1734@compuserve.com Have fun ! Vincent Nguyen Quang Do