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