Wormbo's Enhanced Items
Scripting Guide

Example: Ripper II - Converting a Projectile to EnhancedProjectile

This example will show how projectiles like the ripper blades are converted into a subclass of EnhancedProjectile.

Related Topics

Conversion Basics

Often projectiles will work if you just replace the "extends Projectile" in the class declaration with "extends EnhancedProjectile", but they can't take advantage of the features the EnhancedProjectile class offers.
Projectiles require a bit more work than the projectile weapon they are fired from, but depending on the projectile there might be much less code in the class than before. The changes include the use of the death message variables, the DirectHit and/or SplashDamage functions and the Explosion and SpawnSubmunition functions.
The advantage of placing the death messages in the projectile class will become clearly visible if you try the following: Fire some ripper blades or shock rifle plasma balls at a nenemy that is very far away and quickly switch to another weapon, e.g. the impact hammer. If those shots kill the enemy you will now get the death message message of the currently selected weapon, e.g. "<your enemy's name> was smeared by <your name>'s piston.", which obviously isn't the message that should be displayed.

What exactly do I have to change?

Try to use the Explosion function to handle the projectile's impact effect.

localized string ProjectileName
localized string DirectHitString
localized string SplashHitString
localized string HeadHitString
localized string SuicideString
localized string SuicideFString
localized string HeadSuicideString
localized string HeadSuicideFString
These variables store the actual death messages used by this projectile. The death messages can contain message placeholders like UT's regular death messages with the following differences:
  • Kill messages can contain %k (the killer's name), %o (the victim's name), %w (the killer's weapon) and %p (this projectile's name).
  • Unlike in regular UT death messages every placeholder can be used as often as you like.
  • Suicide messages can contain %o, %w and %p. If %o is not used the suicider's name is placed in front of the message.
ProjectileName contains the name of the projectile. It will be used for %p in death messages. The headshot strings will only be used if bCanHeadShoot is True. (see EnhancedProjectile for details)
Explosion (optional vector HitLocation, optional vector HitNormal, optional actor HitActor) [simulated]
This function creates the impact effects of the projectile. It calles the following functions: (in that order)
SplashDamage (optional vector HitLocation, optional actor Other, optional vector HitNormal, optional bool bSpecialHit)
(for DamageRadius > 0)
DirectHit (vector HitLocation, actor Other, optional vector HitNormal, optional bool bSpecialHit)
(for DamageRadius == 0)
SpawnSubMunition (vector HitLocation, vector HitNormal, int Number)
SpawnExplosionEffects (vector HitLocation, vector HitNormal) [simulated]
BlowUp (vector HitLocation)
Destroy ( )
These functions can be customized to create the desired effects. The SpawnSubMunition function spawns the amount and type of projectiles specified in the SubMunitionCount and SubMunitionClass. These projectiles can be modified through the ModifySubMunition function.
The SpawnExplosionEffects creates the visual impact effects (ExplosionEffectClass) and decals (ExplosionDecal).
If you write custom functions for dealing out damage use DirectHit instead of TakeDamage and SplashDamage instead of HurtRadius unless you are sure that no player is going to be hit, because these functions activate the death messages. If you want to have the death messages when killing a pawn with the Died function you can use these functions:
SetKillType (bool bSplashHit, bool bHeadHit) [final]
RestoreKillType ( ) [final]
SetKillType activates the specified type of death message and RestoreKillType resets it.

Details - What does this mean for the ripper blades?

The ripper blades are an excellent example, since they are capable of doing headshots, they can hit their owner and with secondary firing mode they can also do splash damage.
First we'll look at the Razor2 class, then we'll change the Razor2Alt class. Start by copying both classes and let them extend from EnhancedProjectile. Delete the #exec lines. The original Ripper's models, textures and sounds can be used.
The first thing we'll change in Razor2 is the ProcessTouch function of the Flying state:

simulated function ProcessTouch (Actor Other, Vector HitLocation)   // old
{
    if ( bCanHitInstigator || (Other != Instigator) ) 
    {
        if ( Role == ROLE_Authority )
        {
            if ( Other.bIsPawn && (HitLocation.Z - Other.Location.Z > 0.62 * Other.CollisionHeight) 
                && (!Instigator.IsA('Bot') || !Bot(Instigator).bNovice) )
                Other.TakeDamage(3.5 * damage, instigator,HitLocation,
                    (MomentumTransfer * Normal(Velocity)), 'decapitated' );
            else             
                Other.TakeDamage(damage, instigator,HitLocation,
                    (MomentumTransfer * Normal(Velocity)), 'shredded' );
        }
        if ( Other.bIsPawn )
            PlaySound(MiscSound, SLOT_Misc, 2.0);
        else
            PlaySound(ImpactSound, SLOT_Misc, 2.0);
        destroy();
    }
}

simulated function ProcessTouch (Actor Other, Vector HitLocation)   // new
{
    if ( bCanHitInstigator || Other != Instigator ) {
        if ( Other != None && Other.bIsPawn )
            PlaySound(MiscSound, SLOT_Misc, 2.0);
        else
            PlaySound(ImpactSound, SLOT_Misc, 2.0);
        Explosion(HitLocation,,Other);
    }
}
As you can see there's much less code in that function now. The missing code will not be added anywhere else, everything is handled in the Explosion function.
The ripper blade's code is almost done. Now we'll do the death messages by adding the following lines to the defaultproperties section:
defaultproperties
{
     DirectHitString="%k ripped a chunk of meat out of %o with the %w."
     SplashHitString="%k killed %o with an explosive %p."
     HeadHitString="%k ripped %o's head off!"
     SuicideString=" ripped into himself."
     SuicideFString=" ripped into herself."
     HeadSuicideString=" ripped his own head off."
     HeadSuicideFString=" ripped her own head off."
     bCanHeadShoot=True
     HeadShotDamageFactor=3.500000
     MyDamageType=Sliced
     FiredFrom=<your ripper class here>
     ProjectileName="ripper blade"
     (...)
}
For some reason the damage type was not specified here. (And 'sliced' sounds much more descriptive than 'shredded' in this case.) Set FiredFrom to the class of the ripper you created in the Ripper I tutorial.
The HeadDamageType is not set here, since we still want the "Head Shot!" announcement, which will only be displayed when the damage type is 'Decapitated', the default value of HeadDamageType.
SplashHitString could have been added to the alt fire ripper blade, but it's nicer to have all those strings in one class, if you want to translate them using localization files.
Now we'll change the alt firing blades. First delete the following functions: Their functionality is already implemented in either EnhancedProjectile or the primary fire ripper blade class.
Then change
if ( Level.bDropDetail )
to
if ( Level.bDropDetail || (bDropEffects && !bKeepLightEffects) )
in the PostBeginPlay function. This will disable the light effect of the explosive blade not only in the frame rate is low, but also if the player chose to drop some effects to further increase performance.
Now change the code inside the HitWall function from
Super(Projectile).HitWall(HitNormal, Wall);
to
Super(EnhancedProjectile).HitWall(HitNormal, Wall);
The reason for that change is pretty obvious: The primary blade's parent class is EnhancedProjectile now, instead of Projectile.
We're almost done. The only change left is the defaultproperties section. Remove the MomentumTransfer and MyDamageType lines and add:
     bCanHeadShoot=False
     SplashDamageType=RipperSplashDamage
     DamageRadius=180.000000
     SplashMomentum=87000.000000
     ExplosionEffectClass=Class'Botpack.RipperPulse'
     ExploWallOut=16.000000
The alt firing blade cannot cause headshots, but it does splash damage and plays and explosion animation.
The only thing left to do now is changing the projectile classes in the new ripper class to match the new projectile classes.

Congratulations! You can now compile and test your new Ripper.

Related Topics