Tutorial Scope
This tutorial will show how to code an enemy for EDuke32 in the CON scripting language by walking through a series of general steps that build up a typical enemy from scratch.
We already have a wiki that documents all the CON commands with some examples of how to use them, so I'm not going to dwell on how to use individual commands. However, aspiring CON coders (maybe as many as a dozen in the last 10 years!) sometimes get overwhelmed with the giant list of commands and don't know where to start.
Assumptions
I assume that you have a basic working knowledge of EDuke32 and Duke Nukem 3D: you know what a CON file is, you know what Mapster is, you know what a tile number is, and various other basic facts (trying to list them all would be a project in itself).
I assume that the art for your enemy is already accessible in game. This means that you can go into Mapster and you can view the tiles for the enemy. I also assume that the tiles have been arranged properly for coding. This means that there is a series of animations with various frames and angles, and they are in an order corresponding to some viewtype useable by the action command. In most cases this will be viewtype 5, which is used by pigcop, lizman, etc.
I assume that you have the ability to open up CON files, create them and edit them using the editor of your choice.
Ok let's go!
STEP 1: SOME DEFINITIONS AND FLAGS
Your enemy is going to be an "actor", and the actor is identified with the tile number you define it on. A definition is just a label that you can use in place of a number. In most cases it makes sense to define your enemy as the first tile of its walking animation. If you plan to have a stayput version of the enemy, it is traditionally defined on the very next tile (see the DEFS.CON from the base game). If there are other actors associated with your new enemy and they are not yet defined in the game, then you should define them as well. It's also a good idea to define the health amount(s) for your new enemy -- you could just type in the numbers directly in the actor declaration, but using labels makes it easier for people to edit.
In my case I have this to start with:
define LIZBOSS 5185 define LIZMINE 5246 define LIZBOSSSTRENGTH 5000 define LIZBOSSPALSTRENGTH 1000
Depending on how your files are organized, you might want your definitions for different types of things to be in different files. In the base game, you will find the tile number definitions in DEFS.CON, and you will find enemy strength amounts in USER.CON.
An important note about hitboxes: The hitbox dimensions of your enemy will be determined by the dimensions of tile that you define it on. The cyber-enforcer tile 5185 has dimensions 116x115, which makes the hitbox essentially a square. The square will be bigger or smaller depending on how large or small the enemy is in game (xrepeat and yrepeat values), but those dimensions will not change depending on the animation displayed. If there is an animation where the enemy is lying down, it will have exactly the same hitbox as when it was standing up. This has important implications that will warrant more discussion, but for now let's move on.
Spriteflags
There are special flags we can set on our tiles to globally change how those tiles behave. This is done via the spriteflags command. These flags should be set after the definitions but before the actor code. Here is what I am using for the cyber enforcer:
spriteflags LIZBOSS 5251072 // HURTSPAWNBLOOD + NODAMAGEPUSH + SMOOTHMOVE
STEP 2: ACTIONS AND ANIMATION TEST
The action command has two distinct uses. When used outside of an actor declaration, it defines an animation. When used inside an actor declaration, it causes the actor to initiate the animation of the same name that had been defined earlier. The first thing I do when starting to code an enemy is define all of its animations. The next thing I do is write a simple block of code that will enable me to easily view them all in game. This step is crucial, because I want to make sure that all of the animations are correct before going forward. Any flaws in the frames, animation definitions, speed and offsets will become obvious. If you skip this step and go straight to making the enemy move around, shoot etc., then it will be harder to notice and correct animation problems.
Here is the code that I made for my animation test:
action ALIZBOSSWALKING 0 4 5 1 22 action ALIZBOSSRUNNING 0 4 5 1 14 action ALIZBOSSSTARTSHOOT 20 1 5 1 20 action ALIZBOSSSHOOTING 25 2 5 1 5 action ALIZBOSSFLY 35 1 5 1 10 action ALIZBOSSFALL 35 1 5 1 10 action ALIZBOSSSTAND 40 1 5 1 10 action ALIZBOSSSQUAT 45 1 5 1 60 action ALIZSQUATDONE 50 1 5 1 30 action ALIZBOSSFROZEN 50 1 5 1 10 action ALIZBOSSFLINTCH 55 1 1 1 10 action ALIZBOSSDYING 55 6 1 1 20 action ALIZBOSSLYINGDEAD 60 1 1 1 10 useractor enemy LIZBOSS LIZBOSSSTRENGTH fall ifaction 0 { action ALIZBOSSWALKING ifspritepal 0 sizeat 96 80 else sizeat 48 40 } ifhitspace ifcount 10 { resetcount ifaction ALIZBOSSWALKING action ALIZBOSSRUNNING else ifaction ALIZBOSSRUNNING action ALIZBOSSSTARTSHOOT else ifaction ALIZBOSSSTARTSHOOT action ALIZBOSSSHOOTING else ifaction ALIZBOSSSHOOTING action ALIZBOSSFLY else ifaction ALIZBOSSFLY action ALIZBOSSSTAND else ifaction ALIZBOSSSTAND action ALIZBOSSSQUAT else ifaction ALIZBOSSSQUAT action ALIZSQUATDONE else ifaction ALIZSQUATDONE action ALIZBOSSFLINTCH else ifaction ALIZBOSSFLINTCH action ALIZBOSSDYING else ifaction ALIZBOSSDYING action ALIZBOSSLYINGDEAD else ifaction ALIZBOSSLYINGDEAD action ALIZBOSSWALKING } enda
At the top we have the action declarations which define the animations. Below that is the actor declaration and code, followed by enda to signify the end of the actor's code.
The first block after the useractor declaration detects when the actor has no assigned animation (action) and assigns it the walking animation. The next block will cycle from one animation to the next if the player presses the action key and it has been 10 tics since the last press. With this done, you can place the enemy sprite in a map or spawn it from the console in game, then walk around it and inspect how the animations look. The result can be seen in the video below.
What I found is that JBlade had already done a good job of setting offsets on the tiles, which is why you don't see the cyber enforcer wiggling oddly left, right, up or down. I did find a few pink pixels in the lying dead frame, which I then fixed (if you look carefully you can see the pink pixels in the video).
A few notes about defining actions: It is normal to have multiple actions which use exactly the same frames of animation. Typically, walking and running are identical except for the final parameter of the action, which determines animation speed (the lower the number, the less lag between frames). It is also normal to have multiple actions which are identical in every way except for having different names. This is handy because they can be used when you assign them and check them. "ifaction ALIZBOSSFLY" will have a different value than "ifaction ALIZBOSSFALL" even though what the actions display is identical. As a rule of thumb, if an animation is going to be used for more than one purpose, then go ahead and define it multiple times with different actions corresponding to the different purposes. If it turns out you didn't use them all, you can always delete them later.