Useful Mutator Functions
Some useful Mutator (UT) functions. The code on this page probably doesn't work at all in UT2003.
HUD Mutators
"How do I register a HUD mutator?"
"Why doesn't my HUD mutator work in online/LAN games?"
This is a sample HUD mutator which does nothing more that registering itself and showing, how to get a reference to the local PlayerPawn and the HUD (UT).
class MyHUDMutator extends Mutator; var PlayerPawn MyPlayer; var HUD MyHUD; simulated function Tick(float DeltaTime) { if ( !bHUDMutator && Level.NetMode != NM_DedicatedServer ) RegisterHUDMutator(); } simulated function PostRender(Canvas Canvas) { MyPlayer = Canvas.Viewport.Actor; if ( MyPlayer != None ) MyHUD = MyPlayer.myHUD; // This is important! It allows the next HUD mutator to draw on the canvas. if ( NextHUDMutator != None ) NextHUDMutator.PostRender(Canvas); } defaultproperties { RemoteRole=ROLE_SimulatedProxy bAlwaysRelevant=True bNetTemporary=True // Set to false if you need to replicate data to the clients more than once per level }
Note: Don't use the code of the Team Beacon HUD mutator or the Relics. Team Beacon is far to complicated for this simple task and the RelicHUDMutator (apart from being incompatible to regular HUD mutators) calls its Destroy function without properly unregistering itself. (see Destructable Mutators below)
Note: If you want to execute code once client-side, but wish to do it after values have been replicated from the server to the client then used the PostNetBeginPlay() function.
Also see Netcode Idioms.
Destructable Mutators
Never destroy a Mutator (UT) while it still is in any of the mutator chains. This would effectively disconnect all mutators further down in the list. If you really need to destroy a mutator make sure it was unregistered before destroying it.
The other possible solution is that the mutator unregisters itself:
// correctly unregister this mutator from all linked mutator chains simulated function Destroyed() { local Mutator M; local HUD H; if ( Level.Game != None ) { if ( Level.Game.BaseMutator == Self ) Level.Game.BaseMutator = NextMutator; if ( Level.Game.DamageMutator == Self ) Level.Game.DamageMutator = NextDamageMutator; if ( Level.Game.MessageMutator == Self ) Level.Game.MessageMutator = NextMessageMutator; } ForEach AllActors(Class'Engine.HUD', H) if ( H.HUDMutator == Self ) H.HUDMutator = NextHUDMutator; ForEach AllActors(Class'Engine.Mutator', M) { if ( M.NextMutator == Self ) M.NextMutator = NextMutator; if ( M.NextHUDMutator == Self ) M.NextHUDMutator = NextHUDMutator; if ( M.NextDamageMutator == Self ) M.NextDamageMutator = NextDamageMutator; if ( M.NextMessageMutator == Self ) M.NextMessageMutator = NextMessageMutator; } }
Replacing Inventory Items
The ReplaceWith function does its job in most cases, but not in all maps the items are placed with their default properties. Some have the rotation turned off (either bRotatingPicklup=False, which is the recommended way to do it, or RotationRate.Yaw=0) or float in mid-air though they normally fall down. (Physics=PHYS_None instead of PHYS_Falling)
Also mutator sometimes want to further adjust the properties of the item after spawning it.
The ReplaceWithItem function takes both into account: It modifies the new item's rotation and physics and returns it, so the mutator can modify its properties.
// Modified version of the ReplaceWith function // Replaces an inventory Other with an inventory of class aClassName and // returns a reference to it final function Inventory ReplaceWithItem(Inventory Other, coerce string aClassName) { local Inventory A; local class<Inventory> aClass; local bool bAllowItemFall, bForceItemFall; local bool bAllowItemRotation, bForceItemRotation; if ( Other.Location == vect(0,0,0) ) return None; aClass = class<Inventory>(DynamicLoadObject(aClassName, class'Class')); if ( aClass != None ) A = Spawn(aClass, Other.Owner, Other.Tag, Other.Location + (aClass.Default.CollisionHeight - Other.CollisionHeight) * vect(0,0,1), Other.Rotation); if ( Other.MyMarker != None ) { Other.MyMarker.markedItem = A; if ( A != None ) A.MyMarker = Other.MyMarker; Other.MyMarker = None; } else if ( A != None ) { A.bHeldItem = true; A.Respawntime = 0.0; } if ( A != None ) { if ( Other.Physics != Other.Class.Default.Physics ) { if ( Other.Physics == PHYS_Falling ) bForceItemFall = True; else if ( Other.Class.Default.Physics == PHYS_Falling ) bAllowItemFall = False; } if ( (!Other.bRotatingPickup || Other.RotationRate == rot(0,0,0)) && (Other.Rotation.Pitch != 0 || Other.Rotation.Roll != 0) ) bAllowItemRotation = False; else bAllowItemRotation = (Other.RotationRate != rot(0,0,0) && Other.bRotatingPickup) || !Other.default.bRotatingPickup || Other.default.RotationRate == rot(0,0,0); bForceItemRotation = Other.RotationRate != rot(0,0,0) && Other.bRotatingPickup && (!Other.default.bRotatingPickup || Other.default.RotationRate == rot(0,0,0)); if ( A.Physics == PHYS_Falling && !bAllowItemFall ) A.SetPhysics(PHYS_None); else if ( A.Physics != PHYS_Falling && bForceItemFall ) A.SetPhysics(PHYS_Falling); A.bRotatingPickup = bAllowItemRotation && (A.bRotatingPickup || bForceItemRotation); A.Event = Other.Event; A.Tag = Other.Tag; return A; } return None; }
Giving Weapons To Players
This function is a modified version of the DeathMatchPlus.GiveWeapon function. It adds the weapon to the player's inventory and optionally brings it up as the players selected weapon. The function returns the weapon.
// Give a weapon to a player and optionally bring it up as current weapon. function Weapon GiveWeapon(Pawn PlayerPawn, string aClassName, optional bool bBringUp) { local class<Weapon> WeaponClass; local Weapon NewWeapon; WeaponClass = class<Weapon>(DynamicLoadObject(aClassName, class'Class')); if ( PlayerPawn.FindInventoryType(WeaponClass) != None ) return None; newWeapon = Spawn(WeaponClass); if ( newWeapon != None ) { newWeapon.RespawnTime = 0.0; newWeapon.GiveTo(PlayerPawn); newWeapon.bHeldItem = true; newWeapon.GiveAmmo(PlayerPawn); newWeapon.SetSwitchPriority(PlayerPawn); newWeapon.WeaponSet(PlayerPawn); newWeapon.AmbientGlow = 0; if ( PlayerPawn.IsA('PlayerPawn') ) newWeapon.SetHand(PlayerPawn(PlayerPawn).Handedness); else newWeapon.GotoState('Idle'); if ( bBringUp ) { PlayerPawn.Weapon.GotoState('DownWeapon'); PlayerPawn.PendingWeapon = None; PlayerPawn.Weapon = newWeapon; PlayerPawn.Weapon.BringUp(); } } return newWeapon; }
Related Topics
Kuhal: I have a developed a way to allow a mutator to "appear" to handle Damage to players BEFORE Inventory.ReduceDamage is called. Shall I create a page for this and link to it from OpenSource or would it be better to create the page and link form my homepage until one of the guru's can comment on the content? I investigated this while helping someone on the forums who wanted it specifically for his SpawnProtection code.
Mychaeel: Would you describe that method in a few words? – By the way, I believe the sections on this page would be well worth individual pages; maybe subpages of this one.
Kuhal: Synopsis: Create an Inventory Item with Charge=0; ArmorAbsorption=0; AbsorptionPriority=1000; bIsAnArmor=true;. Override ArmorAbsorbDamage in new class to update member variables for use by the mutator. Make sure that the mutator deals out the damage accordingly because the REAL armor was not damaged in the hit. Has limited usefulness - Why oh why didn't EPIC give Inventory.ReduceDamage the Victim and InstigatedBy vars too? I'm interested in other ideas in this area so by all means hook me up. I have full working source for the SpawnProtection use if needed.
Csimbi: Could someone add an example of adding another mutator from a mutator that is already added? I want to have only one entry in the .int file for practical reasons (the one Mutator would add all the others), and I cannot figure out how to do it. I tried a couple of things, but none of those worked so far. Thank You in advance.
Foxpaw: Depending on what exactly you had in mind, that may be quite easy. Or it may be next to impossible. Do you mean, you only want the one entry in the mutator list and when play begins all the other mutators are added? Or do you mean you want all of them to show up in the mutator list, but only want one listed in the INT file?
Wormbo: Just Level.Game.BaseMutator.AddMutator(Spawn(theMutatorClass)) should do the trick, but why don't you just combine the mutators into a single class?
Csimbi:
Foxpaw, the idea is that one mutator adds all the others.
Wormbo, in which function do You call this? Prebeginplay, PostBeginPlay, or ?
Wormbo: Yes, call it from one of the *BeginPlay functions.
Csimbi: I do this in the Mutator's PostBeginplay: Level.Game.BaseMutator.AddMutator(Spawn(class'CustomTranslocator'));
The error message I get is: "Error, Call to 'AddMutator': type mismatch in parameter 1"
What am I doing wrong?
Wormbo: 'Type mismatch' compiler errors tell you that the function expects a different variable type than you used. This CustomTranslocator class of yours, it's a mutator subclass, isn't it?
Csimbi: My mistake. It's a TournamentWeapon. Changed class name to the mutator's class, and it works like a charm. Thank You.