This tutorial shows how to make your own explosion class, with that you can have lots of fun ;-). When the explosion is triggered, it spawns some projectiles that spread it random directions.
First we make a new folder in 'Source\Game' called for example 'MyFireworkExplosion'. We create 'Classes' dir in there and a new file named 'PyroExplosion.uc'. Like in every class we put the following line in there:
Class PyroExplosion extends Gameplay.Explosion;
var (Explosion) class<Gameplay.Projectile> projectileClass "The class of the projectiles spread in random directions. hehehe..."; var (Explosion) int projectileCount "The number of projectiles to spawn"; var (Explosion) float projectileVelocity "Speed of the projectile when it is fired"
function Trigger(Actor Other, Pawn EventInstigator) { local int i; StoredEventInstigator = EventInstigator; for(i = 0; i < projectileCount; i++) { makeProjectile(Other, RotRand (true), Location); } GotoState('Exploding'); }
function initialiseVelocity(Gameplay.Projectile p, Vector InitialVelocity) { p.InitialVelocity = InitialVelocity; p.Velocity = InitialVelocity; } protected function makeProjectile(Actor A, Rotator fireRot, Vector fireLoc) { local Gameplay.Projectile p; p = spawn(projectileClass, A.Owner, , fireLoc); if (p == None) { LOG("Warning: Explosion "$Name$" spawn projectile "$projectileClass$" failed."); return; } p.SpawnTick = LastTick; p.SetRotation(fireRot); initialiseVelocity(p, (Vector(fireRot) * projectileVelocity)); p.Acceleration = Normal(p.Velocity) * (p.AccelerationMagtitude); }
defaultproperties { damageTypeClass = Class'ExplosionClasses.DamageTypeExplosionDefault' }That's it. Our explosion class is done. Here you can donwload the file, if you dont like copy & paste.
Ok, now we can compile it.
Add 'EditPackages=MyFireworkExplosion' to UCC.ini at the right place and run 'ucc make -nobind'.
Everything should be fine.
You can use your new class in TVed: change the extension to '.pkg', goto class browser and open you package. Then duplicate the class (right click, New...) and store it into another package. Then you can doubleclick the copy and edit it's properties. You can also set defaultproperties directly in the code.
The projectiles are spawned into a random position, so when the explosion is triggered on the ground (a projectile hits the ground), half of them hit it directly. To prevent this, we can make the explosion aligning to the floor and only spawning away from it. Make a new class in our package called 'DirectedPyroExplosion', that extends our 'PyroExplosion':
Class DirectedPyroExplosion extends PyroExplosion;
var (Explosion) float projectileSpread "Max angular deviation from ideal line of fire in degrees";
function Trigger(Actor Other, Pawn EventInstigator) { local int i; local rotator fireRot, r; local float spreadInRotUnits; StoredEventInstigator = EventInstigator; fireRot = Rotator(getHitNormal(Other)); spreadInRotUnits = projectileSpread * 65536 / 360; for(i = 0; i < projectileCount; i++) { r.Yaw = spreadInRotUnits * (2.0f * FRand() - 1); r.Pitch = spreadInRotUnits * (2.0f * FRand() - 1); makeProjectile(Other, fireRot + r, Location); } GotoState('Exploding'); }
function vector getHitNormal(Actor A) { local vector HitLocation, HitNormal; if( A != None && A.Trace(HitLocation, HitNormal, Location + 100 * Vector(Rotation), Location - 10 * Vector(Rotation), true) != None ) return HitNormal; else return Normal(Vector(Rotation) * -1); }
defaultproperties { projectileSpread = 60 }
Heres an example how to use this new explosion classes:
Our Mutator class:
class Mutator extends Gameplay.Mutator; function string MutateSpawnCombatRoleClass(Character c) { local int i, j; for(i = 0; i < c.team().combatRoleData.length; i++) for(j = 0; j < c.team().combatRoleData[i].role.default.armorClass.default.AllowedWeapons.length; j++) if(c.team().combatRoleData[i].role.default.armorClass.default.AllowedWeapons[j].typeClass == Class'EquipmentClasses.WeaponSpinfusor') { c.team().combatRoleData[i].role.default.armorClass.default.AllowedWeapons[j].quantity = 12; } return Super.MutateSpawnCombatRoleClass(c); } function Actor ReplaceActor(Actor Other) { if(Other.IsA('Spinfusor')) { Gameplay.Spinfusor(Other).projectileClass = Class'UltraDisc'; Gameplay.Spinfusor(Other).roundsPerSecond = 0.1; } return Super.ReplaceActor(Other); } defaultproperties { }
class UltraDisc extends EquipmentClasses.ProjectileSpinfusor; defaultproperties { ExplosionClass = Class'DirectedPyroExplosionMortar' LifeSpan = 6 }
Class DirectedPyroExplosionMortar extends DirectedPyroExplosion; defaultproperties { projectileClass = Class'ProjMortar' projectileCount = 10 projectileVelocity = 4000 }
class ProjMortar extends EquipmentClasses.ProjectileMortar; defaultproperties { ExplosionClass = Class'PyroExplosionBurner' }
Class PyroExplosionBurner extends PyroExplosion; defaultproperties { projectileClass = Class'EquipmentClasses.ProjectileBurner' projectileCount = 4 projectileVelocity = 6000 }