2ed0ca7140ed6995069c37b793eee55eb099ebfe
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / hagar.qc
1 #ifndef IMPLEMENTATION
2 CLASS(Hagar, Weapon)
3 /* ammotype  */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets)
4 /* impulse   */ ATTRIB(Hagar, impulse, int, 8)
5 /* flags     */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
6 /* rating    */ ATTRIB(Hagar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
7 /* color     */ ATTRIB(Hagar, wpcolor, vector, '1 1 0.5');
8 /* modelname */ ATTRIB(Hagar, mdl, string, "hagar");
9 #ifndef MENUQC
10 /* model     */ ATTRIB(Hagar, m_model, Model, MDL_HAGAR_ITEM);
11 #endif
12 /* crosshair */ ATTRIB(Hagar, w_crosshair, string, "gfx/crosshairhagar");
13 /* crosshair */ ATTRIB(Hagar, w_crosshair_size, float, 0.8);
14 /* wepimg    */ ATTRIB(Hagar, model2, string, "weaponhagar");
15 /* refname   */ ATTRIB(Hagar, netname, string, "hagar");
16 /* wepname   */ ATTRIB(Hagar, m_name, string, _("Hagar"));
17
18 #define X(BEGIN, P, END, class, prefix) \
19         BEGIN(class) \
20                 P(class, prefix, ammo, float, BOTH) \
21         P(class, prefix, damageforcescale, float, BOTH) \
22         P(class, prefix, damage, float, BOTH) \
23         P(class, prefix, edgedamage, float, BOTH) \
24         P(class, prefix, force, float, BOTH) \
25         P(class, prefix, health, float, BOTH) \
26         P(class, prefix, lifetime, float, PRI) \
27         P(class, prefix, lifetime_min, float, SEC) \
28         P(class, prefix, lifetime_rand, float, SEC) \
29         P(class, prefix, load, float, SEC) \
30         P(class, prefix, load_abort, float, SEC) \
31         P(class, prefix, load_animtime, float, SEC) \
32         P(class, prefix, load_hold, float, SEC) \
33         P(class, prefix, load_linkexplode, float, SEC) \
34         P(class, prefix, load_max, float, SEC) \
35         P(class, prefix, load_releasedeath, float, SEC) \
36         P(class, prefix, load_speed, float, SEC) \
37         P(class, prefix, load_spread, float, SEC) \
38         P(class, prefix, load_spread_bias, float, SEC) \
39         P(class, prefix, radius, float, BOTH) \
40         P(class, prefix, refire, float, BOTH) \
41         P(class, prefix, reload_ammo, float, NONE) \
42         P(class, prefix, reload_time, float, NONE) \
43         P(class, prefix, secondary, float, NONE) \
44         P(class, prefix, speed, float, BOTH) \
45         P(class, prefix, spread, float, BOTH) \
46         P(class, prefix, switchdelay_drop, float, NONE) \
47         P(class, prefix, switchdelay_raise, float, NONE) \
48         P(class, prefix, weaponreplace, string,NONE) \
49         P(class, prefix, weaponstartoverride, float, NONE) \
50         P(class, prefix, weaponstart, float, NONE) \
51         P(class, prefix, weaponthrowable, float, NONE) \
52         END()
53     W_PROPS(X, Hagar, hagar)
54 #undef X
55
56 ENDCLASS(Hagar)
57 REGISTER_WEAPON(HAGAR, hagar, NEW(Hagar));
58
59 #endif
60 #ifdef IMPLEMENTATION
61 #ifdef SVQC
62 spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); }
63
64 // NO bounce protection, as bounces are limited!
65
66 void W_Hagar_Explode()
67 {SELFPARAM();
68         self.event_damage = func_null;
69         RadiusDamage(self, self.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), world, world, WEP_CVAR_PRI(hagar, force), self.projectiledeathtype, other);
70
71         remove(self);
72 }
73
74 void W_Hagar_Explode2()
75 {SELFPARAM();
76         self.event_damage = func_null;
77         RadiusDamage(self, self.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), world, world, WEP_CVAR_SEC(hagar, force), self.projectiledeathtype, other);
78
79         remove(self);
80 }
81
82 void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
83 {
84         if(this.health <= 0)
85                 return;
86
87         float is_linkexplode = ( ((inflictor.owner != world) ? (inflictor.owner == this.owner) : true)
88                 && (inflictor.projectiledeathtype & HITTYPE_SECONDARY)
89                 && (this.projectiledeathtype & HITTYPE_SECONDARY));
90
91         if(is_linkexplode)
92                 is_linkexplode = (is_linkexplode && WEP_CVAR_SEC(hagar, load_linkexplode));
93         else
94                 is_linkexplode = -1; // not secondary load, so continue as normal without exception.
95
96         if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, is_linkexplode))
97                 return; // g_projectiles_damage says to halt
98
99         this.health = this.health - damage;
100         this.angles = vectoangles(this.velocity);
101
102         if(this.health <= 0)
103                 WITH(entity, self, this, W_PrepareExplosionByDamage(attacker, this.think));
104 }
105
106 void W_Hagar_Touch()
107 {SELFPARAM();
108         PROJECTILE_TOUCH;
109         self.use();
110 }
111
112 void W_Hagar_Touch2()
113 {SELFPARAM();
114         PROJECTILE_TOUCH;
115
116         if(self.cnt > 0 || other.takedamage == DAMAGE_AIM) {
117                 self.use();
118         } else {
119                 self.cnt++;
120                 Send_Effect(EFFECT_HAGAR_BOUNCE, self.origin, self.velocity, 1);
121                 self.angles = vectoangles(self.velocity);
122                 self.owner = world;
123                 self.projectiledeathtype |= HITTYPE_BOUNCE;
124         }
125 }
126
127 void W_Hagar_Attack(Weapon thiswep)
128 {SELFPARAM();
129         entity missile;
130
131         W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(hagar, ammo));
132
133         W_SetupShot(self, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
134
135         Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
136
137         missile = new(missile);
138         missile.owner = missile.realowner = self;
139         missile.bot_dodge = true;
140         missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage);
141
142         missile.takedamage = DAMAGE_YES;
143         missile.health = WEP_CVAR_PRI(hagar, health);
144         missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale);
145         missile.event_damage = W_Hagar_Damage;
146         missile.damagedbycontents = true;
147
148         missile.touch = W_Hagar_Touch;
149         missile.use = W_Hagar_Explode;
150         missile.think = adaptor_think2use_hittype_splash;
151         missile.nextthink = time + WEP_CVAR_PRI(hagar, lifetime);
152         PROJECTILE_MAKETRIGGER(missile);
153         missile.projectiledeathtype = WEP_HAGAR.m_id;
154         setorigin(missile, w_shotorg);
155         setsize(missile, '0 0 0', '0 0 0');
156
157         missile.movetype = MOVETYPE_FLY;
158         W_SetupProjVelocity_PRI(missile, hagar);
159
160         missile.angles = vectoangles(missile.velocity);
161         missile.flags = FL_PROJECTILE;
162         missile.missile_flags = MIF_SPLASH;
163
164         CSQCProjectile(missile, true, PROJECTILE_HAGAR, true);
165
166         MUTATOR_CALLHOOK(EditProjectile, self, missile);
167 }
168
169 void W_Hagar_Attack2(Weapon thiswep)
170 {SELFPARAM();
171         entity missile;
172
173         W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo));
174
175         W_SetupShot(self, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
176
177         Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
178
179         missile = new(missile);
180         missile.owner = missile.realowner = self;
181         missile.bot_dodge = true;
182         missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
183
184         missile.takedamage = DAMAGE_YES;
185         missile.health = WEP_CVAR_SEC(hagar, health);
186         missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
187         missile.event_damage = W_Hagar_Damage;
188         missile.damagedbycontents = true;
189
190         missile.touch = W_Hagar_Touch2;
191         missile.cnt = 0;
192         missile.use = W_Hagar_Explode2;
193         missile.think = adaptor_think2use_hittype_splash;
194         missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand);
195         PROJECTILE_MAKETRIGGER(missile);
196         missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY;
197         setorigin(missile, w_shotorg);
198         setsize(missile, '0 0 0', '0 0 0');
199
200         missile.movetype = MOVETYPE_BOUNCEMISSILE;
201         W_SetupProjVelocity_SEC(missile, hagar);
202
203         missile.angles = vectoangles(missile.velocity);
204         missile.flags = FL_PROJECTILE;
205         missile.missile_flags = MIF_SPLASH;
206
207         CSQCProjectile(missile, true, PROJECTILE_HAGAR_BOUNCING, true);
208
209         MUTATOR_CALLHOOK(EditProjectile, self, missile);
210 }
211
212 .float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning;
213 void W_Hagar_Attack2_Load_Release(.entity weaponentity)
214 {SELFPARAM();
215         // time to release the rockets we've loaded
216
217         entity missile;
218         float counter, shots, spread_pershot;
219         vector s;
220         vector forward, right, up;
221
222         if(!self.hagar_load)
223                 return;
224
225         weapon_prepareattack_do(self, weaponentity, true, WEP_CVAR_SEC(hagar, refire));
226
227         W_SetupShot(self, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
228         Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
229
230         forward = v_forward;
231         right = v_right;
232         up = v_up;
233
234         shots = self.hagar_load;
235         missile = world;
236         for(counter = 0; counter < shots; ++counter)
237         {
238                 missile = new(missile);
239                 missile.owner = missile.realowner = self;
240                 missile.bot_dodge = true;
241                 missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
242
243                 missile.takedamage = DAMAGE_YES;
244                 missile.health = WEP_CVAR_SEC(hagar, health);
245                 missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
246                 missile.event_damage = W_Hagar_Damage;
247                 missile.damagedbycontents = true;
248
249                 missile.touch = W_Hagar_Touch; // not bouncy
250                 missile.use = W_Hagar_Explode2;
251                 missile.think = adaptor_think2use_hittype_splash;
252                 missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand);
253                 PROJECTILE_MAKETRIGGER(missile);
254                 missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY;
255                 setorigin(missile, w_shotorg);
256                 setsize(missile, '0 0 0', '0 0 0');
257                 missile.movetype = MOVETYPE_FLY;
258                 missile.missile_flags = MIF_SPLASH;
259
260                 // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar)
261                 spread_pershot = ((shots - 1) / (WEP_CVAR_SEC(hagar, load_max) - 1));
262                 spread_pershot = (1 - (spread_pershot * WEP_CVAR_SEC(hagar, load_spread_bias)));
263                 spread_pershot = (WEP_CVAR_SEC(hagar, spread) * spread_pershot * g_weaponspreadfactor);
264
265                 // pattern spread calculation
266                 s = '0 0 0';
267                 if(counter == 0)
268                         s = '0 0 0';
269                 else
270                 {
271                         makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
272                         s.y = v_forward.x;
273                         s.z = v_forward.y;
274                 }
275                 s = s * WEP_CVAR_SEC(hagar, load_spread) * g_weaponspreadfactor;
276
277                 W_SetupProjVelocity_Explicit(missile, w_shotdir + right * s.y + up * s.z, v_up, WEP_CVAR_SEC(hagar, speed), 0, 0, spread_pershot, false);
278
279                 missile.angles = vectoangles(missile.velocity);
280                 missile.flags = FL_PROJECTILE;
281
282                 CSQCProjectile(missile, true, PROJECTILE_HAGAR, true);
283
284                 MUTATOR_CALLHOOK(EditProjectile, self, missile);
285         }
286
287         weapon_thinkf(self, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
288         self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor();
289         self.hagar_load = 0;
290 }
291
292 void W_Hagar_Attack2_Load(Weapon thiswep, .entity weaponentity)
293 {SELFPARAM();
294         // loadable hagar secondary attack, must always run each frame
295
296         if(time < game_starttime)
297                 return;
298
299         bool loaded = self.hagar_load >= WEP_CVAR_SEC(hagar, load_max);
300
301         // this is different than WR_CHECKAMMO when it comes to reloading
302         bool enough_ammo;
303         if(self.items & IT_UNLIMITED_WEAPON_AMMO)
304                 enough_ammo = true;
305         else if(autocvar_g_balance_hagar_reload_ammo)
306                 enough_ammo = self.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
307         else
308                 enough_ammo = self.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
309
310         bool stopped = loaded || !enough_ammo;
311
312         if(PHYS_INPUT_BUTTON_ATCK2(self))
313         {
314                 if(PHYS_INPUT_BUTTON_ATCK(self) && WEP_CVAR_SEC(hagar, load_abort))
315                 {
316                         if(self.hagar_load)
317                         {
318                                 // if we pressed primary fire while loading, unload all rockets and abort
319                                 self.(weaponentity).state = WS_READY;
320                                 W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo
321                                 self.hagar_load = 0;
322                                 sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
323
324                                 // pause until we can load rockets again, once we re-press the alt fire button
325                                 self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor();
326
327                                 // require letting go of the alt fire button before we can load again
328                                 self.hagar_loadblock = true;
329                         }
330                 }
331                 else
332                 {
333                         // check if we can attempt to load another rocket
334                         if(!stopped)
335                         {
336                                 if(!self.hagar_loadblock && self.hagar_loadstep < time)
337                                 {
338                                         W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo));
339                                         self.(weaponentity).state = WS_INUSE;
340                                         self.hagar_load += 1;
341                                         sound(self, CH_WEAPON_B, SND_HAGAR_LOAD, VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
342
343                                         if(self.hagar_load >= WEP_CVAR_SEC(hagar, load_max))
344                                                 stopped = true;
345                                         else
346                                                 self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor();
347                                 }
348                         }
349                         if(stopped && !self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame
350                         {
351                                 // if this is the last rocket we can load, play a beep sound to notify the player
352                                 sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
353                                 self.hagar_loadbeep = true;
354                                 self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor();
355                         }
356                 }
357         }
358         else if(self.hagar_loadblock)
359         {
360                 // the alt fire button has been released, so re-enable loading if blocked
361                 self.hagar_loadblock = false;
362         }
363
364         if(self.hagar_load)
365         {
366                 // play warning sound if we're about to release
367                 if(stopped && self.hagar_loadstep - 0.5 < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)
368                 {
369                         if(!self.hagar_warning) // prevents the beep from playing each frame
370                         {
371                                 // we're about to automatically release after holding time, play a beep sound to notify the player
372                                 sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
373                                 self.hagar_warning = true;
374                         }
375                 }
376
377                 // release if player let go of button or if they've held it in too long
378                 if(!PHYS_INPUT_BUTTON_ATCK2(self) || (stopped && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0))
379                 {
380                         self.(weaponentity).state = WS_READY;
381                         W_Hagar_Attack2_Load_Release(weaponentity);
382                 }
383         }
384         else
385         {
386                 self.hagar_loadbeep = false;
387                 self.hagar_warning = false;
388
389                 // we aren't checking ammo during an attack, so we must do it here
390                 if(!(thiswep.wr_checkammo1(thiswep) + thiswep.wr_checkammo2(thiswep)))
391                 if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
392                 {
393                         // note: this doesn't force the switch
394                         W_SwitchToOtherWeapon(self);
395                         return;
396                 }
397         }
398 }
399
400 void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
401 {
402         if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock)
403         {
404                 w_ready(thiswep, actor, weaponentity, fire);
405                 return;
406         }
407
408         if(!thiswep.wr_checkammo1(thiswep))
409         if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
410         {
411                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
412                 w_ready(thiswep, actor, weaponentity, fire);
413                 return;
414         }
415
416         W_Hagar_Attack(thiswep);
417
418         int slot = weaponslot(weaponentity);
419         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor();
420         int theframe = WFRAME_FIRE1;
421         entity this = actor.(weaponentity);
422         if(this)
423         {
424                 if(this.wframe == WFRAME_FIRE1)
425                         theframe = WFRAME_DONTCHANGE;
426         }
427         weapon_thinkf(actor, weaponentity, theframe, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto);
428 }
429
430 METHOD(Hagar, wr_aim, void(entity thiswep))
431 {
432     SELFPARAM();
433     if(random()>0.15)
434         PHYS_INPUT_BUTTON_ATCK(self) = bot_aim(self, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
435     else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
436         PHYS_INPUT_BUTTON_ATCK2(self) = bot_aim(self, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
437 }
438 METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
439 {
440     float loadable_secondary;
441     loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary));
442
443     if(loadable_secondary)
444         W_Hagar_Attack2_Load(thiswep, weaponentity); // must always run each frame
445     if(autocvar_g_balance_hagar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload
446         thiswep.wr_reload(thiswep, actor, weaponentity);
447     }
448     else if((fire & 1) && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset
449         {
450                 if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
451                         W_Hagar_Attack_Auto(thiswep, actor, weaponentity, fire);
452         }
453     else if((fire & 2) && !loadable_secondary && WEP_CVAR(hagar, secondary))
454     {
455         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire)))
456         {
457             W_Hagar_Attack2(thiswep);
458             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
459         }
460     }
461 }
462 METHOD(Hagar, wr_gonethink, void(entity thiswep))
463 {
464     SELFPARAM();
465     // we lost the weapon and want to prepare switching away
466     if(self.hagar_load)
467     {
468         .entity weaponentity = weaponentities[0]; // TODO: unhardcode
469         self.(weaponentity).state = WS_READY;
470         W_Hagar_Attack2_Load_Release(weaponentity);
471     }
472 }
473 METHOD(Hagar, wr_setup, void(entity thiswep))
474 {
475     SELFPARAM();
476     self.hagar_loadblock = false;
477
478     if(self.hagar_load)
479     {
480         W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo if necessary
481         self.hagar_load = 0;
482     }
483 }
484 METHOD(Hagar, wr_checkammo1, bool(entity thiswep))
485 {
486     SELFPARAM();
487     float ammo_amount = self.(thiswep.ammo_field) >= WEP_CVAR_PRI(hagar, ammo);
488     ammo_amount += self.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_PRI(hagar, ammo);
489     return ammo_amount;
490 }
491 METHOD(Hagar, wr_checkammo2, bool(entity thiswep))
492 {
493     SELFPARAM();
494     float ammo_amount = self.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
495     ammo_amount += self.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
496     return ammo_amount;
497 }
498 METHOD(Hagar, wr_resetplayer, void(entity thiswep))
499 {
500     SELFPARAM();
501     self.hagar_load = 0;
502 }
503 METHOD(Hagar, wr_playerdeath, void(entity thiswep))
504 {
505     SELFPARAM();
506     .entity weaponentity = weaponentities[0]; // TODO: unhardcode
507     // if we have any rockets loaded when we die, release them
508     if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
509         W_Hagar_Attack2_Load_Release(weaponentity);
510 }
511 METHOD(Hagar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
512 {
513     SELFPARAM();
514     if(!self.hagar_load) // require releasing loaded rockets first
515         W_Reload(self, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD);
516 }
517 METHOD(Hagar, wr_suicidemessage, Notification(entity thiswep))
518 {
519     return WEAPON_HAGAR_SUICIDE;
520 }
521 METHOD(Hagar, wr_killmessage, Notification(entity thiswep))
522 {
523     if(w_deathtype & HITTYPE_SECONDARY)
524         return WEAPON_HAGAR_MURDER_BURST;
525     else
526         return WEAPON_HAGAR_MURDER_SPRAY;
527 }
528
529 #endif
530 #ifdef CSQC
531
532 METHOD(Hagar, wr_impacteffect, void(entity thiswep))
533 {
534     SELFPARAM();
535     vector org2;
536     org2 = w_org + w_backoff * 6;
537     pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
538     if(!w_issilent)
539     {
540         if(w_random<0.15)
541             sound(self, CH_SHOTS, SND_HAGEXP1, VOL_BASE, ATTN_NORM);
542         else if(w_random<0.7)
543             sound(self, CH_SHOTS, SND_HAGEXP2, VOL_BASE, ATTN_NORM);
544         else
545             sound(self, CH_SHOTS, SND_HAGEXP3, VOL_BASE, ATTN_NORM);
546     }
547 }
548
549 #endif
550 #endif