one step towards making the rifle the "standard sniper gun" instead of the Nex: on...
[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 void W_Grenade_Explode (void)
6 {
7         if(other.takedamage == DAMAGE_AIM)
8                 if(other.classname == "player")
9                         if(IsDifferentTeam(self.owner, other))
10                                 if(other.deadflag == DEAD_NO)
11                                         if(IsFlying(other))
12                                                 AnnounceTo(self.owner, "airshot");
13
14         self.event_damage = SUB_Null;
15         self.takedamage = DAMAGE_NO;
16
17         if(self.movetype == MOVETYPE_NONE)
18                 self.velocity = self.oldvelocity;
19
20         RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_primary_damage"), cvar("g_balance_grenadelauncher_primary_edgedamage"), cvar("g_balance_grenadelauncher_primary_radius"), world, cvar("g_balance_grenadelauncher_primary_force"), self.projectiledeathtype, other);
21
22         remove (self);
23 }
24
25 void W_Grenade_Explode2 (void)
26 {
27         if(other.takedamage == DAMAGE_AIM)
28                 if(other.classname == "player")
29                         if(IsDifferentTeam(self.owner, other))
30                                 if(IsFlying(other))
31                                         AnnounceTo(self.owner, "airshot");
32
33         self.event_damage = SUB_Null;
34         self.takedamage = DAMAGE_NO;
35         RadiusDamage (self, self.owner, cvar("g_balance_grenadelauncher_secondary_damage"), cvar("g_balance_grenadelauncher_secondary_edgedamage"), cvar("g_balance_grenadelauncher_secondary_radius"), world, cvar("g_balance_grenadelauncher_secondary_force"), self.projectiledeathtype, other);
36
37         remove (self);
38 }
39
40 void W_Grenade_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
41 {
42         if (self.health <= 0)
43                 return;
44         self.health = self.health - damage;
45         if (self.health <= 0)
46         {
47                 W_PrepareExplosionByDamage(attacker, self.think);
48         }
49 }
50
51 void W_Grenade_Touch1 (void)
52 {
53         PROJECTILE_TOUCH;
54         if(cvar("g_balance_grenadelauncher_primary_sticky") && other.takedamage != DAMAGE_AIM)
55         {
56                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
57
58                 // let it stick whereever it is
59                 self.oldvelocity = self.velocity;
60                 self.velocity = '0 0 0';
61                 self.movetype = MOVETYPE_NONE; // also disables gravity
62                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
63                 UpdateCSQCProjectile(self);
64
65                 // do not respond to any more touches
66                 self.solid = SOLID_NOT;
67
68                 self.nextthink = min(self.nextthink, time + cvar("g_balance_grenadelauncher_primary_lifetime2"));
69         }
70         else
71                 W_Grenade_Explode ();
72 }
73
74 void W_Grenade_Touch2 (void)
75 {
76         PROJECTILE_TOUCH;
77         if(cvar("g_balance_grenadelauncher_secondary_sticky") && other.takedamage != DAMAGE_AIM)
78         {
79                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
80
81                 // let it stick whereever it is
82                 self.oldvelocity = self.velocity;
83                 self.velocity = '0 0 0';
84                 self.movetype = MOVETYPE_NONE; // also disables gravity
85                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
86                 UpdateCSQCProjectile(self);
87
88                 // do not respond to any more touches
89                 self.solid = SOLID_NOT;
90
91                 self.nextthink = min(self.nextthink, time + cvar("g_balance_grenadelauncher_secondary_lifetime2"));
92         }
93         else
94         {
95                 if (other.takedamage == DAMAGE_AIM)
96                 {
97                         self.use ();
98                 }
99                 else
100                 {
101                         float r;
102                         r = random() * 6;
103                         if(r < 1)
104                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
105                         else if(r < 2)
106                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
107                         else if(r < 3)
108                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
109                         else if(r < 4)
110                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
111                         else if(r < 5)
112                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
113                         else
114                                 spamsound (self, CHAN_PROJECTILE, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
115                         self.projectiledeathtype |= HITTYPE_BOUNCE;
116                 }
117         }
118 }
119
120 void W_Grenade_Attack (void)
121 {
122         local entity gren;
123
124         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
125                 self.ammo_rockets = self.ammo_rockets - cvar("g_balance_grenadelauncher_primary_ammo");
126         W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 4, "weapons/grenade_fire.wav", cvar("g_balance_grenadelauncher_primary_damage"));
127         w_shotdir = v_forward; // no TrueAim for grenades please
128
129         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
130
131         gren = spawn ();
132         gren.owner = self;
133         gren.classname = "grenade";
134         gren.bot_dodge = TRUE;
135         gren.bot_dodgerating = cvar("g_balance_grenadelauncher_primary_damage");
136         gren.movetype = MOVETYPE_BOUNCE;
137         PROJECTILE_MAKETRIGGER(gren);
138         gren.projectiledeathtype = WEP_GRENADE_LAUNCHER;
139         setorigin(gren, w_shotorg);
140         setsize(gren, '0 0 -3', '0 0 -3');
141
142         gren.nextthink = time + cvar("g_balance_grenadelauncher_primary_lifetime");
143         gren.think = adaptor_think2use_hittype_splash;
144         gren.use = W_Grenade_Explode;
145         gren.touch = W_Grenade_Touch1;
146
147         gren.takedamage = DAMAGE_YES;
148         gren.health = cvar("g_balance_grenadelauncher_primary_health");
149         gren.event_damage = W_Grenade_Damage;
150         W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_primary);
151
152         gren.angles = vectoangles (gren.velocity);
153         gren.flags = FL_PROJECTILE;
154
155         CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);   
156 }
157
158 void W_Grenade_Attack2 (void)
159 {
160         local entity gren;
161
162         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
163                 self.ammo_rockets = self.ammo_rockets - cvar("g_balance_grenadelauncher_secondary_ammo");
164         W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 4, "weapons/grenade_fire.wav", cvar("g_balance_grenadelauncher_secondary_damage"));
165         w_shotdir = v_forward; // no TrueAim for grenades please
166
167         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
168
169         gren = spawn ();
170         gren.owner = self;
171         gren.classname = "grenade";
172         gren.bot_dodge = TRUE;
173         gren.bot_dodgerating = cvar("g_balance_grenadelauncher_secondary_damage");
174         gren.movetype = MOVETYPE_BOUNCE;
175         gren.bouncefactor = cvar("g_balance_grenadelauncher_secondary_bouncefactor");
176         gren.bouncestop = cvar("g_balance_grenadelauncher_secondary_bouncestop");
177         PROJECTILE_MAKETRIGGER(gren);
178         gren.projectiledeathtype = WEP_GRENADE_LAUNCHER | HITTYPE_SECONDARY;
179         setorigin(gren, w_shotorg);
180         setsize(gren, '0 0 -3', '0 0 -3');
181
182         gren.nextthink = time + cvar("g_balance_grenadelauncher_secondary_lifetime");
183         gren.think = adaptor_think2use_hittype_splash;
184         gren.use = W_Grenade_Explode2;
185         gren.touch = W_Grenade_Touch2;
186
187         gren.takedamage = DAMAGE_YES;
188         gren.health = cvar("g_balance_grenadelauncher_secondary_health");
189         gren.damageforcescale = cvar("g_balance_grenadelauncher_secondary_damageforcescale");
190         gren.event_damage = W_Grenade_Damage;
191         W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_secondary);
192
193         gren.angles = vectoangles (gren.velocity);
194         gren.flags = FL_PROJECTILE;
195
196         if(cvar("g_balance_grenadelauncher_secondary_sticky"))
197                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
198         else
199                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
200 }
201
202 void spawnfunc_weapon_grenadelauncher (void)
203 {
204         weapon_defaultspawnfunc(WEP_GRENADE_LAUNCHER);
205 }
206
207 .float bot_secondary_grenademooth;
208 float w_glauncher(float req)
209 {
210         if (req == WR_AIM)
211         {
212                 self.BUTTON_ATCK = FALSE;
213                 self.BUTTON_ATCK2 = FALSE;
214                 if (self.bot_secondary_grenademooth == 0)
215                 {
216                         if(bot_aim(cvar("g_balance_grenadelauncher_primary_speed"), cvar("g_balance_grenadelauncher_primary_speed_up"), cvar("g_balance_grenadelauncher_primary_lifetime"), TRUE))
217                         {
218                                 self.BUTTON_ATCK = TRUE;
219                                 if(random() < 0.01) self.bot_secondary_grenademooth = 1;
220                         }
221                 }
222                 else
223                 {
224                         if(bot_aim(cvar("g_balance_grenadelauncher_secondary_speed"), cvar("g_balance_grenadelauncher_secondary_speed_up"), cvar("g_balance_grenadelauncher_secondary_lifetime"), TRUE))
225                         {
226                                 self.BUTTON_ATCK2 = TRUE;
227                                 if(random() < 0.02) self.bot_secondary_grenademooth = 0;
228                         }
229                 }
230         }
231         else if (req == WR_THINK)
232         {
233                 if (self.BUTTON_ATCK)
234                 if (weapon_prepareattack(0, cvar("g_balance_grenadelauncher_primary_refire")))
235                 {
236                         if(cvar("g_balance_grenadelauncher_primary2secondary"))
237                                 W_Grenade_Attack2();
238                         else
239                                 W_Grenade_Attack();
240                         weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_grenadelauncher_primary_animtime"), w_ready);
241                 }
242                 if (self.BUTTON_ATCK2 && !cvar("g_balance_grenadelauncher_primary2secondary"))
243                 if (weapon_prepareattack(1, cvar("g_balance_grenadelauncher_secondary_refire")))
244                 {
245                         W_Grenade_Attack2();
246                         weapon_thinkf(WFRAME_FIRE2, cvar("g_balance_grenadelauncher_secondary_animtime"), w_ready);
247                 }
248         }
249         else if (req == WR_PRECACHE)
250         {
251                 precache_model ("models/weapons/g_gl.md3");
252                 precache_model ("models/weapons/v_gl.md3");
253                 precache_model ("models/weapons/h_gl.iqm");
254                 precache_sound ("weapons/grenade_bounce1.wav");
255                 precache_sound ("weapons/grenade_bounce2.wav");
256                 precache_sound ("weapons/grenade_bounce3.wav");
257                 precache_sound ("weapons/grenade_bounce4.wav");
258                 precache_sound ("weapons/grenade_bounce5.wav");
259                 precache_sound ("weapons/grenade_bounce6.wav");
260                 precache_sound ("weapons/grenade_stick.wav");
261                 precache_sound ("weapons/grenade_fire.wav");
262         }
263         else if (req == WR_SETUP)
264                 weapon_setup(WEP_GRENADE_LAUNCHER);
265         else if (req == WR_CHECKAMMO1)
266                 return self.ammo_rockets >= cvar("g_balance_grenadelauncher_primary_ammo");
267         else if (req == WR_CHECKAMMO2)
268                 return self.ammo_rockets >= cvar("g_balance_grenadelauncher_secondary_ammo");
269         return TRUE;
270 };
271 #endif
272 #ifdef CSQC
273 float w_glauncher(float req)
274 {
275         if(req == WR_IMPACTEFFECT)
276         {
277                 vector org2;
278                 org2 = w_org + w_backoff * 12;
279                 pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1);
280                 if(!w_issilent)
281                         sound(self, CHAN_PROJECTILE, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
282         }
283         else if(req == WR_PRECACHE)
284         {
285                 precache_sound("weapons/grenade_impact.wav");
286         }
287         else if (req == WR_SUICIDEMESSAGE)
288         {
289                 if(w_deathtype & HITTYPE_SECONDARY)
290                         w_deathtypestring = "%s tried out his own grenade";
291                 else
292                         w_deathtypestring = "%s detonated";
293         }
294         else if (req == WR_KILLMESSAGE)
295         {
296                 if(w_deathtype & HITTYPE_SPLASH)
297                         if(w_deathtype & HITTYPE_BOUNCE) // (must be secondary then)
298                                 w_deathtypestring = "%s didn't see %s's grenade";
299                         else // unchecked: SECONDARY
300                                 w_deathtypestring = "%s almost dodged %s's grenade";
301                 else // unchecked: SECONDARY, BOUNCE
302                         w_deathtypestring = "%s ate %s's grenade";
303         }
304         return TRUE;
305 }
306 #endif
307 #endif