Wormbo's Enhanced Items
Scripting Guide

Example: Ripper I - Converting a TournamentWeapons to EnhancedWeapon

This example will show how a weapon like the UT Ripper is converted into a subclass of EnhancedWeapon.

Related Topics

Conversion Basics

Most weapons will work if you just replace the "extends TournamentWeapon" in the class declaration with "extends EnhancedWeapon", but they usually can't take advantage of all features the EnhancedWeapon class offers.
The Ripper is a simple projectile weapon. Most of its functionality (like the firing states) resides in its superclass. The EnhancedWeapon class already takes care of those functions and states, so you'll only need to change some functions and/or state code in the weapon class itself.
The changes will concentrate mostly on the PlayAnim, LoopAnim, TweanAnim and SetTimer and Pawn(Owner).PlayRecoil functions where the animation speed will depend on the value of the SpeedScale variable so the weapon's speed can be changed from the PickupPlus class. SpeedScale initially is 1, but you can use a different default value for subclasses of your weapon if you want to change their speed without having to rewrite all those functions. You can think of it as the base speed of the weapon.

What exactly do I have to change?

Since the functions are used differently you will have to find out yourself the which changes work best for your weapon, but I will explain in general and in detail for the Ripper. You shouldn't change the speed for bringing up the weapon and for putting it down, since that might look a bit strange at higher SpeedScale values.

PlayAnim(name Sequence, optional float Rate, optional float TweenTime)
The PlayAnim function's Rate parameter determines, how fast the animation should play. Higher values basically make the animation faster, so just multiply the Rate parameter with the SpeedScale variable. If that parameter is not yet used just add SpeedScale as Rate.
TweenTime specifies how long it takes to transform the current animation sequence to the new one. The new sequence starts after this time has passed. If the TweenTime parameter has a significant influence on the time whole animation need for playing it sould be divided by SpeedScale to make the tween time shorter. If TweenTime is not used it is treated like 0, so it has no influence at all.
LoopAnim(name Sequence, optional float Rate, optional float TweenTime, optional float MinRate)
LoopAnim is changed in the same way like PlayAnim. If the MinRate parameter is used treat it like the Rate parameter.
TweenAnim(name Sequence, optional float Time)
TweenAnim just smoothly transforms the current animation sequence to the new one, but does not play that sequence. The Time parameter should be divided by SpeedScale to speed up the animation when SpeedScale gets larger.
SetTimer(float NewTimerRate, bool bLoop)
NewTimerRate should be divided by SpeedScale if the timer is used spawn projectiles or adjust the firing animation.
Sleep(float Seconds)
Seconds should be divided by SpeedScale if the Sleep function has direct influence on the firing speed, e.g when it's used in the NormalFire state.
Pawn(Owner).PlayRecoil(float Rate)
PlayRecoil is used to animate the mesh of the weapon's owner to play the recoil effect when firing the weapon. The Rate parameter has direct influence on the PlayAnim function of the weapon's owner which plays the effect, so it should also be multiplied with SpeedScale.
Sound AmbientSound
byte SoundPitch
Some weapons like the minigun and the pulsegun use the AmbientSound property to play the firing sound. In this case you can speed up the firing sound by multiplying the default SoundPitch (which usually is 64) with Sqrt(SpeedScale). Play around with this one to get the best results.
I guess you got the point now. If you use other methods to control the firing speed, e.g. a timer variable in a Tick function or using Level.TimeSeconds, also implement SpeedScale there.

Details - What does this mean for the Ripper?

Since the Ripper is a built-in UT class you don't need all those #exec lines. Just delete them after you copied the Ripper class.
Like I mentioned earlier the Ripper mostly uses its suberclass's functions, so there's not much to change in the Ripper class.
The PlayFire function:

simulated function PlayFiring() // old
{
    LoopAnim( 'Fire', 0.7 + 0.6 * FireAdjust, 0.05 );
    PlayOwnedSound(class'Razor2'.Default.SpawnSound, SLOT_None,4.2);
}

simulated function PlayFiring() // new
{
    LoopAnim('Fire', (0.7 + 0.6 * FireAdjust) * SpeedScale, 0.05);
    PlayOwnedSound(ProjectileClass.Default.SpawnSound, SLOT_None,
            4.2 * Pawn(Owner).SoundDampening);
}
The TweenTime of the PlayAnim sequence isn't that interesting in this case. The change in PlayOwnedSound has two reasons:
  1. ProjectileClass is much more flexible than class'Razor2'. Don't go with Epic's way of specifying a fixed class name, you only make it more difficult for yourself and other people to create subclasses.
  2. The SoundDampening value might change to make the owner produce less noise
Same goes for PlayAltFiring:
simulated function PlayAltFiring() // old
{
    LoopAnim('Fire', 0.4 + 0.3 * FireAdjust,0.05);
    PlayOwnedSound(class'Razor2Alt'.Default.SpawnSound, SLOT_None,4.2);
}

simulated function PlayAltFiring() // new
{
    LoopAnim('Fire', (0.4 + 0.3 * FireAdjust) * SpeedScale, 0.05);
    PlayOwnedSound(AltProjectileClass.Default.SpawnSound, SLOT_None,
            4.2 * Pawn(Owner).SoundDampening);
}
In the case of our Ripper we will also add some new default properties:
defaultproperties
{
     SamePriorityLike=Ripper
     IdenticalTo=Ripper
     ...
}
SamePriorityLike automatically sets the new Ripper's autoswitch priority to the same value like the original Ripper's priority.
The value of IdenticalTo can be used by the ClassIsA and OtherIsA functions available in most of the EnhancedItems classes to identify the new class as a Ripper. This can be used in mutators to replace the original version of the Ripper as well as its new version with something else. Let's say you want to play a map that uses EnhancedWeapon version of the UT weapons, but you want to play it using Rockets UT. If the Rockets UT mutator finds an EnhancedWeapon with IdenticalTo set to 'Ripper' it treats the weapon like UT's Ripper and replaces it correctly.
You should change the ProjectileClass and AltProjectileClass to the new classes after you've followed the tutorial for the ripper blades.

Related Topics