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.
|