]> icculus.org git repositories - divverent/nexuiz.git/blob - TeamNexuiz/game/gamec/w_grenades.c
Added a Game C folder
[divverent/nexuiz.git] / TeamNexuiz / game / gamec / w_grenades.c
1 void W_Grenade_DefaultTouch (void)\r
2 {\r
3         if (!self.state & 1 && (other.classname == "player" || other.classname == "corpse"))\r
4                 self.think();   // assume my think was an explosion function\r
5         else\r
6                 sound (self, CHAN_IMPACT, "weapons/grenade_bounce.wav", 1, ATTN_NORM);\r
7 }\r
8 \r
9 void W_Grenade_DefaultDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
10 {\r
11         self.health = self.health - damage;\r
12         if (self.health <= 0)\r
13                 self.think();   // assume my think was an explosion function\r
14 }\r
15 \r
16 float W_ThrowGrenade(void() GrenadeSpecial)\r
17 {\r
18         local entity gren, oself;\r
19         local vector org;\r
20 \r
21         if(self.ammo_shells < 10)\r
22         {\r
23                 sprint(self, "Not enough grenade ammo\n");\r
24                 self.grenade_time = time + 0.3;\r
25                 return FALSE;\r
26         }\r
27         self.ammo_shells = self.ammo_shells - 10;\r
28 \r
29 //      sound (self, CHAN_WEAPON, "weapons/grenade_fire.wav", 1, ATTN_NORM);\r
30         self.punchangle_x = -2;\r
31         org = self.origin + self.view_ofs + v_forward * 15 + v_right * 5 + v_up * -12;\r
32 \r
33         gren = spawn ();\r
34         gren.owner = self;\r
35         gren.classname = "grenade";\r
36         gren.movetype = MOVETYPE_BOUNCE;\r
37         gren.solid = SOLID_BBOX;\r
38         setorigin(gren, org);\r
39 \r
40         gren.takedamage = DAMAGE_YES;\r
41         gren.health = 10;\r
42         //gren.damageforcescale = 4;\r
43         gren.event_damage = W_Grenade_DefaultDamage;\r
44 \r
45         oself = self;\r
46         self = gren;\r
47 \r
48         GrenadeSpecial();\r
49 \r
50         self = oself;\r
51 \r
52         return TRUE;\r
53 }\r
54 \r
55 //--------------------------------------------\r
56 \r
57 void InflictStunStatus(entity targ, float stuntime)\r
58 {\r
59         if(targ.classname != "player")\r
60                 return; // only players can be stunned (fixme: turrets?  vehicles?)\r
61 \r
62         // stun target by increasing attack_finished so he can't attack\r
63         if(targ.attack_finished < time + stuntime)\r
64                 targ.attack_finished = time + stuntime;\r
65 \r
66         // also prevent use of grenades\r
67         // disabled, since the point of the grenades is being\r
68         // able to attack when weapon is busy or disabled\r
69         //if(targ.grenade_time < time + stuntime)\r
70         //      targ.grenade_time = time + stuntime;\r
71 }\r
72 \r
73 //--------------------------------------------\r
74 \r
75 void ExtinguishFirebomb(entity e)\r
76 {\r
77         entity f, b;\r
78         if(e.enemy == world)\r
79         {\r
80                 //bprint("ExtinguishFirebomb failed, no enemy!\n");\r
81                 return;\r
82         }\r
83         //bprint(strcat("ExtinguishFirebomb(", e.classname, ") ", e.enemy.classname, "\n"));\r
84 \r
85         f = e.enemy;\r
86         b = e.enemy.enemy;\r
87 \r
88         e.enemy = world;\r
89         f.enemy = world;\r
90 \r
91         f.think = SUB_Null;\r
92         f.nextthink = -1;\r
93         setmodel(f, "models/sprites/null.spr");\r
94         setattachment(f, world, "");\r
95         if(b)\r
96         {\r
97                 b.think = SUB_Null;\r
98                 b.nextthink = -1;\r
99                 setmodel(b, "models/sprites/null.spr");\r
100                 setattachment(b, world, "");\r
101                 remove(b);\r
102                 //b.think = SUB_Remove;\r
103                 //b.nextthink = time + 2;\r
104         }\r
105         setmodel(e, "");\r
106         //f.think = SUB_Remove;\r
107         //f.nextthink = time + 2;\r
108         //eprint(f);\r
109         remove(f);\r
110         //eprint(f);\r
111 }\r
112 \r
113 void Firebomb_Flame_Check()\r
114 {\r
115         if(self.owner == world || self.owner.model == "")\r
116         {\r
117                 if(self.owner != world && self.owner.enemy == self)\r
118                 {\r
119                         //bprint("Firebomb flame calls ExtinguishFirebomb on owner\n");\r
120 \r
121                         ExtinguishFirebomb(self.owner);\r
122                         self.owner.enemy = world;\r
123                         return;\r
124                 }\r
125                 //bprint("Firebomb flame resorts to deleting itself!\n");\r
126                 setattachment(self, world, "");\r
127                 setattachment(self.enemy, world, "");\r
128                 setmodel(self, "");\r
129                 setmodel(self.enemy, "");\r
130                 self.think = self.enemy.think = SUB_Remove;\r
131                 self.nextthink = self.enemy.nextthink = time + 1;\r
132         }\r
133 \r
134         self.nextthink = time + 0.5;\r
135 }\r
136 \r
137 void W_Firebomb_Think ()\r
138 {\r
139         entity head;\r
140         float flametime, flamedmg, flamerate, flameedge, flameradius, distratio;\r
141 \r
142         self.nextthink = time + cvar("g_balance_grenade_firebomb_burnrate");\r
143 \r
144         if(self.wait < time || self.enemy == world)\r
145         {\r
146                 //bprint("Firebomb deletes its flame\n");\r
147                 ExtinguishFirebomb(self);\r
148 \r
149                 setmodel(self, "");\r
150                 self.think = SUB_Remove;\r
151                 self.nextthink = time + 2.2;\r
152                 return;\r
153         }\r
154 \r
155         flameradius =   cvar("g_balance_grenade_firebomb_burnradius");\r
156         flamedmg =              cvar("g_balance_grenade_firebomb_flamedmg");\r
157         flamerate =             cvar("g_balance_grenade_firebomb_flamerate");\r
158         flametime =             cvar("g_balance_grenade_firebomb_flametime");\r
159         flameedge =             cvar("g_balance_grenade_firebomb_edgeratio");\r
160 \r
161         RadiusDamage (self, self.owner,\r
162                 cvar("g_balance_grenade_firebomb_burndmg"),\r
163                 cvar("g_balance_grenade_firebomb_burndmg")*flameedge,\r
164                 cvar("g_balance_grenade_firebomb_burnradius"), world,\r
165                 0, DEATH_BURNING);\r
166 \r
167         head = findradius(self.origin, flameradius);\r
168         while(head)\r
169         {\r
170                 if(head.takedamage == DAMAGE_AIM)//head.classname == "player" || head.classname == "turret")\r
171                 {\r
172                         traceline(self.origin, head.origin, TRUE, self);\r
173                         if(trace_fraction >= 1)\r
174                         {\r
175                                 distratio = 1 - (vlen(head.origin - self.origin) / flameradius);\r
176                                 IgniteTarget(head, self.owner,\r
177                                         (flametime*flameedge) + (flametime - flameedge)*distratio,\r
178                                         flamedmg,\r
179                                         flamerate,\r
180                                         TRUE);\r
181                         }\r
182                 }\r
183                 head = head.chain;\r
184         }\r
185 \r
186 \r
187 /*      radius = cvar("g_balance_grenade_firebomb_burnradius");\r
188         head = findradius(self.origin, radius);\r
189         while(head)\r
190         {\r
191                 //bprint("firebomb see target\n");\r
192                 if((head.takedamage || head.classname == "grenade") && head != self.enemy && head != self)\r
193                 {\r
194                         //bprint("firebomb try to burn target\n");\r
195                         // don't harm or spread to allies or pyros\r
196                         if(!(head.classname == "player" && (head.team == self.team || head.class == CLASS_PYRO)) )\r
197                         {\r
198                                 //bprint("firebomb burn target\n");\r
199                                 edgeratio = cvar("g_balance_heat_edgeratio");\r
200                                 distratio = ( 1 - (vlen(self.enemy.origin - head.origin) / radius) );\r
201                                 flameratio = edgeratio + distratio*(1 - edgeratio);\r
202 \r
203                                 // deal direct damage from the heat radiated by the fire\r
204                                 damage = flameratio * cvar("g_balance_grenade_firebomb_burndmg");\r
205                                 Damage (head, self, self.owner, damage, DEATH_BURNING, self.enemy.origin, '0 0 0');\r
206 \r
207                                 if(head.takedamage == DAMAGE_AIM)\r
208                                 {\r
209                                         IgniteTarget(head, self.owner,\r
210                                                 cvar("g_balance_grenade_firebomb_flametime"),\r
211                                                 cvar("g_balance_grenade_firebomb_flamedmg"),\r
212                                                 cvar("g_balance_grenade_firebomb_flamerate"), TRUE);\r
213                                 }\r
214                         }\r
215                 }\r
216                 head = head.chain;\r
217         }\r
218         */\r
219 }\r
220 \r
221 void W_Firebomb_Activate (void)\r
222 {\r
223         // set bomb on fire visually\r
224 /*      CreateFlame(self, self.owner);\r
225         self.onfire.think = SUB_Null;\r
226         self.onfire.nextthink = -1;\r
227         self.onfire.colormod = self.onfire.onfire.colormod = '128 255 128'*(1/255)*1;//'255 128 64'*(1/255)*1;//3;\r
228 */\r
229 \r
230         entity f;\r
231         f = self.enemy = spawn();\r
232         f.enemy = spawn();\r
233         f.classname = f.enemy.classname = "firebomb_flame";\r
234         f.owner = self;\r
235         f.dmg = 0;\r
236         f.ltime = 0;\r
237         f.wait = 0;\r
238         f.think = Firebomb_Flame_Check;\r
239         f.nextthink = time;\r
240 \r
241         f.colormod = f.enemy.colormod = \r
242                 stov(cvar_string("g_balance_grenade_firebomb_flamecolor"))*(1/255)\r
243                 *cvar("g_balance_grenade_firebomb_flamec_strength");//0.3;//'128 128 255'*(1/255)*3;//'255 128 64'*(1/255)*1;//3;\r
244         //f.alpha = f.enemy.alpha = 0.6;\r
245         f.effects = f.enemy.effects = EF_ADDITIVE;\r
246         f.scale = f.enemy.scale = 2;\r
247 \r
248         setmodel(f, "models/sprites/fire_top.spr32");\r
249         setmodel(f.enemy, "models/sprites/fire_base.spr32");\r
250 \r
251         setorigin(f.enemy, '0 0 -5' * f.scale);\r
252         setorigin(f, '0 0 2' * f.scale + '0 0 20');\r
253 \r
254         setattachment(f, f.owner, "");\r
255         setattachment(f.enemy, f, "");\r
256 \r
257         // burn targets each frame\r
258         self.wait = time + cvar("g_balance_grenade_firebomb_burntime");\r
259         self.event_damage = SUB_Null;\r
260         self.takedamage = 0;\r
261         self.movetype = MOVETYPE_TOSS;\r
262         self.angles = '0 0 0';\r
263         self.touch = SUB_Null;\r
264         self.think = W_Firebomb_Think;\r
265         self.nextthink = time;\r
266 }\r
267 \r
268 void W_Firebomb()\r
269 {\r
270         setmodel(self, "models/grenademodel.md3");\r
271         setsize(self, '-6 -6 -3', '6 6 3');\r
272 \r
273         self.classname = "firebomb"; // don't get bounced around like other grenades\r
274 \r
275         self.nextthink = time + 3;\r
276         self.think = W_Firebomb_Activate;\r
277         self.touch = W_Firebomb_Activate;\r
278         self.state = 1; // don't explode on contact with player\r
279         self.velocity = v_forward * cvar("g_balance_grenade_firebomb_speed")\r
280                 + v_up * cvar("g_balance_grenade_frag_firebomb_up");\r
281         self.avelocity = '-500 0 0';\r
282         //self.event_damage = SUB_Null;\r
283 \r
284         self.colormod = '255 128 0'*(1/255)*2;\r
285         self.scale = 1.5;\r
286 \r
287         self.angles = vectoangles (self.velocity);\r
288 }\r
289 \r
290 //--------------------------------------------\r
291 \r
292 void W_ConcussionGrenade_Explode (void)\r
293 {\r
294         vector  org2;\r
295         float radius, spd, spd_up, stuntime, sim_jump_pad;\r
296         entity head;\r
297 \r
298         org2 = findbetterlocation (self.origin);\r
299         te_explosionrgb (org2, (1/255)*'255 128 0');\r
300         // fixme: need different sound\r
301         sound (self, CHAN_VOICE, "weapons/grenade_impact.wav", 1, ATTN_NORM);\r
302 \r
303         self.event_damage = SUB_Null;\r
304 \r
305         radius = cvar("g_balance_grenade_concussion_radius");\r
306         spd = cvar("g_balance_grenade_concussion_force");\r
307         spd_up = cvar("g_balance_grenade_concussion_force_up");\r
308         stuntime = cvar("g_balance_grenade_concussion_stuntime");\r
309         sim_jump_pad = !cvar("g_balance_grenade_concussion_restrictflight");\r
310 \r
311         // extinguish flames in radius (even those not burning a player)\r
312         head = world;\r
313         do\r
314         {\r
315                 head = find(head, classname, "burning");\r
316                 if(head != world && vlen(head.enemy.origin - self.origin) <= radius)\r
317                 {\r
318                         traceline(self.origin, head.enemy.origin, TRUE, self);\r
319                         if(trace_fraction >= 1)\r
320                         {\r
321                                 ExtinguishFlame(head.enemy);\r
322                         }\r
323                 }\r
324         }while(head);\r
325 \r
326 \r
327 \r
328         // extinguish firebombs\r
329         head = world;\r
330         do\r
331         {\r
332                 head = find(head, classname, "firebomb");\r
333                 if(head != world && vlen(head.origin - self.origin) <= radius)\r
334                 {\r
335                         traceline(self.origin, head.origin, TRUE, self);\r
336                         if(trace_fraction >= 1)\r
337                         {\r
338                                 ExtinguishFirebomb(head);\r
339                         }\r
340                 }\r
341         }while(head);\r
342 \r
343 \r
344 \r
345         // also extinguish napalm\r
346         head = world;\r
347         do\r
348         {\r
349                 head = find(head, classname, "napalm");\r
350                 if(head != world && vlen(head.origin - self.origin) <= radius)\r
351                 {\r
352                         traceline(self.origin, head.origin, TRUE, self);\r
353                         if(trace_fraction >= 1)\r
354                         {\r
355                                 head.think = SUB_Remove;\r
356                                 head.nextthink = time;\r
357                                 //remove(head);\r
358                                 //return;\r
359                         }\r
360                 }\r
361         }while(head);\r
362 \r
363 \r
364 \r
365 \r
366         // throw hapless players\r
367         head = world;\r
368         do\r
369         {\r
370                 head = findfloat(head, movetype, MOVETYPE_WALK);\r
371                 if(head != world && vlen(head.origin - self.origin) <= radius)\r
372                 {\r
373                         traceline(self.origin, head.origin, TRUE, self);\r
374                         if(trace_fraction >= 1)\r
375                         {\r
376                                 if(sim_jump_pad && head.classname == "player" && !head.jump_pad)\r
377                                         head.jump_pad = 1;\r
378                                 head.velocity = head.velocity + normalize(head.origin - self.origin) * spd + '0 0 1'*spd_up;\r
379 \r
380                                 // stun status\r
381                                 InflictStunStatus(head, stuntime);\r
382                         }\r
383                 }\r
384         }while(head);\r
385 \r
386         remove (self);\r
387 }\r
388 \r
389 void W_ConcussionGrenade()\r
390 {\r
391         setmodel(self, "models/grenademodel.md3");\r
392         setsize(self, '-6 -6 -3', '6 6 3');\r
393 \r
394         self.nextthink = time + 1.5 + random()*0.5;\r
395         self.think = W_ConcussionGrenade_Explode;\r
396         self.touch = W_Grenade_DefaultTouch;\r
397         self.state = 1; // don't explode on contact with player\r
398         self.velocity = v_forward * cvar("g_balance_grenade_concussion_speed")\r
399                 + v_up * cvar("g_balance_grenade_concussion_speed_up");\r
400         self.avelocity = '100 150 100';\r
401 \r
402         self.angles = vectoangles (self.velocity);\r
403 }\r
404 \r
405 //--------------------------------------------\r
406 \r
407 // touch the player and go *poke*\r
408 void W_NailGrenade_Spike_Poke (void)\r
409 {\r
410         if (other == self.owner)\r
411                 return;\r
412         else if (pointcontents (self.origin) == CONTENT_SKY)\r
413         {\r
414                 remove (self);\r
415                 return;\r
416         }\r
417 \r
418         if(other.takedamage)\r
419                 Damage (other, self, self.enemy, self.dmg, WEP_NAMEK, self.origin, normalize(self.velocity) * cvar("g_balance_grenade_nail_spikeforce"));\r
420         else\r
421         {\r
422                 te_spark(self.origin, normalize(self.velocity) * -70 - '0 0 100', 30);\r
423                 te_gunshot(self.origin);\r
424         }\r
425 \r
426         remove (self);\r
427 }\r
428 \r
429 void W_NailGrenade_Spike(vector org, vector vel, float damage)\r
430 {\r
431         entity spike;\r
432         spike = spawn ();\r
433         spike.enemy = self.owner;\r
434         spike.owner = self;\r
435         spike.classname = "spike";\r
436 \r
437         // some downward curving\r
438         spike.movetype = MOVETYPE_BOUNCE;\r
439         spike.gravity = cvar("g_balance_grenade_nail_spikegrav");\r
440         spike.solid = SOLID_BBOX;\r
441         setmodel(spike, "models/plasmatrail.mdl");\r
442         setsize(spike, '0 0 0', '0 0 0');\r
443         setorigin(spike, org);\r
444 \r
445         spike.dmg = damage;\r
446 \r
447         spike.nextthink = time + 3 + random()*0.5;\r
448         spike.think = SUB_Remove;\r
449         spike.touch = W_NailGrenade_Spike_Poke;\r
450         spike.velocity = vel;\r
451         spike.angles = vectoangles (spike.velocity);\r
452         spike.scale = 0.75;\r
453 }\r
454 \r
455 void W_NailGrenade_Attack (void)\r
456 {\r
457         float r, num, maxnum, is_odd, spread, starty, incy;\r
458         vector ang;\r
459 \r
460         r = ceil(random()*3);\r
461         if(r == 1)\r
462                 sound (self, CHAN_IMPACT, "weapons/ric1.wav", 1, ATTN_NORM);\r
463         else if(r == 2)\r
464                 sound (self, CHAN_IMPACT, "weapons/ric2.wav", 1, ATTN_NORM);\r
465         else\r
466                 sound (self, CHAN_IMPACT, "weapons/ric3.wav", 1, ATTN_NORM);\r
467 \r
468         num = 0;\r
469         maxnum = cvar("g_balance_grenade_nail_numspikes");\r
470         spread = cvar("g_balance_grenade_nail_spread");\r
471 \r
472         is_odd = (floor(maxnum / 2) != maxnum / 2); //true if maxnum is an odd number\r
473 \r
474         if(is_odd)\r
475         {\r
476                 incy = (1 / maxnum) * spread;\r
477                 starty = self.mangle_y - (incy * maxnum/2) + incy*0.5;\r
478         }\r
479         else\r
480         {\r
481                 incy = (1 / maxnum) * spread;\r
482                 starty = self.mangle_y - (incy * maxnum/2) + incy*0.5;\r
483         }\r
484 \r
485         ang_z = self.mangle_z;\r
486         ang_x = self.mangle_x;\r
487         ang_y = starty;\r
488 \r
489         while(num < maxnum)\r
490         {\r
491                 makevectors(ang);\r
492                 W_NailGrenade_Spike(self.origin, v_forward*cvar("g_balance_grenade_nail_spikespeed")     , cvar("g_balance_grenade_nail_spikedamage"));\r
493                 W_NailGrenade_Spike(self.origin, v_forward*cvar("g_balance_grenade_nail_spikespeed") * -1, cvar("g_balance_grenade_nail_spikedamage"));\r
494                 num = num + 1;\r
495                 ang_y = ang_y + incy;\r
496         }\r
497 \r
498         self.count = self.count - 1;\r
499 \r
500         if(self.count > 0)\r
501         {\r
502                 // change shooting angle and fire again\r
503                 self.mangle_y = self.mangle_y + cvar("g_balance_grenade_nail_anglechange");\r
504                 self.think = W_NailGrenade_Attack;\r
505                 self.nextthink = time + cvar("g_balance_grenade_nail_cluster_rate");\r
506         }\r
507         else\r
508         {\r
509                 // fall to ground & disappear\r
510                 self.alpha = 0.5;\r
511                 self.gravity = 0;\r
512                 self.touch = SUB_Remove;\r
513                 self.think = self.touch;\r
514                 self.nextthink = time + 3;\r
515                 self.movetype = MOVETYPE_TOSS;\r
516                 self.solid = SOLID_BBOX;\r
517                 self.velocity = '0 0 20';\r
518                 self.avelocity = '300 200 100';\r
519                 setorigin(self, self.origin);\r
520         }\r
521 }\r
522 \r
523 void W_NailGrenade_Activate (void)\r
524 {\r
525         self.velocity = '0 0 0';\r
526         self.think = W_NailGrenade_Attack;\r
527         self.nextthink = time + 0.3;\r
528 //      self.origin = self.dest1;\r
529 \r
530         self.count = cvar("g_balance_grenade_nail_numclusters");\r
531 }\r
532 \r
533 // lift into air\r
534 void W_NailGrenade_Deploy (void)\r
535 {\r
536         float thtime, dheight, spd;\r
537         entity oldself;\r
538 \r
539         self.touch = SUB_Null;\r
540 \r
541         dheight = cvar("g_balance_grenade_nail_deployheight");\r
542         thtime = cvar("g_balance_grenade_nail_deploytime");\r
543         spd = dheight / thtime;\r
544 \r
545         // GRRR!  Quake's floor fetish is driving me crazy.\r
546         // The ONLY way I've found to get the grenade off the floor once it's on\r
547         // is to delete it and spawn a new entity in its place.\r
548 \r
549         oldself = self;\r
550         self = spawn();\r
551         self.owner = oldself.owner;\r
552         self.angles_y = oldself.angles_y;\r
553         self.angles_x = self.angles_z = 0; // point upright\r
554         self.mangle = oldself.mangle;\r
555 \r
556         setmodel(self, oldself.model);\r
557         setsize(self, '0 0 0', '0 0 0');\r
558         setorigin(self, oldself.origin);\r
559         self.flags = self.flags - (self.flags & FL_ONGROUND);\r
560         self.oldorigin = self.origin;\r
561 \r
562         //self.flags = self.flags - (self.flags & FL_ONGROUND);\r
563         self.movetype = MOVETYPE_FLY;\r
564         self.solid = SOLID_NOT;\r
565         self.velocity = '0 0 1' * spd;\r
566         self.nextthink = time + thtime;\r
567         self.think = W_NailGrenade_Activate;\r
568 \r
569         self.dest1 = self.origin + '0 0 1'*dheight;\r
570         traceline(self.origin, self.dest1, TRUE, self);\r
571         self.dest1 = trace_endpos;\r
572 \r
573         remove(oldself);\r
574 }\r
575 \r
576 void W_NailGrenade_Touch (void)\r
577 {\r
578         sound (self, CHAN_IMPACT, "weapons/grenade_bounce.wav", 1, ATTN_NORM);\r
579         traceline(self.origin, self.origin - '0 0 10', TRUE, self);\r
580         if(self.flags & FL_ONGROUND || trace_fraction < 1)\r
581                 W_NailGrenade_Deploy();\r
582 }\r
583 \r
584 void W_NailGrenade()\r
585 {\r
586         setmodel(self, "models/grenademodel.md3");\r
587         setsize(self, '-6 -6 -3', '6 6 3');\r
588 \r
589         self.takedamage = DAMAGE_NO;\r
590         self.event_damage = SUB_Null;\r
591 \r
592         self.nextthink = time + 2.5;\r
593         self.think = W_NailGrenade_Activate;\r
594         self.touch = W_NailGrenade_Touch;\r
595         self.state = 1; // don't explode on contact with player\r
596         self.velocity = v_forward * cvar("g_balance_grenade_nail_speed")\r
597                 + v_up * cvar("g_balance_grenade_nail_speed_up");\r
598         self.avelocity = '100 150 100';\r
599 \r
600         self.angles = vectoangles (self.velocity);\r
601 \r
602         // decide on a direction to fire in\r
603         self.mangle_y = self.angles_y;// + 45;\r
604         self.mangle_z = 0;\r
605         self.mangle_x = 0;\r
606 }\r
607 \r
608 //--------------------------------------------\r
609 \r
610 void W_MirvGrenade_Explode (void)\r
611 {\r
612         vector  org2;\r
613         org2 = findbetterlocation (self.origin);\r
614         te_explosion (org2);\r
615         effect (org2, "models/sprites/grenadeexplosion.spr32", 0, 20, 30);\r
616         sound (self, CHAN_BODY, "weapons/grenade_impact.wav", 1, ATTN_NORM);\r
617 \r
618         self.event_damage = SUB_Null;\r
619         RadiusDamage (self, self.owner, cvar("g_balance_grenade_mirv_damage"), cvar("g_balance_grenade_mirv_edgedamage"), cvar("g_balance_grenade_mirv_radius"), world, cvar("g_balance_grenade_mirv_force"), WEP_GRENADE_MIRV);\r
620 \r
621         remove (self);\r
622 }\r
623 \r
624 void W_MirvBomb(vector org, vector vel)\r
625 {\r
626         entity gren;\r
627         gren = spawn ();\r
628         gren.owner = self.owner;\r
629         gren.classname = "grenade";\r
630         gren.movetype = MOVETYPE_BOUNCE;\r
631         gren.solid = SOLID_BBOX;\r
632         setmodel(gren, "models/grenademodel.md3");\r
633         //setsize(self, '-6 -6 -3', '6 6 3');\r
634         setsize(gren, '0 0 0', '0 0 0');\r
635         setorigin(gren, org);\r
636 \r
637         gren.nextthink = time + 2 + crandom()*0.15;\r
638         gren.think = W_MirvGrenade_Explode;\r
639         gren.touch = W_Grenade_DefaultTouch;\r
640         gren.state = 1; // don't explode on contact\r
641         gren.takedamage = DAMAGE_YES;\r
642         gren.health = 20;\r
643         gren.damageforcescale = 4;\r
644         gren.event_damage = W_Grenade_DefaultDamage;\r
645         gren.velocity = vel;\r
646         gren.avelocity = '100 150 100' * (1 + crandom()*0.5);\r
647         gren.angles = vectoangles (gren.velocity);\r
648         gren.scale = 0.75;\r
649 }\r
650 \r
651 void W_MirvGrenade_Split (void)\r
652 {\r
653         float num, maxnum, spread;\r
654         vector dir, vel;\r
655         sound (self, CHAN_IMPACT, "weapons/grenade_bounce.wav", 1, ATTN_NORM);\r
656 \r
657         if(self.flags & FL_ONGROUND)\r
658         {\r
659                 //traceline(self.origin, self.origin - '0 0 5', TRUE, self);\r
660                 //dir = normalize(self.velocity) + trace_plane_normal + '0 0 1';\r
661                 //dir = normalize(dir * (1/3));\r
662                 dir = normalize(self.velocity)*0.2 + normalize('0 0 1');\r
663                 dir = normalize(dir);\r
664         }\r
665         else\r
666         {\r
667                 dir = normalize(self.velocity);\r
668         }\r
669 \r
670         spread = cvar("g_balance_grenade_mirv_spread");\r
671 \r
672         maxnum = cvar("g_balance_grenade_mirv_count");\r
673         for (num = 0; num < maxnum; num = num + 1)\r
674         {\r
675                 vel_x = dir_x + crandom()*spread;\r
676                 vel_y = dir_y + crandom()*spread;\r
677                 vel_z = dir_z + crandom()*spread;\r
678                 vel = normalize(vel) * cvar("g_balance_grenade_mirv_speed2");\r
679                 W_MirvBomb(self.origin, vel);\r
680         }\r
681         remove(self);\r
682 }\r
683 \r
684 void W_MirvGrenade()\r
685 {\r
686         setmodel(self, "models/grenademodel.md3");\r
687         setsize(self, '-6 -6 -3', '6 6 3');\r
688 \r
689         self.nextthink = time + 2.5;\r
690         self.think = W_MirvGrenade_Split;\r
691         self.touch = W_MirvGrenade_Split;\r
692         self.velocity = v_forward * cvar("g_balance_grenade_mirv_speed")\r
693                 + v_up * cvar("g_balance_grenade_mirv_speed_up");\r
694         self.avelocity = '100 150 100';\r
695 \r
696         self.angles = vectoangles (self.velocity);\r
697         self.scale = 2.5;\r
698 }\r
699 \r
700 //--------------------------------------------\r
701 \r
702 void W_FragGrenade_Explode (void)\r
703 {\r
704         vector  org2;\r
705         org2 = findbetterlocation (self.origin);\r
706         te_explosion (org2);\r
707         effect (org2, "models/sprites/grenadeexplosion.spr32", 0, 20, 30);\r
708         sound (self, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM);\r
709         sound (self, CHAN_VOICE, "weapons/grenade_impact.wav", 1, ATTN_NORM);\r
710 \r
711         self.event_damage = SUB_Null;\r
712         RadiusDamage (self, self.owner, cvar("g_balance_grenade_frag_damage"), cvar("g_balance_grenade_frag_edgedamage"), cvar("g_balance_grenade_frag_radius"), world, cvar("g_balance_grenade_frag_force"), WEP_GRENADE_FRAG);\r
713 \r
714         remove (self);\r
715 }\r
716 \r
717 void W_FragGrenade()\r
718 {\r
719         setmodel(self, "models/grenademodel.md3");\r
720         setsize(self, '-6 -6 -3', '6 6 3');\r
721 \r
722         self.nextthink = time + 2;//1.5 + random()*0.5;\r
723         self.think = W_FragGrenade_Explode;\r
724         self.touch = W_Grenade_DefaultTouch;\r
725         self.state = 1; // don't explode on contact with player\r
726         self.velocity = v_forward * cvar("g_balance_grenade_frag_speed")\r
727                 + v_up * cvar("g_balance_grenade_frag_speed_up");\r
728         self.avelocity = '100 150 100';\r
729 \r
730         self.angles = vectoangles (self.velocity);\r
731 }\r
732 \r
733 //--------------------------------------------\r
734 \r
735 void PoisonGrenadeGib(entity gib, string mdlname, vector org, vector v, float destroyontouch)\r
736 {\r
737         entity e;\r
738         e = TossGib(gib, mdlname, org, v, destroyontouch);\r
739         e.colormod = 5 * '0 1 0'; // bright green\r
740         e.scale = 0.2;\r
741         e.gravity = 0.8;\r
742         e.velocity = e.velocity * e.gravity;// * 0.8;\r
743 }\r
744 \r
745 void W_PoisonGrenade_Explode (void)\r
746 {\r
747         vector  org2, org1;\r
748         entity head;\r
749 \r
750         org2 = findbetterlocation (self.origin);\r
751         org1 = org2 + '0 0 5';\r
752         te_explosionrgb (org2, (1/255)*'0 255 0');\r
753         sound (trace_ent, CHAN_VOICE, "misc/gib.wav", 1, ATTN_NORM);\r
754         PoisonGrenadeGib (world, "models/gibs/gib5.md3", org1, '-500 0 450',1);\r
755         PoisonGrenadeGib (world, "models/gibs/gib6.md3", org1, '0 500 450',1);\r
756         PoisonGrenadeGib (world, "models/gibs/gib6.md3", org1, '0 -500 450',1);\r
757         PoisonGrenadeGib (world, "models/gibs/gib6.md3", org1, '500 0 450',1);\r
758         PoisonGrenadeGib (world, "models/gibs/chunk.mdl", org1, '0 0 450',1);\r
759 \r
760 \r
761         head = findradius(self.origin, cvar("g_balance_grenade_poison_radius"));\r
762         while(head)\r
763         {\r
764                 if(head.classname == "player") // only players can be poisoned\r
765                 {\r
766                         traceline(self.origin, head.origin, TRUE, self);\r
767                         if(trace_fraction >= 1)\r
768                         {\r
769                                 PoisonTarget(head, cvar("g_balance_grenade_poison_damage"), cvar("g_balance_grenade_poison_duration"), TRUE);\r
770                                 //bprint(strcat(head.netname, " is poisoned for ", ftos(head.poison_damage), " damage\n"));\r
771                         }\r
772                 }\r
773                 head = head.chain;\r
774         }\r
775 \r
776         remove (self);\r
777 }\r
778 \r
779 void W_PoisonGrenade()\r
780 {\r
781         setmodel(self, "models/grenademodel.md3");\r
782         setsize(self, '-6 -6 -3', '6 6 3');\r
783 \r
784         self.nextthink = time + 1.5 + random()*0.5;\r
785         self.think = W_PoisonGrenade_Explode;\r
786         self.touch = W_Grenade_DefaultTouch;\r
787         self.state = 1; // don't explode on contact with player\r
788         self.velocity = v_forward * cvar("g_balance_grenade_poison_speed")\r
789                 + v_up * cvar("g_balance_grenade_poison_speed_up");\r
790         self.avelocity = '100 150 100';\r
791 \r
792         self.angles = vectoangles (self.velocity);\r
793 }\r
794 \r
795 //--------------------------------------------\r
796 \r
797 void W_ArmorGrenade_Explode (void)\r
798 {\r
799         entity head;\r
800         vector  org2;\r
801 \r
802         org2 = findbetterlocation (self.origin);\r
803         te_explosion (org2);\r
804         effect (org2, "models/sprites/grenadeexplosion.spr32", 0, 20, 30);\r
805         sound (self, CHAN_BODY, "weapons/grenade_impact.wav", 1, ATTN_NORM);\r
806 \r
807         head = findradius(self.origin, cvar("g_balance_grenade_armor_radius"));\r
808         while(head)\r
809         {\r
810                 if(head.classname == "player") // only players are affected\r
811                 {\r
812                         traceline(self.origin, head.origin, TRUE, self);\r
813                         if(trace_fraction >= 1)\r
814                         {\r
815                                 head.armorvalue = 0; // wipe out target's armor\r
816                         }\r
817                 }\r
818                 head = head.chain;\r
819         }\r
820 \r
821         remove (self);\r
822 }\r
823 \r
824 void W_ArmorGrenade()\r
825 {\r
826         setmodel(self, "models/grenademodel.md3");\r
827         setsize(self, '-6 -6 -3', '6 6 3');\r
828 \r
829         self.nextthink = time + 1.5 + random()*0.5;\r
830         self.think = W_ArmorGrenade_Explode;\r
831         self.touch = W_Grenade_DefaultTouch;\r
832         self.state = 1; // don't explode on contact with player\r
833         self.velocity = v_forward * cvar("g_balance_grenade_armor_speed")\r
834                 + v_up * cvar("g_balance_grenade_armor_speed_up");\r
835         self.avelocity = '100 150 100';\r
836 \r
837         self.angles = vectoangles (self.velocity);\r
838 }\r
839 \r
840 //--------------------------------------------\r
841 \r