Human Cluster Bombs
Author: [SL1CK]-=MERL1N=-Source: https://www.insideqc.com/qctut/qctut-48.shtml
Yes, that's right, I have no respect for human life whatsoever :)
A Human Cluster Bomb (if you dont know) is like the human grenade in l.o.d. (legion of doom), but this is better :)
I did steal the original cluster bomb code from some patch that didn't seem to have any name (?!?) and the code had no author or copyright (if you are the author TELL me!) all I can take credit for is changing the code quite a bit, changing sounds and models etc. So, make a new file called "hum_clus.qc" and open it up. The first code we will be adding is the code for when the little or 'mini' grenades that pop out of the main bomb explode...Step 1 -
Add this in at the top of the file...
Obviouly you can edit this a little, if you want the little grenades to do less or more damage, change the '90' in 'T_RadiusDamage' to like '40' or '140'.void() MiniGrenadeExplode = { T_RadiusDamage (self, self.owner, 90, world); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_EXPLOSION); WriteCoord (MSG_BROADCAST, self.origin_x); WriteCoord (MSG_BROADCAST, self.origin_y); WriteCoord (MSG_BROADCAST, self.origin_z); BecomeExplosion (); };
The only other editible thing is that if you have some other explosion animation with a 3D model etc, you must change the 'BecomeExplosion();' to what ever you have as the function.
After that add in a little shortcutted function right underneath, that we'll define later...
Yes, its the function for the main cluster bomb when it is stepped on :)void() MultiGrenadeTouch;
Step 2 -
Phew! Big eh? Lets rip it apart...void(string gibbomb, float offsetAngle) MiniGrenadeLaunch = { local entity missile, mpuff; local float tempRand; missile = spawn (); missile.realattacker = missile.owner = self.owner; missile.movetype = MOVETYPE_BOUNCE; missile.solid = SOLID_BBOX; missile.classname = "MiniGrenade"; // set missile speed missile.v_angle = self.v_angle; missile.v_angle_y = missile.v_angle_y + offsetAngle; makevectors (missile.v_angle); missile.velocity = v_forward*100 + v_up*400; tempRand = (crandom()*60) - 30; missile.velocity = missile.velocity + tempRand * v_forward; tempRand = (crandom()*40) - 20; missile.velocity = missile.velocity + tempRand * v_right; tempRand = (crandom()*60) - 30; missile.velocity = missile.velocity + tempRand * v_up; missile.avelocity = '300 300 300'; missile.angles = vectoangles(missile.velocity); missile.touch = MultiGrenadeTouch; setmodel (missile, gibbomb); missile.skin = 1; setsize (missile, '0 0 0', '0 0 0'); setorigin (missile, self.origin); // set missile duration missile.nextthink = time + 1 + (crandom() * 0.5); missile.think = MiniGrenadeExplode; };
Now after the spawning func you will see a new .float entitled '.realattacker', this I have some idea of what's going on, but couldn't be bothered taking the time to work it out :) So open up defs.qc, scroll to the very bottom and add in...Save it and close, now back to the func....float realattacker
You should know the 'self.movetype' and 'self.solid' bits, all of this 'tempRand' stuff is just a long way to make the cluster fly up in the air and a little to the side, and you can now see why we skipped a function earlier under 'missile.touch'.
Now this 'setmodel (missile, gibbomb);' allows us to have different models for the same code, like gibs, when we actually say 'throw the clusters in the air', we define what the model will be, so that we can have different gibs, arms, legs, heads or whatever we want to fly instead of just 6 'gib1' models.
The part about 'missile.nextthink' makes the clusters explode at different times, if you want em all to blow up at once change the 'time + 1 + (crandom() * 0.5); to like 'time + 2' or something. And the rest you should understand.Step 3 -
Next, under all that add this...
Here is where you can define what the bomb models are, as you can see I have made them gibs and a head, you can change the model to anything you care to precache :), I suggest limbs, to make the effect good.void() MultiGrenadeExplode = { MiniGrenadeLaunch("progs/gib1.mdl", 0); MiniGrenadeLaunch("progs/gib2.mdl", 72); MiniGrenadeLaunch("progs/gib3.mdl", 144); MiniGrenadeLaunch("progs/gib2.mdl", 216); MiniGrenadeLaunch("progs/h_player.mdl", 288); GrenadeExplode (); };
The number on the end helps along the 'tempRand' bit, so my head model will be the last peice to explode, since its number (288) is the highest of the lot.Step 4 -
Now, we better add in the touch function that we skipped earlier, so add this code in underneath everything...
Ok, the main useful chunk of code here is the lower bit with the sounds in it, it ask whether it is a mini grenade or the whole cluster bomb and plays the apropriate sound. the sound of 'zombie/z_miss.wav' should be precached in 'weapons.qc' up the top, but the player's sound will already be precached, as long as a player is in the game :)void() MultiGrenadeTouch = { if (other == self.owner) return; // don't explode on owner if ((self.classname != "MiniGrenade")&&(other.takedamage == DAMAGE_AIM)) { self.nextthink = time + 0.1; return; } if (self.classname == "MiniGrenade") sound (self, CHAN_WEAPON, "zombie/z_miss.wav", 1, ATTN_NORM); else sound (self, CHAN_WEAPON, "player/land2.wav", 1, ATTN_NORM); if (self.velocity == '0 0 0') self.avelocity = '0 0 0'; };
What these sounds are are the gibbish squelch of a missed zombie attack, and the "crunch, uurrggghhhhh" when you fall from a huge height, so a mini genade (gib) will go "squish" and the main grenade (a man) will scream in agony as the plastic explosives cut into his belly :)Step 5 -
Right! Time to put it all together, add this under everything...
Right, its big, but easy...void() W_FireHumanCluster = { local entity missile, mpuff; if (self.ammo_rockets < 5) { self.weapon = W_BestWeapon (); W_SetCurrentAmmo (); return; } self.ammo_rockets = self.ammo_rockets - 5; if (random() > 0.8) self.noise = "player/death1.wav"; else if (random() > 0.6) self.noise = "player/death2.wav"; else if (random() > 0.4) self.noise = "player/death3.wav"; else if (random() > 0.2) self.noise = "player/death4.wav"; else self.noise = "player/death5.wav"; sound (self, CHAN_WEAPON, self.noise, 1, ATTN_NORM); self.punchangle_x = -2; missile = spawn (); missile.realattacker = missile.owner = self; missile.movetype = MOVETYPE_BOUNCE; missile.solid = SOLID_BBOX; missile.classname = "MultiGrenade"; // set missile speed makevectors (self.v_angle); if (self.v_angle_x) missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; else { missile.velocity = aim(self, 10000); missile.velocity = missile.velocity * 600; missile.velocity_z = 200; } missile.avelocity = '300 300 300'; missile.angles = vectoangles(missile.velocity); missile.touch = MultiGrenadeTouch; // set missile duration missile.nextthink = time + 1; missile.think = MultiGrenadeExplode; setmodel (missile, "progs/player.mdl"); missile.skin = 1; missile.frame = 69; setsize (missile, '0 0 0', '0 0 0'); setorigin (missile, self.origin); };
You know the 'if we aint got 5 rockets, change weapons' jive, and you know that if plan on this weapon only taking up 1 rocket each time, to get rid of it.
Well you know now :)
You know the taking away of 5 rockets and you know to change it to however many you want taken away...
You also know the random selecting of a player death sound and the playin of it, so that you shoot the gun and he screams. And you knpow if you think that that is crap to get rid of it...
You do, of course, know that its classname must be 'MultiGrenade' to fit in with the other earlier touch code.
You know you can fiddle about the 'missile.velocity's to get it just right, and you know that the "player.mdl" will already be precached.
If you want your model to have a random skin, you should be able to fiddle with that, and you know that if you dont want the frame as the guy sitting down (after death) to fire up your model editor and pick your own frame.
Gee, you hardly need these tuts :)Step 6 -
OK, almost there, check your precaches and open up weapons.qc.
Go down to 'player weapon use', you should be familiar with it, just above it all add in this:Then head to the first bunch of weapon code where the weapon models are defined.void() W_FireHumanCluster();
Find 'IT_GRENADE_LAUNCHER'. If you have a weapon model that is more suited to throw a human than a grenade launcher is, precache it and change it here.
Now , keep heading down until you find 'w_attack' go down to 'IT_GRENADE_LAUNCHER'. Change the 'W_FireGrenade();' (or whatever) to 'W_FireHumanCLuster();' and you may also want to fiddle with the 'self.attack_finished' value.
As you know this defines how quickly you will shoot the gun if the trigger is held, I do not reccomend you make it too fast, as each time you fire you are making 6 models, not 1, so you may get overflow errors, plus it would make the gun less fun :)Step 7 -
If you want an exploding 'trick gib' to appear every so often when you gib an enemy, open up 'player.qc' and scroll down about 2/3 of the way till you find...
and make those changes, i think you know that you can change the random values if you want a trick gib more often, and I dont think that I need to explain this code, cause its pretty easy...void(string gibname, float dm) ThrowGib = { local entity new; // possible trick bombed gib if (random() < 0.05) { new = spawn(); new.origin = self.origin; setmodel (new, gibname); setsize (new, '0 0 0', '0 0 0'); new.velocity = VelocityForDamage (dm); new.movetype = MOVETYPE_BOUNCE; new.solid = SOLID_BBOX; new.classname = "MiniGrenade"; new.avelocity_x = random()*200; new.avelocity_y = random()*200; new.avelocity_z = random()*500; new.touch = MultiGrenadeTouch; new.nextthink = time + 2; new.think = MiniGrenadeExplode; new.frame = 0; new.flags = 0; } else { new = spawn(); new.origin = self.origin; setmodel (new, gibname); setsize (new, '0 0 0', '0 0 0'); new.velocity = VelocityForDamage (dm); new.movetype = MOVETYPE_BOUNCE; new.solid = SOLID_NOT; new.avelocity_x = random()*600; new.avelocity_y = random()*600; new.avelocity_z = random()*600; new.think = SUB_Remove; new.ltime = time; new.nextthink = time + 10 + random()*10; new.frame = 0; new.flags = 0; } };
Step 8 -
The only other thing to do now is to open up 'progs.src' and add in the new file just under "weapons.qc"
If you are hell bent on keeping this gun (or any) head down to 'items.qc' and change the player backpacks and weapon pick ups to say "Human Cluster Bomb Launcher" (or whatever) instead of "Grenade Launcher" and while you're there, go slowly through it and you will find all info on what it says when you bash into a door without a key and you get messages, 'cause 'items.qc' and 'client.qc' are where you'll find most of quake's text.
Anyway, compile and have fun, and if you do get a better model than the grenade launcher for this gun, please tell me about it :) cause the only thing sucking about this weapon is the weapon model, which is out of my hands :)
Happy Quake'n
Tags: tutorial, quakec, qc, insideqc, monster, ai