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