Here's the stock method which handles for plane crashes on land such effects as initial explosion, longer lasting fire/smoke, surface crater, ground shock wave and sound.
public static void AirDrop_Land(Point3d point3d)
{
if(!Config.isUSE_RENDER())
return;
float f = 4F;
float f1 = 18F;
o.set(0.0F, 90F, 0.0F);
l.set(point3d, o);
Eff3DActor.New(l, 1.0F, "effects/Explodes/Aircraft/Land/Base.eff", f);
Eff3DActor.New(l, 1.0F, "effects/Explodes/Aircraft/Land/Burn.eff", f);
Eff3DActor.New(l, 1.0F, "effects/Explodes/Aircraft/Land/Dirt.eff", f);
Eff3DActor.New(l, 1.0F, "effects/Explodes/Aircraft/Land/Smoke.eff", f);
Eff3DActor.New(l, 1.0F, "effects/Explodes/Aircraft/Land/Smoke2.eff", f);
o.set(0.0F, 0.0F, 0.0F);
l.set(point3d, o);
ExplodeSurfaceWave(0, 300F, 0.8F);
SurfaceLight(0, 150F, 0.5F);
if(Mission.isDeathmatch())
bEnableActorCrater = false;
new ActorSnapToLand("3do/Effects/Explosion/AircraftCrater.sim", true, l, 0.2F, f1, f1 + 2.0F, 1.0F, 0.0F, 1500F);
if(bEnableActorCrater)
{
int i;
for(i = 64; i >= 2 && f1 < (float)i; i /= 2);
if(i >= 2)
new ActorCrater("3do/Effects/Explosion/Crater" + i + "/Live.sim", l, 1500F);
}
bEnableActorCrater = true;
SfxExplosion.crashAir(point3d, 0);
l = new Loc(point3d);
World.cur();
l.getPoint().z = World.land().HQ(((Tuple3d) (point3d)).x, ((Tuple3d) (point3d)).y);
Eff3DActor.New(l, 1.0F, "EFFECTS/Smokes/SmokeBoiling.eff", 1200F);
Eff3DActor.New(l, 1.0F, "3DO/Effects/Aircraft/BlackHeavyGND.eff", 1200F);
Eff3DActor.New(l, 1.75F, "3DO/Effects/Aircraft/FireGND.eff", 1200F);
int j = World.rnd().nextInt(1, 4);
for(int k = 0; k < j; k++)
{
Point3d point3d1 = new Point3d(point3d);
double d = World.rnd().nextDouble(0.0D, 10D) - 5D;
if(d < 0.5D && d > -0.5D)
d = 0.5D;
point3d1.x += d;
d = World.rnd().nextDouble(0.0D, 16D) - 8D;
if(d < 0.5D && d > -0.5D)
d = 0.5D;
point3d1.y += d;
Loc loc = new Loc(point3d1);
World.cur();
loc.getPoint().z = World.land().HQ(((Tuple3d) (point3d1)).x, ((Tuple3d) (point3d1)).y);
Eff3DActor.New(loc, 1.0F, "3DO/Effects/Aircraft/FireGND.eff", 1200F - World.rnd().nextFloat(10F, 200F));
}
}
Here's what has evolved under my Neanderthalic hammerings over the past 3 years or so. Comments follow "//" characters. A key variable is "iEng", which is the number of engines.
public static void AirDrop_Land(Point3d point3d, int iEng) //gets NEW variable value for iEng from MODDED Aircraft.class, method doExplosion()
{
if (Config.isUSE_RENDER())
{
float fBurn = 3F; //equals the burn particle emit time in the .eff
int probLo = 1; //value for 1 engine
int probHi = 3; //value for 1 engine; max of 2
float scaleLight = 1F; //scales illumination distance based roughly on engine count
float f = TrueRandom.nextFloat(240F, 360F); //live time of longer-lasting smoke; 4 - 6 min
boolean bNoFireball = false; //to identify the absence of any fireball
boolean bBigFireball = false; //to identify bigger fireball having occurred
float f_2_ = 18.0F; //crater size
if (iEng >= 2 && TrueRandom.nextFloat() < 0.33F) //to randomly apply next larger fireball/smoke
iEng = 4;
if (iEng == 1 && TrueRandom.nextFloat() < 0.33F) //to randomly apply next larger fireball/smoke
iEng = 2;
scaleFire = MiscEffects.cvt(World.Sun().ToSun.z, -0.25F, 0.05F, 2.0F, 1.0F);
o.set(0.0F, 90.0F, 0.0F);
l.set(point3d, o);
if (iEng < 1 || (iEng == 1 && TrueRandom.nextFloat() < 0.5F))
{
bNoFireball = true;
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Base_0.eff", -1F); //just a large smoke/dust cloud
}
else
if (iEng == 1)
{
fBurn = 3F; //fireballs emitted for 3 sec in Burn.eff
scaleLight = 1F;
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Base1.eff", -1F);
Eff3DActor.New(l, TrueRandom.nextFloat(1.0F, 1.25F), "Effects/Explodes/Aircraft/Land/Burn1.eff", -1F);
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Smoke1.eff", -1F);
}
else
if (iEng <= 3) //2 or 3 engines
{
fBurn = 6F; //fireballs emitted for 6 sec in Burn2.eff
probLo = 1;
probHi = 4; //max of 3
scaleLight = 1.4F;
bBigFireball = true;
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Base2.eff", -1F);
Eff3DActor.New(l, TrueRandom.nextFloat(1.0F, 1.25F), "Effects/Explodes/Aircraft/Land/Burn2.eff", -1F);
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Smoke2.eff", -1F);
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Dirt.eff", -1F); //ballistic fire drops
}
else
if (iEng >= 4)
{
fBurn = 10F; //fireballs emitted for 10 sec in Burn3.eff
probLo = 2;
probHi = 5; //max of 4
scaleLight = 2F;
bBigFireball = true;
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Base3.eff", -1F);
Eff3DActor.New(l, TrueRandom.nextFloat(1.0F, 1.25F), "Effects/Explodes/Aircraft/Land/Burn3.eff", -1F);
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Smoke3.eff", -1F);
Eff3DActor.New(l, 1.0F, "Effects/Explodes/Aircraft/Land/Dirt.eff", -1F); //ballistic fire drops
}
if (World.Sun().ToSun.z < 0.05F && !bNoFireball) //0.05 is +3 deg; -0.1, is -6 deg
{
float distanceLight = MiscEffects.cvt(World.Sun().ToSun.z, -0.25F, 0.05F, 75.0F, 10.0F); //from Sun +3 deg to -15 deg, Emit distance 10m to 75m.
Eff3DActor eff3dactor = Eff3DActor.New(l, 1.0F, "Effects/Explodes/Cannon/Object/light.eff", fBurn + 0.1F); //invisible reference for following...
eff3dactor.postDestroy(Time.current() + (long) fBurn * 1000L);
LightPointActor lightpointactor = new LightPointActor(new LightPointWorld(), new Point3d(0.0D, 0.0D, 0.0D));
lightpointactor.light.setColor(1.0F, 0.6F, 0.2F);
lightpointactor.light.setEmit(1.0F, scaleLight * distanceLight);
eff3dactor.draw.lightMap().put("light", lightpointactor);
}
if (iEng >= 1) //create firey, smoking ballistic projectiles
{
Vector3d vector3d = new Vector3d();
int j = TrueRandom.nextInt(probLo, probHi); //create SLOW ballistic particles when #engines >= 1
for (int j_1_ = 0; j_1_ < j; j_1_++)
{
vector3d.set((double) TrueRandom.nextFloat(-1.0F, 1.0F), (double) TrueRandom.nextFloat(-1.0F, 1.0F), (double) TrueRandom.nextFloat(0.5F, 1.0F));
vector3d.normalize();
vector3d.scale((double) TrueRandom.nextFloat(15.0F, 35.0F)); //speed scale factor, to get m/s
float f_1 = TrueRandom.nextFloat(3F, 6F); //range of live time in seconds
BallisticProjectile ballisticprojectile = new BallisticProjectile(l.getPoint(), vector3d, f_1);
if (iEng >= 2 && TrueRandom.nextFloat() < 0.33F)
{
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "3DO/Effects/Aircraft/FireSPDShort.eff", f_1);
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "3DO/Effects/Aircraft/TankSmokeHeavySPD.eff", f_1);
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "3DO/Effects/Aircraft/EngineSmokeMediumTSPD.eff", f_1);
} else
if (TrueRandom.nextFloat() < 0.5F)
{
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "Effects/Explodes/Wreckage_Burn_2.eff", f_1); //NEW effect
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "3DO/Effects/Aircraft/EngineSmokeMediumSPD.eff", f_1);
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "3DO/Effects/Aircraft/EngineSmokeMediumTSPD.eff", f_1);
} else
{
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "Effects/Explodes/Wreckage_Burn_2.eff", f_1); //NEW effect
Eff3DActor.New(ballisticprojectile, null, null, 1.0F, "Effects/Explodes/Wreckage_Smoke_2.eff", f_1); //NEW effect, thinner smoke trail
}
}
}
if (Mission.isDeathmatch())
bEnableActorCrater = false;
new ActorSnapToLand("3do/Effects/Explosion/AircraftCrater.sim", true, l, 0.2F, f_2_, f_2_ + 2.0F, 1.0F, 0.0F, 1500.0F); //exists for 25 minutes
if (bEnableActorCrater)
{
int i;
for (i = 64; i >= 2 && f_2_ < (float) i; i /= 2)
if (i >= 2)
new ActorCrater(("3do/Effects/Explosion/Crater" + i + "/Live.sim"), l, 1500.0F);
}
bEnableActorCrater = true;
SfxExplosion.crashAir(point3d, 0);
l = new Loc(point3d);
Point3d point3d_2 = l.getPoint();
World.cur();
point3d_2.z = World.land().HQ(point3d_2.x, point3d_2.y);
o.set(0.0F, 90.0F, 0.0F);
l.set(point3d_2, o);
if (bNoFireball) //if no fireball at all, make smaller single fire and smaller smoke column
{
Eff3DActor.New(l, 1.0F, "Effects/Smokes/ArtyDyingSmoke.eff", f); //lighter, bluer, smaller, shorter lived
Eff3DActor.New(l, 0.7F * scaleFire, "3DO/Effects/Aircraft/FireCrash.eff", f - TrueRandom.nextFloat(60F, 120F)); //single small fire, ends 1 to 2 min before smoke
return; //nothing below applies...
}
if (bBigFireball) //if bigger fireball, for 2+ engines
{
Eff3DActor.New(l, 1.0F, "3DO/Effects/Aircraft/AircraftSmokeBoiling2.eff", f * 0.25F * (fBurn / 6F)); //NEW effect! fatter smoke column for 60-90 (or 96-144) sec (particle live time is 90)
Eff3DActor.New(l, 1.0F, "Effects/Smokes/SmokeOilTank1.eff", f * 0.25F * (fBurn / 6F)); //additional larger fire (as used for train oil wagon, 12m start size)
Eff3DActor.New(l, 1.0F, "Effects/Smokes/SmokeOilTank4.eff", f * 0.25F * (fBurn / 6F)); //NEW effect! accompanying animated billowing smoke for lowest part of the smoke column
Eff3DActor.New(l, 1.0F, "3DO/Effects/Aircraft/AircraftSmokeBoiling.eff", f); //usual smoke column, for full duration
}
else //no big fireball has occurred
{
if(TrueRandom.nextFloat() < 0.5F)
Eff3DActor.New(l, 1.0F, "3DO/Effects/Aircraft/AircraftSmokeBoiling_light.eff", f); //NEW! lighter, bluer alternate to the following
else
Eff3DActor.New(l, 1.0F, "3DO/Effects/Aircraft/AircraftSmokeBoiling.eff", f); //NEW! (replaces Smokes/SmokeBoiling.eff); original eff grp 1200 sec (20 min!)
}
if(!bNoFireball) //if any fireball has occurred, create fires
{
Eff3DActor.New(l, 1.0F * scaleFire, "3DO/Effects/Aircraft/FireCrash.eff", f - 30F); //NEW effect; 5m start size
if (TrueRandom.nextFloat() < 0.5F)
Eff3DActor.New(l, 1.0F, "Effects/Smokes/SmokeOilTank1.eff", TrueRandom.nextFloat(20F, 60F)); //additional larger fire (same as used for train oil wagons, 12m start size) for 20 to 60 seconds
int i = TrueRandom.nextInt(1, 4); //add up to 3 extra fires, randomly placed
for (int i_1_ = 0; i_1_ < i; i_1_++)
{
Point3d point3d_3 = new Point3d(point3d);
double d = TrueRandom.nextDouble(0.0, 20.0) - 10.0; //= -10 to 10
point3d_3.x += d;
d = TrueRandom.nextDouble(0.0, 20.0) - 10.0; //= -10 to 10
point3d_3.y += d;
point3d_3.z = World.land().HQ(point3d_3.x, point3d_3.y); //added new
Loc loc = new Loc(point3d_3);
Eff3DActor.New(loc, TrueRandom.nextFloat(0.55F, 0.85F) * scaleFire, "3DO/Effects/Aircraft/FireCrash.eff", (f - 30F) - TrueRandom.nextFloat(20F, 120F)); //NEW effect; variably smaller, lasts variably shorter time
}
}
}
}