Game Development Community

Issues with my code

by Nathan Snell · in Torque Game Engine · 02/02/2006 (2:40 pm) · 3 replies

What i'm trying to do is make it so a projectile is exploded remotely by the player after having fired it.
Eg: You fire the crossbow, you wait the duration for it to "arm" then you fire again and it explodes where ever it's at.

I have all of the above working except the explosion part. I can get the projectiles explode function (CrossbowProjectile::Explode) to get executed, but no actual explosion blast (animation thing) occurs. Damage does happen, but the visual explosion doesn't. I also notice that if I don't detonate the projectile and just let the arm time elapse, it doesn't do the explosion animation either.

I've tried a bunch of debugging and stuff but haven't had any luck as to uncovering the solution. Any help people could give would be great. The script of importance:

function CrossbowProjectile::onCollision(%this,%obj,%col,%fade,%pos,%normal)
{
	$MDD[%client, fired] = 0;
	$MDD[%client, ready] = 0;


   if (%col.getType() & $TypeMasks::ShapeBaseObjectType) 
	%col.damage(%obj,%pos,%this.directDamage,"CrossbowBolt");

   // 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);

}

//This = %proj -> Projectile, comes from on fire
//%obj = %p -> comes from on fire
function CrossbowProjectile::Explode(%this,%obj)
{
	echo("We exploded.");

	%pos = %obj.getPosition();

radiusDamage(%obj,%pos,%this.damageRadius,%this.radiusDamage,%this.damageType,%this.impulse);

	%obj.schedule(9, "delete");
}

function SetReady(%client)
{
$MDD[%client, ready] = 1;
echo("We're ready.");
}

function CheckDetonate(%client, %proj)
{
	if($MDD[%client, fired] && $MDD[%client, ready])
	{
	echo("%Proj: " @ %proj);
	%proj.Explode($MDD[%client, p]);

	$MDD[%client, fired] = 0;
	$MDD[%client, ready] = 0;
	}

}

function CrossbowImage::onFire(%this, %obj, %slot)
{

   %projectile = %this.projectile;

	if($MDD[%obj.client, fired]) //If a projectile has already been fired.
	{
	echo("We know it's been fired.");
	CheckDetonate(%obj.client, %projectile);
	return;
	}

   // Decrement inventory ammo. The image's ammo state is update
   // automatically by the ammo inventory hooks.
   %obj.decInventory(%this.ammo,1);

   // Determin initial projectile velocity based on the 
   // gun's muzzle point and the object's current velocity
   %muzzleVector = %obj.getMuzzleVector(%slot);
   %objectVelocity = %obj.getVelocity();
   %muzzleVelocity = VectorAdd(
      VectorScale(%muzzleVector, %projectile.muzzleVelocity),
      VectorScale(%objectVelocity, %projectile.velInheritFactor));

   // Create the projectile object
   %p = new projectile() {
      dataBlock        = %projectile;
      initialVelocity  = %muzzleVelocity;
      initialPosition  = %obj.getMuzzlePoint(%slot);
      sourceObject     = %obj;
      sourceSlot       = %slot;
      client           = %obj.client;
   };
   MissionCleanup.add(%p);

$MDD[%obj.client, fired] = 1; //Set the global variable that the projectile has been fired
$MDD[%obj.client, p] = %p;

schedule(3000, 0, SetReady, %obj.client);

   return %p;
}

Also, is there a way to set a variable using the schedule command? It would be nice to be able to do that with $MDD[%client, ready] instead of having to call a full on function for it. Thanks to any help people my provide :)

#1
02/02/2006 (3:43 pm)
Try this

function CrossbowProjectile::Explode(%datablock,%obj){
echo("Explode called");
%pos = %obj.getPosition();
%explosion = new (Explosion)()
{
dataBlock = %datablock.explosion;
position = %pos;
};
MissionCleanup.add(%explosion);
radiusDamage(%obj, %pos, %datablock.damageRadius, %datablock.radiusDamage, "radius", %datablock.areaImpulse);
%obj.delete();
}
#2
02/02/2006 (7:19 pm)
Unfortunately, this is a slight problem with how projectiles are handled in Torque--you can't actually network an explosion, only a projectile--and since the client is what does the rendering (in the source code itself), you can't do this only with script.

Some comments from the projectile.cc Projectile::processTick():

// Ok, here is how this works:
         // onCollision is called to notify the server scripts that a collision has occured, then
         // a call to explode is made to start the explosion process. The call to explode is made
         // twice, once on the server and once on the client.
         // The server process is responsible for two things:
         //    1) setting the ExplosionMask network bit to guarantee that the client calls explode
         //    2) initiate the explosion process on the server scripts
         // The client process is responsible for only one thing:
         //    1) drawing the appropriate explosion

         // It is possible that during the processTick the server may have decided that a hit
         // has occured while the client prediction has decided that a hit has not occured.
         // In this particular scenario the client will have failed to call onCollision and
         // explode during the processTick. However, the explode function will be called
         // during the next packet update, due to the ExplosionMask network bit being set.
         // onCollision will remain uncalled on the client however, therefore no client
         // specific code should be placed inside the function!

Currently, the only way for an explosion from a projectile to be rendered is by making sure the Projectile::explode() (c++ method, NOT script) is called, and it's not exposed to script via ConsoleMethod().

It is possible that Master Treb's idea may work for you--give it a try to see if it works. If it doesn't, then we can discuss this a bit more in depth.
#3
02/02/2006 (9:19 pm)
I see.

Master Trebs idea did work for me in fixing one of the two issues. That is, if i detonate the projectile prematurely (that is, before the armdelay) it explodes properly. However, when I let it go to the armdelay and wait for it to explode, the first time it will explode after the duration as it should, any time after that it doesn't.

Another strange thing: If i remove the return; from the CrossbowImage::onfire, the projectiles explode after the duration every time. They also explode correctly when detonated early. The only thing is without the return it seems that the "click once to fire, again to detonate" method doesn't work. Instead it just keeps firing another projectile.

Based on what you said above, however, I have another question. Will Master Trebs suggestion keep the client/server consistency?

Thanks for the help thus far as well :)