| Home Page | Recent Changes | Preferences

Weapon Mutator Tutorial (UT2003)

This page is one of a series of UnrealScript Lessons.

The UT2004 version of this tutorial is available at Weapon Mutator Tutorial.

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

Discussion

RegularX: I think there are still netcode issues with spawning the ExplosionCrap I haven't worked out yet.

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". :D

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

Araes: (3/25/04) Alright, having a bit of trouble getting this to work, so I'll attempt to describe everything involved. One of the main things that's confusing me is that I assume this worked fine in UT2003, however, even out of the zip, this mod is no longer working in UT2004. Its erroring out on the MiniGunHEAltFire.uc file. What its having trouble with is the explosion (Explode, BlowUp) spawn which was derived from the RocketProj.uc. The specific error code is:

I:\GAMES\UT2004\Xpak\Classes\MinigunHEAltFire.uc(66) : Error, 'PlaySound': Bad command or expression

However, since it was doing this I figured I'd just avoid the sound and only spawn the explosion.

I:\GAMES\UT2004\Xpak\Classes\MinigunHEAltFire.uc(67) : Error, Bad or missing expression in 'If'

No dice. So then I figured I'd just avoid the whole explosion and just get the damage to work.

I:\GAMES\UT2004\Xpak\Classes\MinigunHEAltFire.uc(60) : Error, 'HurtRadius': Bad command or expression

All of this seems quite wierd as HurtRadius, PlaySound, and Spawn are all inherited from the most general Actor class. Note, it does compile fine and run otherwise if I get rid of all the code in Explode and BlowUp and just leave them as empty functions. Anybody have any idea what has changed between versions or where my problem might lie? The full code for this section is as shown:

function BlowUp(vector HitLocation)
{
    HurtRadius(100, 220, DamageType, 750, 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 + HitNormal, rotator(HitNormal));
    }

    BlowUp(HitLocation);
}

pgibbs: I think the WeaponFire class now derives from Object, where it derived from Actor in UT2003; which'd explain the error messages you are getting (as those are defined in Actor).

RegularX: Yeah, you're trying to compile a UT2003 mutator with some WeaponFire specific stuff into UT2004, and both the Minigun and the LinkGun have had code modifications, plus the mentioned fact that WeaponFires are Objects now in 2k4, not Actors - which could explain the PlaySound() and other issues ... unforturnately the other weapon tute around here is for the LinkGun, so you'll probably run into similar issues. Regardless - weapon code from the xxxpak probably isn't going to play nice w/ 2k4 without some modification.

I will try to make an updated UT2004 tutorial, but it's going to be this weekend at the very earliest.

Areas: Alright, could have sworn looking at it last night that it was an Actor still, but maybe that was just me being distracted by the tree of dependancies. I'll look into it tonight.

Areas: Yep, you're right. Instantfire, Minigunfire, and Weaponfire are now all direct children of Object rather than Actor. Since this is the case, what is the syntax for referencing the use of a function within another class that is not within your direct dependancy? For example, using the HurtRadius() function in Actor?

RegularX: Look to ProjectileFire for an example. Since Object doesn't have Spawn(), ProjectileFire asks it's Weapon to do it for it ( Weapon.Spawn(ProjectileClass,,, Start, Dir); ) ... So either point to something the WeaponFire has access to, like it's Weapon or Instigator, and have them handle the function - or have them spawn an actor which can handle the function.

Araes: Thanks. Changing the code to a version with large references to instigators:

function BlowUp(vector HitLocation)
{
    Instigator.HurtRadius(100, 220, DamageType, 750, HitLocation );
    Instigator.MakeNoise(1.0);
}

simulated function Explode(vector HitLocation, vector HitNormal)
{
    Instigator.PlaySound(sound'WeaponSounds.BExplosion3',,2.5*TransientSoundVolume);
    if ( Instigator.EffectIsRelevant(Instigator.Location,false) )
    {
        Instigator.Spawn(class'HEBulletExplosion',,,HitLocation + HitNormal*16,rotator(HitNormal));
        Instigator.Spawn(class'ExplosionCrap',,, HitLocation + HitNormal, rotator(HitNormal));
           }

    BlowUp(HitLocation);
}

As so, seems to have worked fine and fixed the problem.

Geist: So, if I've adapted this tutorial to work with UT2004, what's the current Wiki-wide policy on how to handle differences in different UT versions (esp. if I'm not sure how it works in UT or UT2003?). Should it be in a /UT2004 subpage? A "UT2004 Differences and Caveats" section of the same page? Let me know, and I'll add my version in the appropriate place. Then hopefully someone more savvy about UT2003 can clean it up? (I'd almost like to refactor this whole page – esp. since I didn't use any XXXpak resources, thus making it more "vanilla" – but I don't want to clobber anything that is UT2003-specific.)

Tarquin: I guess it depends on how much difference there is. If it's just something minor, then a heading for that part should do. If the differences are fundamental, then I'd say put the 2004 version here and move the 2003 version to a page "Weapon Mutator Tutorial (UT2003)", leaving a link at the top of this one. If your version of this tutorial doesn't use the XXXpak, it sounds to me like it's rather different, so maybe the 2 page approach is best.

RegularX: Yeah, I would agree. This page woefully needs refactoring, did since day one really - and that was like week after UT2003 was released. I didn't even realize what a poor choice the minigun is because of it's odd use of the attachment class. So archiving this page just for UT2003 reference and replacing it with a UT2004, non-XXXpak variant sounds dandy.

Geist: Done. See the link at the top of this page (and the reciprical link at the top of the old page). Notice that this page still needs refactoring for UT2003!


Category Tutorial

Refactor Me

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In