]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_grenadelauncher.qc
Create a RELOADABLE weapon flag, and use it to prevent running useless shared code...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_grenadelauncher.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(GRENADE_LAUNCHER, w_glauncher, IT_ROCKETS, 4, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "gl", "grenadelauncher", _("Mortar"))
3 #else
4 #ifdef SVQC
5 .float gl_detonate_later;
6 .float gl_bouncecnt;
7
8 void W_GrenadeLauncher_SetAmmoCounter()
9 {
10         // set clip_load to the weapon we have switched to, if the gun uses reloading
11         if(!autocvar_g_balance_grenadelauncher_reload_ammo)
12                 self.clip_load = 0; // also keeps crosshair ammo from displaying
13         else
14         {
15                 self.clip_load = self.weapon_load[WEP_GRENADE_LAUNCHER];
16                 self.clip_size = autocvar_g_balance_grenadelauncher_reload_ammo; // for the crosshair ammo display
17         }
18 }
19
20 void W_GrenadeLauncher_Reload()
21 {
22         self.reload_ammo_player = ammo_rockets;
23         self.reload_ammo_min = min(autocvar_g_balance_grenadelauncher_primary_ammo, autocvar_g_balance_grenadelauncher_secondary_ammo);
24         self.reload_ammo_amount = autocvar_g_balance_grenadelauncher_reload_ammo;
25         self.reload_time = autocvar_g_balance_grenadelauncher_reload_time;
26         self.reload_sound = "weapons/reload.wav";
27
28         W_Reload();
29 }
30
31 void W_Grenade_Explode (void)
32 {
33         if(other.takedamage == DAMAGE_AIM)
34                 if(other.classname == "player")
35                         if(IsDifferentTeam(self.owner, other))
36                                 if(other.deadflag == DEAD_NO)
37                                         if(IsFlying(other))
38                                                 AnnounceTo(self.owner, "airshot");
39
40         self.event_damage = SUB_Null;
41         self.takedamage = DAMAGE_NO;
42
43         if(self.movetype == MOVETYPE_NONE)
44                 self.velocity = self.oldvelocity;
45
46         RadiusDamage (self, self.owner, autocvar_g_balance_grenadelauncher_primary_damage, autocvar_g_balance_grenadelauncher_primary_edgedamage, autocvar_g_balance_grenadelauncher_primary_radius, world, autocvar_g_balance_grenadelauncher_primary_force, self.projectiledeathtype, other);
47
48         remove (self);
49 }
50
51 void W_Grenade_Explode2 (void)
52 {
53         if(other.takedamage == DAMAGE_AIM)
54                 if(other.classname == "player")
55                         if(IsDifferentTeam(self.owner, other))
56                                 if(IsFlying(other))
57                                         AnnounceTo(self.owner, "airshot");
58
59         self.event_damage = SUB_Null;
60         self.takedamage = DAMAGE_NO;
61
62         if(self.movetype == MOVETYPE_NONE)
63                 self.velocity = self.oldvelocity;
64
65         RadiusDamage (self, self.owner, autocvar_g_balance_grenadelauncher_secondary_damage, autocvar_g_balance_grenadelauncher_secondary_edgedamage, autocvar_g_balance_grenadelauncher_secondary_radius, world, autocvar_g_balance_grenadelauncher_secondary_force, self.projectiledeathtype, other);
66
67         remove (self);
68 }
69
70 void W_Grenade_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
71 {
72         if (self.health <= 0)
73                 return;
74         self.health = self.health - damage;
75         if (self.health <= 0)
76         {
77                 W_PrepareExplosionByDamage(attacker, self.think);
78         }
79 }
80
81 void W_Grenade_Think1 (void)
82 {
83         self.nextthink = time;
84         if (time > self.cnt)
85         {
86                 other = world;
87                 self.projectiledeathtype |= HITTYPE_BOUNCE;
88                 W_Grenade_Explode ();
89                 return;
90         }
91         if(self.gl_detonate_later && self.gl_bouncecnt >= autocvar_g_balance_grenadelauncher_primary_remote_minbouncecnt)
92                 W_Grenade_Explode();
93 }
94
95 void W_Grenade_Touch1 (void)
96 {
97         PROJECTILE_TOUCH;
98         if (other.takedamage == DAMAGE_AIM || autocvar_g_balance_grenadelauncher_primary_type == 0) // always explode when hitting a player, or if normal mortar projectile
99         {
100                 self.use ();
101         }
102         else if (autocvar_g_balance_grenadelauncher_primary_type == 1) // bounce
103         {
104                 float r;
105                 r = random() * 6;
106                 if(r < 1)
107                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
108                 else if(r < 2)
109                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
110                 else if(r < 3)
111                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
112                 else if(r < 4)
113                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
114                 else if(r < 5)
115                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
116                 else
117                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
118                 self.projectiledeathtype |= HITTYPE_BOUNCE;
119                 self.gl_bouncecnt += 1;
120         }
121         else if(autocvar_g_balance_grenadelauncher_primary_type == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
122         {
123                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
124
125                 // let it stick whereever it is
126                 self.oldvelocity = self.velocity;
127                 self.velocity = '0 0 0';
128                 self.movetype = MOVETYPE_NONE; // also disables gravity
129                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
130                 UpdateCSQCProjectile(self);
131
132                 // do not respond to any more touches
133                 self.solid = SOLID_NOT;
134
135                 self.nextthink = min(self.nextthink, time + autocvar_g_balance_grenadelauncher_primary_lifetime2);
136         }
137 }
138
139 void W_Grenade_Touch2 (void)
140 {
141         PROJECTILE_TOUCH;
142         if (other.takedamage == DAMAGE_AIM || autocvar_g_balance_grenadelauncher_secondary_type == 0) // always explode when hitting a player, or if normal mortar projectile
143         {
144                 self.use ();
145         }
146         else if (autocvar_g_balance_grenadelauncher_secondary_type == 1) // bounce
147         {
148                 float r;
149                 r = random() * 6;
150                 if(r < 1)
151                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
152                 else if(r < 2)
153                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
154                 else if(r < 3)
155                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
156                 else if(r < 4)
157                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
158                 else if(r < 5)
159                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
160                 else
161                         spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
162                 self.projectiledeathtype |= HITTYPE_BOUNCE;
163                 self.gl_bouncecnt += 1;
164         }
165         else if(autocvar_g_balance_grenadelauncher_secondary_type == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
166         {
167                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
168
169                 // let it stick whereever it is
170                 self.oldvelocity = self.velocity;
171                 self.velocity = '0 0 0';
172                 self.movetype = MOVETYPE_NONE; // also disables gravity
173                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
174                 UpdateCSQCProjectile(self);
175
176                 // do not respond to any more touches
177                 self.solid = SOLID_NOT;
178
179                 self.nextthink = min(self.nextthink, time + autocvar_g_balance_grenadelauncher_secondary_lifetime2);
180         }
181 }
182
183 void W_Grenade_Attack (void)
184 {
185         local entity gren;
186
187         // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
188         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
189         {
190                 if(autocvar_g_balance_grenadelauncher_reload_ammo)
191                 {
192                         self.clip_load -= autocvar_g_balance_grenadelauncher_primary_ammo;
193                         self.weapon_load[WEP_GRENADE_LAUNCHER] = self.clip_load;
194                 }
195                 else
196                         self.ammo_rockets -= autocvar_g_balance_grenadelauncher_primary_ammo;
197         }
198
199         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CHAN_WEAPON, autocvar_g_balance_grenadelauncher_primary_damage);
200         w_shotdir = v_forward; // no TrueAim for grenades please
201
202         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
203
204         gren = spawn ();
205         gren.owner = self;
206         gren.classname = "grenade";
207         gren.bot_dodge = TRUE;
208         gren.bot_dodgerating = autocvar_g_balance_grenadelauncher_primary_damage;
209         gren.movetype = MOVETYPE_BOUNCE;
210         gren.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor;
211         gren.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop;
212         PROJECTILE_MAKETRIGGER(gren);
213         gren.projectiledeathtype = WEP_GRENADE_LAUNCHER;
214         setorigin(gren, w_shotorg);
215         setsize(gren, '-3 -3 -3', '3 3 3');
216
217         gren.cnt = time + autocvar_g_balance_grenadelauncher_primary_lifetime;
218         gren.nextthink = time;
219         gren.think = W_Grenade_Think1;
220         gren.use = W_Grenade_Explode;
221         gren.touch = W_Grenade_Touch1;
222
223         gren.takedamage = DAMAGE_YES;
224         gren.health = autocvar_g_balance_grenadelauncher_primary_health;
225         gren.damageforcescale = autocvar_g_balance_grenadelauncher_primary_damageforcescale;
226         gren.event_damage = W_Grenade_Damage;
227         W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_primary);
228
229         gren.angles = vectoangles (gren.velocity);
230         gren.flags = FL_PROJECTILE;
231
232         if(autocvar_g_balance_grenadelauncher_primary_type == 0 || autocvar_g_balance_grenadelauncher_primary_type == 2)
233                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
234         else
235                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
236
237         other = gren; MUTATOR_CALLHOOK(EditProjectile);
238 }
239
240 void W_Grenade_Attack2 (void)
241 {
242         local entity gren;
243
244         // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
245         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
246         {
247                 if(autocvar_g_balance_grenadelauncher_reload_ammo)
248                 {
249                         self.clip_load -= autocvar_g_balance_grenadelauncher_secondary_ammo;
250                         self.weapon_load[WEP_GRENADE_LAUNCHER] = self.clip_load;
251                 }
252                 else
253                         self.ammo_rockets -= autocvar_g_balance_grenadelauncher_secondary_ammo;
254         }
255
256         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CHAN_WEAPON, autocvar_g_balance_grenadelauncher_secondary_damage);
257         w_shotdir = v_forward; // no TrueAim for grenades please
258
259         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
260
261         gren = spawn ();
262         gren.owner = self;
263         gren.classname = "grenade";
264         gren.bot_dodge = TRUE;
265         gren.bot_dodgerating = autocvar_g_balance_grenadelauncher_secondary_damage;
266         gren.movetype = MOVETYPE_BOUNCE;
267         gren.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor;
268         gren.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop;
269         PROJECTILE_MAKETRIGGER(gren);
270         gren.projectiledeathtype = WEP_GRENADE_LAUNCHER | HITTYPE_SECONDARY;
271         setorigin(gren, w_shotorg);
272         setsize(gren, '-3 -3 -3', '3 3 3');
273
274         gren.nextthink = time + autocvar_g_balance_grenadelauncher_secondary_lifetime;
275         gren.think = adaptor_think2use_hittype_splash;
276         gren.use = W_Grenade_Explode2;
277         gren.touch = W_Grenade_Touch2;
278
279         gren.takedamage = DAMAGE_YES;
280         gren.health = autocvar_g_balance_grenadelauncher_secondary_health;
281         gren.damageforcescale = autocvar_g_balance_grenadelauncher_secondary_damageforcescale;
282         gren.event_damage = W_Grenade_Damage;
283         W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_secondary);
284
285         gren.angles = vectoangles (gren.velocity);
286         gren.flags = FL_PROJECTILE;
287
288         if(autocvar_g_balance_grenadelauncher_secondary_type == 0 || autocvar_g_balance_grenadelauncher_secondary_type == 2)
289                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
290         else
291                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
292
293         other = gren; MUTATOR_CALLHOOK(EditProjectile);
294 }
295
296 void spawnfunc_weapon_grenadelauncher (void)
297 {
298         weapon_defaultspawnfunc(WEP_GRENADE_LAUNCHER);
299 }
300
301 .float bot_secondary_grenademooth;
302 float w_glauncher(float req)
303 {
304         entity nade;
305         float nadefound;
306         float ammo_amount;
307
308         if (req == WR_AIM)
309         {
310                 self.BUTTON_ATCK = FALSE;
311                 self.BUTTON_ATCK2 = FALSE;
312                 if (self.bot_secondary_grenademooth == 0)
313                 {
314                         if(bot_aim(autocvar_g_balance_grenadelauncher_primary_speed, autocvar_g_balance_grenadelauncher_primary_speed_up, autocvar_g_balance_grenadelauncher_primary_lifetime, TRUE))
315                         {
316                                 self.BUTTON_ATCK = TRUE;
317                                 if(random() < 0.01) self.bot_secondary_grenademooth = 1;
318                         }
319                 }
320                 else
321                 {
322                         if(bot_aim(autocvar_g_balance_grenadelauncher_secondary_speed, autocvar_g_balance_grenadelauncher_secondary_speed_up, autocvar_g_balance_grenadelauncher_secondary_lifetime, TRUE))
323                         {
324                                 self.BUTTON_ATCK2 = TRUE;
325                                 if(random() < 0.02) self.bot_secondary_grenademooth = 0;
326                         }
327                 }
328         }
329         else if (req == WR_THINK)
330         {
331                 if(autocvar_g_balance_grenadelauncher_reload_ammo && self.clip_load < min(autocvar_g_balance_grenadelauncher_primary_ammo, autocvar_g_balance_grenadelauncher_secondary_ammo)) // forced reload
332                         W_GrenadeLauncher_Reload();
333                 else if (self.BUTTON_ATCK)
334                 {
335                         if (weapon_prepareattack(0, autocvar_g_balance_grenadelauncher_primary_refire))
336                         {
337                                 W_Grenade_Attack();
338                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_grenadelauncher_primary_animtime, w_ready);
339                         }
340                 }
341                 else if (self.BUTTON_ATCK2)
342                 {
343                         if (cvar("g_balance_grenadelauncher_secondary_remote_detonateprimary"))
344                         {
345                                 nadefound = 0;
346                                 for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.owner == self)
347                                 {
348                                         if(!nade.gl_detonate_later)
349                                         {
350                                                 nade.gl_detonate_later = TRUE;
351                                                 nadefound = 1;
352                                         }
353                                 }
354                                 if(nadefound)
355                                         sound (self, CHAN_WEAPON2, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
356                         }
357                         else if (weapon_prepareattack(1, autocvar_g_balance_grenadelauncher_secondary_refire))
358                         {
359                                 W_Grenade_Attack2();
360                                 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_grenadelauncher_secondary_animtime, w_ready);
361                         }
362                 }
363         }
364         else if (req == WR_PRECACHE)
365         {
366                 precache_model ("models/weapons/g_gl.md3");
367                 precache_model ("models/weapons/v_gl.md3");
368                 precache_model ("models/weapons/h_gl.iqm");
369                 precache_sound ("weapons/grenade_bounce1.wav");
370                 precache_sound ("weapons/grenade_bounce2.wav");
371                 precache_sound ("weapons/grenade_bounce3.wav");
372                 precache_sound ("weapons/grenade_bounce4.wav");
373                 precache_sound ("weapons/grenade_bounce5.wav");
374                 precache_sound ("weapons/grenade_bounce6.wav");
375                 precache_sound ("weapons/grenade_stick.wav");
376                 precache_sound ("weapons/grenade_fire.wav");
377                 precache_sound ("weapons/reload.wav");
378         }
379         else if (req == WR_SETUP)
380         {
381                 weapon_setup(WEP_GRENADE_LAUNCHER);
382                 W_GrenadeLauncher_SetAmmoCounter();
383         }
384         else if (req == WR_CHECKAMMO1)
385         {
386                 ammo_amount = self.ammo_rockets >= autocvar_g_balance_grenadelauncher_primary_ammo;
387                 ammo_amount += self.weapon_load[WEP_GRENADE_LAUNCHER] >= autocvar_g_balance_grenadelauncher_primary_ammo;
388                 return ammo_amount;
389         }
390         else if (req == WR_CHECKAMMO2)
391         {
392                 ammo_amount = self.ammo_rockets >= autocvar_g_balance_grenadelauncher_secondary_ammo;
393                 ammo_amount += self.weapon_load[WEP_GRENADE_LAUNCHER] >= autocvar_g_balance_grenadelauncher_secondary_ammo;
394                 return ammo_amount;
395         }
396         else if (req == WR_RELOAD)
397         {
398                 W_GrenadeLauncher_Reload();
399         }
400         return TRUE;
401 };
402 #endif
403 #ifdef CSQC
404 float w_glauncher(float req)
405 {
406         if(req == WR_IMPACTEFFECT)
407         {
408                 vector org2;
409                 org2 = w_org + w_backoff * 12;
410                 pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1);
411                 if(!w_issilent)
412                         sound(self, CHAN_PROJECTILE, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
413         }
414         else if(req == WR_PRECACHE)
415         {
416                 precache_sound("weapons/grenade_impact.wav");
417         }
418         else if (req == WR_SUICIDEMESSAGE)
419         {
420                 if(w_deathtype & HITTYPE_SECONDARY)
421                         w_deathtypestring = _("%s tried out his own grenade");
422                 else
423                         w_deathtypestring = _("%s detonated");
424         }
425         else if (req == WR_KILLMESSAGE)
426         {
427                 if(w_deathtype & HITTYPE_SPLASH)
428                         if(w_deathtype & HITTYPE_BOUNCE) // (must be secondary then)
429                                 w_deathtypestring = _("%s didn't see %s's grenade");
430                         else // unchecked: SECONDARY
431                                 w_deathtypestring = _("%s almost dodged %s's grenade");
432                 else // unchecked: SECONDARY, BOUNCE
433                         w_deathtypestring = _("%s ate %s's grenade");
434         }
435         return TRUE;
436 }
437 #endif
438 #endif