Project: Sample Unreal Tournament Mutator, Part Two


Background

I recently got an inquiry from a person who wanted to know how to modify my previous mutator to not cause damage. Following is a description of how to do this. This also updates my mutator mod info for the UnrealEd 2.0 tool.

Preparing for UnrealScript development

First you need to have Unreal Tournament and UnrealEd installed. Fire up UnrealED 2.0 and fire up the Actor Class browser by selecting View->Actor Class Browser:


Once in the actor class browser export the source source code by selecting File->Export All Scripts:


This will dump all of Unreal Tournament's classes into the directory (Botpack) where Unreal Tournament is installed. You only need to do this once*. I strongly encourage you to browse the scripts in these directories. They're very instructional. The rest of the steps are the sames as for Boom.

*Actually it's a good idea to do this each time you patch Unreal Tournament to pick up the latest fixes.

Develop "BoomNoDamage"

Boom is a specialization of two classes; Boom (from the last exercise) and Shockwave (from UT). The Showckwave class does not have a damage attribute so we need to subclass it and slightly modify its logic. Boom needs to be modified to call the new shockwave class. The changes will be made in such a manner so that the original Boom mod is left in the game also.

Script files

All locations are relative to the UT game directory.
<gamedir>\MyPackages\Classes\ShockwaveNoDamage.uc (new)
<gamedir>\MyPackages\Classes\BoomNoDamage.uc (new)
<gamedir>\System\MyPackages.int (Modified)
<gamedir>\System\UnrealTournament.ini (Modified)

Script: ShockwaveNoDamage.uc

This is a basically a stripped down version of <gamedir>\Botpack\Classes\Shockwave.uc with just the timer method remaining:

class ShockWaveNoDamage extends ShockWave;

simulated function Timer()
{
  local actor Victims;
  local float damageScale, dist, MoScale;
  local vector dir;

  ShockSize =  13 * (Default.LifeSpan - LifeSpan) + 3.5/(LifeSpan/Default.LifeSpan+0.05);
  if ( Level.NetMode != NM_DedicatedServer )
  {
    if (ICount==4) spawn(class'WarExplosion2',,,Location);
    ICount++;

    if ( Level.NetMode == NM_Client )
    {
      foreach VisibleCollidingActors( class 'Actor', Victims, ShockSize*29, Location )
      {
        if ( Victims.Role == ROLE_Authority )
        {
          dir = Victims.Location - Location;
          dist = FMax(1,VSize(dir));
          dir = dir/dist +vect(0,0,0.3); 
          if ( (dist> OldShockDistance) || (dir dot Victims.Velocity <= 0))
          {
            MoScale = FMax(0, 1100 - 1.1 * Dist);
            Victims.Velocity = Victims.Velocity + dir * (MoScale + 20);     
//          Victims.TakeDamage
//          (
//            MoScale,
//            Instigator, 
//            Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,
//            (1000 * dir),
//            'RedeemerDeath'
//          );
          }
        }
      }
      return;
    }
  }

  foreach VisibleCollidingActors( class 'Actor', Victims, ShockSize*29, Location )
  {
    dir = Victims.Location - Location;
    dist = FMax(1,VSize(dir));
    dir = dir/dist + vect(0,0,0.3); 
    if (dist> OldShockDistance || (dir dot Victims.Velocity < 0))
    {
      MoScale = FMax(0, 1100 - 1.1 * Dist);
      if ( Victims.bIsPawn )
        Pawn(Victims).AddVelocity(dir * (MoScale + 20));
      else
        Victims.Velocity = Victims.Velocity + dir * (MoScale + 20);     
//    Victims.TakeDamage
//    (
//      MoScale,
//      Instigator, 
//      Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,
//      (1000 * dir),
//      'RedeemerDeath'
//    );
    }
  }       
  OldShockDistance = ShockSize*29;        
}

Note I have commented out the damage routines but have left in the physics code so the shockwave still throws people and bots around.

Script: BoomNoDamage.uc

This is a the same as <gamedir>\MyPackages\Classes\Boom.uc Except that the class has been renamed and the spawn command spawns ShockwaveNoDamage instead of Shockwave:

class BoomNoDamage expands Mutator;

var bool Initialized;

function PostBeginPlay()
{
  //
  //  Install only once
  // 
  if (Initialized)
    return;
  Initialized = True;

  Level.Game.RegisterDamageMutator( Self );
}

function MutatorTakeDamage(
  out int    ActualDamage, 
  Pawn       Victim, 
  Pawn       InstigatedBy, 
  out Vector HitLocation,
  out Vector Momentum, 
  name       DamageType
  )
{
  local Shockwave explosion;

  if (Victim.IsA('Bot') || Victim.IsA('PlayerPawn'))
  {
    if (Victim.Health <= ActualDamage)
    {
      explosion = Spawn(class 'ShockwaveNoDamage',,,Victim.Location);
      explosion.Instigator = Victim;
    }
  }

  //
  //  Pass on the event to the next handler
  // 
  if ( NextDamageMutator != None )
    NextDamageMutator.MutatorTakeDamage( ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType );
}

Interface file: MyPackages.int

This simply describes the two new classes for UT:
[Public]
Object=(Name=MyPackages.Boom,Class=Class,MetaClass=Engine.Mutator,Description="Boom: When person dies they explode.")
Object=(Name=MyPackages.BoomNoDamage,Class=Class,MetaClass=Engine.Mutator,Description="Boom (no Damage): When person dies they explode.")
Object=(Name=MyPackages.ShockWaveNoDamage,Class=Class,MetaClass=Engine.Effect,Description="Redeemer shockwave but without damage.")

Register the script with Unreal Tournament

Edit System/unrealtournament.ini. Find the selection with lines like EditPackages=. At the end of the section add the line:

EditPackages=MyPackages
This should be there from the previous mod but just to make certain...

Build the scripts

Create a DOS (Command) Window. cd to your Unreal Tournament System directory. Type:

del mypackages.u & ucc make
and you should see something like:
C:\Games\UnrealTournament\System>del MyPackages.u & ucc make all
--------------------Core--------------------
--------------------Engine--------------------
--------------------Editor--------------------
--------------------UWindow--------------------
--------------------Fire--------------------
--------------------IpDrv--------------------
--------------------UWeb--------------------
--------------------UBrowser--------------------
--------------------UnrealShare--------------------
--------------------UnrealI--------------------
--------------------UMenu--------------------
--------------------IpServer--------------------
--------------------Botpack--------------------
--------------------UTServerAdmin--------------------
--------------------UTMenu--------------------
--------------------UTBrowser--------------------
--------------------MyPackages--------------------
Analyzing...
Parsing Boom
Parsing BoomNoDamage
Parsing ShockWaveNoDamage
Compiling Boom
Compiling BoomNoDamage
Compiling ShockWaveNoDamage
Success - 0 error(s), 0 warnings

The delete command is to make certain that MyPackage.int builds. For some reason ucc does not seem to do a date check all of the time to see what needs to be built.

Test the script

Fire up Unreal Tournament. Pick a practice session, a map, the mutator "Boom (no damage): When person dies they explode.", several bots, and press "Start". Kill someone and watch them go boom!

Download the code

This zip file contains all of the code except for the modification to unrealtournament.ini.