Live a week in the life of "The Postal Dude"; a hapless everyman just trying to check off some chores. Buying milk, returning an overdue library book, getting Gary Coleman's autograph, what could possibly go wrong? Blast, chop and piss your way through a freakshow of American caricatures in this darkly humorous first-person adventure. Meet Krotchy: the toy mascot gone bad, visit your Uncle Dave at his besieged religious cult compound and battle sewer-dwelling Taliban when you least expect them! Endure the sphincter-clenching challenge of cannibal rednecks, corrupt cops and berserker elephants. Accompanied by Champ, the Dude's semi-loyal pitbull, battle your way through open environments populated with amazingly unpredictable AI. Utilize an arsenal of weapons ranging from a humble shovel to a uniquely hilarious rocket launcher. Collect a pack of attack dogs! Use cats as silencers! Piss and pour gasoline on anything and everyone! YOU KNOW YOU WANT TO!

Post tutorial Report RSS Coding Tutorial 5: Advanced Party Bomb Weapon

Tutorials using Unreal Script specific to POSTAL 2.

Posted by on - Advanced Client Side Coding

Tutorial originally released on Running With Scissor's Postal website. Was taken down more than 10 years ago. Mirrored here for archival purposes.

Advanced Changes to the Party Bomb

Code Tutorial 5

You must be running the new Share the Pain version of POSTAL 2 for any of this to be relevant. Using these tutorials with older code may cause you problems.

These tutorials are to be completed in succession with one another. If you jump ahead to a later tutorial, I will be discussing things from earlier tutorials that you may not recognize. Following each one in order should greatly improve your learning experience.

This will teach you to make the Party Bomb from Code Tutorial 4 into something even more unique. The idea is to change the functionality of the bomb projectile. I want the new bomb to do the following:

  1. It will no longer harm things when it explodes
  2. The explosion effect will now tell all NPC’s in the area to start dancing instead.
  3. Dance music will play for a bit, then the NPC characters will return to their daily life.

The first step here will be to make our own explosion class. Here’s the hierarchy for an explosion in POSTAL 2:

Engine.Emitter> BaseFx.P2Emitter > BaseFx.Wemitter > BaseFx.Explosion> Postal2Game.P2Explosion> Fx.GrenadeExplosion

Review the Explosion.uccode. It basically enters it’sExplodingstate, Plays a sound, waits a moment, checks to hurt everything in the area, then notifies the NPC’s about what happened. That’s not exactly what we’d like to have happen, but it’s close. We basically want a sound to play (the music), we want everyone in the area to be notified by the bomb, and we want a visual to be created (an emitter).

If we make the explosion hurt nothing (no damage is delivered), and we make the proper code notifies for the NPC, then this will work for us.

If you look at GrenadeExplosion.uc, you’ll see it does even more things, like determining if it should also make blood effects when it hits a person. Since we don’t want any of this functionality, we should just extend the parent below that: P2Explosion.uc.

Starting PartyExplosion

Start, like usual, by copying the file and renaming it. Postal2Game\Classes\P2Explosion.uc should be copied to SuperPack\Classes and renamed PartyExplosion.uc.

Change the header at the top to appropriately reflect our new class:

///////////////////////////////////////////////////////////////////////////////
// PartyExplosion
// No damage is done, but confetti is thrown about and everyone
// reasonably close starts to party down! Lasts several seconds.
///////////////////////////////////////////////////////////////////////////////
class PartyExplosion extends P2Explosion;

Delete everything from below the class definition down to the default properties.

Pop open BaseFx\Classes\Explosion.uc for reference.

If you’ll look at the default properties, you’ll see that this thing really wants to hurt stuff. Our current PartyExplosion will do the same thing, because it eventually inherits these properties and currently nothing is changing them.

Copy over the block this block into the def props of PartyExplosion:

          ExplosionMag=50000
          ExplosionRadius=500
          ExplosionDamage=100
          MyDamageType = class'ExplodedDamage'
          ExplodingSound=Sound'WeaponSounds.explosion_long'

Let’s make the damage and the magnitude 0 to ensure nothing is hurt. And let’s increase the party size to 1000.

          ExplosionMag=0
          ExplosionRadius=1000
          ExplosionDamage=0

This will have double the range of the grenade, but it will hurt no one. But currently, because of the Notify we’re inheriting from P2Explosion, everyone will run away from it, scared (like they would from a normal explosion).

Let’s change the damage to be 0, and the sound to be some party music. I found the music by opening the editor and browsing some of the sound packages. You could certainly import your own music, just be sure to create a separate package for it and include it when you send the new weapon to people.

          MyDamageType = None
          ExplodingSound=Sound'AmbientSounds.hornyClub’

We should go ahead and make some visuals before we compile first. Sure, the people won’t dance yet, but it’s good to go in smaller steps to avoid bugs.

Adding the Explosion Visuals

A Particle Emitter will be used to create the visuals. These are very powerful, very complicated objects. If you plan to do effects, it’s important to experiment a lot with them.

In fact, if you’re not familiar at all with them, you should check out the tutorial for UT 2003 on this site:

Udn.epicgames.com

Again, that tutorial is for older UT2003 code—not POSTAL 2—but in this case, most things in there should be of great help.

Once you’re comfortable with that idea, let’s make our new effect.

Start the editor. Open a test map for experimentation. I like to keep a simple, large box level called ‘emitters.fuk’ for saving all my neat effects for later use and changes.

I’m not actually going to explain how to do any of this. I will explain the final results of the effect and what certain parts do. To explain every step would take way too long.

Sidebar

However, let me point out how to get an emitter out of the editor and into a *.uc file. Here’s a sure way to extract them. Once you’re finished creating your emitter effects, select the emitter in the editor. Copy the emitter (Crtl+C). Now go to an open folder and make a new temporary text file. Paste into that text file. You should get a lot of text. This text is all the default properties of that emitter.

The contents should look something like this:

Begin Map
Begin Actor Class=Emitter Name=Emitter1
          *Begin Object Class=SpriteEmitter Name=SpriteEmitter3
          Blah
          Blah
          Blah
          End Object
          *Emitters(0)=SpriteEmitter’MyLevel.SpriteEmitter3’
          bNetDirty=true
          LastRenderTime=53.892034
          Tag=”Emitter”
          Blah
          Blah
          Blah
End Actor
End Map

See the ‘*’ before Begin Object and End Object? Everything in between those lines and including them is what you want. If you’ve made two or more subemitters within the same emitter, they’ll all be listed together. So if you’ve got an explosion, or just an emitter you want to make into a *.uc, extend it from P2Emitter, and put that block of code in the default properties. You don’t need anything else around it. You should end up with something like this:

          Begin Object Class=SpriteEmitter Name=SpriteEmitter3
          Blah
          Blah
          End Object
          Emitters(0)=SpriteEmitter&rsquo;<strong>MyLevel</strong>.SpriteEmitter3&rsquo;

That would do it except for one more thing. See the highlighted MyLevel.? Delete that part. If you don’t delete that, more than likely you’re emitter won’t show up in the game when you try to spawn it. You should notice errors in the ucc.log from it. So you’re left with:

          Emitters(0)=SpriteEmitter&rsquo;SpriteEmitter3&rsquo;

Obviously the 3 at the end can be different. The editor will supply you with a new number each time. It simply uses that to put it into the Emitters array at the bottom. Let’s continue the example here and hopefully seeing it in a more concrete example will help.

End SideBar

Okay, I’ve finished making the cool effect in the editor. I had no guidelines save what I was imagining. My general idea turned into the following. Each Emitter can actually be an array (list) of subemitters. They’ll all just happen around the same spot. I wanted a fairly complex motion of them exploding quickly from a single point, spraying upwards, then slowing falling back down, all around, and spinning and twirling slowly everywhere. To accomplish this I actually made two subemitters. The first one sends a lot of my confetti particles quickly into the air, upwards. Those particles die, and on a delayed start the second emitter begins. Those particles all fall slowly moving sideways randomly to better simulate confetti. All the while the colors are cycled to simulate more particles and more colors. Confetti is difficult to simulate in a real-time environment. I compromised on the particle count by lowering it a lot to ensure the framerate never gets too bad. Also, I don’t make the confetti particles collide with anything, again because it’s hard to notice and it would cost more processor power.

Here are the default properties of my current explosion with the new emitter data copied into it: (I’ve used a smaller font to try to make this more manageable)

Default properties
{

These are our previous values:

  ExplosionMag=0
  ExplosionRadius=1000
  ExplosionDamage=0
  MyDamageType = None
  ExplodingSound=Sound'AmbientSounds.hornyClub&rsquo;

This is the first emitter:

    Begin Object Class=SpriteEmitter Name=SpriteEmitter17

Add in a little gravity to slow them down near their peak:

        Acceleration=(Z=-400.000000)

This block cycles the colors:

        UseColorScale=True
        ColorScale(0)=(Color=(R=255))
        ColorScale(1)=(RelativeTime=0.500000,Color=(B=255))
        ColorScale(2)=(RelativeTime=1.000000,Color=(G=255,R=255))

How many particles we’ll have:

        MaxParticles=30

This ensures they start at interesting angles, and continue to rotate in a good fashion:

        SpinParticles=True
        SpinsPerSecondRange=(X=(Min=0.500000,Max=2.000000))
        StartSpinRange=(X=(Max=1.000000))

This sets the size to pretty small:

        StartSizeRange=(X=(Min=2.000000,Max=4.000000),Y=(Min=2.000000,Max=4.000000))

I picked this to have the texture show up more vibrantly. For energy effects, fire, smoke, you’d probably want something more like translucent or brighten.

        DrawStyle=PTDS_Regular

This is the best white rectangle I could find at the time. It turned out good. The idea here is, I picked a white rectangle, but used the emitter to color it various ways to save me texture-making time. I could have made lots of different colored textures and lots of different emitters to make each one, also.

        Texture=Texture'MpHUD.HUD.MessageWindow'

These first ones only live about a second:

        LifetimeRange=(Min=1.000000,Max=1.500000)

This is how they fly through the air. The X and Y are the same (side to side) while the Z is positive (for up).

        StartVelocityRange=(X=(Min=-500.000000,Max=500.000000),Y=(Min=-500.000000,Max=500.000000),Z=(Min=500.000000,Max=600.000000))
        Name="SpriteEmitter17"
    End Object
    Emitters(0)=SpriteEmitter'MyLevel.SpriteEmitter17'

Second emitter for the slowly falling particles:

    Begin Object Class=SpriteEmitter Name=SpriteEmitter18

This direction part, along with making them rotate, ensures they flip and spin like real confetti. Nothing was too specific, I just knew I wanted an interesting motion:

        UseDirectionAs=PTDU_RightAndNormal

These fall much slower and have little gravity:

        Acceleration=(Z=-30.000000)
        UseColorScale=True
        ColorScale(0)=(Color=(G=255,R=255))
        ColorScale(1)=(RelativeTime=0.300000,Color=(G=255))
        ColorScale(2)=(RelativeTime=0.600000,Color=(B=255))
        ColorScale(3)=(RelativeTime=1.000000,Color=(R=255))
        ColorScaleRepeats=2.000000
        MaxParticles=60

This makes the group that will fall, start about where the first emitter particles stop. The idea is, these should look like the same particles:

        StartLocationOffset=(Z=300.000000)

This makes them start within a large, box area of the following dimensions. Making them all fall from the same point wouldn’t look real:

        StartLocationRange=(X=(Min=-300.000000,Max=300.000000),Y=(Min=-300.000000,Max=300.000000),Z=(Max=100.000000))
        SpinParticles=True
        SpinsPerSecondRange=(X=(Min=0.500000,Max=2.000000))
        StartSpinRange=(X=(Max=1.000000))
        StartSizeRange=(X=(Min=2.000000,Max=4.000000),Y=(Min=2.000000,Max=4.000000))
        DrawStyle=PTDS_Regular
        Texture=Texture'MpHUD.HUD.MessageWindow'

These fall for much longer. As you can see here, Max is 5, but the Min is not specified. This is because it is 4 seconds and that is the default lifetime. When you extract things like this from the editor, it will not specify any values that are the same as the usual defaults:

        LifetimeRange=(Max=5.000000)

This keeps them from happening right off the bat. I delay them till about when the other particles are finished:

        InitialDelayRange=(Min=1.000000,Max=1.000000)
        StartVelocityRange=(X=(Min=-100.000000,Max=100.000000),Y=(Min=-100.000000,Max=100.000000),Z=(Min=-60.000000,Max=-30.000000))

This sort of pushes them in different sideways directions for more interesting motion.

        VelocityLossRange=(X=(Min=-0.500000,Max=0.500000),Y=(Min=-0.500000,Max=0.500000))
        Name="SpriteEmitter18"
    End Object
    Emitters(1)=SpriteEmitter'MyLevel.SpriteEmitter18'
}

Here are the full default properties for PartyExplosion, complete with the new emitter effects. I would use this as your default properties if you didn’t make your own emitter effects.

Default properties
{
  ExplosionMag=0
  ExplosionRadius=1000
  ExplosionDamage=0
  MyDamageType = None
  ExplodingSound=Sound'AmbientSounds.hornyClub&rsquo;
    Begin Object Class=SpriteEmitter Name=SpriteEmitter17
        Acceleration=(Z=-400.000000)
        UseColorScale=True
        ColorScale(0)=(Color=(R=255))
        ColorScale(1)=(RelativeTime=0.500000,Color=(B=255))
        ColorScale(2)=(RelativeTime=1.000000,Color=(G=255,R=255))
        MaxParticles=30
        SpinParticles=True
        SpinsPerSecondRange=(X=(Min=0.500000,Max=2.000000))
        StartSpinRange=(X=(Max=1.000000))
        StartSizeRange=(X=(Min=2.000000,Max=4.000000),Y=(Min=2.000000,Max=4.000000))
        DrawStyle=PTDS_Regular
        Texture=Texture'MpHUD.HUD.MessageWindow'
        LifetimeRange=(Min=1.000000,Max=1.500000)
        StartVelocityRange=(X=(Min=-500.000000,Max=500.000000),Y=(Min=-500.000000,Max=500.000000),Z=(Min=500.000000,Max=600.000000))
        Name="SpriteEmitter17"
    End Object
    Emitters(0)=SpriteEmitter'SpriteEmitter17'
    Begin Object Class=SpriteEmitter Name=SpriteEmitter18
        UseDirectionAs=PTDU_RightAndNormal
        Acceleration=(Z=-30.000000)
        UseColorScale=True
        ColorScale(0)=(Color=(G=255,R=255))
        ColorScale(1)=(RelativeTime=0.300000,Color=(G=255))
        ColorScale(2)=(RelativeTime=0.600000,Color=(B=255))
        ColorScale(3)=(RelativeTime=1.000000,Color=(R=255))
        ColorScaleRepeats=2.000000
        MaxParticles=60
        StartLocationOffset=(Z=300.000000)
        StartLocationRange=(X=(Min=-300.000000,Max=300.000000),Y=(Min=-300.000000,Max=300.000000),Z=(Max=100.000000))
        SpinParticles=True
        SpinsPerSecondRange=(X=(Min=0.500000,Max=2.000000))
        StartSpinRange=(X=(Max=1.000000))
        StartSizeRange=(X=(Min=2.000000,Max=4.000000),Y=(Min=2.000000,Max=4.000000))
        DrawStyle=PTDS_Regular
        Texture=Texture'MpHUD.HUD.MessageWindow'
        LifetimeRange=(Max=5.000000)
        InitialDelayRange=(Min=1.000000,Max=1.000000)
        StartVelocityRange=(X=(Min=-100.000000,Max=100.000000),Y=(Min=-100.000000,Max=100.000000),Z=(Min=-60.000000,Max=-30.000000))
        VelocityLossRange=(X=(Min=-0.500000,Max=0.500000),Y=(Min=-0.500000,Max=0.500000))
        Name="SpriteEmitter18"
    End Object
    Emitters(1)=SpriteEmitter'SpriteEmitter18'
}

There are still a few things to change though for the emitters. We want the emitters to happen only once and not repeat. That is, we want the spray of particles to happen very quickly and not continue spraying. We can set these values in the editor, but there are editor bugs that make the emitters disappear when we try to use the joystick to test it.

Take a look at the emitter effects at the bottom of GrenadeExplosion.uc again.

You’ll see something like this in all of them:

RespawnDeadParticles=False

and this:

InitialParticlesPerSecond=100.000000

AutomaticInitialSpawning=False

The first one means that once MaxParticles have been made, make no more. The second part means that 100 particles will be made in a single second. If more particles need to be made they will. But once it gets to MaxParticles, it will stop. Our MaxParticles for the first group was set to 30. This will make all of our particles within a third of a second. That sounds about right. So let’s copy this into our PartyExplosion. Put it into the first emitter only like so:

MaxParticles=30
RespawnDeadParticles=False
InitialParticlesPerSecond=100.000000
AutomaticInitialSpawning=False
SpinParticles=True

That makes our first emitter explode properly. Our second emitter though needs to change similarly. Because I expect the particles to be more lazy, I’m going to lower the creation rate. Put it into your second emitter like this:

MaxParticles=60
RespawnDeadParticles=False
InitialParticlesPerSecond=30.000000
AutomaticInitialSpawning=False
StartLocationOffset=(Z=300.000000)

One final thing to add at the top of your default properties is this:

Default properties
{
AutoDestroy=true
ExplosionMag=0

It doesn’t always have to be at the top, I just put it there to let you know it’s outside of the emitter parts but it does affect them. This makes sure your emitters get cleaned up properly. Otherwise, you could have explosions that look like they’re gone but are still hogging system resources in the background.

We’re almost ready to test our new explosion but we must link it into the game! Right? How else would the game know about our cool new explosion?

If you’ll recall, PartyProjectile controls our explosion creation. Open that file.

In the function GenExplosion is where we spawn our explosion. Several things need to change. At the top it makes the following variable:

local GrenadeExplosion exp;

We need it to be our new explosion type like this:

local PartyExplosion exp;

Next, find where the explosion is spawned. Change the line to this:

exp = spawn(class'PartyExplosion',GetMaker(),,HitLocation + ExploWallOut*HitNormal);

The last part is we don’t need all the other functions called in this explosion. So delete these:

          exp.CheckForHitType(Other);
          exp.ShakeCamera(exp.ExplosionDamage);
          exp.ForceLocation = WallHitPoint;

(Technically we don’t need to save the explosion to a local variable either now, but I’m keeping it there just in case come back to it and want to call a function afterwards).

Save PartyExplosion.uc.

Build SuperPack.

Let’s run and test the new explosion! (Open the editor first, and put a few NPC’s in your test level to test the new bomb against, if you haven’t already.)

Testing and Debugging

Our new bomb should not hurt anyone when it hits. Let’s see what happens.
Okay… as usual, not everything is perfect on our first run. You should notice a few problems.

  1. If hit by the bomb, the Dude complains about being hurt by the explosion but sustains no actual damage.
  2. The visual effect didn’t last long enough to see particles fall down.
  3. The music only plays for about a second.
  4. People run from us when we walk up to them with the Party bomb out.
  5. People still run from the explosion when it goes off.

Let’s take each problem in turn. The first is due to the fact that the CheckHurtRadius call in Explosion.uc is still sending a TakeDamage call to everyone in the area even if the damage is 0.

Instead of changing CheckHurtRadius down in Explosion, let’s simply not call it, by overriding the Exploding state in our explosion.

(If you’re unfamiliar with Unreal states, you don’t need a lot of knowledge here, but skimming a tutorial like this may be helpful:

Unreal.epicgames.com

)

Let’s override the Exploding state from Explosion.uc in our new PartyExplosion.uc. Open up BaseFX\Classes\Explosion.uc.

Copy this block of code over (near the bottom of the file):

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
auto state Exploding
{
Begin:
                PlaySound(ExplodingSound,,1.0,,,,true);
                Sleep(DelayToHurtTime);

                if(Level.Game != None
                                && FPSGameInfo(Level.Game).bIsSinglePlayer)
                                CheckHurtRadius(ExplosionDamage, ExplosionRadius, MyDamageType, ExplosionMag, ForceLocation);
                else
                                CheckHurtRadius(ExplosionDamageMP, ExplosionRadiusMP, MyDamageType, ExplosionMagMP, ForceLocation);
 
                Sleep(DelayToNotifyTime);
                NotifyPawns();
}

Put it below your class definition but above the default properties in PartyExplosion.

As you can see, it plays the sound, waits, then checks to hurt things around it, then waits, then notifies the surrounding pawns.
Let’s cut out the CheckHurtRadius calls to leave this:

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
auto state Exploding
{
Begin:
          PlaySound(ExplodingSound,,1.0,,,,true);
          Sleep(DelayToHurtTime);
          Sleep(DelayToNotifyTime);
          NotifyPawns();
}

It’s a little redundant to have two sleeps in a row. Let’s also cut out this line:

Sleep(DelayToHurtTime);

Now, DelayToNotifyTime makes more sense.

We now have the sound playing, it waits for moment, and then it tells the pawns to be scared.

We’ve fixed the first problem we had—the dude would act like he’s hurt but not really. Let’s try to fix problems 2 and 3. I think that’s caused because the LifeSpan on our explosion is by default, only one second.

LifeSpan=1.0

Copy this over to our default props in PartyExplosion and make it

LifeSpan=20.0

This should give our ‘party’ a long enough time to play out.

Save PartyExplosion.uc.

Build SuperPack.

Run the game and see how it looks.

Pretty cool, but we still have some problems. When the bomb goes off the visuals now play out as expected, but the sound quits very quickly still. We know the explosion is lasting long enough because the particles are still around. The sound doesn’t keep playing properly because we’re not making it loop. There are many ways to do this. I’m going to tackle it from a code perspective.

All Actors have an AmbientSound variable. This loops whatever sound is it. In thinking about the effect, I think the actual explosion of the gift with the confetti needs more ‘punch’. I’m going leave the original PlaySound in and replace ExplodingSound with the original grenade sound like this in the default properties:

ExplodingSound=Sound'WeaponSounds.Grenade_ExplodeGround'

That will make an explosion sound when the bomb goes off, but now we have no party music. Let’s add a new variable at the top of the file, just under the class definition like this:

class GrenadeExplosion extends P2Explosion;

var Sound PartyMusic; // music to be looped for the party

Next we need to assign a value to PartyMusic in the default properties, like this:

PartyMusic= Sound'AmbientSounds.hornyClub

SoundRadius and SoundVolume in Actor control AmbientSound. Let’s crank those up here:

SoundRadius=500

SoundVolume=255

Now we just need to make the party music play. As soon as the AmbientSound variable is assigned in single player, the sound starts playing. Let’s allow the explosion sound to go off, then when we notify pawns that the party is started, we’ll also start our music.

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
auto state Exploding
{
Begin:
          PlaySound(ExplodingSound,,1.0,,,,true);
          Sleep(DelayToNotifyTime);
          AmbientSound=PartyMusic;
          NotifyPawns();
}

Let’s save our file, build the package and test it in the game.

Cool! It’s looking and sounding pretty good now.

The only thing left to do is to change the behavior of the NPC’s around it.

Modifying our New Weapon for AI changes

The problem 4 is simple to fix. Since this is supposed to just be a gift in our hands that we’re walking around with, let’s make it so cops and bystanders don’t bother us as we walk around. Right now, the AI ‘sees’ a grenade in our hands—or a least something very dangerous. Let’s turn it into something innocuous that they’ll ignore.

Open PartyWeapon.uc.

In the default properties, find this line:

ViolenceRank=6

That means that from 0 to 10, 10 being highest, this thing is pretty violent. Like the HandsWeapon, let’s make it 0.

ViolenceRank=0

The other thing we’ll want to add is this line:

bBumpStartsFight=false

This ensures that as we carry this thing and around and we bump into people with it, no one will mind.

Compile SuperPack and run the game to test these new changes.

As you can see now, when you walk around with the Party bomb and bump into people they’re fine with it. The only big thing left is to have the people not scream and run and instead dance when the bomb goes off.

This will involve extending the AI classes that the default characters start with. It will be a rather complicated process.

Making new AI classes

This could certainly be the topic of several tutorials, but that would take forever. Instead, I’m going to give what simple examples I can in hopes it helps you break into the AI to help you write your own. The AI Controllers in POSTAL 2 are some of the most complicated code in the game. Most variables are explained with comments in the code.

Here is the hierarchy of all the basic AI in the game:

Engine.Controllerà Engine.AIControllerà FPSGame.ScriptedControllerà FPSGame.FPSControllerà Postal2Game.LambController

The cat (for example) then extends additionally in this manner:

Postal2Game.LambControllerà Postal2Game.AnimalControllerà AIPack.CatController

A normal person then extends it in this manner:

Postal2Game.LambControllerà Postal2Game.PersonControllerà AIPack.BystanderController

Most human characters use BystanderController as their final AI. Several go on to use AIPack.CashierController, including Gary, Krotchy, and Habib.

Police actually branch off at PersonController and start AIPack.PoliceController. Military come from police AI.

The general idea of extending the AI and then making it affect everyone in the current game of POSTAL 2 wouldn’t work easily. What will work is if we extend our own AI and then place new characters in a new level. Making your own levels and putting them together with your own AI in a new package would actually be easier than modifying the existing AI in POSTAL 2. This is because the Pawns are placed in the levels and saved, and these pawns specify what AI they want to start with.

You could obviously directly modify BystanderController and then release your own AIPack.u but then you’d be back in the Dark Ages of modification, requiring people to overwrite their own *.u files. That is the wrong direction.

For the moment I’ll show you how to make brand new AI for new characters that we place in our test level. Later I’ll try to show you how to make new AI that can be used in existing POSTAL 2 levels.

Let’s make a new controller. Make a new text file in SuperPack\Classes and name it PedController.uc (after pedestrian). This will extend BystanderController but because it’s such a large file and we need so little, we’ll start it from scratch and copy things as needed.

Start with this at the top:

//////////////////////////////////////////////////////////////////////////////
class PedController extends BystanderController;

What we need to have happen is add to the way a controller is notified and then decides what to do. For explosions and the like, this happens with the function MarkerIsHere.

Copy this over as your first function header:

///////////////////////////////////////////////////////////////////////////////
// Some danger occurred
///////////////////////////////////////////////////////////////////////////////
function MarkerIsHere(class<TimedMarker> bliphere,
                                                  FPSPawn CreatorPawn,
                                                  Actor OriginActor,
                                                  vector blipLoc)

Nope, we don’t have a TimedMarker just yet, but we’ll make one soon. We simply want to check for a party and then call another function when it happens. Our yet-to-be made marker will be called PartyMarker. Mimicking the other lines of PersonController.MarkerIsHere, we’ll add this to our function:

{
          bool bDontCallSuper;

          if(!MyPawn.bIgnoresSenses)
          {
                   if(bliphere == class'PartyMarker')
                   {
                             SetupPartying(OriginActor);
                             bDontCallSuper=true;
                   }
}
if(!bDontCallSuper)
          Super.MarkerIsHere(bliphere, CreatorPawn, OriginActor, blipLoc);
}

Let’s look at the individual parts.

if(!MyPawn.bIgnoresSenses)

Make sure they can notice what’s happening around them.

                   if(bliphere == class'PartyMarker')
                   {
                             SetupPartying(OriginActor);

If it’s about parties, then call our setup function (we’ll write that soon too).

bDontCallSuper=true;

We did what we wanted; so don’t call the old stuff down in PersonController.

if(!bDontCallSuper)

Super.MarkerIsHere(bliphere, CreatorPawn, OriginActor, blipLoc);

If it wasn’t a partying marker, then handle all the other cases.

In an Unreal Script function, to call an ancestor’s function, use the Super call. We’ll use it here, because we want everything a normal person responds too, but we also want them to respond to our party call.

Let’s write the function that will put the person in the correct state to start partying.

If we look at PersonController.MarkerIsHere we’ll see that the first case it handles, it calls the function SetupWatchParade. This could be a similar situation to our party, so we’ll look at it for ideas.

We can see in the code that it does some checks about starting or not, then proceeds to go through a lot of steps to get the person in the correct position and orientation to watch the parade. For our party, we probably just want the person to start dancing where they are. Let’s write a small conditional statement to handle some of the cases, and then use the already existing state DanceHere:

            Function SetupPartying(Actor OriginActor)
            {
            if(InterestPawn == None
                        && Attacker == None)
{
            GotoStateSave(&lsquo;DanceHere&rsquo;);
            SetNextState(&lsquo;Thinking&rsquo;);
}
}

If we look at the state PersonController.DanceHere we see that they’ll dance for a while and then eventually try to do their next state, if they have one provided. SetNextState gives them their next thing to do.

The conditional near the top means only people that aren’t fighting or running from someone will start dancing.

The fortunate part is that I knew the dancing state was already written. That saved us a lot of time. It’s important to know the code well. When you can recall seeing something happening in the game, do extensive searches to find the code responsible. Then use the code as an example.

Now that we have a new controller, we need a new Pawn to use it. This pawn will have to replace the Pawns currently in our test level. Let’s start a new file called SuperPack\Peds.uc

Extend the class from Bystanders.

///////////////////////////////////////////////////////////////////////////////
class Peds extends Bystanders;

We need to inherit everything from the Bystanders class except the controller class. Add this as the default properties:

          defaultproperties
{
          ControllerClass=class'PedController'
}

We’re done with that!

Save Peds.uc.

We next need the marker to notify the AI and call our new function. Again if we use SuperWatchParade as an example we’ll see that it uses ParadeMarker to notify things. Copy Postal2Game\Classes\ParadeMaker.uc to SuperPack\Classes and rename it PartyMaker.uc.

Change the class definition to the following to keep all the default properties but inherit from TimedMarker instead:

///////////////////////////////////////////////////////////////////////////////
// Lets get this party started!
///////////////////////////////////////////////////////////////////////////////
class PartyMarker extends TimedMarker;

TimedMarker marker tells everyone about itself, and then hangs around for a time. BlipMarker that extends it, like radar, continues to notify people about itself at intervals. We simply want this to happen once therefore we extend TimedMarker.

Delete this line from our default properties:

NotifyTime=6.0

That’s a variable from BlipMarker that we don’t have and don’t need. It controls how often BlipMarker continues to notify controllers. That should be it for PartyMarker.

Save it!

We now need our explosion to call that new marker.

Open PartyExplosion.uc.

We need to change the class it uses to notify the pawns. We want this to be about a party instead of running and screaming for their lives.

Add this to the default props in PartyExplosion:

ExplosionMarkerMade=class'PartyMarker'

Save the file.

Wrapping Up

At this point we’re very close to getting to test the new dancing functionality. But if you recall we only added this AI to our new pawn and controller. That means that the old pawn in our test level still specifies the old BystanderController. When the PartyBomb goes off and tells them about the PartyMarker, as a default they won’t care or they’ll run screaming. We need only pawns that have the new controller in our level.

First, save all your *.uc files and build SuperPack.u.

Open your test map in the editor.

Delete all NPC’s in your test level.

Add a few new Peds. They’ll act just like Bystanders, in that they’ll generate random meshes and textures, but now they’ll use our new AI.

Save your level.

Run the game and throw a Party bomb around an NPC. Let’s see if they dance!

Wow! It worked! When you throw the bomb, people start dancing, the confetti falls, and they eventually stop dancing. Pretty cool! Congrats!

(If you’re running into errors and they’re not working, it will probably help to place log statements in your code (such as this: log(self$” SetupParty got called “); There are many examples in the code about logs) then you can look at the log to see what order things happened in and if they indeed happened at all. You can show the log during gameplay if you Alt-Tab the game to windowed mode, and then type ‘showlog’ in the command console. Also, for AI debugging, you can open Postal2.ini and make LogStates=1. If you have lots of NPC’s in the level it may flood the log, which is not helpful. It’s best to test with a limited number of characters.)

So look at you—you have a new fancy weapon that’s stand-alone and even affects AI in new ways. What’s next? Well there’re lots of enhancements and improvements to be done, that’s for sure.

  1. You’re new weapon won’t work in the original POSTAL 2 levels. We’ll try to address that in the next tutorial.
  2. Obviously the first person visuals need to be a new mesh to represent the weapon you’re using.
  3. You may want to play with the emitter effects on the explosion. Perhaps adding a third and fourth subemitter to represent the pieces of the gift itself and some smoke or sparks or something.
  4. Maybe the weapon needs some crazy Alt-Fire ability also.
  5. Right now the only NPC’s that care about your bomb are Peds that you made here. Cops you place in your level, Habib, Krotchy, Rednecks… no one else will notice the bomb correctly. You’ll have to individually extend each AI class you want to notice the bomb. To make things simpler though, most of the time CashierControllers (all special characters and cashiers themselves) ignore most special things. It’d be much easier to only extend a few classes like PoliceControllerand RedneckController.

(Here are the only classes that don’t extend from CashierController:

PoliceController,

MilitaryController,

RedneckController,

KumquatController,

GimpController,

MuggerController,

RobberController,

RWSController,

ProtestorController,

VinceController.

While that may seem like a lot, the majority of characters in the game simply use BystanderController, so extending that will affect the most characters.)

All in all, you have done a lot now and can use this to make lots of other weapons of your own creation. Enjoy!

Nathan Fouts

Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

Follow Report Profile
Icon
POSTAL 2
Platforms
Windows, Mac, Linux
Developer & Publisher
Running With Scissors
Contact
Send Message
Release date
Game watch
Follow
Purchase
Tutorial
Browse
Tutorials
Report
Report
Views
13 (1 today)
Share
Related Games
POSTAL 2
POSTAL 2 First Person Shooter
Related Engines
Unreal Engine 2
Unreal Engine 2 Commercial
Related Groups
Running With Scissors
Running With Scissors Developer & Publisher