Computer enthusiasts serving computer enthusiasts!

news about forums links mods tools tutorials contact
Pipebombs - author: [Parser]

Ok, for this tutorial I'll show you how to code a near-perfect pipebomb cannon and a pipebomb. The whole thing is relatively simple so I'll just go straight into the basics with the code.

For the weapon, we'll have it fire out multiple pipebombs and have alt-fire detonate them. Here's the code for the weapon complete with comments:

class PipebombCannon expands TournamentWeapon;

var bool bAlreadyFiring;

// Don't really need this but I'm a lefty so it's nice to see a left handed model :-)
function setHand(float Hand)
{
  Super.SetHand(Hand);
  if ( Hand == 1 )
    Mesh = mesh(DynamicLoadObject("Botpack.PulseGunL", class'Mesh'));
  else
    Mesh = mesh'PulseGunR';
}

// Could use rateself but the bots didn't use the gun right

function PlayFiring() // Plays the firing animation
{
  Owner.PlaySound(FireSound, SLOT_Misc,2.0*Pawn(Owner).SoundDampening);
  PlayAnim( 'Boltend', 0.3 );    
  bWarnTarget = (FRand() < 0.2);
}

function AltFire( float Value ) // This is the main core of the gun
{
  local Vector Start, X,Y,Z;
  local Pipebomb PB;
  ForEach AllActors(class'PipeBomb', PB) // Calls all pipebomb actors
  {
    // Used settimer() because Explosion() blows player up
    PB.Settimer(0.000001, false);
  }
  // Necessary otherwise the gun gets jammed
  GoToState('AltFiring2');
}

//////////////////////////////////////////////////
// Normal projectile stuff
state NormalFire
{
  function Projectile ProjectileFire(class ProjClass, float ProjSpeed, bool bWarn)
  {
    local Projectile D;
    local int i;
    local vector Start,X,Y,Z;
    local Rotator StartRot, AltRotation;

    D = Global.ProjectileFire(ProjClass, ProjSpeed, bWarn);
    StartRot = D.Rotation;
    Start = D.Location;
    // Try changing 4 to 15 for ultimate carnage
    for (i = 0; i< 4; i++)
    {
      if (AmmoType.UseAmmo(1))
      {
        AltRotation = StartRot;
        AltRotation.Pitch += FRand()*3000-1500;
        AltRotation.Yaw += FRand()*3000-1500;
        AltRotation.Roll += FRand()*9000-4500;
        D = Spawn(ProjectileClass,,, Start - 2 * VRand(), AltRotation);
      }
    }
  }

Begin:
  FinishAnim();  
  PlayAnim('Still');
  Sleep(0.4);
  // Turns the pulsegun barrel at the end of firing
  PlayPostSelect();
  Finish()
}

///////////////////////////////////////////////////
// This just makes sure that the gun doesn't jam up
state AltFiring2
{
  function EndState()
  {
    Finish();
  }

  function AnimEnd()
  {
    Finish();
  }

Begin:
  GotoState('Idle');
}

///////////////////////////////////////////////////
function PlayIdleAnim()
{
  PlayAnim('Still');
}

defaultproperties
{
   AmmoName=Class'Botpack.PAmmo'
   PickupAmmoCount=40
   bAltWarnTarget=True
   FireOffset=(X=15.000000,Y=-15.000000,Z=2.000000)
   ProjectileClass=Class'Pipebomb'
   AltProjectileClass=Class'Botpack.Plasmasphere'br>    shakemag=120.000000
   AIRating=0.400000
   RefireRate=0.800000
   FireSound=Sound'UnrealShare.ASMD.TazerFire'
   SelectSound=Sound'Botpack.PulseGun.PulsePickup'
   Misc1Sound=Sound'UnrealShare.Stinger.EndFire'
   DeathMessage="%o was boomed by %k's %w"
   AutoSwitchPriority=6
   InventoryGroup=6
   PickupMessage="You picked up the Pipebomb cannon"
   ItemName="Pipebomb cannon"
   PlayerViewOffset=(X=1.700000,Z=-2.000000)
   PlayerViewMesh=LodMesh'Botpack.PulseGunR'
   PickupViewMesh=LodMesh'Botpack.PulsePickup'
   ThirdPersonMesh=LodMesh'Botpack.PulseGun3rd'
   ThirdPersonScale=0.400000
   PickupSound=Sound'UnrealShare.Pickups.WeaponPickup'
   Mesh=LodMesh'Botpack.PulsePickup'
   bNoSmooth=False
   SoundRadius=64
   SoundVolume=255
   CollisionRadius=27.000000
   CollisionHeight=8.000000
}

Ok, so as you can see, altfire is where the real difference is. Now all we need is a pipebomb. Here's the code, the projectile doesn't have to be anything special:

class Pipebomb expands Projectile;

var bool bCanHitOwner, bHitWater;
var float Count, SmokeRate;
var ScriptedPawn WarnTarget;
var int NumExtraGrenades;

simulated function PostBeginPlay()
{
  local vector X,Y,Z;
  local rotator RandRot;

  Super.PostBeginPlay();
  if ( Level.NetMode != NM_DedicatedServer )
    PlayAnim('WingIn');
  //This'll take a while to blow up
  SetTimer(999999+FRand()*1,false);

  if ( Role == ROLE_Authority )
  {
    GetAxes(Instigator.ViewRotation,X,Y,Z);  
    Velocity = X * (Instigator.Velocity Dot X)*0.4 + Vector(Rotation)
             * (Speed + FRand() * 100);
    Velocity.z += 210;
    RandRot.Pitch = FRand() * 1400 - 700;
    RandRot.Yaw = FRand() * 1400 - 700;
    RandRot.Roll = FRand() * 1400 - 700;
    MaxSpeed = 1000;
    Velocity = Velocity >> RandRot;
    RandSpin(50000);  
    bCanHitOwner = False;
    if (Instigator.HeadRegion.Zone.bWaterZone)
    {
      bHitWater = True;
      Disable('Tick');
      Velocity=0.6*Velocity;
    }
  }
}

simulated function ZoneChange( Zoneinfo NewZone )
{
  local waterring w;
  
  if (!NewZone.bWaterZone || bHitWater) Return;

  bHitWater = True;
  w = Spawn(class'WaterRing',,,,rot(16384,0,0));
  w.DrawScale = 0.2;
  w.RemoteRole = ROLE_None;
  Velocity=0.6*Velocity;
}

simulated function Timer()
{
  // Try to use Explosion in the alt-fire and for some reason the owner blows up
  Explosion(Location+Vect(0,0,1)*16);
}

simulated function Landed( vector HitNormal )
{
  HitWall( HitNormal, None );
}

simulated function ProcessTouch( actor Other, vector HitLocation )
{
  if ( (Other!=instigator) || bCanHitOwner )
    Explosion(HitLocation);
}

simulated function HitWall( vector HitNormal, actor Wall )
{
  bCanHitOwner = True;
  // Reflect off Wall w/damping
  Velocity = 0.8*(( Velocity dot HitNormal ) * HitNormal * (-2.0) + Velocity);
  RandSpin(100000);
  speed = VSize(Velocity);
  if ( Level.NetMode != NM_DedicatedServer )
    PlaySound(ImpactSound, SLOT_Misc, FMax(0.5, speed/800) );
  if ( Velocity.Z > 400 )
    Velocity.Z = 0.5 * (400 + Velocity.Z);
  else if ( speed < 20 )
  {
    bBounce = False;
    SetPhysics(PHYS_None);
  }
}

///////////////////////////////////////////////////////
function BlowUp(vector HitLocation)
{
  // Basic damage stuff
  HurtRadius(damage, 200, 'corroded', MomentumTransfer, HitLocation);
  MakeNoise(1.0);
}

simulated function Explosion(vector HitLocation)
{
  local UT_FlameExplosion s;

  BlowUp(HitLocation);
  s = spawn(class'UT_FlameExplosion',,,HitLocation);
  Spawn(Class'UT_Ringexplosion');
  s.RemoteRole = ROLE_None;
  Destroy();
}

defaultproperties
{
   speed=600.000000
   MaxSpeed=1000.000000
   Damage=90.000000
   MomentumTransfer=50000
   ImpactSound=Sound'UnrealShare.ASMD.TazerFire'
   Physics=PHYS_Falling
   RemoteRole=ROLE_SimulatedProxy
   AnimSequence=WingIn
   Texture=Texture'Botpack.ASMDAlt.ASMDAlt_a00'
   DrawScale=0.400000
   DrawType=DT_Sprite
   Style=STY_Translucent
   AmbientGlow=64
   bUnlit=True
   bBounce=True
   bFixedRotationDir=True
   DesiredRotation=(Pitch=12000,Yaw=5666,Roll=2334)
}

Ok, so basically what we have is a pulsegun that fires out a bunch of pipebombs. You can then use alt-fire to detonate them prematurely. This can probably be improved, for example there's no code to distinquish the owner of the pipebombs. So in theory any pipebombs on the level shot by anybody can be blown up. However this is just a simple example of a pipebomb weapon. Hope it helps.

suggestions? questions? comments? please send them all to ray@beyondunreal.com


copyright 2001 ray davis.  this site and all content belongs to me unless otherwise noted, with the exception of some tutorials.  the authors retain the rights to their material, and are hosted here with their express permission.  everything else is mine though, so don't steal it. bastards.
privacy