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