Clips for guns (with animations)
by Benny Peake · in Torque 3D Professional · 09/26/2009 (9:11 pm) · 11 replies
Mathmatics
I got the idea for this resouce from www.garagegames.com/community/resources/view/2225
First, to start off, go to your weapon of choice .cs file, I'll be using the RocketLauncher.cs file.
Go to your weapon image for that weapon and add in the following.
Change it to this.
The one last thing you should do is go into the GUI and change the length of the ammo text so its bigger.
There are two quick things that need to be changed, 1, in weapon.cs change
Animations
Alright, so now that we have all the mathmatics of the reloading down, lets add a reload animation. Unfortunetly, there is no function to make the gun animate unless we use states. So, to start, go to your NoAmmo state inside RocketLauncher.cs. Change the state to:
Now, what does this all do, well first, when the No ammo is called, it waits until we have ammo. The reload function waits until the fire is complete and then tells the gun it has ammo, the gun then plays the reload clip animation then goes back to being ready. If there are any problems, comments, or suggestions, please post, I might have forgoten something. Thank you.
I got the idea for this resouce from www.garagegames.com/community/resources/view/2225
First, to start off, go to your weapon of choice .cs file, I'll be using the RocketLauncher.cs file.
Go to your weapon image for that weapon and add in the following.
usesclip = true; ammoInClip = 3; clipSize = 3; reloadSec = 2.3; reloadType = "Clip";you can add this anywhere before the state names for the animations. Next go to your script/server folder and scroll down to the Weapon.cs. Then go to the WeaponImage::onMount and change it to
function WeaponImage::onMount(%this, %obj, %slot)
{
// Images assume a false ammo state on load. We need to
// set the state according to the current inventory.
if(%this.ammo !$= "")
{
if(%this.usesclip == false)
{
if (%obj.getInventory(%this.ammo))
{
%obj.setImageAmmo(%slot, true);
%currentAmmo = %obj.getInventory(%this.ammo);
}
else
%currentAmmo = 0;
%obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle);
}
else{
if(%obj.getInventory(%this.ammo) || %this.ammoInClip > 0)
{
%currentAmmo = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%currentAmmo, %this.item.previewImage,
%this.item.reticle);
if(%this.ammoInClip <= 0)
{
%this.reload(%obj, %slot, %this.clipSize, %this.reloadSec, %this.reloadType);
}
else{
%obj.setImageAmmo(%slot, true);
}
}
}
}
}This will have it check to see if the weapon has a clip or not. All I added was to see if it needed to reload, if it does, it will call the reload function, which we will add next. For this to work we need two functions. One to do all the math of the reloading, and one to tell the game you have reloaded. Add this to the end of the same file.function WeaponImage::reload(%this, %obj, %slot, %bulletsLeft, %time, %reloadType)
{
%obj.setImageAmmo(%slot, false);
if(%obj.getInventory(%this.ammo) <= 0)//no Ammo left
{
%obj.setImageAmmo(%slot, false);
return;
}
if(%bulletsLeft == 0)//see if there are no bullets to reload, if so stop the reload process
{
%obj.setImageAmmo(%slot, true);
return;
}
if(%reloadType $= "Clip")//Clip type
{
if(((%obj.getInventory(%this.ammo)) - %bulletsLeft) >= 0)//see if we have enough bullets
{
%obj.decInventory(%this.ammo,%bulletsLeft);//if so decrease the ammount we need
%this.ammoInClip = %bulletsLeft;// set it to the number of bullets we needed
%bulletsLeft -= %bulletsLeft;//set bullets left to zero
//ready to go
}
else{//if not then put bullets in until where out
%bulletsLeft = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo),
%this.item.previewImage, %this.item.reticle);
%obj.setImageAmmo(%slot, false);
for (%i = 0; %i < %bulletsLeft; %i++)
{
%obj.decInventory(%this.ammo,1);//decrease the bullets by one
%this.ammoInClip += 1;//give one to the clip and then repeat if we can still add more
}
}
//schedule(%time * 1000, %obj, "setImageAmmo",%slot,true);//reload done, set ready
echo(%time);
%this.schedule(%time*1000, "onReloadDone",%obj,%slot);
%obj.setImageAmmo(%slot, false);
}
}
function WeaponImage::onReloadDone(%this,%obj, %slot)
{
%obj.setImageAmmo(%slot, true);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo), %this.item.previewImage, %this.item.reticle);
}The reload function is fairly easy to figure out. First we set the gun to play its no ammo animation, Next, if it has no ammo left in the pile we jump out of the function so the gun will stay with no ammo, then we check the clip type, if its a full clip at a time, we reload all at once.We check to see if we have enough ammo to fill the clip, if so, we fill it up all the way, if not, we use a for loop to reload until were out of ammo. Then we schedual the reload done so the gun will be ready in the ammount of time you want it to reload for. Now, there is one last thing to change, and that is the onFire function.Change it to this.
function WeaponImage::onFire(%this, %obj, %slot)
{
//echo("c4WeaponImage::onFire( "@%this.getName()@", "@%obj.client.nameBase@", "@%slot@" )");
// Decrement inventory ammo. The image's ammo state is updated
// automatically by the ammo inventory hooks.
if(%this.usesclip == false)
{
if ( !%this.infiniteAmmo )
{
%obj.decInventory(%this.ammo, 1);
%currentAmmo = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle);
}
if (%this.projectileSpread)
{
// We'll need to "skew" this projectile a little bit. We start by
// getting the straight ahead aiming point of the gun
%vec = %obj.getMuzzleVector(%slot);
// Then we'll create a spread matrix by randomly generating x, y, and z
// points in a circle
for(%i = 0; %i < 3; %i++)
%matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.projectileSpread @ " ";
%mat = MatrixCreateFromEuler(%matrix);
// Which we'll use to alter the projectile's initial vector with
%muzzleVector = MatrixMulVector(%mat, %vec);
}
else
{
// Weapon projectile doesn't have a spread factor so we fire it using
// the straight ahead aiming point of the gun
%muzzleVector = %obj.getMuzzleVector(%slot);
}
// Get the player's velocity, we'll then add it to that of the projectile
%objectVelocity = %obj.getVelocity();
%muzzleVelocity = VectorAdd(
VectorScale(%muzzleVector, %this.projectile.muzzleVelocity),
VectorScale(%objectVelocity, %this.projectile.velInheritFactor));
// Create the projectile object
%p = new (%this.projectileType)()
{
dataBlock = %this.projectile;
initialVelocity = %muzzleVelocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
// Do some recoil if this weapon has the required field: recoilType
if (%this.recoilType !$= "")
CommandToClient(%obj.client, 'DoRecoil', %this.recoilType);
return %p;
}
else{
//%obj.decInventory(%this.ammo, 1);
%currentAmmo = %obj.getInventory(%this.ammo);
%this.ammoInClip -= 1;
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%currentAmmo, %this.item.previewImage,
%this.item.reticle);
if (%this.projectileSpread)
{
// We'll need to "skew" this projectile a little bit. We start by
// getting the straight ahead aiming point of the gun
%vec = %obj.getMuzzleVector(%slot);
// Then we'll create a spread matrix by randomly generating x, y, and z
// points in a circle
for(%i = 0; %i < 3; %i++)
%matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.projectileSpread @ " ";
%mat = MatrixCreateFromEuler(%matrix);
// Which we'll use to alter the projectile's initial vector with
%muzzleVector = MatrixMulVector(%mat, %vec);
}
else
{
// Weapon projectile doesn't have a spread factor so we fire it using
// the straight ahead aiming point of the gun
%muzzleVector = %obj.getMuzzleVector(%slot);
}
// Get the player's velocity, we'll then add it to that of the projectile
%objectVelocity = %obj.getVelocity();
%muzzleVelocity = VectorAdd(
VectorScale(%muzzleVector, %this.projectile.muzzleVelocity),
VectorScale(%objectVelocity, %this.projectile.velInheritFactor));
// Create the projectile object
%p = new (%this.projectileType)()
{
dataBlock = %this.projectile;
initialVelocity = %muzzleVelocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
if(%this.ammoInClip <= 0)
%this.reload(%obj, %slot, %this.clipSize, %this.reloadSec, %this.reloadType);
// Do some recoil if this weapon has the required field: recoilType
if (%this.recoilType !$= "")
CommandToClient(%obj.client, 'DoRecoil', %this.recoilType);
return %p;
}
}All this basicly does is take ammo from the clip instead of the ammo pile and reload if were out of ammo.The one last thing you should do is go into the GUI and change the length of the ammo text so its bigger.
There are two quick things that need to be changed, 1, in weapon.cs change
if ($weaponOrderIndex[%requestedSlot] !$= "" && %this.hasInventory($weaponOrderIndex[%requestedSlot]) && %this.hasAmmo($weaponOrderIndex[%requestedSlot]))to
if (($weaponOrderIndex[%requestedSlot] !$= "" && %this.hasInventory($weaponOrderIndex[%requestedSlot]) && %this.hasAmmo($weaponOrderIndex[%requestedSlot])) || ($weaponOrderIndex[%requestedSlot] !$= "" && %this.hasInventory($weaponOrderIndex[%requestedSlot]) && %this.hasClip($weaponOrderIndex[%requestedSlot])))finaly add
function ShapeBase::hasClip(%this,%weapon)
{
return(%weapon.image.ammoInClip > 0);
}to inventory.cs and thats all.Animations
Alright, so now that we have all the mathmatics of the reloading down, lets add a reload animation. Unfortunetly, there is no function to make the gun animate unless we use states. So, to start, go to your NoAmmo state inside RocketLauncher.cs. Change the state to:
stateName[6] = "NoAmmo"; stateScript[6] = "onNoAmmo"; stateTransitionOnAmmo[6] = "ClipReload"; stateSequence[6] = "NoAmmo"; stateTransitionOnTriggerDown[6] = "DryFire";next at the end of the file add
stateName[19] = "ClipReload"; stateTransitionOnTimeout[19] = "Ready"; stateTimeoutValue[19] = 5.3; stateAllowImageChange[19] = false; stateSequence[19] = "magReload"; stateEjectShell[19] = false; // set to true to enable shell casing eject stateSound[19] = RocketLauncherReloadSound;with the current code we have this will not work, so we have to modify it a little. Go to the WeaponImage::Reload and change it to
function WeaponImage::reload(%this, %obj, %slot, %bulletsLeft, %time, %reloadType)
{
%obj.setImageAmmo(%slot, false);
if(%obj.getInventory(%this.ammo) <= 0)//no Ammo left
{
%obj.setImageAmmo(%slot, false);
return;
}
if(%bulletsLeft == 0)//see if there are no bullets to reload, if so stop the reload process
{
%obj.setImageAmmo(%slot, true);
return;
}
if(%reloadType $= "Clip")//Clip type
{
if(((%obj.getInventory(%this.ammo)) - %bulletsLeft) >= 0)//see if we have enough bullets
{
%obj.decInventory(%this.ammo,%bulletsLeft);//if so decrease the ammount we need
%this.ammoInClip = %bulletsLeft;// set it to the number of bullets we needed
%bulletsLeft -= %bulletsLeft;//set bullets left to zero
//ready to go
}
else{//if not then put bullets in until where out
%bulletsLeft = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo),
%this.item.previewImage, %this.item.reticle);
%obj.setImageAmmo(%slot, false);
for (%i = 0; %i < %bulletsLeft; %i++)
{
%obj.decInventory(%this.ammo,1);//decrease the bullets by one
%this.ammoInClip += 1;//give one to the clip and then repeat if we can still add more
}
}
%this.schedule(%time*1000, "onReloadDone",%obj,%slot);
%this.schedule(%time*1000, "reloadPhase2",%obj,%slot);
%obj.setImageAmmo(%slot, false);
//no Longer have to wait to set ammo true, reload animation happens right after ammo is gotten
}
}now go to WeaponImage::onReloadDone and change it tofunction WeaponImage::onReloadDone(%this,%obj, %slot)
{
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo), %this.item.previewImage, %this.item.reticle);
}then at the bottom of the file addfunction WeaponImage::onNoAmmo(%this, %obj, %slot)
{
if(%obj.getInventory(%this.ammo) <=0 && %this.ammoInClip == 0)
{
%obj.setImageAmmo(%slot, false);//no ammo
}
}
function WeaponImage::reloadPhase2(%this,%obj,%slot)
{
%obj.setImageAmmo(%slot, true);
}now there is one last thing to do, in the RocketLauncher.cs find the reloadSec and change it to the transition time for the fire sequence(0.833).Now, what does this all do, well first, when the No ammo is called, it waits until we have ammo. The reload function waits until the fire is complete and then tells the gun it has ammo, the gun then plays the reload clip animation then goes back to being ready. If there are any problems, comments, or suggestions, please post, I might have forgoten something. Thank you.
About the author
#2
02/02/2010 (10:42 am)
yeah this is workin quite well but i have a question that i just cant seem to hammer out...anyway to make this count clips instead of total ammo? thanks!
#3
First, add this to your weapon.cs
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo),
%this.item.previewImage, %this.item.reticle);
to this
02/02/2010 (10:14 pm)
I think I have what you are looking for.First, add this to your weapon.cs
function WeaponImage::getClipNumber(%this,%obj,%slot)
{
if(%obj.getInventory(%this.ammo) % %this.clipSize == 0)//can be broken evenly
return %obj.getInventory(%this.ammo)/%this.clipSize;
else
{
%bulletNumber = %obj.getInventory(%this.ammo);
while(%bulletNumber % %this.clipSize != 0)
{
%bulletNumber++;//find nearest number that it is divisible by
}
return %bulletNumber/%this.clipSize;
}
}Will get how many clips are in a weapon. To display it to the hud change any of the places you see this%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%obj.getInventory(%this.ammo),
%this.item.previewImage, %this.item.reticle);
to this
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/%this.getClipNumber(%obj,%slot)),
%this.item.previewImage, %this.item.reticle);that should do it. Tell me if this works for you.
#4
So I think that you need to call the reload() function on Ammo pickup in weapon.cs to update on getting new ammo ... or you get this odd extra bullet instantly loaded which you have to clear before clip reloading begins.
My own clip/ammo system is based on a slightly different resource and it's accompanying comments -linky- and then my own mods and changes.
But essentially they all do the same thing ...
02/02/2010 (11:52 pm)
Actually Will, I found an issue with your script. When the player has completely run out of bullets/clips and then picks some new ammo up, he has to fire a shot to clear the breach before it'll reload a new clip.So I think that you need to call the reload() function on Ammo pickup in weapon.cs to update on getting new ammo ... or you get this odd extra bullet instantly loaded which you have to clear before clip reloading begins.
My own clip/ammo system is based on a slightly different resource and it's accompanying comments -linky- and then my own mods and changes.
But essentially they all do the same thing ...
#5
>>> Advanced script error report. Line 489.
>>> Some error context, with ## on sides of error halt:
%bulletsLeft -= %bulletsLeft;//set bullets left to zero
//ready to go
}
else{//if not then put bullets in until where out
%bulletsLeft = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/%this.getClipNumber(%obj,%slot)),
## ## %this.item.previewImage, %this.item.reticle);
%obj.setImageAmmo(%slot, false);
for (%i = 0; %i < %bulletsLeft; %i++)
{
%obj.decInventory(%this.ammo,1);//decrease the bullets by one
%this.ammoInClip += 1;//give one to the clip and then repeat if we can still add more
>>> Error report complete.
im sure im just doin something dumb. you see anything?
THANKS!!
02/03/2010 (11:35 am)
well im not sure what i did but every time i put this in...and no matter how i put it in i get this error.>>> Advanced script error report. Line 489.
>>> Some error context, with ## on sides of error halt:
%bulletsLeft -= %bulletsLeft;//set bullets left to zero
//ready to go
}
else{//if not then put bullets in until where out
%bulletsLeft = %obj.getInventory(%this.ammo);
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/%this.getClipNumber(%obj,%slot)),
## ## %this.item.previewImage, %this.item.reticle);
%obj.setImageAmmo(%slot, false);
for (%i = 0; %i < %bulletsLeft; %i++)
{
%obj.decInventory(%this.ammo,1);//decrease the bullets by one
%this.ammoInClip += 1;//give one to the clip and then repeat if we can still add more
>>> Error report complete.
im sure im just doin something dumb. you see anything?
THANKS!!
#6
try
02/03/2010 (1:36 pm)
Sorry, I posted the wrong codetry
%obj.client.RefreshWeaponHud(%currentAmmo@"/"@%this.getClipNumber(%obj,%slot), %this.item.previewImage, %this.item.reticle);I left out a " next to the slash. My bad.
#7
so it should look like this
There was one more thing that I fixed, before whenever you reloaded the ammo counter would disipear sometimes and then reapear. To fix this, go to your scripts/client folder and go into [bold]client.cs[/bold]
change the clientCmdRefreshWeaponHUD to this
02/03/2010 (7:40 pm)
I think I have a fix for when you pick up ammo. Go to the datablock file for your weapon of choice and where you added the clip information (clipSize, hasclip, etc.) add one more called outOfAmmo and set it to falseso it should look like this
usesclip = true; ammoInClip = 0; clipSize = 3; reloadSec = 2.3; reloadType = "Clip"; outOfAmmo = false;next go to your states and change your clip reload to this
stateName[12] = "ClipReload"; stateTransitionOnTimeout[12] = "Ready"; stateTimeoutValue[12] = 5.3; stateAllowImageChange[12] = false; stateSequence[12] = "magReload"; stateEjectShell[12] = false; // set to true to enable shell casing eject stateScript[12] = "onReload"; stateSound[12] = RocketLauncherReloadSound;now all we have to do is add two more function. Go to [bold]weapon.cs[/bold] and these functions
function WeaponImage::onReload(%this,%obj,%slot)
{
if(!%this.outOfAmmo)
return;
%this.outOfAmmo = false;
%this.mathReload(%obj,%slot,%this.clipSize);
}
function WeaponImage::mathReload(%this, %obj, %slot, %bulletsLeft)
{
if(((%obj.getInventory(%this.ammo)) - %bulletsLeft) >= 0)//see if we have enough bullets
{
%obj.decInventory(%this.ammo,%bulletsLeft);//if so decrease the ammount we need
%this.ammoInClip = %bulletsLeft;// set it to the number of bullets we needed
%bulletsLeft -= %bulletsLeft;//set bullets left to zero
//ready to go
}
else{//if not then put bullets in until where out
%bulletsLeft = %obj.getInventory(%this.ammo);
for (%i = 0; %i < %bulletsLeft; %i++)
{
%obj.decInventory(%this.ammo,1);//decrease the bullets by one
%this.ammoInClip += 1;//give one to the clip and then repeat if we can still add more
}
}
//schedule(%time * 1000, %obj, "setImageAmmo",%slot,true);//reload done, set ready
%this.onReloadDone(%obj,%slot);
//%obj.setImageAmmo(%slot, true);
}that should fix that bug. Thank you for pointing that out Steve.There was one more thing that I fixed, before whenever you reloaded the ammo counter would disipear sometimes and then reapear. To fix this, go to your scripts/client folder and go into [bold]client.cs[/bold]
change the clientCmdRefreshWeaponHUD to this
function clientCmdRefreshWeaponHUD(%amount, %preview, %ret)
{
AmmoAmount.setVisible(true);
AmmoAmount.setText("Ammo: "@ %amount);
if (%preview $= "")
WeaponHUD.setVisible(false);//PreviewImage.setVisible(false);
else
{
WeaponHUD.setVisible(true);//PreviewImage.setVisible(true);
PreviewImage.setbitmap("art/gui/weaponHud/"@ detag(%preview));
}
if (%ret $= "")
Reticle.setVisible(false);
else
{
Reticle.setVisible(true);
Reticle.setbitmap("art/gui/weaponHud/"@ detag(%ret));
}
}I think that should do it.
#8
thanks again!
02/04/2010 (3:00 am)
ok this works great! thanks a bunch. im messin with it a bit so that i can get the clip amount and the clip count as opposed to total ammo and clip count....ie 30/10 as opposed to 300/10 and allso have the 30 decrease by one each time the trigger is pulled. (30 is the ammount in the clip and 10 is the amount of clips) and (300 is total ammount of ammo allowed) i hope im not buggin ya too much cuz i really appreciate the help.thanks again!
#9
02/04/2010 (3:43 pm)
No problem. I think you can use%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"%this.getClipNumber(%obj,%slot)),
%this.item.previewImage, %this.item.reticle);to show the ammount of ammo in the current clip and the number of clips you have in your possesion. If you put this inside any of the WeaponImage functions, this should show it.
#10
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"%this.getClipNumber(%obj,%slot)),
%this.item.previewImage, %this.item.reticle);
to...
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%this.getClipNumber(%obj,%slot),
%this.item.previewImage,
%this.item.reticle);
other than that it works superb. thanks a bunch!
02/05/2010 (12:04 am)
hey that works like a charm. i did have to change it a bit tho. i changed %obj.client.RefreshWeaponHud(%this.ammoInClip@"/"%this.getClipNumber(%obj,%slot)),
%this.item.previewImage, %this.item.reticle);
to...
%obj.client.RefreshWeaponHud(%this.ammoInClip@"/"@%this.getClipNumber(%obj,%slot),
%this.item.previewImage,
%this.item.reticle);
other than that it works superb. thanks a bunch!
#11
02/05/2010 (12:58 am)
No problem, thanks for using my resource. Sorry, I think I had made a typo while puting down that code.
Associate Steve Acaster
[YorkshireRifles.com]