Jump to content

Random Explosions For Projectiles


Steve_Yorkshire

Recommended Posts

Projectiles are a little boring having only one explosion and if you want to use an animated particle sprite sheet or an explosion model it's going to get even more repetitive.


Here's the general idea, the projectile has up to 4 explosion datablocks instead of 1. On exploding the projectile looks up how many explosion datablocks in can reference and then randomly chooses one. If it can't find any additional explosions it will default to the single stock one.


Obviously you can do the same with water explosions, but ehre I'm only using dirt/dry explosions.


I am currently in "art mode" so here's a -

**warning of terrible C++ ahead**

- feel free to expand on the idea and make it less horrible.


First up, add new datablocks and information for explosions to the projectile. Here I am using the stock projectile in art/datablocks/Lurker.cs. This requires adding new particles and emitters, as well as editing the stock BulletDirtExplosion emitters to make it easier to see.

 

//...
datablock ParticleData(BulletDirtDust)
{
   textureName          = "art/particles/impact";
   dragCoefficient      = "1";
   gravityCoefficient   = "-0.100122";
   windCoefficient      = 0;
   inheritedVelFactor   = 0.0;
   constantAcceleration = "-0.83";
   lifetimeMS           = 800;
   lifetimeVarianceMS   = 300;
   spinRandomMin = -180.0;
   spinRandomMax =  180.0;
   useInvAlpha   = true;
 
   colors[0]     = "0.496063 0.393701 0.299213 0.692913";
   colors[1]     = "0.692913 0.614173 0.535433 0.346457";
   colors[2]     = "0.897638 0.84252 0.795276 0";
 
   sizes[0]      = "0.997986";
   sizes[1]      = "2";
   sizes[2]      = "2.5";
 
   times[0]      = 0.0;
   times[1]      = "0.498039";
   times[2]      = 1.0;
   animTexName = "art/particles/impact";
};
 
datablock ParticleEmitterData(BulletDirtDustEmitter)
{
   ejectionPeriodMS = 20;
   periodVarianceMS = 10;
   ejectionVelocity = "1";
   velocityVariance = 1.0;
   thetaMin         = 0.0;
   thetaMax         = 180.0;
   lifetimeMS       = 250;
   particles = "BulletDirtDust";
   blendStyle = "ADDITIVE";//yorks "NORMAL";
};
 
//yorks start
datablock ParticleData(BulletDirtDust1)
{
   textureName          = "art/particles/impact";
   dragCoefficient      = "1";
   gravityCoefficient   = "-0.1";
   windCoefficient      = 0;
   inheritedVelFactor   = 0.0;
   constantAcceleration = "-0.83";
   lifetimeMS           = 800;
   lifetimeVarianceMS   = 300;
   spinRandomMin = -180.0;
   spinRandomMax =  180.0;
   useInvAlpha   = true;
 
   colors[0]     = "1 0 0 0";
   colors[1]     = "1 0 0.0 0.5";
   colors[2]     = "1 0 0.0 0";
 
   sizes[0]      = "1";
   sizes[1]      = "2";
   sizes[2]      = "2.5";
 
   times[0]      = 0.0;
   times[1]      = "0.5";
   times[2]      = 1.0;
   animTexName = "art/particles/impact";
};
 
datablock ParticleEmitterData(BulletDirtDust1Emitter)
{
   ejectionPeriodMS = 20;
   periodVarianceMS = 10;
   ejectionVelocity = "1";
   velocityVariance = 1.0;
   thetaMin         = 0.0;
   thetaMax         = 180.0;
   lifetimeMS       = 250;
   particles = "BulletDirtDust1";
   blendStyle = "ADDITIVE";
};
 
datablock ParticleData(BulletDirtDust2)
{
   textureName          = "art/particles/impact";
   dragCoefficient      = "1";
   gravityCoefficient   = "-0.1";
   windCoefficient      = 0;
   inheritedVelFactor   = 0.0;
   constantAcceleration = "-0.83";
   lifetimeMS           = 800;
   lifetimeVarianceMS   = 300;
   spinRandomMin = -180.0;
   spinRandomMax =  180.0;
   useInvAlpha   = true;
 
   colors[0]     = "0.0 0.0 1 1";
   colors[1]     = "0.0 0.0 1 0.5";
   colors[2]     = "0.0 0.0 1 0";
 
   sizes[0]      = "1";
   sizes[1]      = "2";
   sizes[2]      = "3";
 
   times[0]      = 0.0;
   times[1]      = "0.5";
   times[2]      = 1.0;
   animTexName = "art/particles/impact";
};
 
datablock ParticleEmitterData(BulletDirtDust2Emitter)
{
   ejectionPeriodMS = 20;
   periodVarianceMS = 10;
   ejectionVelocity = "1";
   velocityVariance = 1.0;
   thetaMin         = 0.0;
   thetaMax         = 180.0;
   lifetimeMS       = 250;
   particles = "BulletDirtDust2";
   blendStyle = "ADDITIVE";
};
 
datablock ParticleData(BulletDirtDust3)
{
   textureName          = "art/particles/impact";
   dragCoefficient      = "1";
   gravityCoefficient   = "-0.1";
   windCoefficient      = 0;
   inheritedVelFactor   = 0.0;
   constantAcceleration = "-0.83";
   lifetimeMS           = 800;
   lifetimeVarianceMS   = 300;
   spinRandomMin = -180.0;
   spinRandomMax =  180.0;
   useInvAlpha   = true;
 
   colors[0]     = "0.0 1 0 1";
   colors[1]     = "0.0 1 0 0.5";
   colors[2]     = "0.0 1 0 0";
 
   sizes[0]      = "1";
   sizes[1]      = "2";
   sizes[2]      = "3";
 
   times[0]      = 0.0;
   times[1]      = "0.5";
   times[2]      = 1.0;
   animTexName = "art/particles/impact";
};
 
datablock ParticleEmitterData(BulletDirtDust3Emitter)
{
   ejectionPeriodMS = 20;
   periodVarianceMS = 10;
   ejectionVelocity = "1";
   velocityVariance = 1.0;
   thetaMin         = 0.0;
   thetaMax         = 180.0;
   lifetimeMS       = 250;
   particles = "BulletDirtDust3";
   blendStyle = "ADDITIVE";
};
//yorks end
 
//-----------------------------------------------------------------------------
// Explosion
//-----------------------------------------------------------------------------
datablock ExplosionData(BulletDirtExplosion)
{
   soundProfile = BulletImpactSound;
   lifeTimeMS = 65;
 
   // Volume particles
   particleEmitter = BulletDirtDustEmitter;
   particleDensity = 4;
   particleRadius = 0.3;
 
   //yorks start
   /*
   // Point emission
   emitter[0] = BulletDirtSprayEmitter;
   emitter[1] = BulletDirtSprayEmitter;
   emitter[2] = BulletDirtRocksEmitter;
   */
      // Point emission
   emitter[0] = BulletDirtDustEmitter;
   emitter[1] = BulletDirtDustEmitter;
   emitter[2] = BulletDirtDustEmitter;
   //yorks end
};
 
//yorks start
datablock ExplosionData(BulletDirtExplosion1)
{
   soundProfile = BulletImpactSound;
   lifeTimeMS = 65;
 
   // Volume particles
   particleEmitter = BulletDirtDust1Emitter;
   particleDensity = 4;
   particleRadius = 0.3;
 
   // Point emission
   emitter[0] = BulletDirtDust1Emitter;
   emitter[1] = BulletDirtDust1Emitter;
   emitter[2] = BulletDirtDust1Emitter;
};
 
datablock ExplosionData(BulletDirtExplosion2)
{
   soundProfile = BulletImpactSound;
   lifeTimeMS = 65;
 
   // Volume particles
   particleEmitter = BulletDirtDust2Emitter;
   particleDensity = 4;
   particleRadius = 0.3;
 
   // Point emission
   emitter[0] = BulletDirtDust2Emitter;
   emitter[1] = BulletDirtDust2Emitter;
   emitter[2] = BulletDirtDust2Emitter;
};
 
datablock ExplosionData(BulletDirtExplosion3)
{
   soundProfile = BulletImpactSound;
   lifeTimeMS = 65;
 
   // Volume particles
   particleEmitter = BulletDirtDust3Emitter;
   particleDensity = 4;
   particleRadius = 0.3;
 
   // Point emission
   emitter[0] = BulletDirtDust3Emitter;
   emitter[1] = BulletDirtDust3Emitter;
   emitter[2] = BulletDirtDust3Emitter;
};
//yorks end
 
//...
 
datablock ProjectileData( BulletProjectile )
{
   projectileShapeName = "";
 
   directDamage        = 5;
   radiusDamage        = 0;
   damageRadius        = 0.5;
   areaImpulse         = 0.5;
   impactForce         = 1;
 
   explosion           = BulletDirtExplosion;
   decal               = BulletHoleDecal;
 
   //yorks start
   explosion1           = BulletDirtExplosion1;
   explosion2           = BulletDirtExplosion2;
   explosion3           = BulletDirtExplosion3;
   //yorks end
 
   muzzleVelocity      = 120;
   velInheritFactor    = 1;
 
   armingDelay         = 0;
   lifetime            = 992;
   fadeDelay           = 1472;
   bounceElasticity    = 0;
   bounceFriction      = 0;
   isBallistic         = false;
   gravityMod          = 1;
};
//...

 

So we have now have 4 explosions which will show white (original), red, blue and green so we can see the difference.


Next up we need to crack open the C++. Open up soure/T3D/projectile.h and list the new explosion data.

 

/...
   ExplosionData* explosion;
   S32 explosionId;
 
   //yorks start
   ExplosionData* explosion1;
   S32 explosion1Id;
 
   ExplosionData* explosion2;
   S32 explosion2Id;
 
   ExplosionData* explosion3;
   S32 explosion3Id;
   //yorks end
 
   ExplosionData* waterExplosion;      // Water Explosion Datablock
//...

 

And then the meat of the sandwich, source/T3D/projectile.cpp.

 

ProjectileData::ProjectileData()
{
//...
   explosion = NULL;
   explosionId = 0;
 
   //yorks start
   explosion1 = NULL;
   explosion1Id = 0;
 
   explosion2 = NULL;
   explosion2Id = 0;
 
   explosion3 = NULL;
   explosion3Id = 0;
   //yorks end
 
   waterExplosion = NULL;
//...
}
 
//...
 
void ProjectileData::initPersistFields()
{
//...
   addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, ProjectileData),
      "@brief Explosion datablock used when the projectile explodes outside of water.\n\n");
   addField("waterExplosion", TYPEID< ExplosionData >(), Offset(waterExplosion, ProjectileData),
      "@brief Explosion datablock used when the projectile explodes underwater.\n\n");
 
   //yorks start
   addField("explosion1", TYPEID< ExplosionData >(), Offset(explosion1, ProjectileData),
	   "@brief Explosion1 datablock used when the projectile explodes outside of water.\n\n");
   addField("explosion2", TYPEID< ExplosionData >(), Offset(explosion2, ProjectileData),
	   "@brief Explosion2 datablock used when the projectile explodes outside of water.\n\n");
   addField("explosion3", TYPEID< ExplosionData >(), Offset(explosion3, ProjectileData),
	   "@brief Explosion3 datablock used when the projectile explodes outside of water.\n\n");
   //yorks end
 
   addField("splash", TYPEID< SplashData >(), Offset(splash, ProjectileData),
      "@brief Splash datablock used to create splash effects as the projectile enters or leaves water\n\n");
//...
}
 
//...
 
bool ProjectileData::preload(bool server, String &errorStr)
{
//...
      if (!explosion && explosionId != 0)
         if (Sim::findObject(explosionId, explosion) == false)
            Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId);
 
	  //yorks start
	  if (!explosion1 && explosion1Id != 0)
		  if (Sim::findObject(explosion1Id, explosion1) == false)
			  Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion1): %d", explosion1Id);
 
	  if (!explosion2 && explosion2Id != 0)
		  if (Sim::findObject(explosion2Id, explosion2) == false)
			  Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion2): %d", explosion2Id);
 
	  if (!explosion3 && explosion3Id != 0)
		  if (Sim::findObject(explosion3Id, explosion3) == false)
			  Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion3): %d", explosion3Id);
	  //yorks end
 
      if (!waterExplosion && waterExplosionId != 0)
//...
}
 
//--------------------------------------------------------------------------
void ProjectileData::packData(BitStream* stream)
{
//...
   if (stream->writeFlag(explosion != NULL))
      stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst,
                                                 DataBlockObjectIdLast);
 
   //yorks start
   if (stream->writeFlag(explosion1 != NULL))
	   stream->writeRangedU32(explosion1->getId(), DataBlockObjectIdFirst,
	   DataBlockObjectIdLast);
 
   if (stream->writeFlag(explosion2 != NULL))
	   stream->writeRangedU32(explosion2->getId(), DataBlockObjectIdFirst,
	   DataBlockObjectIdLast);
 
   if (stream->writeFlag(explosion3 != NULL))
	   stream->writeRangedU32(explosion3->getId(), DataBlockObjectIdFirst,
	   DataBlockObjectIdLast);
   //yorks end
 
   if (stream->writeFlag(waterExplosion != NULL))
//...
}
 
void ProjectileData::unpackData(BitStream* stream)
{
//...
   if (stream->readFlag())
      explosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 
   //yorks start
   if (stream->readFlag())
	   explosion1Id = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 
   if (stream->readFlag())
	   explosion2Id = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 
   if (stream->readFlag())
	   explosion3Id = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
   //yorks end
 
   if (stream->readFlag())
//...
}
 
//...
 
void Projectile::explode( const Point3F &p, const Point3F &n, const U32 collideType )
{
//...
else 
   {
      // Client just plays the explosion at the right place...
      //       
      Explosion* pExplosion = NULL;
 
	  //yorks in
	  S32 mRandomize = 0;
 
      if (mDataBlock->waterExplosion && pointInWater(p))
      {
         pExplosion = new Explosion;
         pExplosion->onNewDataBlock(mDataBlock->waterExplosion, false);
      }
	  else
		  /*yorks out start
	  if (mDataBlock->explosion)
	  {
	  pExplosion = new Explosion;
	  pExplosion->onNewDataBlock(mDataBlock->explosion, false);
	  }
	  //yorks out end */
	  //yorks in start
	  {
		  //yorks start with 1 continue check with others if exists else default to normal explosion
		  if (mDataBlock->explosion1)
		  {
			  //yorks we have >1 explosion, check for others
			  if (mDataBlock->explosion2)
			  {
				  if (mDataBlock->explosion3)
					  mRandomize = 3;
				  else
					  mRandomize = 2;
			  }
			  else
				  mRandomize = 1;
		  }
 
		  //yorks get the random
		  if (mRandomize == 0)
		  {
			  //stock explosion
			  if (mDataBlock->explosion)
			  {
				  pExplosion = new Explosion;
				  pExplosion->onNewDataBlock(mDataBlock->explosion, false);
			  }
		  }
		  else
		  {
			  //yorks get random number betweem 0 and mRandomize
			  S32 mChoose = (gRandGen.randI(0, mRandomize));
 
			  if (mChoose == 1)
			  {
				  pExplosion = new Explosion;
				  pExplosion->onNewDataBlock(mDataBlock->explosion1, false);
			  }
			  else
			  {
				  if (mChoose == 2)
				  {
					  pExplosion = new Explosion;
					  pExplosion->onNewDataBlock(mDataBlock->explosion2, false);
				  }
				  else
				  {
					  if (mChoose == 3)
					  {
						  pExplosion = new Explosion;
						  pExplosion->onNewDataBlock(mDataBlock->explosion3, false);
					  }
					  else
					  {
						  pExplosion = new Explosion;
						  pExplosion->onNewDataBlock(mDataBlock->explosion, false);
					  }
				  }
			  }
		  }
	  }
	  //yorks in end
 
      if( pExplosion )
//...
}
 
//...

 

And that should be that. It ain't elegant but it works, as I said up top, feel free to improve it (no really plz). :lol:


And this is what it looks like in action:


KdaqnWd11Ck

Link to comment
Share on other sites

  • 4 weeks later...

I've had a bit more of play around using the subExplosion type found in explosion.cpp/h:

 

//projectile.h
class ProjectileData : public GameBaseData
{
//...
public:
 
	enum ExplosionConsts
	{
		EC_MAX_EXPLOSIONS = 4,
	};
 
   // variables set in datablock definition:
 
//...
 
   S32 fadeDelay;    // the IRangeValidatorScaled field validator
 
   /*
   ExplosionData* explosion;
   S32 explosionId;
*/
 
   ExplosionData*    explosionList[EC_MAX_EXPLOSIONS];
   S32               explosionIDList[EC_MAX_EXPLOSIONS];
 
   ExplosionData* waterExplosion;      // Water Explosion Datablock
//...

 

//projectile.cpp 
//...
void ProjectileData::initPersistFields()
{
//...
   //addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, ProjectileData),
   //   "@brief Explosion datablock used when the projectile explodes outside of water.\n\n");
 
   addField("explosion", TYPEID< ExplosionData >(), Offset(explosionList, ProjectileData), EC_MAX_EXPLOSIONS,
	   "List of additional ExplosionData objects to create at the start of the "
	   "explosion.");
 
   addField("waterExplosion", TYPEID< ExplosionData >(), Offset(waterExplosion, ProjectileData),
      "@brief Explosion datablock used when the projectile explodes underwater.\n\n");
//...
}
 
//...
bool ProjectileData::preload(bool server, String &errorStr)
{
//...
            Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(particleWaterEmitter): %d", particleWaterEmitterId);
 
	  /*
      if (!explosion && explosionId != 0)
         if (Sim::findObject(explosionId, explosion) == false)
            Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId);
	  */
 
	  for (S32 k = 0; k
	  {
		  if (!explosionList[k] && explosionIDList[k] != 0)
		  {
			  if (Sim::findObject(explosionIDList[k], explosionList[k]) == false)
			  {
				  Con::errorf(ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(explosion): 0x%x", explosionIDList[k]);
			  }
		  }
	  }
 
      if (!waterExplosion && waterExplosionId != 0)
//...
}
//...
 
void ProjectileData::packData(BitStream* stream)
{
//...
   /*
   if (stream->writeFlag(explosion != NULL))
      stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst,
                                                 DataBlockObjectIdLast);
   */
   for (S32 i = 0; i
   {
	   if (stream->writeFlag(explosionList != NULL))
	   {
		   stream->writeRangedU32(explosionList->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
	   }
   }
 
   if (stream->writeFlag(waterExplosion != NULL))
//...
}
 
void ProjectileData::unpackData(BitStream* stream)
{
//...
   /*
   if (stream->readFlag())
      explosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
   */
 
   for (S32 k = 0; k
   {
	   if (stream->readFlag())
	   {
		   explosionIDList[k] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
	   }
   }
 
   if (stream->readFlag())
      waterExplosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
//...
}
//...
 
void Projectile::explode( const Point3F &p, const Point3F &n, const U32 collideType )
{
//...
}


However it all rather ground to a halt because I am not familiar with how to get the explosionList[EC_MAX_EXPLOSIONS] in the actual explode() function of projectile.cpp. :?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...