Weapon Mutator Tutorial
This page is one of a series of UnrealScript Lessons.
UTute 3: A Weapon Mute – Did someone say Firestorm?
I was reading a preview of Unreal Tournament 2003 that said that the alt fire would have explosive bullets. I thought, neat - that's just like firestorm rounds in my mod, Freehold! But when I got the game, I realized that Epic had a different definition of "explosive" than I did. So I set out to change that.
What I like to do with something like this is run through the code and grab everything I might need in order to complete it. In UT2003, the weapon code is more subdivided than in UT, with seperate classes for the weapon, the weapon's pickup, the ammo, the fire, the damage, etc. At first, it's actually a bit annoying - but in practice quite flexible. Since all I wanted to do was change the altfire of the Minigun, I was going to extend the following classes:
- Minigun - for the weapon, to reference the new fire and pickup class
- MinigunFire - for the new explosive fire
- Mutator - to make a pickup mutator so that it can be seen in the game
- MinigunPickup - to make a pickup class for my new weapon
So I made four new classes which extended those:
- MinigunHE - for the weapon, to reference the new fire and pickup class
- MinigunHEAltFire - for the new explosive fire
- MinigunHEArena - to make a pickup mutator so that it can be seen in the game
- MinigunHEPickup - to make a pickup class for my new weapon
Quicknote about MinigunHEArena. I call any mutator I use to alter pickups in the game an "arena" class, but this might be somewhat confusing to some other definitions of Arena.
In MiniGunHE, all I needed to do was point two default properties to my new classes. Essentially I modified the following:
FireModeClass(1)=Class'XPak.MinigunHEAltFire' PickupClass=Class'Xpak.MinigunHEPickup'
Each weapon in UT2003 has two FireModeClasses, primary (0) and alt (1). Here, I want the alt fire to be my class. The PickUpClass is something that confused me at first, but essentially it points to the class which should be used to pick up this weapon in the map itself. It becomes important with the mutator.
In the MinigunHEAltFire, I use the DoTrace which is inherited from the InstantFire parent class.. DoTrace is a function called when an instant hit weapon needs to determine the target that it is pointing at. It generates a vector called HitLocation and another called HitNormal to describe the point that the hit would occur. I added:
Explode(HitLocation, HitNormal);
After those vectors are determined. I then defined Explode, and its damaging function BlowUp (based from UT code actually) as:
function BlowUp(vector HitLocation) { HurtRadius(15, 45, DamageType, 250, HitLocation ); MakeNoise(1.0); } simulated function Explode(vector HitLocation, vector HitNormal) { PlaySound(sound'WeaponSounds.BExplosion3',,2.5*TransientSoundVolume); if ( EffectIsRelevant(Location,false) ) { Spawn(class'HEBulletExplosion',,,HitLocation + HitNormal*16,rotator(HitNormal)); Spawn(class'ExplosionCrap',,, HitLocation, rotator(HitNormal)); } BlowUp(HitLocation); }
Now, when the DoTrace returns its HitLocation, there is also a little explosion and a damage radius. Now that's an explosion. The other class directly related to the weapon is the pickup itself. Here, it's enough to extend the MinigunPickUp and merely change a few default properties:
defaultproperties { InventoryType=Class'XPak.MinigunHE' PickupMessage="You got the Minigun HE." }
Finally, we write a mutator to replace that old Minigun with the shiny new one.
class MinigunHEArena extends Mutator config(user); function bool CheckReplacement( Actor Other, out byte bSuperRelevant ) { bSuperRelevant = 0; if ( xWeaponBase(Other) != None ) { if ( xWeaponBase(Other).WeaponType == class'Minigun' ) xWeaponBase(Other).WeaponType = class'MinigunHE'; else return true; } else if ( WeaponPickup(Other) != None ) { if ( string(Other.Class) ~= "xWeapons.MinigunPickup" ) ReplaceWith( Other, "xPak.MinigunHEPickUP"); else return true; } else return true; return false; } defaultproperties { GroupName="Minigun HE" FriendlyName="Minigun HE" Description="Minigun with High Explosive Firestorm rounds" }
CheckReplacement is a Mutator function which hits every actor and asks if it should be replaced. It's an extremely powerful call and if done wrong can completely crash your game. Here, it prety much just looks for Miniguns and MinigunPickups and replaces them with new classes. Now, add two lines to the Public section of the INT file:
Object=(Class=Class,MetaClass=Engine.Mutator,Name=XPak.MinigunHEArena,Description="Minigun HE.") Object=(Class=Class,MetaClass=Engine.Weapon,Name=XPak.MinigunHE,Description="Customized Minigun with Firestorm Rounds.")
The bottom one is weapon specific, and is the line that adds the weapon to the Weapon Database. There you go, the basics of a weapon mutator and real explosions to boot.
Quicknote about the "HEBulletExplosion" call in the "Explosion" function. Yes, that's a custom job as well - but I left it out of the tute as it's not >actually needed. It essentially extends the explosion used for rockets and cuts back the scale.
This tutorial was originally part of RegularX's UTutes series.
Related Topics
- UnrealScript Lessons – all the UnrealScript tutorials
- UnrealScript – all the reference pages
- Mutator Topics – more on mutators
- Making Mods – more on the organizational and social aspects
- Creating A New Weapontype – more on weapon creation/modification
Comments
RegularX: I think there are still netcode issues with spawning the ExplosionCrap I haven't worked out yet.
ioreklpi : This tutorial is not enough clear. I had many problems with it. Please do it more clear. For example, give the complete code of each created class at the end of the tutorial. Thanks.
Nullzero:Well everything seems to be compiling right for me except for my MinigunHEAltfire class, the way I read the tutorial you have a function call "Explode(HitLocation, HitNormal);" where you're passing in the values from the parent class to your later defined function and then calling the BlowUP function from within Explode but I'm getting an Unexpected'Explode' when I try to compile any comments on where I might have gone wrong would be helpfull or maybe full text of your classes just so I could compare. Thanks.
CorDharel:Same thing on my computer. It's also not clear, i have problems to understand it. Would be cool if i can get the whole code of this mut. Thx.
RegularX: [| Complete Source of the XXXpak] - this will be out of date shortly though as I'll be releasing a new version of the XXXpak soon. These classes are in the xpak package.
Alex AC: I'm not quite clear about the bit saying;
"In the MinigunHEAltFire, I simply modified the original fire's DoTrace slightly."
all i see in the minigunaltfire is
class MinigunAltFire extends MinigunFire; defaultproperties { BarrelRotationsPerSec=1.000000 FiringSound=Sound'WeaponSounds.Minigun.minialtfireb' WindUpTime=0.150000 FiringForce="minialtfireb" DamageType=Class'XWeapons.DamTypeMinigunAlt' DamageMin=12 DamageMax=14 FireLoopAnimRate=3.000000 PreFireTime=0.150000 SmokeEmitterClass=Class'XEffects.MinigunAltMuzzleSmoke' Spread=0.030000 }
am i being stupid, or is there just a miscomunication?
RegularX: It's misleading, and more evidence I need to rewrite this guy - but I was referring to adding DoTrace to the AltFire which will update its super.function. So "its" is the one it inherited.
AlexAC: buh?
RegularX: I just double checked. MinigunHEAltFire extends MinigunFire, but the DoTrace it is replacing/updating is the one from inherited from InstantFire. (I'm assuming all this OO stuff makes sense ?). Updated the text to try and make it more clear.
Also - I have a networked version that puts the explosion effects in the correct place (oddly, they seem to work off the attachment) for the upcoming X3Pak version.
Almo: Found a problem. I copied this:
if ( string(Other.Class) == "xWeapons.FlakCannonPickup" )
and changed it to this:
if ( string(Other.Class) == "xWeapons.FlakAmmoPickup" )
and it didn't replace flak ammo. It was a small error in xWeapon:
if ( string(Other.Class) == "XWeapons.FlakAmmoPickup" )
Note the Capital "X".
Mychaeel: Actually I'd be better to do a case-insensitive comparison to start with; I have changed the code in the tutorial accordingly. On the other hand, I don't fully understand why the tutorial's author did a string comparison at all; simply comparing the classes directly would have done famously.
Foxpaw: I would have used the IsA function.. that way you are still covered if, for whatever reason, Epic or Digital Extremes chose to change which package the minigun is in in a later patch. It would also catch subclasses.
RegularX: Well, I wrote it like that because that's how MutArena is arranged, although it's done like that due to how the ArenaConfig is set up. An IsA would seem to be the way to go.
Mychaeel: Probably not if you're replacing a class by a custom subclass of it; then you'd get into an infinite loop and crash the game. Doing a string comparison prevents a package dependency (at that place), but at least the comparison should be case-insensitive as is the rest of the game. No biggie, but something you can easily spend hours on trying to track down.
Foxpaw: Hmm, you have a point. In which case, you could do if ( IsA( 'BaseClass' ) && !IsA( 'CustomSubclass' ) )
***Rand*m N**B*** I have been following the tutorials and this seems to be the last one directly linked,as so it seems tat there is a very steep curve in the jump from unexperience to the more knowledgable.I find this bridge very confusing to cross,so much is still unexplained