| Home Page | Recent Changes

TheHealer/TUTHealerFire

TUTHealerFire - The Healer Part 5 of 9

In this part of the Healer tutorial, we will set up our Fire class – the primary fire mode, or the beam that will Heal your teammates. Before we get into the code, make sure that your file is set up correctly.

Filename: {Base Directory}/TheHealer/classes/TUTHealerFire.uc

If you've changed the name of the package folder or the filename itself, make sure you make those changes throughout the code we'll be working with.

Quick Link: This is the complete source code for people who want to skip ahead. If you want to copy/paste into your file, this is the page to do it on.

This is where all the cool stuff happens. Setting up the Weapon, Pickup, Ammo, and Ammo Pickup classes was necessary, but let's face it – most aspects of those files came right from the Link Gun. Well, most of this comes from the Link Gun too, but here is where we change the core functionality of the weapon to make it our own.

Heading and Class Declaration

In order to keep our code reader-friendly (always a good thing if you need someone's help!), we will add a standard comment block to the top of the file. This will describe what's going on in the file.

After the comment block, we declare the class. Make sure you name the class with the same name as the filename, or bad things happen.

//------------------------------------------------------------------------------
// class name : TUTHealerFire
// class type : Weapon Fire
// description: The Healer's Primary Fire mode
// author     : HSDanClark
//------------------------------------------------------------------------------
// TODO       :
//
//------------------------------------------------------------------------------
class TUTHealerFire extends WeaponFire;
  • Our pickup extends (or is a child of, so-to-speak) the WeaponFire class.

Exec Directives and Variable Declarations

No Exec directives.

const NoDamage = 0;

var     TUTHealerBeamEffect Beam;
var     Sound MakeLinkSound;
var     float UpTime;
var     Pawn LockedPawn;
var     float LinkBreakTime;
var()   float LinkBreakDelay;
var     float LinkScale[6];

var     String MakeLinkForce;

var()   class<DamageType> DamageType;
var()   int Damage;
var()   float MomentumTransfer;

var()   float TraceRange;
var()   float LinkFlexibility;

var bool bDoHit;
var()   bool bFeedbackDeath;
var bool bInitAimError;
var bool bLinkFeedbackPlaying;
var bool bStartFire;

var rotator DesiredAimError, CurrentAimError;

Functions

simulated function DestroyEffects()
{
    Super.DestroyEffects();
    if ( Level.NetMode != NM_Client )
    {
        if (Beam != None)
            Beam.Destroy();
    }
}

The next function, ModeTick, is where the beam damage and such is defined. Pay close attention to what's going on in here.

simulated function ModeTick(float dt)
{
    local Vector StartTrace, EndTrace, X,Y,Z;
    local Vector HitLocation, HitNormal, EndEffect;
    local Actor Other;
    local Rotator Aim;
    local TUTHealer TUTHealer;
    local float MT, Step;
    local bot B;
    local bool bShouldStop;
    local TUTHealerBeamEffect LB;
    local Pawn P;

    if (!bIsFiring)
    {
    bInitAimError = true;
        return;
    }
    TUTHealer = TUTHealer(Weapon);


    if (FlashEmitter != None)
    {
        // set muzzle flash color
        FlashEmitter.Skins[0] = Texture'XEffectMat.link_muz_green';

        // adjust muzzle flash size to link size
        FlashEmitter.mSizeRange[0] = FlashEmitter.default.mSizeRange[0] * 1;
        FlashEmitter.mSizeRange[1] = FlashEmitter.mSizeRange[0];
    }

    if ( TUTHealer.Ammo[ThisModeNum].AmmoAmount >= AmmoPerFire && ((UpTime > 0.0) || (Instigator.Role < ROLE_Authority)) )
    {
        UpTime -= dt;
        TUTHealer.GetViewAxes(X,Y,Z);

        // the to-hit trace always starts right in front of the eye
        StartTrace = Instigator.Location + Instigator.EyePosition() + X*Instigator.CollisionRadius;

        TraceRange = default.TraceRange;

        if ( Instigator.Role < ROLE_Authority )
        {
            if ( Beam == None )
                foreach DynamicActors(class'TUTHealerBeamEffect', LB )
            if ( !LB.bDeleteMe && (LB.Instigator != None) && (LB.Instigator == Instigator) )
        {
        Beam = LB;
            break;
            }
        if ( Beam != None )
        LockedPawn = Beam.LinkedPawn;
    }
        if ( LockedPawn != None )
        TraceRange *= 1.5;


    if ( LockedPawn != None )
    {
        EndTrace = LockedPawn.Location + LockedPawn.EyeHeight*Vect(0,0,0.5); // beam ends at approx gun height
    }

        if ( LockedPawn == None )
        {
            if ( Bot(Instigator.Controller) != None )
            {
        if ( bInitAimError )
        {
            CurrentAimError = AdjustAim(StartTrace, AimError);
            bInitAimError = false;
        }
        else
        {
            BoundError();
            CurrentAimError.Yaw = CurrentAimError.Yaw + Instigator.Rotation.Yaw;
        }
        // smooth aim error changes
        Step = 7500.0 * dt;
        if ( DesiredAimError.Yaw ClockWiseFrom CurrentAimError.Yaw )
        {
            CurrentAimError.Yaw += Step;
            if ( !(DesiredAimError.Yaw ClockWiseFrom CurrentAimError.Yaw) )
            {
            CurrentAimError.Yaw = DesiredAimError.Yaw;
            DesiredAimError = AdjustAim(StartTrace, AimError);
            }
        }
        else
        {
            CurrentAimError.Yaw -= Step;
            if ( DesiredAimError.Yaw ClockWiseFrom CurrentAimError.Yaw )
            {
            CurrentAimError.Yaw = DesiredAimError.Yaw;
            DesiredAimError = AdjustAim(StartTrace, AimError);
            }
        }
        CurrentAimError.Yaw = CurrentAimError.Yaw - Instigator.Rotation.Yaw;
        if ( BoundError() )
            DesiredAimError = AdjustAim(StartTrace, AimError);
        
                CurrentAimError.Yaw = CurrentAimError.Yaw + Instigator.Rotation.Yaw;

        Aim = Rotator(Instigator.Controller.Target.Location - StartTrace);

        Aim.Yaw = CurrentAimError.Yaw;

        // save difference
        CurrentAimError.Yaw = CurrentAimError.Yaw - Instigator.Rotation.Yaw;
        }
        else
            Aim = AdjustAim(StartTrace, AimError);

            X = Vector(Aim);
            EndTrace = StartTrace + TraceRange * X;
        }

        Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
        if (Other != None && Other != Instigator)
            EndEffect = HitLocation;
        else
        EndEffect = EndTrace;

        if (Beam != None)
        Beam.EndEffect = EndEffect;

        if ( Instigator.Role < ROLE_Authority )
        return;

        if (Other != None && Other != Instigator)
        {
            // beam is updated every frame, but damage is only done based on the firing rate
            if (bDoHit)
            {
                if (Beam != None)
                Beam.bLockedOn = false;

                Instigator.MakeNoise(1.0);

                if ( !Other.bWorldGeometry )
                {
                    P = Pawn(Other);

                    if (Other.Physics == PHYS_Falling || Other.Physics == PHYS_Flying
                        || Other.Physics == PHYS_Swimming)
                        MT = DeathMatch(Level.Game).LinkLockDownFactor * MomentumTransfer;
                    else
                        MT = 0.0;

                    // This is the key, this is where we give health instead of taking damage.
                    P.GiveHealth(3,199);
  
                    if (Beam != None)
                        Beam.bLockedOn = true;
                }
            }
        }
    }

    if ( bShouldStop )
    B.StopFiring();
    else
    {
    // beam effect is created and destroyed when firing starts and stops

        if ( (Beam == None) && bIsFiring )
        Beam = Spawn(class'TUTHealerBeamEffect',Instigator);

    if (Beam != None)
    {
            Beam.LinkColor = 0;
            Beam.LinkedPawn = LockedPawn;
        Beam.bHitSomething = (Other != None);
        Beam.EndEffect = EndEffect;
        }
        else
        {
            StopFiring();
        }
    }
    bStartFire = false;
    bDoHit = false;
}

Yikes. Well, that's the most important bit right in there. I will add more comments in that code later, but let's press on.

function bool BoundError()
{
    CurrentAimError.Yaw = CurrentAimError.Yaw & 65535;
    if ( CurrentAimError.Yaw > 2048 )
    {
        if ( CurrentAimError.Yaw < 32768 )
    {
        CurrentAimError.Yaw = 2048;
        return true;
    }
    else if ( CurrentAimError.Yaw < 63487 )
    {
        CurrentAimError.Yaw = 63487;
        return true;
    }
    }
    return false;
}

function DoFireEffect()
{
    bDoHit = true;
    UpTime = FireRate+0.1;
}
function PlayFiring()
{
    if (Weapon.Ammo[ThisModeNum].AmmoAmount >= AmmoPerFire)
    ClientPlayForceFeedback("BLinkGunBeam1");
    Super.PlayFiring();
}

function StopFiring()
{
    if (Beam != None)
    {
        Beam.Destroy();
        Beam = None;
    }

    bStartFire = true;
    bFeedbackDeath = false;
    StopForceFeedback("BLinkGunBeam1");
}

The two functions above control the starting and stopping of the firing process. As you can see, the PlayFiring() function checks to make sure you have enough ammo, and if you do, will start the force feedback feature should the player have it enabled. The StopFiring() function does the opposite, Destroy()ing the beam you've created, setting it to none, then stopping the force feedback feature.

simulated function vector GetFireStart(vector X, vector Y, vector Z)
{
    return Instigator.Location + Instigator.EyePosition() + X*Instigator.CollisionRadius;
}
function StartBerserk()
{
//    Damage = default.Damage * 1.33;
//    Damage = default.Damage * 1.33;
}

function StopBerserk()
{
//    Damage = default.Damage;
//    Damage = default.Damage;
}

These two functions define what happens when you use the Beserk combo. They have been commented out from the original Link Gun code, but left in as an example. Our Healer will not be affected by the Beserk adrenaline combo.

Default Properties

defaultproperties
{
    NoAmmoSound=Sound'WeaponSounds.P1Reload5'

    AmmoClass=class'TUTHealerAmmo'
    AmmoPerFire=1
    DamageType=class'DamTypeLinkShaft'
    Damage=-15
    MomentumTransfer=20000.0
    bPawnRapidFireAnim=true

    FireAnim=AltFire
    FireEndAnim=None

    MakeLinkSound=Sound'WeaponSounds.LinkGun.LinkActivated'
    MakeLinkForce="LinkActivated"

    FlashEmitterClass=class'xEffects.LinkMuzFlashBeam1st'

    TraceRange=1100      // range of everything
    LinkFlexibility=0.64 // determines how easy it is to maintain a link.
                         // 1=must aim directly at linkee, 0=linkee can be 90 degrees to either side of you

    LinkBreakDelay=0.5   // link will stay established for this long extra when blocked (so you don't have to worry about every last tree getting in the way)

    FireRate=0.2

    BotRefireRate=0.99
    WarnTargetPct=+0.2

    ShakeOffsetMag=(X=0.0,Y=1.0,Z=1.0)
    ShakeOffsetRate=(X=1000.0,Y=1000.0,Z=1000.0)
    ShakeOffsetTime=3
    ShakeRotMag=(X=0.0,Y=0.0,Z=60.0)
    ShakeRotRate=(X=0.0,Y=0.0,Z=4000.0)
    ShakeRotTime=6

    bInitAimError=true

    LinkScale(0)=0.0
    LinkScale(1)=0.5
    LinkScale(2)=0.9
    LinkScale(3)=1.2
    LinkScale(4)=1.4
    LinkScale(5)=1.5
}

Now, these default properties are going to be a little strange. This code is not optimized for our Healer weapon. In fact, most of the properties are in some way linked (get it? linked?) to the alt-fire of the Link Gun. They have been left in for now, though I intend to optimize the code to remove all unnecessary traits of the Link Gun at a later time. For now, this works.

The Next Step

Continue to Part 6, TheHealer/TUTHealerAltFire

The complete tutorial map:

  1. /TUTHealer – Our main weapon class.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealer.uc
  2. /TUTHealerPickup – Our weapon's pickup class, what you see on the ground.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerPickup.uc
  3. /TUTHealerAmmo – Our weapon's ammo class.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerAmmo.uc
  4. /TUTHealerAmmoPickup – The ammo's pickup class, what you see on the ground.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerAmmoPickup.uc
  5. /TUTHealerFire – Our weapon's primary fire class.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerFire.uc
  6. /TUTHealerAltFire – Our weapon's alternate (secondary) fire class.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerAltFire.uc
  7. /TUTHealerBeamEffect? – The Healer's Beam Effect
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerBeamEffect.uc
  8. /TUTHealerAttachment? – Our weapon's attachment class.
    1. Filename: {Base Directory}/TheHealer/classes/TUTHealerAttachment.uc
  9. [/TUT The End]? – Putting it all together.

Discussion

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

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