]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_mortar.qc
Begin adding support for weaponstart and weaponreplace properties
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / w_mortar.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id */ MORTAR,
4 /* function */ w_glauncher,
5 /* ammotype */ IT_ROCKETS,
6 /* impulse  */ 4,
7 /* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
8 /* rating   */ BOT_PICKUP_RATING_MID,
9 /* model    */ "gl",
10 /* netname  */ "grenadelauncher",
11 /* fullname */ _("Mortar")
12 );
13
14 #define MORTAR_SETTINGS(w_cvar,w_prop) \
15         w_cvar(WEP_MORTAR, mortar, MO_BOTH, ammo) \
16         w_cvar(WEP_MORTAR, mortar, MO_BOTH, animtime) \
17         w_cvar(WEP_MORTAR, mortar, MO_NONE, bouncefactor) \
18         w_cvar(WEP_MORTAR, mortar, MO_NONE, bouncestop) \
19         w_cvar(WEP_MORTAR, mortar, MO_BOTH, damage) \
20         w_cvar(WEP_MORTAR, mortar, MO_BOTH, damageforcescale) \
21         w_cvar(WEP_MORTAR, mortar, MO_BOTH, edgedamage) \
22         w_cvar(WEP_MORTAR, mortar, MO_BOTH, force) \
23         w_cvar(WEP_MORTAR, mortar, MO_BOTH, health) \
24         w_cvar(WEP_MORTAR, mortar, MO_BOTH, lifetime) \
25         w_cvar(WEP_MORTAR, mortar, MO_SEC,  lifetime_bounce) \
26         w_cvar(WEP_MORTAR, mortar, MO_BOTH, lifetime_stick) \
27         w_cvar(WEP_MORTAR, mortar, MO_BOTH, radius) \
28         w_cvar(WEP_MORTAR, mortar, MO_BOTH, refire) \
29         w_cvar(WEP_MORTAR, mortar, MO_SEC,  remote_detonateprimary) \
30         w_cvar(WEP_MORTAR, mortar, MO_PRI,  remote_minbouncecnt) \
31         w_cvar(WEP_MORTAR, mortar, MO_BOTH, speed) \
32         w_cvar(WEP_MORTAR, mortar, MO_BOTH, speed_up) \
33         w_cvar(WEP_MORTAR, mortar, MO_BOTH, speed_z) \
34         w_cvar(WEP_MORTAR, mortar, MO_BOTH, spread) \
35         w_cvar(WEP_MORTAR, mortar, MO_BOTH, type) \
36         w_prop(WEP_MORTAR, mortar, float,  reloading_ammo, reload_ammo) \
37         w_prop(WEP_MORTAR, mortar, float,  reloading_time, reload_time) \
38         w_prop(WEP_MORTAR, mortar, float,  switchdelay_raise, switchdelay_raise) \
39         w_prop(WEP_MORTAR, mortar, float,  switchdelay_drop, switchdelay_drop)
40
41 #ifdef SVQC
42 MORTAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
43 .float gl_detonate_later;
44 .float gl_bouncecnt;
45 #endif
46 #else
47 #ifdef SVQC
48
49 void W_Grenade_Explode (void)
50 {
51         if(other.takedamage == DAMAGE_AIM)
52                 if(IS_PLAYER(other))
53                         if(DIFF_TEAM(self.realowner, other))
54                                 if(other.deadflag == DEAD_NO)
55                                         if(IsFlying(other))
56                                                 Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
57
58         self.event_damage = func_null;
59         self.takedamage = DAMAGE_NO;
60
61         if(self.movetype == MOVETYPE_NONE)
62                 self.velocity = self.oldvelocity;
63
64         RadiusDamage (self, self.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), world, world, WEP_CVAR_PRI(mortar, force), self.projectiledeathtype, other);
65
66         remove (self);
67 }
68
69 void W_Grenade_Explode2 (void)
70 {
71         if(other.takedamage == DAMAGE_AIM)
72                 if(IS_PLAYER(other))
73                         if(DIFF_TEAM(self.realowner, other))
74                                 if(other.deadflag == DEAD_NO)
75                                         if(IsFlying(other))
76                                                 Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
77
78         self.event_damage = func_null;
79         self.takedamage = DAMAGE_NO;
80
81         if(self.movetype == MOVETYPE_NONE)
82                 self.velocity = self.oldvelocity;
83
84         RadiusDamage (self, self.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), world, world, WEP_CVAR_SEC(mortar, force), self.projectiledeathtype, other);
85
86         remove (self);
87 }
88
89 void W_Grenade_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
90 {
91         if (self.health <= 0)
92                 return;
93                 
94         if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
95                 return; // g_projectiles_damage says to halt
96                 
97         self.health = self.health - damage;
98         
99         if (self.health <= 0)
100                 W_PrepareExplosionByDamage(attacker, self.use);
101 }
102
103 void W_Grenade_Think1 (void)
104 {
105         self.nextthink = time;
106         if (time > self.cnt)
107         {
108                 other = world;
109                 self.projectiledeathtype |= HITTYPE_BOUNCE;
110                 W_Grenade_Explode ();
111                 return;
112         }
113         if(self.gl_detonate_later && self.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt))
114                 W_Grenade_Explode();
115 }
116
117 void W_Grenade_Touch1 (void)
118 {
119         PROJECTILE_TOUCH;
120         if (other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
121         {
122                 self.use ();
123         }
124         else if (WEP_CVAR_PRI(mortar, type) == 1) // bounce
125         {
126                 float r;
127                 r = random() * 6;
128                 if(r < 1)
129                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
130                 else if(r < 2)
131                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
132                 else if(r < 3)
133                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
134                 else if(r < 4)
135                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
136                 else if(r < 5)
137                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
138                 else
139                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
140                 pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
141                 self.projectiledeathtype |= HITTYPE_BOUNCE;
142                 self.gl_bouncecnt += 1;
143         }
144         else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
145         {
146                 spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
147
148                 // let it stick whereever it is
149                 self.oldvelocity = self.velocity;
150                 self.velocity = '0 0 0';
151                 self.movetype = MOVETYPE_NONE; // also disables gravity
152                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
153                 UpdateCSQCProjectile(self);
154
155                 // do not respond to any more touches
156                 self.solid = SOLID_NOT;
157
158                 self.nextthink = min(self.nextthink, time + WEP_CVAR_PRI(mortar, lifetime_stick));
159         }
160 }
161
162 void W_Grenade_Touch2 (void)
163 {
164         PROJECTILE_TOUCH;
165         if (other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
166         {
167                 self.use ();
168         }
169         else if (WEP_CVAR_SEC(mortar, type) == 1) // bounce
170         {
171                 float r;
172                 r = random() * 6;
173                 if(r < 1)
174                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
175                 else if(r < 2)
176                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
177                 else if(r < 3)
178                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
179                 else if(r < 4)
180                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
181                 else if(r < 5)
182                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
183                 else
184                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
185                 pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
186                 self.projectiledeathtype |= HITTYPE_BOUNCE;
187                 self.gl_bouncecnt += 1;
188                 
189                 if (WEP_CVAR_SEC(mortar, lifetime_bounce) && self.gl_bouncecnt == 1)
190                         self.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce);
191                         
192         }
193         else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
194         {
195                 spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
196
197                 // let it stick whereever it is
198                 self.oldvelocity = self.velocity;
199                 self.velocity = '0 0 0';
200                 self.movetype = MOVETYPE_NONE; // also disables gravity
201                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
202                 UpdateCSQCProjectile(self);
203
204                 // do not respond to any more touches
205                 self.solid = SOLID_NOT;
206
207                 self.nextthink = min(self.nextthink, time + WEP_CVAR_SEC(mortar, lifetime_stick));
208         }
209 }
210
211 void W_Grenade_Attack (void)
212 {
213         entity gren;
214
215         W_DecreaseAmmo(ammo_rockets, WEP_CVAR_PRI(mortar, ammo), autocvar_g_balance_mortar_reload_ammo); // WEAPONTODO
216
217         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
218         w_shotdir = v_forward; // no TrueAim for grenades please
219
220         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
221
222         gren = spawn ();
223         gren.owner = gren.realowner = self;
224         gren.classname = "grenade";
225         gren.bot_dodge = TRUE;
226         gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage);
227         gren.movetype = MOVETYPE_BOUNCE;
228         gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
229         gren.bouncestop = WEP_CVAR(mortar, bouncestop);
230         PROJECTILE_MAKETRIGGER(gren);
231         gren.projectiledeathtype = WEP_MORTAR;
232         setorigin(gren, w_shotorg);
233         setsize(gren, '-3 -3 -3', '3 3 3');
234
235         gren.cnt = time + WEP_CVAR_PRI(mortar, lifetime);
236         gren.nextthink = time;
237         gren.think = W_Grenade_Think1;
238         gren.use = W_Grenade_Explode;
239         gren.touch = W_Grenade_Touch1;
240
241         gren.takedamage = DAMAGE_YES;
242         gren.health = WEP_CVAR_PRI(mortar, health);
243         gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
244         gren.event_damage = W_Grenade_Damage;
245         gren.damagedbycontents = TRUE;
246         gren.missile_flags = MIF_SPLASH | MIF_ARC;
247         WEP_SETUPPROJVELOCITY_UP_PRI(gren, mortar);
248
249         gren.angles = vectoangles (gren.velocity);
250         gren.flags = FL_PROJECTILE;
251
252         if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2)
253                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
254         else
255                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
256
257         other = gren; MUTATOR_CALLHOOK(EditProjectile);
258 }
259
260 void W_Grenade_Attack2 (void)
261 {
262         entity gren;
263
264         W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(mortar, ammo), autocvar_g_balance_mortar_reload_ammo);
265
266         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
267         w_shotdir = v_forward; // no TrueAim for grenades please
268
269         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
270
271         gren = spawn ();
272         gren.owner = gren.realowner = self;
273         gren.classname = "grenade";
274         gren.bot_dodge = TRUE;
275         gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage);
276         gren.movetype = MOVETYPE_BOUNCE;
277         gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
278         gren.bouncestop = WEP_CVAR(mortar, bouncestop);
279         PROJECTILE_MAKETRIGGER(gren);
280         gren.projectiledeathtype = WEP_MORTAR | HITTYPE_SECONDARY;
281         setorigin(gren, w_shotorg);
282         setsize(gren, '-3 -3 -3', '3 3 3');
283
284         gren.nextthink = time + WEP_CVAR_SEC(mortar, lifetime);
285         gren.think = adaptor_think2use_hittype_splash;
286         gren.use = W_Grenade_Explode2;
287         gren.touch = W_Grenade_Touch2;
288
289         gren.takedamage = DAMAGE_YES;
290         gren.health = WEP_CVAR_SEC(mortar, health);
291         gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
292         gren.event_damage = W_Grenade_Damage;
293         gren.damagedbycontents = TRUE;
294         gren.missile_flags = MIF_SPLASH | MIF_ARC;
295         WEP_SETUPPROJVELOCITY_UP_SEC(gren, mortar); // WEAPONTODO
296
297         gren.angles = vectoangles (gren.velocity);
298         gren.flags = FL_PROJECTILE;
299
300         if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2)
301                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
302         else
303                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
304
305         other = gren; MUTATOR_CALLHOOK(EditProjectile);
306 }
307
308 void spawnfunc_weapon_grenadelauncher (void)
309 {
310         weapon_defaultspawnfunc(WEP_MORTAR);
311 }
312
313 .float bot_secondary_grenademooth;
314 float w_glauncher(float req)
315 {
316         entity nade;
317         float nadefound;
318         float ammo_amount;
319         switch(req)
320         {
321                 case WR_AIM:
322                 {
323                         self.BUTTON_ATCK = FALSE;
324                         self.BUTTON_ATCK2 = FALSE;
325                         if (self.bot_secondary_grenademooth == 0) // WEAPONTODO: merge this into using WEP_CVAR_BOTH
326                         {
327                                 if(bot_aim(WEP_CVAR_PRI(mortar, speed), WEP_CVAR_PRI(mortar, speed_up), WEP_CVAR_PRI(mortar, lifetime), TRUE))
328                                 {
329                                         self.BUTTON_ATCK = TRUE;
330                                         if(random() < 0.01) self.bot_secondary_grenademooth = 1;
331                                 }
332                         }
333                         else
334                         {
335                                 if(bot_aim(WEP_CVAR_SEC(mortar, speed), WEP_CVAR_SEC(mortar, speed_up), WEP_CVAR_SEC(mortar, lifetime), TRUE))
336                                 {
337                                         self.BUTTON_ATCK2 = TRUE;
338                                         if(random() < 0.02) self.bot_secondary_grenademooth = 0;
339                                 }
340                         }
341                         
342                         return TRUE;
343                 }
344                 /*case WR_CALCINFO:
345                 {
346                         wepinfo_pri_refire = max3(sys_frametime, WEP_CVAR_PRI(mortar, refire), WEP_CVAR_PRI(mortar, animtime));
347                         wepinfo_pri_dps = (WEP_CVAR_PRI(mortar, damage) * (1 / wepinfo_pri_refire));
348                         wepinfo_pri_speed = (1 / max(1, (10000 / max(1, WEP_CVAR_PRI(mortar, speed)))));
349
350                         // for the range calculation, closer to 1 is better
351                         wepinfo_pri_range_max = 2000 * wepinfo_pri_speed;
352                         wepinfo_pri_range = wepinfo_pri_speed * WEP_CVAR_PRI(mortar, 
353                         
354                         wepinfo_sec_refire = max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime));
355                         wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / wepinfo_sec_refire));
356                         
357                         wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime))));
358                         wepinfo_ter_dps = 0;
359                         */
360                 case WR_THINK:
361                 {
362                         if(autocvar_g_balance_mortar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) // forced reload
363                                 WEP_ACTION(self.weapon, WR_RELOAD);
364                         else if (self.BUTTON_ATCK)
365                         {
366                                 if (weapon_prepareattack(0, WEP_CVAR_PRI(mortar, refire)))
367                                 {
368                                         W_Grenade_Attack();
369                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
370                                 }
371                         }
372                         else if (self.BUTTON_ATCK2)
373                         {
374                                 if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
375                                 {
376                                         nadefound = 0;
377                                         for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self)
378                                         {
379                                                 if(!nade.gl_detonate_later)
380                                                 {
381                                                         nade.gl_detonate_later = TRUE;
382                                                         nadefound = 1;
383                                                 }
384                                         }
385                                         if(nadefound)
386                                                 sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
387                                 }
388                                 else if (weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire)))
389                                 {
390                                         W_Grenade_Attack2();
391                                         weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
392                                 }
393                         }
394                         
395                         return TRUE;
396                 }
397                 case WR_INIT:
398                 {
399                         precache_model ("models/weapons/g_gl.md3");
400                         precache_model ("models/weapons/v_gl.md3");
401                         precache_model ("models/weapons/h_gl.iqm");
402                         precache_sound ("weapons/grenade_bounce1.wav");
403                         precache_sound ("weapons/grenade_bounce2.wav");
404                         precache_sound ("weapons/grenade_bounce3.wav");
405                         precache_sound ("weapons/grenade_bounce4.wav");
406                         precache_sound ("weapons/grenade_bounce5.wav");
407                         precache_sound ("weapons/grenade_bounce6.wav");
408                         precache_sound ("weapons/grenade_stick.wav");
409                         precache_sound ("weapons/grenade_fire.wav");
410                         MORTAR_SETTINGS(WEP_SKIPCVAR, WEP_SET_PROP)
411                         return TRUE;
412                 }
413                 case WR_SETUP:
414                 {
415                         self.current_ammo = ammo_rockets;
416                         return TRUE;
417                 }
418                 case WR_CHECKAMMO1:
419                 {
420                         ammo_amount = self.ammo_rockets >= WEP_CVAR_PRI(mortar, ammo);
421                         ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_PRI(mortar, ammo);
422                         return ammo_amount;
423                 }
424                 case WR_CHECKAMMO2:
425                 {
426                         ammo_amount = self.ammo_rockets >= WEP_CVAR_SEC(mortar, ammo);
427                         ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_SEC(mortar, ammo);
428                         return ammo_amount;
429                 }
430                 case WR_CONFIG:
431                 {
432                         MORTAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
433                         return TRUE;
434                 }
435                 case WR_RELOAD:
436                 {
437                         W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), "weapons/reload.wav"); // WEAPONTODO
438                         return TRUE;
439                 }
440                 case WR_SUICIDEMESSAGE:
441                 {
442                         if(w_deathtype & HITTYPE_SECONDARY)
443                                 return WEAPON_MORTAR_SUICIDE_BOUNCE;
444                         else
445                                 return WEAPON_MORTAR_SUICIDE_EXPLODE;
446                 }
447                 case WR_KILLMESSAGE:
448                 {
449                         if(w_deathtype & HITTYPE_SECONDARY)
450                                 return WEAPON_MORTAR_MURDER_BOUNCE;
451                         else
452                                 return WEAPON_MORTAR_MURDER_EXPLODE;
453                 }
454         }
455         return TRUE;
456 }
457 #endif
458 #ifdef CSQC
459 float w_glauncher(float req)
460 {
461         switch(req)
462         {
463                 case WR_IMPACTEFFECT:
464                 {
465                         vector org2;
466                         org2 = w_org + w_backoff * 12;
467                         pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1);
468                         if(!w_issilent)
469                                 sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
470                                 
471                         return TRUE;
472                 }
473                 case WR_INIT:
474                 {
475                         precache_sound("weapons/grenade_impact.wav");
476                         return TRUE;
477                 }
478         }
479         return TRUE;
480 }
481 #endif
482 #endif