news | about | forums | links | mods | tools | tutorials | contact

Mutator Class - author: [Pfhoenix] [mod exemplar]
The Mutator class extends the Info class. The following are all the variables (and their purposes that I've discerned) and function members and their purposes (that I've discerned).

#### Variable members :

var Mutator NextMutator | This is the next generic mutator object in the list of generic mutators
var Mutator NextDamageMutator | This is the next damage mutator object in the list of damage mutators
var Mutator NextHUDMutator | This is the next HUD mutator object in the list of HUD mutators
var bool bHUDMutator | This bool is a flag for whether or not the mutator is a HUDMutator
var class<Weapon> DefaultWeapon | Not sure exactly what this is good for, but an educated guess leads me to believe that if set, this var will override any other set default weapon for the level/game (via MutatedDefaultWeapon() and MyDefaultWeapon(), of which I've never used either)

#### Function members :

event PreBeginPlay() | This is called before gameplay starts.
simulated event PostRender(canvas Canvas) | Called every tick of gameplay *on the machine which starts this mutator* - i.e. this is will not work over network play. See below for further information on HUD mutators.
function ModifyPlayer(Pawn Other) | Called whenever a player joins the game. Other references the player's pawn object
function ScoreKill(Pawn Killer, Pawn Other) | If a kill is made, this function is called, passing a reference to the Killer (person who scored) and Other (the victim who died). Note - this function is called right on the tick of the kill, before any rendering or other processing has taken place.
function Class<Weapon> MutatedDefaultWeapon() | Returns the default weapon taking into account other mutators.
function Class<Weapon> MyDefaultWeapon() | Called by MutatedDefaultWeapon() to get what the default weapon for this mutator is.
function AddMutator(Mutator M) | Adds the mutator M to the game's mutator list. Normally, this call is not necessary for a generic mutator.
function bool ReplaceWith(Actor Other, string aClassName) | Replaces the Actor class object Other with another Actor class object whose name is aClassName. This is not called automatically by the mutator.
function bool AlwaysKeep(Actor Other) | Called by UT when an actor is going to be destroyed. By returning true, Other won't be destroyed. Default is false.
function bool IsRelevant(Actor Other, out byte bSuperRelevant) | Checks to see if mutators will allow this object to be changed. I'm not certain what purpose bSuperRelevant has yet.
function bool CheckReplacement(Actor Other, out byte bSuperRelevant) | Called by IsRelevant to determine whether or not the Actor class object Other should be replaced. I'm not certain what purpose bSuperRelevant has yet.
function Mutate(string MutateString, PlayerPawn Sender) | Called when players enter commands. Allows mutator to add commands.
function MutatorTakeDamage(out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType) | Called whenever a player takes damage. Note - this function is called before any damage processing is done to allow mutators to change (or play with) the damage dealt or hit location.
simulated function RegisterHUDMutator() | This registers the mutator as a HUD mutator so that it will receive the PostRender call.

Notice that there are only 5 variable and 14 function members to this class. However, since it extends the Info class (which extends the Actor class), we have access (through class inheritance) to many many MANY more variables and functions. The most important of which would probably be PostBeginPlay(). Let's start with a simple mutator of mine for an example.

// Spectre // A modification of GhostBoy example provided online class Spectre extends Mutator; var bool Initialized; function PostBeginPlay()
{
if (Initialized) return;

Initialized = True;

Level.Game.RegisterDamageMutator(Self);
}

function ScoreKill(Pawn Killer, Pawn Other)
{
// we have a winner, bump up his glowing and reset the victims'
if ((Killer != Other) && (Other != None) && (Killer != None))
{
Killer.ScaleGlow += 0.2;
Other.ScaleGlow = 1.5;
}

// we have a kevorkian, give him his dues
if ((Other != None) && ((Killer == None) || (Killer == Other))) Other.ScaleGlow = 1.5;

// set visibility so bots react appropriately
Other.Visibility = Other.Default.Visibility * Other.ScaleGlow;
Killer.Visibility = Killer.Default.Visibility * Killer.ScaleGlow;

// call our parent class counter-part function
Super.ScoreKill(Killer, Other);
}

function bool CheckReplacement( actor Other, out byte bSuperRelevant)
{
// Players, bots and carcasses (including gibs) need to be translucent.
if ( Other.IsA('Pawn') || Other.IsA('Carcass')) Other.Style = STY_Translucent;

// Invisibility pickups mess with our Visibility settings.
// We'll just get rid of them completely...
if (Other.IsA('Invisibility')) return false;

return true;
}

function MutatorTakeDamage(out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType)
{
local float dglow;

// reduce victim's glowing
dglow = float(ActualDamage) / 70.0;
if (dglow <= 0.1) dglow = 0.1;
Victim.ScaleGlow -= dglow;
if (Victim.ScaleGlow <= 0.1) Victim.ScaleGlow = 0.1;

// raise glow of attacker
if (InstigatedBy != Victim) InstigatedBy.ScaleGlow += dglow;

// set visibility so bots react appropriately
Victim.Visibility = Victim.Default.Visibility * Victim.ScaleGlow;
InstigatedBy.Visibility = InstigatedBy.Default.Visibility * InstigatedBy.ScaleGlow;
Super.MutatorTakeDamage(ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType);
}

#### The Breakdown :

Let's start from the top and work our way down. First is our class declarator. Specifically, we are subclassing (extending) the Mutator class. Then there are the variable declarations. All variables declared outside the functions are global and static to the object from the point of it's creation.
Next is the function PostBeginPlay(). PostBeginPlay() is called right after the game is fully ready to begin (all objects init'd / created / managed) and the game will start right after we return from this (well, as soon as all classes return from this). However, with the mutator class, PostBeginPlay() is called twice. In order to avoid doing our initialization code more than once, we use a global bool to flag our init'd state or not. The only initialization we handle is registering our mutator as a damage mutator, so that the level will call our MutatorTakeDamage() function. As a good practice (albeit necessary one), initialize all your global vars in PostBeginPlay(). Even though objects are initialized by the engine automatically, never assume your vars have a particular value (this is a good lesson to learn - do paranoia checks on variables just as a matter of safegaurding against potential bugs).
Next is the function ScoreKill(). Every mutator type has this function called. This is the best place to do anything you want to two specific player pawns - the killer and the victim. Players do not have their objects deleted when they die, only reset. However, only certain properties (variables) are reset when a player respawns (by the engine). This is where you want to reset/modify/tweak whatever variables in the player's pawns that you're playing with.
Now we have CheckReplacement(). This function is called for every actor in the level. If there is a particular actor class object that you do not want in the level no matter what, this is the convenient place to do it. By returning false, you are telling UT to get rid of the actor.
Finally, we have the beef of the mutator, MutatorTakeDamage(). Since we registered the mutator as a damage mutator up in PostBeginPlay(), every time a pawn *is about to take damage* this function is called. This allows you to change the damage dealt to the pawn, where it was hit, or process other things based on the information passed to the function, such as changing the translucency of the pawn based on how much damage was taken.

suggestions? questions? comments? please send them all to ray@beyondunreal.com

copyright 2001 ray davis. this site and all content belongs to me unless otherwise noted, with the exception of some tutorials. the authors retain the rights to their material, and are hosted here with their express permission. everything else is mine though, so don't steal it. bastards.
privacy