Download for the impatient.
Back in the day, someone wrote an Explosive Ammo mutator which made all of the ammo clips sitting on the ground blow up and shoot rockets or bullets or goo or schrapnel all over the place when you shot them. We found that mutator very amusing and it is sadly missed in the latest in the UT series: UT2004.
Since we're reasonably intelligent hackers, Brian Vargas and I decided that we couldn't live without this mutator and set out to develop our own. Having not done any Unreal scripting since the original Unreal Tournament, we were a little rusty. This page describes our journey and presents the Explosive Ammo Mutator to the world.
First of all, our knowledge of mutators and, honestly, the whole scripting engine is minimal, so if something we're doing seems really stupid, please let us know. We'd like to do this the "right way", but we'll get it done any way that works for now.
Our basic plan was to replace all of the ammo clips in the game with our own, which would simply 'wrap' existing ammo types with our exploding ones. The exploding ammo class would be sensitive to taking damage, and pretty much just generate a lot of flying death of the proper type when shot.
There are a few problems with our master plan. First, although Unreal Script is very object-oriented, there are still these 'properties' that classes can have, and we don't get to intercept get/set operations on those peoperties, so we can't actually wrap any existing ammo class with a generic exploding one: we have to create a new exploding ammo clip for each known type of clip. This sucks, because it won't extend to new ammo types without a code change. Perhaps if we're smart enough to read a config file, we could do these on the fly and so we'd only have to change a config file instead of the actual code. We'll see.
The second problem is that not all ammo works the same. There are some types of ammo which act like we thought they would: you can create an object of, say, the Rocket type, and send it off in any direction at any speed. No problem: just send off a bunch of them and let 'em go. Well, that works fine for things like rockets, link goo, and flak balls. However, ammunition for the assult rifle, lightning gun, minigun, etc. have different types of ammo that don't work that way.
Since ammo types are not generic, we decided to replace each ammo pickup with a subclass of the original. We would just override the appropriate behaviors and go with it. While this minimizes our opportunities for code-reuse, we figured it wasn't a big deal because the standard types of ammo are well-defined and aren't likely to change. Also, using this technique will not interfere with other mutators or even if new ammo types are added. (It will, however, totally crap itself if some ammo type is removed from the game. The liklihood of this happening, though, is ... low).
As I mentioned, projectiles (rockets, flak clunks, grenades, mines, etc.) are all very easy to do : you simple spawn a projectile pointing in a particular direction with a speed vector, etc. and off it goes. Here's the actual code for doing this:
local Projectile projectile; projectile = ammo.Spawn(class'BioGlob', ammo, , ammo.Location, ammo.RotRand());
The Spawn
method is defined in the Actor
class,
and takes the following arguments:
RotRand
function (defined in Object)
to pick a random direction for the new projectile.After projectiles, all other weapons do their own dirty-work. That means that when you fire the weapon, the damage is done instantaneously to any targets that are found. The weapon (the minigun, for example) actually follows lines outward, looking for hits. When it finds a hit, it will either damage the target (like a person or vehicle) or produce some effect (like a bullet ricochet or burn mark on a wall).
For these types of ammo, we have performed our own hit-scans in multiple, random directions. Here's the code that fires the weapon once:
local Actor other; local Vector hitLocation, hitNormal, endLocation, direction, momentum; local Rotator fireRotation; local int damage; fireRotation = RotRand(); direction = Vector(fireRotation); other = self.Trace(hitLocation, hitNormal, endLocation, self.Location, true); if (other == none) { // Missed everything == hit the sky hitLocation = endLocation; hitNormal = Normal(self.Location - endLocation); } else if (!other.bWorldGeometry && other != self) { // We've hit something other than the "world" or the ammo pickup itself damage = class'UTClassic.ClassicSniperFire'.default.DamageMin + Rand(class'UTClassic.ClassicSniperFire'.default.DamageMax - class'UTClassic.ClassicSniperFire'.default.DamageMin); momentum = hitNormal * class'UTClassic.ClassicSniperFire'.default.Momentum; // Inflict damage on the player other.TakeDamage(damage, self.Instigator, hitLocation, momentum, class'DamTypeClassicSniper'); }
To make things feel "better", we have added a few other features to these ammo pickups:
Here's the mutator as it stands right now. It currently handles all ammo types that have pickups (that's pretty much everything except for the Redeemer, tank shells, etc.).
To install, put the files ExplosiveAmmo.u
and
ExplosiveAmmo.int
in the System
directory
of your UT2004 installation.
Installation on a server is somewhat more involved, and I'll put some instructions here shortly.
The source code is also available.
Early in the development of this mutator, I was testing out a feature that I found amusing: generating perverse quantities of flying ammo when explosing an ammo pickup.
I tried this with the bio-rifle ammo and things ended poorly for me.
I got a horrible frame-rate, even on my P4 3GHz(ht). We're talking 0.5 fps or so. Maybe it was worse. I can't remember. It was too funny.
(coming soon)