Game Development Community

dev|Pro Game Development Curriculum

Realistic shotgun script with multiple projectiles and impulse blast

by Britton LaRoche · 08/16/2004 (10:19 pm) · 15 comments

Download Code File

I worked with Sebastiao Alemedia (Irix) to create this weapon at Game Beavers. Here is the shotgun.cs file contents. Add it to your starter.fps/server/scripts directoy. To load it you'll need to modify your game.cs file. Open the game.cs file and look for the exec statements. Add the code after the exec(item.cs); and exec weapon.cs files:

game.cs
..
   exec("./inventory.cs");
   exec("./shapeBase.cs");
   exec("./item.cs");
   exec("./health.cs");
   exec("./staticShape.cs");
   exec("./weapon.cs");
   exec("./radiusDamage.cs");
   exec("./crossbow.cs");
   exec("./player.cs");
   exec("./chimneyfire.cs");
   [b]exec("./shotgun.cs");[/b]
...

To get the impulse blast you will want to add in the impulse resource here.

And now for the contents of the shotgun.cs file:

//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Shotgun weapon. This file contains all the items related to this weapon
// including explosions, ammo, the item and the weapon item image.
// These objects rely on the item & inventory support system defined
// in item.cs and inventory.cs
//-----------------------------------------------------------------------------

datablock AudioProfile(ShotgunFire)
{
   filename    = "~/data/sound/weapons/shotgun_fire.wav";
   description = AudioDefault3d;
   preload = true;
};

datablock AudioProfile(shotgunReload)
{
   filename    = "~/data/sound/weapons/shotgun_reload.wav";
   description = AudioDefault3d;
   preload = true;
};

datablock AudioProfile(shotgunActivate)
{
   filename    = "~/data/sound/weapons/shotgun_switch.wav";
   description = AudioClosest3d;
   preload = true;
};

datablock AudioProfile(shotgunNoAmmo)
{
   filename    = "~/data/sound/weapons/shotgun_noammo.wav";
   description = AudioDefault3d;
   preload = true;
};
//-----------------------------------------------------------------------------
// Projectile trail emitter
datablock ParticleData(RifleSmokeParticle)
{
   textureName          = "~/data/shapes/weapons/shotgun/smokeParticle";

   dragCoefficient     = 0.0;
   gravityCoefficient   = -0.2;  // rises
   inheritedVelFactor   = 0.00;

   lifetimeMS           = 300;   // time in ms
   lifetimeVarianceMS   = 150;   // ...more or less

   useInvAlpha = false;
   spinRandomMin = -30.0;
   spinRandomMax = 30.0;

   colors[0]     = "0.3 0.2 0.2 1.0";
   colors[1]     = "0.4 0.3 0.3 1.0";
   colors[2]     = "0 0 0 0";

   sizes[0]      = 0.25;
   sizes[1]      = 0.4;
   sizes[2]      = 0.6;

   times[0]      = 0.0;
   times[1]      = 0.3;
   times[2]      = 1.0;
};

datablock ParticleEmitterData(RifleSmokeEmitter)
{
   ejectionPeriodMS = 10;
   periodVarianceMS = 5;

   ejectionVelocity = 0.25;
   velocityVariance = 0.10;

   thetaMin         = 0.0;
   thetaMax         = 90.0;  

   particles = RifleSmokeParticle;
};


//-----------------------------------------------------------------------------
// Weapon fire emitter

datablock ParticleData(RifleFireParticle)
{
   textureName          = "~/data/shapes/weapons/shotgun/smokeParticle";

   dragCoefficient     = 0.0;
   gravityCoefficient   = -0.1;  // rises
   inheritedVelFactor   = 0.3;

   lifetimeMS           = 200;   // Time in ms
   lifetimeVarianceMS   = 50;    // ...more or less

   useInvAlpha = false;
   spinRandomMin = -30.0;
   spinRandomMax = 30.0;

   colors[0]     = "0.3 0.2 0.2 1.0";
   colors[1]     = "0.7 0.7 0.7  1.0";
   colors[2]     = "0 0 0 0";

   sizes[0]      = 0.2;
   sizes[1]      = 0.5;
   sizes[2]      = 1;

   times[0]      = 0.0;
   times[1]      = 0.3;
   times[2]      = 1.0;
};

datablock ParticleEmitterData(RifleFireEmitter)
{
   ejectionPeriodMS = 30;
   periodVarianceMS = 5;

   ejectionVelocity = 2;
   ejectionOffset   = 0.1;
   velocityVariance = 0.10;

   thetaMin         = 0.0;
   thetaMax         = 10.0;  

   particles = RifleFireParticle;
};


//-----------------------------------------------------------------------------
// Projectile Explosion

datablock ParticleData(RifleExplosionParticle)
{
   dragCoefficient      = 2;
   gravityCoefficient   = 0.2;
   inheritedVelFactor   = 0.2;
   constantAcceleration = 0.0;
   lifetimeMS           = 750;
   lifetimeVarianceMS   = 150;
   textureName          = "~/data/shapes/weapons/shotgun/smokeParticle";
   colors[0]     = "0.7 0.3 0.3 1.0";
   colors[1]     = "0.7 0.7 0.6 0.0";
   sizes[0]      = 0.5;
   sizes[1]      = 1.0;
};

datablock ParticleEmitterData(RifleExplosionEmitter)
{
   ejectionPeriodMS = 7;
   periodVarianceMS = 0;
   ejectionVelocity = 1;
   velocityVariance = 1.0;
   ejectionOffset   = 0.0;
   thetaMin         = 0;
   thetaMax         = 60;
   phiReferenceVel  = 0;
   phiVariance      = 360;
   overrideAdvance = false;
   particles = "RifleExplosionParticle";
};

datablock ExplosionData(shotgunExplosion)
{
   //explosionShape = "~/data/shapes/fx/effect_plasma_explosion.dts";
   lifeTimeMS = 1500;
   //sound = ShotgunExpSound;

   particleEmitter = RifleExplosionEmitter;
   particleDensity = 50;
   particleRadius = 1;

   faceViewer     = true;
   explosionScale = "1 1 1";

   shakeCamera = true;
   camShakeFreq = "10.0 11.0 10.0";
   camShakeAmp = "1.0 1.0 1.0";
   camShakeDuration = 0.5;
   camShakeRadius = 10.0;

   // Dynamic light
   lightStartRadius = 1;
   lightEndRadius = 0;
   lightStartColor = "0.6 0.6 0.6";
   lightEndColor = "0 0 0";
};


//-----------------------------------------------------------------------------
// Projectile Object

datablock ProjectileData(shotgunProjectile)
{
   projectileShapeName = "~/data/shapes/weapons/shotgun/projectile.dts";
   directDamage        = 30;
   radiusDamage        = 20;
   damageRadius        = 2;

   areaImpulse         = 500;

   explosion           = shotgunExplosion;
   particleEmitter     = RifleSmokeEmitter;


   isShotGun = true; // **** Added in Projectile Spread Tutorial
// This defines whether the weapon should act like a shotgun or not.
// Feel free to change as you see fit for fun ;)


   muzzleVelocity      = 500;
   velInheritFactor    = 1;

   armingDelay         = 0;
   lifetime            = 2000;
   fadeDelay           = 1500;
   bounceElasticity    = 0;
   bounceFriction      = 0;
   isBallistic         = true;
   gravityMod = 0.10;

   hasLight    = true;
   lightRadius = 3.0;
   lightColor  = "0.6 0.5 0.5";
};

function shotgunProjectile::onCollision(%this,%obj,%col,%fade,%pos,%normal)
{
   //alxPlay("ShotgunExpSound",%pos); //play rifle fire sound

   // Apply damage to the object all shape base objects
   if (%col.getType() & $TypeMasks::ShapeBaseObjectType)
      %col.damage(%obj,%pos,%this.directDamage,"RifleBullet");

   // Radius damage is a support scripts defined in radiusDamage.cs
   // Push the contact point away from the contact surface slightly
   // along the contact normal to derive the explosion center. -dbs
//   radiusDamage
//     (%obj, VectorAdd(%pos, VectorScale(%normal, 0.01)),
//      %this.damageRadius,%this.radiusDamage,"Radius",40);
   radiusDamage
     (%obj, VectorAdd(%pos, VectorScale(%normal, 0.01)),
      %this.damageRadius,%this.radiusDamage,"Radius", %this.areaImpulse);

}


//-----------------------------------------------------------------------------
// Ammo Item

datablock ItemData(shotgunAmmo)
{
   // Mission editor category
   category = "Ammo";

   // Add the Ammo namespace as a parent.  The ammo namespace provides
   // common ammo related functions and hooks into the inventory system.
   className = "Ammo";

   // Basic Item properties
   shapeFile = "~/data/shapes/weapons/shotgun/ammo.dts";
   mass = 1;
   elasticity = 0.2;
   friction = 0.6;

	// Dynamic properties defined by the scripts
	pickUpName = "shotgun ammo";
   maxInventory = 12;
};


//--------------------------------------------------------------------------
// Rifle shell that's ejected during reload.

datablock DebrisData(RifleShell)
{
   shapeFile = "~/data/shapes/weapons/shotgun/shell.dts";
   lifetime = 3.0;
   minSpinSpeed = 300.0;
   maxSpinSpeed = 800.0;
   elasticity = 0.5;
   friction = 0.2;
   numBounces = 4;
   staticOnMaxBounce = true;
   snapOnMaxBounce = false;
   fade = true;
};


//--------------------------------------------------------------------------
// Weapon Item.  This is the item that exists in the world, i.e. when it's
// been dropped, thrown or is acting as re-spawnable item.  When the weapon
// is mounted onto a shape, the RifleImage is used.

datablock ItemData(shotgun)
{
   // Mission editor category
   category = "Weapon";

   // Hook into Item Weapon class hierarchy. The weapon namespace
   // provides common weapon handling functions in addition to hooks
   // into the inventory system.
   className = "Weapon";

   // Basic Item properties
   //shapeFile = Borrow the laser mesh
   shapeFile = "~/data/shapes/weapons/shotgun/shotgun.dts";
   mass = 70;
   elasticity = 0.2;
   friction = 0.6;
   emap = true;

   gravity = 0;

   static = false;
   rotate = true;

	// Dynamic properties defined by the scripts
	pickUpName = "shotgun";
	image = shotgunImage;
};

//function shotgun::onAdd(%this,%obj)
//{
    // %obj is the object being added to the world
    // with %this datablock. This gives us the hook
    // to set the skin
    //%tag = 'blue';
    //%obj.setSkinName(%tag);
//}

//--------------------------------------------------------------------------
// Rifle image which does all the work.  Images do not normally exist in
// the world, they can only be mounted on ShapeBase objects.

datablock ShapeBaseImageData(shotgunImage)
{
   // Basic Item properties
   //shapeFile = FOR now we borrow the laser rifle
   shapeFile = "~/data/shapes/weapons/shotgun/shotgun.dts";
   emap = true;

   // Specify mount point & offset for 3rd person, and eye offset
   // for first person rendering.
   mountPoint = 0;
   offset = "0 0 0";
   eyeOffset = "0.35 0.35 -0.35";



   projectileSpread = 16/1000; // **** Added in Projectile Spread Tutorial
// This defines the spread of the projectiles. If no value is set the gun will fire
// deadly accurate.


   // When firing from a point offset from the eye, muzzle correction
   // will adjust the muzzle vector to point to the eye LOS point.
   // Since this weapon doesn't actually fire from the muzzle point,
   // we need to turn this off.  
   correctMuzzleVector = false;

   // Add the WeaponImage namespace as a parent, WeaponImage namespace
   // provides some hooks into the inventory system.
   className = "WeaponImage";

   // Projectile && Ammo.
   item = shotgun;
   ammo = shotgunAmmo;
   projectile = shotgunProjectile;
   projectileType = Projectile;
   casing = RifleShell;

   // Images have a state system which controls how the animations
   // are run, which sounds are played, script callbacks, etc. This
   // state system is downloaded to the client so that clients can
   // predict state changes and animate accordingly.  The following
   // system supports basic ready->fire->reload transitions as
   // well as a no-ammo->dryfire idle state.

   // Initial start up state
   stateName[0]                     = "Preactivate";
   stateTransitionOnLoaded[0]       = "Activate";
   stateTransitionOnNoAmmo[0]       = "NoAmmo";

   // Activating the gun.  Called when the weapon is first
   // mounted and there is ammo.
   stateName[1]                     = "Activate";
   stateTransitionOnTimeout[1]      = "Ready";
   stateTimeoutValue[1]             = 0.5;
   stateSequence[1]                 = "Activate";
   stateSound[1]                    = shotgunActivate;
   statescript[1]                   = "onActivated";
   
   // Ready to fire, just waiting for the trigger
   stateName[2]                     = "Ready";
   stateTransitionOnNoAmmo[2]       = "NoAmmo";
   stateTransitionOnTriggerDown[2]  = "Fire";
   stateSequence[2]                 = "Bob";

   // Fire the weapon. Calls the fire script which does 
   // the actual work.
   stateName[3]                     = "Fire";
   stateTransitionOnTimeout[3]      = "Reload";
   stateTimeoutValue[3]             = 0.2;
   stateFire[3]                     = true;
   stateRecoil[3]                   = LightRecoil;
   stateAllowImageChange[3]         = false;
   stateSequence[3]                 = "Fire";
   stateScript[3]                   = "onFire";
   stateEmitter[3]                  = RifleFireEmitter;
   stateEmitterTime[3]              = 0.3;
   stateSound[3]                    = ShotgunFire;

   // Play the relead animation, and transition into
   stateName[4]                     = "Reload";
   stateTransitionOnNoAmmo[4]       = "NoAmmo";
   stateTransitionOnTimeout[4]      = "Ready";
   stateTimeoutValue[4]             = 0.8;
   stateAllowImageChange[4]         = false;
   stateSequence[4]                 = "Reload";
   stateEjectShell[4]               = true;
   stateSound[4]                    = ShotgunReload;

   // No ammo in the weapon, just idle until something
   // shows up. Play the dry fire sound if the trigger is
   // pulled.
   stateName[5]                     = "NoAmmo";
   stateTransitionOnAmmo[5]         = "Reload";
   stateSequence[5]                 = "NoAmmo";
   stateTransitionOnTriggerDown[5]  = "DryFire";
   //stateScript[5]                   = "noAmmo";
   // No ammo dry fire
   stateName[6]                     = "DryFire";
   stateTimeoutValue[6]             = 1.0;
   stateTransitionOnTimeout[6]      = "NoAmmo";
   stateSound[6]                    = shotgunNoAmmo;
   //stateScript[6]                   = "noAmmo";

};


//-----------------------------------------------------------------------------


function shotgunImage::onFire(%this, %obj, %slot)
	{
		// Decrease the weapons ammo on fire
		%obj.decInventory(%this.ammo,1);

		// Get the type of projectile we are gonna fire
		%projectile = %this.projectile;

		// Get the weapons projectile spread and ensure it is never 0
		//   (we need some spread direction even if it is extremely tiny)
		%spread = %this.projectileSpread;
		%spread = %spread $= "" ? 0.001 : %spread;
		%spread = %spread <= 0 ? 0.001 : %spread;

		// Determine if we are using a shotgun class weapon or not
		//   If we are using a shotgun, set the number of projectiles we are going to fire to 12

        %shellcount = 5;
		//if(%projectile.isShotGun)
        //     %shellcount = 12;


		// Create each projectile and send it on its way
		for(%shell=0; %shell<%shellcount; %shell++)
		{
           	// Get the muzzle vector.  This is the dead straight aiming point of the gun
			%vector = %obj.getMuzzleVector(%slot);

			// Get our players velocity.  We must ensure that the players velocity is added
			//   onto the projectile
			%objectVelocity = %obj.getVelocity();

			// Determine scaled projectile vector.  This is still in a straight line as
			//   per the default example
			%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
			%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
			%velocity = VectorAdd(%vector1,%vector2);

			// Determine our random x, y and z points in our spread circle and create
			//   a spread matrix.
			%x = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
			%y = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
			%z = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
			%mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z);

			// Alter our projectile vector with our spread matrix
			%velocity = MatrixMulVector(%mat, %velocity);


			// Create our projectile
			%p = new (%this.projectileType)()
			{
				dataBlock        = %projectile;
				initialVelocity  = %velocity;
				initialPosition  = %obj.getMuzzlePoint(%slot);
				sourceObject     = %obj;
				sourceSlot       = %slot;
				client           = %obj.client;
			};
			MissionCleanup.add(%p);
		}

		return %p;
	}


You can see the whole thing in action with many other great weapons by grabbing the new release of Beaver Patrol Alpha 1.3 here

All the code to Beaver Patrol is freely available for you to use in your game. Join us at [url]Game Beavers[/url] and you can use all the art, models, interiors, vehicles, textures, sounds and music in your game too!

#1
08/17/2004 (2:21 am)
The download link seems to be broken :(
#2
08/17/2004 (9:33 am)
You can grab the file here too:

shotgun.cs
(right click save target as... to save it as a cs file instead of viewing it in the browser)

One you get the file move it to your game's server directory.. for example
C:\mygame\starer.fps\server\scripts\shotgun.cs
#3
08/17/2004 (11:39 am)
Want the full effect? You can use the shotgun sounds and images to learn how it all fits together. If you want to use the weapon in your game feel free to do so. All you need to do is join Game Beaver's and contribute. to get the shot gun dts and everything else we have.

Get the shot gun here:
shot gun images / sounds
Model and Skin by Matt Mitman / Sounds by Ryan Jaeger

Unzip the contents of the shapes.zip to a new directory:
starter.fps\data\shapes\weapons\shotgun

Unzip the sounds.zip to a new directory
starter.fps\data\sound\weapons

And you have a fully animated shotgun with all the effects, images and sounds. The shotgun has weapon animations for fire, reload, idle and slection. Its a lot of good quality work. You can get all this and more at

www.gamebeavers.org

Join us, contribute and everything belongs to you, including 10 other super cool weapons. If you dont want to join feel free to use our code base. (yes the art work is a piece of candy... with a little fish hook... here fishy fishy)
I admit it. The artwork is a ploy to get you to join us. But who wouldn't want a nice shotgun in their game? Jay says I have natural marketing tallent. I think I'd make a good crack dealer. The first hit is free. Luckily I picked video games instead of crack.
#4
08/20/2004 (12:41 am)
Here is a free shot gun that I made with my own two hands. Its not great but its totaly free to do what you want with it. Sell it commercially if you like.

Get the FooStick shot gun

It has animations for:

1. Bob (idle)
2. Fire
3. Reload
4. Select

The file has the game ready dts file and texture. It also has the max file and 3ds file for modification. No restrictions... royalty free.
#5
08/20/2004 (3:55 pm)
Anyway you have your choice! Choose the quality shotgun and sign up and join us, or use the foostick if you don't want to join.

To put it another way, "We are giving you a choice... Choose the red pill and we will show you how deep the beaver hole goes. Choose the blue pill and you can wake up and believe... what ever you want to believe." But, good luck with your game either way!
#6
08/21/2004 (10:36 pm)
Wouldn't a bullet knock a target back in the direction the bullet was traveling, instead of the direction from the bullet hit position to player center?

I think that it would be better if the impulse is applied by doing something like the following:

function shotgunProjectile::onCollision(%this,%obj,%col,%fade,%pos,%normal)
{
if (%col.getType() & $TypeMasks::ShapeBaseObjectType)
%col.damage(%obj,%pos,%this.directDamage,"RifleBullet");
%col.applyimpulse(%col.getPosition(),vectorscale(vectornormalize(%obj.initialvelocity),500);
}

perhaps you would use the radius damage explosion-center-to-player-center method if the shotgun had exploding bullets.
#7
08/22/2004 (10:43 pm)
Found the link to the resource we used for the shot gun originally... Original Resource


Yiding have you tried the code? Does it work?
#8
08/22/2004 (11:15 pm)
If you'd like to go all te way and be able to swicth weapons... here is a forum post on swithcing between weapons:


Switching between Weapons
Another method
#9
04/11/2006 (5:41 pm)
Thanks for the resource, it works great except for one thing...

When firing multiple projectiles at a high velocity (using 600)
they never all hit an object at the same time unless that object/the ground is very far away or the projectiles are moving very slowly.

What happens is that they all play the ShotgunExplosion particles over the course of a 1-1.5 seconds and it looks really strange. The only thing I can think of is that w/ high velocity, some projectiles are already hitting whatever they are going to hit and somehow, them hitting something takes priority over the weapon finishing firing? Does this make sense?

In any case, I was wondering if anyone had similar problems and/or a solution.

Thanks,

Brian
#10
10/17/2006 (8:43 pm)
Anyone still have this in the zip form? Either 1 of 2 things have happened: 1) My comp is screwing with me :) or 2) GB forgot to pay a webhosting bill

Anyways, thanks in advance, even if you don't have it

-Oliphant1
#11
10/25/2006 (3:04 am)
Unfortunately GameBeavers has been down for quite a while (at least a year I believe)
#12
12/10/2006 (4:35 am)
does anyone have this? email it to me and ill host it somewhere
#13
03/03/2008 (9:48 am)
could someone post the resource shotgun.cs and any other files that would go with the shot gun again.
#14
05/12/2008 (5:06 pm)
eww. Gamebeavers is now an adult site.
Yeah, i need the shotgun.cs file too.
#15
08/10/2008 (3:54 am)
new forum with download for Beaver Patrol and Source:
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12687