Legacy:Weapon Modification (UT)

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to navigation Jump to search

This page is one of a series of UnrealScript Lessons.

Introduction

This is my first UnrealScript Tutorial.

This tutorial assume that you have basic UnrealScript knowledge.

Weapon Modification: Faster Sniper Rifle

You will learn how to modify a standard Weapon, in this case the Sniper Rifle, for the original Unreal Tournament. Here, we will try to enhance the Sniper Rifle to make it shoot faster. Some modifications will be made on the new weapon's ammo for conveniance.

Two classes will be created:

SniperRifle >> FastRifle 
The sniper rifle modification class
Mutator >> FastRifleMutator 
The mutator class to replace stuff

FastRifle.uc

<uscript> class FastRifle extends SniperRifle; // We are modifying the Sniper Rifle


var float fRifleAnimRate; // Custom rifle animation rate multiplier


// Play firing sound and animation (override PlayFiring() in SniperRifle) simulated function PlayFiring() {

   PlayOwnedSound( FireSound, SLOT_None, Pawn(Owner).SoundDampening * 3.0 );
   PlayAnim( FireAnims[Rand(5)], fRifleAnimRate, 0.05 );
   if ( ( PlayerPawn(Owner) != None ) && ( PlayerPawn(Owner).DesiredFOV == PlayerPawn(Owner).DefaultFOV ) )
       bMuzzleFlash++;

}


defaultproperties {

   fRifleAnimRate=6.0   // Custom rifle animation rate multiplier
   PickupAmmoCount=40   // Defined in SniperRifle class Default = 8

} </uscript>

class FastRifle extends SniperRifle;

To get access to its functions and properties, we must subclass the SniperRifle class. Our new weapon is now the same as the SniperRifle.

var float fRifleAnimRate;

This is a new variable we create here to store animation rate multiplier. I will talk about it in details in the following sections.

PlayFiring()

This function is declared in TournamentWeapon, the parent class of the SniperRifle.

It is overriden in the SniperRifle class of the Botpack package:

<uscript> simulated function PlayFiring() {

   PlayOwnedSound(FireSound, SLOT_None, Pawn(Owner).SoundDampening*3.0);
   PlayAnim(FireAnims[Rand(5)], 0.5 + 0.5 * FireAdjust, 0.05);
   if ( (PlayerPawn(Owner) != None) && (PlayerPawn(Owner).DesiredFOV == PlayerPawn(Owner).DefaultFOV) )
       bMuzzleFlash++;

} </uscript>

Basically, PlayFiring() is called when you press the Fire button and it is called again and again as long as you hold the fire button and the current weapon still contains some ammos.

The interesting part here is the PlayAnim() function. It is defined in the Actor class:

PlayAnim (name Sequence, optional float Rate, optional float TweenTime) 
Plays an Animation.
Sequence
Anim sequence Name.
Rate: Animation Rate multiplier.
TweenTime: Amount of Time to "tween" into the first frame of this animation sequence if in a different sequence.

This function plays the given Sequence. The Rate parameter is a multiplier. That means the animation will be played Rate times faster than normal. The faster the animation is played, the sooner PlayFiring() will be called again. :)

The FireAdjust variable is defined in TournamentWeapon and will always be 1.0 if Pawn is not a Bot. Since we don't want the bots to shoot at a different Rate than us, we will get rid of this variable in the animation rate and replace it with 1.0. Then now, 0.5 + 0.5 * 1.0 = 1.0, the default firing rate is 1.0.

So, we need to overrides PlayFiring() in order to modify this animation Rate multiplier. The new function will be:

<uscript> simulated function PlayFiring() {

   PlayOwnedSound( FireSound, SLOT_None, Pawn(Owner).SoundDampening * 3.0 );
   PlayAnim( FireAnims[Rand(5)], fRifleAnimRate, 0.05 );
   if ( ( PlayerPawn(Owner) != None ) && ( PlayerPawn(Owner).DesiredFOV == PlayerPawn(Owner).DefaultFOV ) )
       bMuzzleFlash++;

} </uscript>

The only difference with our new function is the Rate of the PlayAnim() function. This was a fixed value of 1.0, and now it is set to our new fRifleAnimRate variable.

defaultproperties

Here we define a new property and we override an existing one.

fRifleAnimRate 
Modify fRifleAnimRate value to change firing speed.
I do not recommend setting higher than 30.0. Anyway, number of ammo will be largely insufficient for the firing speed.
SniperRifle
1.0, Epic Original SniperRifle
AssaultRifle: 5.0, Zark AssaultRifle
CyberRifle: 9.0, Ramx CyberRifle
PickupAmmoCount 
Modify the initial number of ammo.
Since our new weapon is faster than the standard SniperRifle, it would be wise to raise the initial number of ammos the weapon contains when picked up.
Our weapon uses BulletBox ammo type, that support a MaxAmmo count of 50, so setting an initial ammo count higher than 50 would cause some illogical behaviors... excepts if you subclass BulletBox to modify his defaultproperties.

For more weapon properties, see Default Properties For Weapons.

FastRifleMutator.uc

<uscript> class FastRifleMutator extends Mutator;


// Only allow one instance of this mutator. function AddMutator(Mutator M) {

   if ( M != Self )
       Super.AddMutator(M);

}


// Do not allow FastRifle to be replaced or removed. function bool AlwaysKeep(Actor Other) {

   if( Other.IsA('FastRifle') )
       return true;
   return Super.AlwaysKeep(Other);

}


// Replace the default weapon with FastRifle when a player spawn. function ModifyPlayer(Pawn Other) {

   DeathMatchPlus(Level.Game).GiveWeapon( Other, "FastRifle.FastRifle" );
   Super.ModifyPlayer(Other);

}


// Replace SniperRifle with the FastRifle. function bool CheckReplacement(Actor Other, out byte bSuperRelevant) {

   if ( Other.IsA('Weapon') ) {
       if ( Other.IsA('SniperRifle') ) {
           ReplaceWith( Other, "FastRifle.FastRifle" );
           return false;
       }
   }
   return true;

} </uscript>

class FastRifleMutator extends Mutator;

This is added to the mutator list and will be used to replace any SniperRifle in the game with our new FastRifle.

AddMutator()

This will prevent this mutator to try adding himself recursively.

AlwaysKeep()

This will prevent your new weapon to be replaced or removed by another mutator or mod and also avoid to get an endless recursive loop in CheckReplacement(), because our new weapon IsA SniperRifle.

ModifyPlayer()

This function allow to init some stuff when the player spawn. We might want to give the FastRifle as main weapon when spawning.

This is not recommended behavior, but you can add some way to disable this, like configuration files or such.

CheckReplacement()

This will Check for the presence of any Actor and allow replacement with ReplaceWith(). This is where we will actually replace all original SniperRifle in the current game with our new customized FastRifle.

Conclusion

Now compile the package that contains those 2 classes and start a game with this mutator. I'm not sure about network compatibility, i'm new to UnrealScript and need to learn more on replication.

Related Topics

Comments

Ramx: OK, so it should be complete. Please correct me if I am wrong. :)

Newbie notes (by cla):

The needed .int must be called FastRifle.int, and have only two lines:

  [Public]
  Object=(Name=FastRifle.FastRifleMutator,Class=Class,MetaClass=Engine.Mutator,Description="Fast Rifle Replacement.")

To see this mutator you must be starting a DM or TDM game. By example,It wont be visible starting a CTF game.Thats because the line DeathMatchPlus(Level.Game).GiveWeapon( Other, "FastRifle.FastRifle" );

For the code to work the package must be called FastRifle, thats the same as say 'the .uc files must be under $UTdir\FastRifle\Classes'

If you want to change the package name, say to 'FARifle', then

  put the .uc files under $UTdir\FARifle\Classes 
  change all ocurrences of 'FastRifle.FastRifle' to 'FARifle.FastRifle' in the .uc files
  change 'FastRifle.FastRifleMutator' to 'FARifle.FastRifleMutator' in the .int file
  rename the .int file to 'FARifle.int'