Always snap to grid

Legacy:Postal 2/Double Health Mutator

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search

This is a tutorial geared at Postal 2 coders who wish to learn how to make mutators for Postal 2.

This tutorial assumes you have already read the Postal 2 Code Tutorials.

Let's say you're messing around with your SuperPack from the Code Tutorials. You want to make a multiplayer mutator. We'll start with something simple, a player modification. An easy mutator would be to double the player's starting health. First, let's create a mutator class.

///////////////////////////////////////////////////////////////////////////////
// MutDoubleHealth.uc
// blahblah@spamsite.com
//
// Doubles player health
//
///////////////////////////////////////////////////////////////////////////////
class MutDoubleHealth extends xMutator;
 
defaultproperties
{
    GroupName="Health"
    FriendlyName="Double Health"
    Description="Player starts with double health."
}

Mutator is the base class for a simple game mutator. You can mix and match mutators to customize your game. For example you can combine Low Gravity with Always Gib. Let's have a look at the default properties.

GroupName
A Group Name is used to specify which mutators are compatible with each other. This particular mutator's GroupName is "Health". You can specify any group name you want. If two mutators have the same GroupName, only one of them will be used. So if you were to create a Half Health mutator, you should use "Health" for that mutator's GroupName as well, because trying to use a Half Health and Double Health mutator would just cancel each other out.
FriendlyName
This is the string that shows up in the mutator selection listbox under the Modifiers tab.
Description
And this is the string that shows the mutator's description in the Modifiers tab. Use a pipe symbol "|" for a line break.

Our mutator doesn't do anything yet, but let's compile our SuperPack anyway and see if we can use our mutator in a game. Build SuperPack with UCC and run Postal 2. Go to Multiplayer and host a new game, then check the Modifiers tab.

Hey, our mutator isn't there?!

Actually, there's something we forgot to do! Postal 2 doesn't know our mutator exists because there isn't any reference to it. Fortunately, those clever bastards at Epic came up with the idea of an .INT file. Creating an INT file will let Postal 2 know what kind of mutators are in your package.

Go open up your favorite text editor and create a new file called SuperPack.int (or whatever your package is called). Your INT file should consist of the following:

[Public]
Object=(Class=Class,MetaClass=Engine.Mutator,Name=SuperPack.MutDoubleHealth,Description="Double Health")

What does all this crap mean?! In order for Postal 2 to know your mutator exists, it has to be spawned as an object. The only two things you need to worry about for making other mutators are the Name= and the Description=. Name= should be the full class name of your mutator, in this case it's SuperPack.MutDoubleHealth. And the Description= should be one or two words describing your mutator. This description will show up in the server info page to people joining your server.

Now save your file and load Postal 2 again. You don't need to re-compile your SuperPack. Lo and behold, Double Health shows up in the Modifiers tab!

But our Mutator won't do anything right now. We need to give it some functions so that it'll do something when we play. There are different functions for Mutators to do different things. For now the function we want to look at is called ModifyPlayer.

function ModifyPlayer(Pawn Other)
{
    Super.ModifyPlayer(Other);
}

The ModifyPlayer function is called by the Unreal engine whenever a new player enters the game. Other is the Pawn for the player. Any changes you make to Other in ModifyPlayer passes back to the new player. Before we do anything, know that it is imperative that the Super version of ModifyPlayer is called after we make our modifications.

Mutators work in a chain. Each function in a mutator calls the same function of the next mutator in the chain. This way all mutators get to do their stuff and everyone's happy. If Super.ModifyPlayer isn't called, then the next mutator in the chain won't get to do its stuff, nor will the rest of the mutators in the chain. So if you leave that function out and your mutator happens to be the first in the chain, guess what? You just broke all the other mutators. Good job.

Just remember to call Super.ModifyPlayer after (or before) your modifiations.

Now, what were we trying to do again? Oh yeah, double health to all players. Here we go.

function ModifyPlayer(Pawn Other)
{
    Other.Health = Other.Health * 2;
    Super.ModifyPlayer(Other);
}

All good to go, right?

Wrong! This won't be good enough. All we're doing is altering the player's CURRENT health. So the player will start with double health, but if their health falls to below normal maximum health, they won't be able to heal back up to double health. Now, if this is what you WANT for your modifier, you are in fact good to go. But for our modifier we want the player to be able to heal ALL of that health back. So one more line is necessary.

function ModifyPlayer(Pawn Other)
{
    Other.Health = Other.Health * 2;
    FPSPawn(Other).HealthMax = FPSPawn(Other).HealthMax * 2;
    Super.ModifyPlayer(Other);
}

HealthMax is a variable specified in the FPSPawn class, and does not exist in the base Pawn class, so we must use FPSPawn(Other) instead of just Other.

Now let's compile our new mutator and try it...

Hey, wait a damn minute! Why does our health still say only "100"? Well, this is because the number shown for your health is actually a PERCENTAGE, not the actual NUMBER of health you have. To ensure your mod really does work, go play with some Morons and take a few NON-HEADSHOTS. You'll notice that you seem to be taking less damage than normal. You're actually taking the same amount of damage but since your health is shown as a percent, it appears that you are taking less damage.

Note that players connecting through the Internet will actually see the number 200 for their health, because the HealthMax variable isn't replicated. If you don't know what that means, just hit the "I Believe" button and continue on.

Note that headshots are independent of max health. Two headshots will kill a guy at max health, whether their max health is 300 or 3000.

Congratulations! Your mutator is complete! Now go make a name for yourself.

Here is the finished code.

///////////////////////////////////////////////////////////////////////////////
// MutDoubleHealth.uc
// blahblah@spamsite.com
//
// Doubles player health
//
///////////////////////////////////////////////////////////////////////////////
class MutDoubleHealth extends xMutator;
 
function ModifyPlayer(Pawn Other)
{
    Other.Health = Other.Health * 2;
    FPSPawn(Other).HealthMax = FPSPawn(Other).HealthMax * 2;
    Super.ModifyPlayer(Other);
}
 
defaultproperties
{
    GroupName="Health"
    FriendlyName="Double Health"
    Description="Player starts with double health."
}

Before I wrap up, I should point out another very useful Mutator function.

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)

This function can be used in a mutator to alter ANY actor, not just a player. You can change the properties of the existing actor, or even replace the actor with a different actor. If you'll have a look at MultiGame.MutNoCrack, you can see an example of CheckReplacement in action:

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
    // Check normal crack pickups
    if(CrackPickup(Other) != None)
    {
        return !(ReplaceWith(Other, "Inventory.PizzaPickup"));
    }
    // Remove also any crack from cycling pickups
    else if(MultiPickup(Other) != None)
    {
        MultiPickup(Other).SwapClass(class'CrackPickup', class'MedKitPickup');
    }
 
    return true;
}

The first "if" statement check to see if the actor is a CrackPickup. If so, then it calls ReplaceWith to swap that actor with a PizzaPickup. If ReplaceWith is successful, then a value of FALSE is returned, otherwise TRUE is returned. If you are going to destroy or replace the actor Other, you should return FALSE.

The "else if" statement checks to see if the actor is a MultiPickup. MultiPickups are the pickups that randomly switch between armor, crack pipes, rocket launchers, etc. The MultiPickup actor has a nice little SwapClass function so you can replace any object inside the MultiPickup with something else. Note that the function returns TRUE in this case. This is because the actor itself is a MultiPickup and NOT the actual crack itself. We aren't replacing or destroying the MultiPickup, we're just altering a property of the MultiPickup.

There is a pitfall here, though. If the health pipe is embedded within some part of the level, the engine won't be able to spawn a new health pipe at that exact location, and then the health pipe will not be replaced. Depending on the scope of the mutator, you may want to destroy the health pipe if it doesn't get replaced.

Now, go forth my children, and make mutators!

Related Topics[edit]