CREATING YOUR OWN PIGPORT.CON FILE

After you read chapter 12, Editing CON Files, in the book, you will 
know all the basics for coming up with some cool ideas for new actions by the 
Duke Nukem 3D enemies. One that I came up with was to make the pig cops 
teleport in and out like the assault troopers sometimes do. Here is a list of the 
things I needed to modify to make the pig cop accomplish this new action.

1. Three new actions, APIGABOUTHIDE, APIGHIDE, and APIGREAPPEAR.

2. A new ai definition, named AIPIGPORT

3. Changing the existing pig cop code to teleport occasionally

4. A new state code block to handle the teleporting in and out (I named 
this state pigportstate)

Let's go through each of the new parts of PIGPORT.CON file in detail, 
to see how each part interacts with the existing code to create a new function 
for the pig cop.

1. Create actions
The pig cop's teleporting is actually broken up into three smaller 
subtasks these are: a) the pig cop stopping any previous action (a few frames 
of not moving), b) the pig cop disappearing, and 3) the pig cop reappearing. 
Each of these subtasks needs its own action definition, which gives us the 
sprite sequence used when the actor is performing each subtask.
I first gave each of these actions their own names, APIGABOUTHIDE 
for the rest action, APIGHIDE for the disappearing action, and 
APIGREAPPEAR for the reappearing action. These names follow the naming 
convention used throughout the CON files:  action names usually start with the 
letter A, followed by the monster's name, and then a word or two to describe 
the action.

By coincidence, all three of the actions I needed for this task are 
defined the same way. Because I couldn't find a more suitable sprite sequence 
for the pig cop teleporting, I chose a simple one-frame standing sequence for 
all three actions. They are defined as follows.

action APIGABOUTHIDE  30 1 5 1 25
action APIGHIDE       30 1 5 1 25
action APIGREAPPEAR   30 1 5 1 25

These three actions will all play frame 2030 (30 added to the pig cop 
base frame 2000) for 25 ticks. I could have used the same action for all three 
subtasks, but I wanted to define three separate actions with three separate 
names to enhance the readability of the code.

2. Create the ai
Actions by themselves are simply a sequence of sprites. To get the 
actor to move in a certain manner, you need to connect the action to a type of 
movement using the ai keyword. Here is the line for our new ai, named 
AIPIGPORT:

ai AIPIGPORT APIGABOUTHIDE PIGSTOPPED faceplayer 

The start action for this ai is APIGABOUTHIDE. The movement is 
PIGSTOPPED (not moving), and the basic ai type is faceplayer. This ai 
command merely stops the pig cop in his tracks and causes him to face toward 
the player.

3.  Change the Existing Pig Cop Actor Code
The main actor block of the pig cop handles all of his possible actions. 
There is a large block that checks which ai the pig cop is currently performing. 
This block appears as follows:

...
        ifai AIPIGSEEKENEMY state pigseekenemystate
        else ifai AIPIGDODGE state pigseekenemystate
        else ifai AIPIGSHOOTENEMY state pigshootenemystate
        else ifai AIPIGFLEEENEMY state pigfleeenemystate
        else ifai AIPIGDIVING state pigdivestate
        else ifai AIPIGCHARGE state pigseekenemystate
        ifhitweapon state checkpighitstate
        ifrnd 1
        {
            ifrnd 32 soundonce PIG_ROAM
            else ifrnd 64 soundonce PIG_ROAM2
            else soundonce PIG_ROAM3
        }
        
...

All I have to do here is add a check for the new ai, AIPIGPORT.  If the 
pig cop is performing AIPIGPORT, then I will call a new block of code, state 
pigportstate. The resulting code has only one line added, which is highlighted 
in boldface in the following block:

        ifai AIPIGSEEKENEMY state pigseekenemystate
        else ifai AIPIGDODGE state pigseekenemystate
        else ifai AIPIGSHOOTENEMY state pigshootenemystate
        else ifai AIPIGFLEEENEMY state pigfleeenemystate
        else ifai AIPIGDIVING state pigdivestate
        else ifai AIPIGCHARGE state pigseekenemystate
        else ifai AIPIGPORT   state pigportstate         
        ifhitweapon state checkpighitstate
        ifrnd 1
        {
            ifrnd 32 soundonce PIG_ROAM
            else ifrnd 64 soundonce PIG_ROAM2
            else soundonce PIG_ROAM3
        }

Now, all I need to do is find a good spot to set the pig cop's ai to 
AIPIGPORT. I decided that I would make the pig cop teleport after being shot. 
There is already a nice block of code handling when the pig cop gets shot: This 
block of code is named checkpighitstate.  The first part of this state handles 
the pig cop dying, and the second part handles when he is shot but doesn't die. 
I added the last line of this state else ai AIPIGPORT to make the pig cops ai 
my new AIPIGPORT, only if the other ifrnd conditions above it fail.

state checkpighitstate
    spawn BLOOD
    ifdead
    {
        addkills 1
        state random_wall_jibs
        ifrnd 16 spawn SHIELD else state drop_shotgun
        ifwasweapon RADIUSEXPLOSION { sound SQUISHED state 
standard_jibs state delete_enemy }
        else ifwasweapon RPG { sound SQUISHED state standard_jibs 
state delete_enemy }
        else ifwasweapon THROWFLAME spawn BURNING

        ai AIPIGDYING sound PIG_DYING
    }
    else
    {
        sound PIG_PAIN
        state random_wall_jibs
        ifwasweapon SHRINKSPARK ai AIPIGSHRINK
        else ifwasweapon THROWFLAME quote 14
        else ifrnd 64 ai AIPIGHIT
        else ifrnd 64 ai AIPIGSHOOTENEMY
        else ifrnd 64 { ai AIPIGDIVING action APIGDIVESHOOT }
        else ai AIPIGPORT                                      
    }
ends

4. Create a New state Code Block to Handle Teleporting
In the last step, I called a new state, pigportstate, whenever the 
pig cop is performing the new ai. I now need to create the code for 
pigportstate. This state has to come before any calls to it because if you 
call a state that you havent defined until later in the CON file, Duke 
Nukem 3D won't run properly. The code for pigportstate is as follows:

state pigportstate
    ifaction APIGREAPPEAR
    {
        ifactioncount 2 { 
                        sound TELEPORTER 
                        ifrnd 128 ai AIPIGFLEEENEMY else ai AIPIGSEEKENEMY
                        cstat 257 
                         }
        else { sizeto 48 40 sizeto 48 40 sizeto 48 40 sizeto 48 40 spawn FRAMEEFFECT1 }
    }
    else ifaction APIGWALK
    {
        ifceilingdistl 48 break
        ifp pfacing break
        ifgapzl 64 { } else 
        ifawayfromwall { spawn TRANSPORTERSTAR 
                         action APIGREAPPEAR move 0 
                         break 
                        }
        
    }
    else ifaction APIGHIDE
    {
        ifactioncount 2
        {
            spawn TRANSPORTERSTAR
            sound TELEPORTER
            action APIGWALK move PIGWALKVELS faceplayersmart
            cstat 32768 
        }
        else
        {
            sizeto 0 40
            sizeto 0 40
            sizeto 0 40
            sizeto 0 40
            spawn FRAMEEFFECT1
        }
    }
    else ifaction APIGABOUTHIDE ifactioncount 2 { action APIGHIDE 
cstat 0 }
ends

I'll break down this entire state to show you what's going on. The main 
code is broken into four ifaction tests. Three of these four actions correspond 
to the three new actions I created as the first step of this process. If you look 
back to the ai definition of AIPIGPORT, you'll notice that the start action for 
this ai is AIPIGABOUTHIDE. The last line of the state block handles this 
action.  It reads as follows:

    else ifaction APIGABOUTHIDE ifactioncount 2 { action APIGHIDE cstat 0 }

In other words, if the action is APIGABOUTHIDE, and the action 
count for this action is 2, then I switch the action to APIGHIDE. If the action 
count is not 2, then nothing additional happens. (Internally, an actioncount 
variable for this action is increased by 1. Because the actioncount variable 
starts at 1 every time an action is started, this line of code will execute twice. 
The first time the actioncount will be 1. The second time it will be 2, and the 
{action APIGHIDE cstat 0} block will be executed).
After two ticks of action APIGABOUTHIDE, the actor switches to 
action APIGHIDE. The code for this action is as follows:

    else ifaction APIGHIDE
    {
        ifactioncount 2
        {
            spawn TRANSPORTERSTAR
            sound TELEPORTER
            action APIGWALK move PIGWALKVELS faceplayersmart
            cstat 32768 
        }
        else
        {
            sizeto 0 40
            sizeto 0 40
            sizeto 0 40
            sizeto 0 40
            spawn FRAMEEFFECT1
        }
    }

Again, ifactioncount is used to break this action up into two ticks of 
the game clock. The first tick, four sizeto 0 40 commands are executed. 
These commands change the size of the actor by 0 percent in the x direction, 
and by 40/256 = 15.6 percent in the y direction. This has the effect of the actor 
growing thinner and thinner. After these four sizeto commands, a new actor 
is spawned, FRAMEEFFECT1. Because there is no additional code defining 
actor FRAMEEFFECT1 anywhere in the CON files, this actor must be 
handled internally, and I need not worry further about it.
During the second tick of action APIGHIDE, four additional commands 
are executed. Each command is summarized as follows:

spawn TRANSPORTERSTAR

A new actor, the TRANSPORTERSTAR is created. I reviewed this 
actor in chapter 12 in the book. It plays six frames forward of a star type 
graphic that grows in size, and then the same six frames in reverse order.

sound TELEPORTER

This plays the sound TELEPORTER. The sound number for this sound 
is defined in DEFS.CON.

action APIGWALK move PIGWALKVELS faceplayersmart

This switches the actor's action to APIGWALK, makes him start 
moving at speed PIGWALKVELS (72), and performs the basic ai 
faceplayersmart.

cstat 32768 

This last command makes the actor invisible. He will stay invisible until 
the command cstat 257 is issued. While invisible, the actor is impervious to 
bullets so that, even if you shot in the spot where you knew that the actor was, 
he would not take damage.

Putting these four commands together, a TRANSPORTERSTAR  
actor is created, a sound is made, and the actor disappears and starts walking 
toward the player. Also, the actor's action is changed to APIGWALK. The 
next time this state is executed then, the action is APIGWALK. Let's look at 
the code for this action:

    else ifaction APIGWALK
    {
        ifceilingdistl 48 break
        ifp pfacing break
        ifgapzl 64 { } else 
        ifawayfromwall { spawn TRANSPORTERSTAR 
                         action APIGREAPPEAR move 0 
                         break 
                        }
     }

A number of tests are performed to see if the pig cop is in a good 
position to reappear. Each test is described as follows.

ifceilingdistl 48 break

First, the distance from the actor to the ceiling is checked, and if it is 
less than 48 units high, the code falls through to the end (breaks).

ifp pfacing break

If the player is facing this actor, it falls through to the end. This ensures 
that the actor will always appear somewhere behind the player. (Pretty 
devious, huh?)

ifgapzl 64 {} else

This statement tests the total height of the sector. If the total sector 
height is less than 64, the block {} (nothing) is executed.

ifawayfromwall 
This last check tests that the actor is a sufficient distance from a solid 
wall, so that he does not reappear half inside the wall.

Finally, if all of these tests pass, the last block is executed. A second 
TRANSPORTERSTAR is spawned, and the action is switched to 
APIGREAPPEAR. The code for this final action is as follows:

    ifaction APIGREAPPEAR
    {
        ifactioncount 2 { 
                        sound TELEPORTER 
                        ifrnd 128 ai AIPIGFLEEENEMY else ai AIPIGSEEKENEMY
                        cstat 257 
                         }
        else { sizeto 48 40 sizeto 48 40 sizeto 48 40 sizeto 48 40 spawn FRAMEEFFECT1 }


Most of these commands are explained in some depth in chapter 12 in 
the book, so I'll only cover them briefly here. Once again, the action
is broken up into two ticks of the game clock. On the first tick, the
actor is  growing back to his original size (four sizeto 48 40
commands), and the mysterious actor FRAMEEFFECT1 is spawned.  In the
second tick, the sound TELEPORTER is played, the actor is made
visible (cstat 257), and finally, the ai changes to either
AIPIGFLEEENEMY or  AIPIGSEEKENEMY. Once this ai is changed, the
pigportstate code will no longer be executed. The reason for this is
the large block of  ifai <x> else ifai <y>... in the main actor
definition.

