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 6: Basic AI Changes

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.

Changing AI

in existing POSTAL 2 levels

Code Tutorial 6

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 tutorial will teach you how to retrofit any new AI classes you’ve made to work in old POSTAL 2 levels. It will also teach you how to add your new weapon to the cheats lists.

In Code Tutorials 4 and 5, we made a new weapon called the Party Bomb and expanded on it. It is a thrown bomb similar to the grenade, but when it hits, it hurts no one, and instead makes bystanders start dancing for a little while. It’s more of a gag weapon than anything, but it required a lot of changes to get it to work. That is the reason I thought of making it instead of something simple like an AK-47 to replace the existing machinegun.

What we’ve done in the past two tutorials is make a new weapon that will only work on AI in new levels with the new Peds NPC’s. This direction is the best way to handle a new weapon. By making new levels and only populating them with characters that will react to your new weapon, you can be sure everything will work with your weapon.

Sidebar

There is no current tutorial on how make a new machinegun weapon or how to change the mesh on the shovel into a katana. Why? Because from a code standpoint it’s extremely simple. After you create the new weapon mesh in a new package, simple extend the old weapon class with your new one and reassign the Mesh property in the defaults. That’s it. As long as you named the new animations for your new mesh the same as for the weapon you mimicking, it should work. Of course, examine each animation of the weapon you’re changing to retain any notifies that may be in there (like on the grenade anims). If you’re making a new weapon that programmatically behaves the same as the old ones, you won’t need to do everything I’ve done for the Party Bomb. If you’re weapon doesn’t affect the AI in new ways then you won’t need new AI. If all you want is a larger explosion radius or bullets that fire at a different rate, that can all be handled by simply extending a given weapon class (or explosion) and inserting it into the cheats list to get it in your new level (that will be covered in this tutorial). The AI in the current POSTAL 2 levels already knows how to handle things like bullets and explosions so any simple change like that will work fine in current levels. Things like the Party Bomb that make NPCs dance on impact are what require more complex changes to make them work in current POSTAL 2 levels.

End Sidebar

There are two parts to getting your new weapon to work in existing POSTAL 2 levels:

  1. Being able to spawn the weapon in the level for use by the player.
  2. Changing existing AI to work with the new weapon.

The first part is the probably the only thing that needs to be done for most weapons. Let’s address that now.

Spawning Your New Weapon (with cheats)

We can get the new weapon in the player’s hands in a variety of ways. Most commonly and obviously, the weapon should have a pickup counterpart placed somewhere in the level. That is PartyPickup in our case. This will only work in levels that are made after the weapon exists. Then you can place the pickup in the level and the player will be able to grab it. But that doesn’t work for levels made before your weapon—levels like those shipped with POSTAL 2 (or even user made levels made without your weapons code).

The next best way around that would be to simply add a cheat that gives us the new weapon. Fortunately most of the work is already done here. We made the SuperCheatManager.uc back in Code Tutorial 2.

Open GameTypes\Classes\P2CheatManager.uc for reference and copying. Search for the function PackNHeat. It’s about a third of the way down the file. This is POSTAL 2’s basic ‘get all the weapons’ cheat. Let’s add the PartyBomb to the list.

Open SuperPack\Classes\SuperCheatManager.uc. We’re only adding to our own CheatManager not the original. We’ll want to call the Super function to get the original functionality of PackNHeat. Copy this much of the function from P2CheatManager and paste it into SuperCheatManager.uc:

///////////////////////////////////////////////////////////////////////////////
// Gives player every destructive weapon in the game
///////////////////////////////////////////////////////////////////////////////
exec function PackNHeat()
{
          local Inventory thisinv;
 
          if(!P2Player(Outer).CheatsAllowed())
                   return;

Delete this line:

local Inventory thisinv;

We won’t need any local variables.

We want to do the actual spawning of our weapon, but first we need to spawn all the other weapons. That means calling our Super. Add this next:

Super.PackNHeat();

Now we’re ready to add our new weapon. You can copy any of the other weapons over like this (you only need one line):

P2Pawn(Pawn).CreateInventory("Inventory.BatonWeapon");

Change the old weapon name to your new one and make sure to specify the correct package:

P2Pawn(Pawn).CreateInventory("SuperPack.PartyWeapon");

(Like I’ve said before, because SuperCheatManager.uc is in the same package as PartyWeapon.uc, it’s currently okay to leave of the package part, but if you ever move the weapon to it’s own weapon package, you’ll then need to specify.)

That should be about it. Here’s the final function:

///////////////////////////////////////////////////////////////////////////////
// Gives player every destructive weapon in the game (including our new
// one!
///////////////////////////////////////////////////////////////////////////////
exec function PackNHeat()
{
          if(!P2Player(Outer).CheatsAllowed())
                   return;
          // Make sure to add in all the old levels.
Super.PackNHeat();
// Add our new weapon
P2Pawn(Pawn).CreateInventory("SuperPack.PartyWeapon");
}

This gets you all the weapons with the PackNHeat cheat. Your new weapon will also be added to the player’s inventory with the IAmSoLame cheat because it uses the prior function (you can see it in the P2CheatManager.uc code).

If you want an individual function that simply adds your new weapon, I’d suggest mimicking a smaller function like IAmTheLaw (but making it add your weapon instead).

That completes the first part of getting the new weapon into the game.

Changing Existing AI to Work with the Party Bomb

This second part is only necessary for weapons that change the AI. Like I’ve said before, if you’re doing a simple ‘stronger bullets, stronger explosions’ weapon change, you won’t need to do this step. After the above step, your new weapon can now be used in the game.

Preaching

The following thing I’m going to teach you is a code hack. The term ‘hack’ is thrown around in programming circles to mean any poor programming method (and doesn’t exactly mean the same thing as hacking into a database or the typical idea of hacking). The general idea of retrofitting some AI Controllers in existing levels to use new controllers is poor. Everything about it screams bad design. The proper way to do this would be to not allow the new weapon to work in the old levels. Consider that no pickups for this new weapon can be placed in the existing levels. The only way to get the new weapon would be through a cheat code. That, by its nature is poor design. If however, you made a new weapon and a new level, the new level could have any new AI that you needed, plus you could place pickups for the new weapon in the level to have it played like normal. I just want to impart upon you that this is just the wrong direction to take. The reason I’m explaining such a bad thing to you is because more than likely, as a programmer, you’ll only make a new weapon and want to use it against other characters. You won’t be making new, cool levels to use it in. Hopefully by making it work in the old levels you can attract some modelers and level makers to all work together on a full, new level with your new weapon.

End Preaching

For weapons like the PartyBomb though, we’re going to throw in a hack to get our new AI (PedController) to be used by the NPC’s in the old levels. There are several ways do this.

The general problem is Bystander.uc specifies BystanderController as its AI class. The method not to use would be to modify Bystander.uc and then force people to overwrite their BasePeople.u file. Because the controller is specified in the class, all Pawns placed in the original levels use that AI. Also, any new Pawns that are spawned dynamically during gameplay will also use that old AI.

We’re going to basically go through all the Bystanders in the level and replace their current BystanderController with a PedController. This can be very messy.

The other place that could have Pawns we care about is in spawners. Here is the hierarchy for spawners in POSTAL 2.

FPSGame.Spawner> FPSPawn.PawnSpawner

A Spawner can make just about anything. A PawnSpawner is much more powerful and is there to make pawns. If you simply place one and fill in the SpawnClass with ‘BasePeople.Bystanders’ it will start making as many Bystanders as specified. Look at the class variable comments for details. Also, you can look at example uses in the game levels. The problem we’re going to fix is that the PawnSpawners in the level may spawn new Bystanders who will have BystanderControllers. We need to also handle those.

What we’ll do is change all our Pawns just before the game really gets started in each level. This requires some research into Postal2Game\Classes\P2GameInfoSingle.uc. Open up the file and scroll down to this line:

auto state StartUp

This is the first state it enters. From all the comments you can see that this state handles changing things at the last second before a level begins. Scroll down to the Begin portion of the state. There you will see all the functions that get called. You should see these two functions getting called:

PrepActorsByGroup();

PrepSliderPawns();

The first does a variety of things and generally handles any changes to every single actor in the level. The second handles putting some pawns into stasis. Putting pawns into an invisible stasis and then bringing them out when other pawns have died keeps the framerate higher while making sure it seems like the streets are more alive. (You can read more about it in the code if you wish).

We’re going to extend the function PrepActorsByGroup in our own game info. It will change everything we need to change, including the spawners. Search on the function name in that file and find where it’s defined.

Copy the function header from P2GameInfoSingle.uc and paste it into our own gameinfo SuperPack\Classes\MegaGameSP.uc.

First things first—we need to call the super for that function. You should now have something like the following:

///////////////////////////////////////////////////////////////////////////////
// Change all bystanders placed in the level and in spawners
// to our new Peds to work with the PartyBomb.
///////////////////////////////////////////////////////////////////////////////
function PrepActorsByGroup()
{
          local PersonPawn CheckP;
          local PedController tempc;
          local PawnSpawner CheckS;

          Super.PrepActorsByGroup();
}

What about those local variables? Yeah… I just snuck those in. You should add those in too. You’ll need them later.

If you look at the original function in P2GameInfoSingle, you’ll see it uses an iterator to go through all the Actors and change them various ways. We need to do this twice—once for Bystanders and once for PawnSpawners. Because we know the classes we’ll be changing we can use the faster iterator DynamicActors. (If you’re confused by iterators, look for examples in the code, in Actor.uc and other files, and in Epic code tutorials).

What we really want though is to change any Pawn with a BystanderController to use our new controller. This may mean that someone else like a Bum who isn’t a Bystanders Pawn could be using this controller. Let’s iterate through PersonPawn instead, and check their controllers to see if it’s the one we want to change. When we find one, we’ll spawn a new controller and hook it up.

          foreach DynamicActors(class’PersonPawn’, CheckP)
{
// Make sure he has a controller in the first place (could be dead on
// level start)
          if(CheckP.Controller != None
          // Make sure it’s exactly a bystander controller in this pawn
&& CheckP.Controller.class == class’BystanderController)
{
// Make our new one controller
tempc= spawn(class’PedController’);
if(tempc != None)
{
// if it worked,  unhook old controller
CheckP.Controller.Pawn = None;
CheckP.Unpossessed();
CheckP.Controller.Destroy();
// Assign and set up new one
CheckP.Controller = tempc;
CheckP.Controller.Possess(CheckP);
CheckP.CheckForAIScript();
}
}
}

That goes through all pawns that have exactly and nothing more than a BystanderController (CashierController extends BystanderController—if you check with the IsA function or a similar typecast, you could end up converting a Pawn with a controller you don’t want to change).

Let’s make the spawners to work now too. We only want to change spawners that have Pawns that default to BystanderController or spawners that specify the controller class as BystanderController. Again, we’ll use DynamicActors to iterator through them.

Foreach DynamicActors(class’PawnSpawners’, CheckS)
{
// First, use this special function to check to make sure it’s a descendant of our PersonPawn (and not an
// animal, or any other actor.
                if(ClassIsChildOf(CheckS.SpawnClass, class’PersonPawn’)
                // Next is even trickier, cast the spawnclass to our person pawn, then look at the default version
                // and make sure it would only make a BystanderController (and not one extended
                // from a BystanderController, like a CashierController.
&& class<PersonPawn>(CheckS.SpawnClass).default.ControllerClass == class&rsquo;BystanderController&rsquo;)
{
                // What we actually want to do is simple&mdash;just reassign the spawn class.
                CheckS.SpawnClass = class&rsquo;Peds&rsquo;;
}
// Also check to see if they specified the controller class and change that to our new one too
if(CheckS.InitControllerClass == class&rsquo;BystanderController&rsquo;)
                CheckS.InitControllerClass = class&rsquo;PedController&rsquo;;
}

The final function should look like this:

///////////////////////////////////////////////////////////////////////////////
// Change all bystanders placed in the level and in spawners
// to our new Peds to work with the PartyBomb.
///////////////////////////////////////////////////////////////////////////////
function PrepActorsByGroup()
{
                local PersonPawn CheckP;
                local PedController tempc;
                local PawnSpawner CheckS;
 
                Super.PrepActorsByGroup();
 
                // change over the pawns themselves
                foreach DynamicActors(class&rsquo;PersonPawn&rsquo;, CheckP)
{
// Make sure he has a controller in the first place (could be dead on
// level start)
                if(CheckP.Controller != None
                // Make sure it&rsquo;s exactly a bystander controller in this pawn
&& CheckP.Controller.class == class&rsquo;BystanderController)
{
// Make our new one controller
tempc= spawn(class&rsquo;PedController&rsquo;);
if(tempc != None)
{
// if it worked,  unhook old controller
CheckP.Controller.Pawn = None;
CheckP.Unpossessed();
CheckP.Controller.Destroy();
// Assign and set up new one
CheckP.Controller = tempc;
CheckP.Controller.Possess(CheckP);
CheckP.CheckForAIScript();
}
}
}
                // Change over the pawn spawners
Foreach DynamicActors(class&rsquo;PawnSpawners&rsquo;, CheckS)
{
// First, use this special function to check to make sure it&rsquo;s a descendant of our PersonPawn (and not an
// animal, or any other actor.
                if(ClassIsChildOf(CheckS.SpawnClass, class&rsquo;PersonPawn&rsquo;)
                // Next is even trickier, cast the spawnclass to our person pawn, then look at the default version
                // and make sure it would only make a BystanderController (and not one extended
                // from a BystanderController, like a CashierController.
&& class<PersonPawn>(CheckS.SpawnClass).default.ControllerClass == class&rsquo;BystanderController&rsquo;)
{
                // What we actually want to do is simple&mdash;just reassign the spawn class.
                CheckS.SpawnClass = class&rsquo;Peds&rsquo;;
}
// Also check to see if they specified the controller class and change that to our new one too
if(CheckS.InitControllerClass == class&rsquo;BystanderController&rsquo;)
                CheckS.InitControllerClass = class&rsquo;PedController&rsquo;;
}
}

That should be about it. Compile SuperPack.u. Now, run an old level that should have original Bystanders in it (not your test level, unless you replaced the Peds with Bystanders).

Excellent! Between this conversion and the cheats we’ve added, you should be able to run this:

Postal2 suburbs-3.fuk

And give yourself the new PartyBomb with the cheats in the original starting level for POSTAL 2! Enjoy!

If you run this level you should see that it won’t have make the Redneck who’s around the corner start dancing. Instead, he’ll just run away from you and watch you do things. This is because he has a RedneckController (as mentioned in the previous tutorial) instead. The same goes for the cops. But like I mentioned before, this is up to you to change if you want. (But you should see everyone else dancing merrily—those goofy-ass dances just crack me up)

The power here is that you can now zip up you’re the small SuperPack.u file and send it to anyone and they’ll be able to use the cheats to get your brand new weapon and it’ll even change the AI around to work with it. Like I’ve said, several times before, if you’ve gone through all the trouble of making a weapon that complicated, then you should really make it for all new levels, but sometimes you can’t make those and seeing it work, as least partially, in the original levels is much more satisfying.

Nathan Fouts

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: