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