]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/weapon/electro.qc
Fix #2567 "electro balls sticking to players who respawn have the balls teleported...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / electro.qc
1 #include "electro.qh"
2
3 #ifdef SVQC
4 #include <common/effects/qc/_mod.qh>
5
6 void W_Electro_TriggerCombo(vector org, float rad, entity own)
7 {
8         entity e = WarpZone_FindRadius(org, rad, !WEP_CVAR(electro, combo_comboradius_thruwall));
9         while(e)
10         {
11                 if(e.classname == "electro_orb")
12                 {
13                         // do we allow thruwall triggering?
14                         if(WEP_CVAR(electro, combo_comboradius_thruwall))
15                         {
16                                 // if distance is greater than thruwall distance, check to make sure it's not through a wall
17                                 if(vdist(e.WarpZone_findradius_dist, >, WEP_CVAR(electro, combo_comboradius_thruwall)))
18                                 {
19                                         WarpZone_TraceLine(org, e.origin, MOVE_NOMONSTERS, e);
20                                         if(trace_fraction != 1)
21                                         {
22                                                 // trigger is through a wall and outside of thruwall range, abort
23                                                 e = e.chain;
24                                                 continue;
25                                         }
26                                 }
27                         }
28
29                         // change owner to whoever caused the combo explosion
30                         e.realowner = own;
31                         e.takedamage = DAMAGE_NO;
32                         e.classname = "electro_orb_chain";
33
34                         // now set the next one to trigger as well
35                         setthink(e, W_Electro_ExplodeCombo);
36
37                         // delay combo chains, looks cooler
38                         float delay = 0;
39                         if (WEP_CVAR(electro, combo_speed))
40                                 delay = vlen(e.WarpZone_findradius_dist) / WEP_CVAR(electro, combo_speed);
41                         e.nextthink = time + delay;
42                 }
43                 e = e.chain;
44         }
45 }
46
47 void W_Electro_ExplodeCombo(entity this)
48 {
49         W_Electro_TriggerCombo(this.origin, WEP_CVAR(electro, combo_comboradius), this.realowner);
50
51         this.event_damage = func_null;
52
53         RadiusDamage(
54                 this,
55                 this.realowner,
56                 WEP_CVAR(electro, combo_damage),
57                 WEP_CVAR(electro, combo_edgedamage),
58                 WEP_CVAR(electro, combo_radius),
59                 NULL,
60                 NULL,
61                 WEP_CVAR(electro, combo_force),
62                 WEP_ELECTRO.m_id | HITTYPE_BOUNCE, // use THIS type for a combo because primary can't bounce
63                 this.weaponentity_fld,
64                 NULL
65         );
66
67         delete(this);
68 }
69
70 void W_Electro_Explode(entity this, entity directhitentity)
71 {
72         if(directhitentity.takedamage == DAMAGE_AIM)
73                 if(IS_PLAYER(directhitentity))
74                         if(DIFF_TEAM(this.realowner, directhitentity))
75                                 if(!IS_DEAD(directhitentity))
76                                         if(IsFlying(directhitentity))
77                                                 Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
78
79         this.event_damage = func_null;
80         this.takedamage = DAMAGE_NO;
81
82         if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
83         {
84                 RadiusDamage(
85                         this,
86                         this.realowner,
87                         WEP_CVAR_SEC(electro, damage),
88                         WEP_CVAR_SEC(electro, edgedamage),
89                         WEP_CVAR_SEC(electro, radius),
90                         NULL,
91                         NULL,
92                         WEP_CVAR_SEC(electro, force),
93                         this.projectiledeathtype,
94                         this.weaponentity_fld,
95                         directhitentity
96                 );
97         }
98         else
99         {
100                 W_Electro_TriggerCombo(this.origin, WEP_CVAR_PRI(electro, comboradius), this.realowner);
101                 RadiusDamage(
102                         this,
103                         this.realowner,
104                         WEP_CVAR_PRI(electro, damage),
105                         WEP_CVAR_PRI(electro, edgedamage),
106                         WEP_CVAR_PRI(electro, radius),
107                         NULL,
108                         NULL,
109                         WEP_CVAR_PRI(electro, force),
110                         this.projectiledeathtype,
111                         this.weaponentity_fld,
112                         directhitentity
113                 );
114         }
115
116         delete(this);
117 }
118
119 void W_Electro_Explode_use(entity this, entity actor, entity trigger)
120 {
121         W_Electro_Explode(this, trigger);
122 }
123
124 void W_Electro_TouchExplode(entity this, entity toucher)
125 {
126         PROJECTILE_TOUCH(this, toucher);
127         W_Electro_Explode(this, toucher);
128 }
129
130
131 void sys_phys_update_single(entity this);
132
133 void W_Electro_Bolt_Think(entity this)
134 {
135         // sys_phys_update_single(this);
136         if(time >= this.ltime)
137         {
138                 this.use(this, NULL, NULL);
139                 return;
140         }
141
142         if(WEP_CVAR_PRI(electro, midaircombo_radius))
143         {
144                 float found = 0;
145                 entity e = WarpZone_FindRadius(this.origin, WEP_CVAR_PRI(electro, midaircombo_radius), true);
146
147                 // loop through nearby orbs and trigger them
148                 while(e)
149                 {
150                         if(e.classname == "electro_orb")
151                         {
152                                 bool explode;
153                                 if (this.owner == e.owner)
154                                 {
155                                         explode = WEP_CVAR_PRI(electro, midaircombo_own);
156                                 }
157                                 else if (SAME_TEAM(this.owner, e.owner))
158                                 {
159                                         explode = WEP_CVAR_PRI(electro, midaircombo_teammate);
160                                 }
161                                 else
162                                 {
163                                         explode = WEP_CVAR_PRI(electro, midaircombo_enemy);
164                                 }
165
166                                 if (explode)
167                                 {
168                                         // change owner to whoever caused the combo explosion
169                                         e.realowner = this.realowner;
170                                         e.takedamage = DAMAGE_NO;
171                                         e.classname = "electro_orb_chain";
172
173                                         // Only first orb explosion uses midaircombo_speed, others use the normal combo_speed.
174                                         // This allows to avoid the delay on the first explosion which looks better
175                                         // (the bolt and orb should explode together because they interacted together)
176                                         // while keeping the chaining delay.
177                                         setthink(e, W_Electro_ExplodeCombo);
178                                         float delay = 0;
179                                         if (WEP_CVAR_PRI(electro, midaircombo_speed))
180                                                 delay = vlen(e.WarpZone_findradius_dist) / WEP_CVAR_PRI(electro, midaircombo_speed);
181                                         e.nextthink = time + delay;
182
183                                         ++found;
184                                 }
185                         }
186                         e = e.chain;
187                 }
188
189                 // if we triggered an orb, should we explode? if not, lets try again next time
190                 if(found && WEP_CVAR_PRI(electro, midaircombo_explode))
191                         { this.use(this, NULL, NULL); }
192                 else
193                         { this.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), this.ltime); }
194         }
195         else { this.nextthink = this.ltime; }
196         // this.nextthink = time;
197 }
198
199 void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity)
200 {
201         entity proj;
202
203         W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(electro, ammo), weaponentity);
204
205         W_SetupShot_ProjectileSize(
206                 actor,
207                 weaponentity,
208                 '0 0 -3',
209                 '0 0 -3',
210                 false,
211                 2,
212                 SND_ELECTRO_FIRE,
213                 CH_WEAPON_A,
214                 WEP_CVAR_PRI(electro, damage),
215                 thiswep.m_id
216         );
217
218         W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
219
220         proj = new(electro_bolt);
221         proj.owner = proj.realowner = actor;
222         proj.bot_dodge = true;
223         proj.bot_dodgerating = WEP_CVAR_PRI(electro, damage);
224         proj.use = W_Electro_Explode_use;
225         setthink(proj, W_Electro_Bolt_Think);
226         proj.nextthink = time;
227         proj.ltime = time + WEP_CVAR_PRI(electro, lifetime);
228         PROJECTILE_MAKETRIGGER(proj);
229         proj.projectiledeathtype = thiswep.m_id;
230         proj.weaponentity_fld = weaponentity;
231         setorigin(proj, w_shotorg);
232
233         // if (IS_CSQC)
234         set_movetype(proj, MOVETYPE_FLY);
235         W_SetupProjVelocity_PRI(proj, electro);
236         proj.angles = vectoangles(proj.velocity);
237         settouch(proj, W_Electro_TouchExplode);
238         setsize(proj, '0 0 -3', '0 0 -3');
239         proj.flags = FL_PROJECTILE;
240         IL_PUSH(g_projectiles, proj);
241         IL_PUSH(g_bot_dodge, proj);
242         proj.missile_flags = MIF_SPLASH;
243
244         CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true);
245
246         MUTATOR_CALLHOOK(EditProjectile, actor, proj);
247         // proj.com_phys_pos = proj.origin;
248         // proj.com_phys_vel = proj.velocity;
249 }
250
251 void W_Electro_Orb_Follow_Think(entity this)
252 {
253         if (time > this.death_time)
254         {
255                 adaptor_think2use_hittype_splash(this);
256                 return;
257         }
258         if (this.move_movetype == MOVETYPE_FOLLOW)
259         {
260                 int lost = LostMovetypeFollow(this);
261                 if (lost == 2)
262                 {
263                         // FIXME if player disconnected, it isn't possible to drop the orb at player's origin
264                         // see comment in LostMovetypeFollow implementation
265                         delete(this);
266                         return;
267                 }
268                 if (lost)
269                 {
270                         // drop the orb at the corpse's location
271                         PROJECTILE_MAKETRIGGER(this);
272                         set_movetype(this, MOVETYPE_TOSS);
273
274                         setthink(this, adaptor_think2use_hittype_splash);
275                         this.nextthink = this.death_time;
276                         return;
277                 }
278         }
279         this.nextthink = time;
280 }
281
282 void W_Electro_Orb_Stick(entity this, entity to)
283 {
284         entity newproj = spawn();
285         newproj.classname = this.classname;
286
287         newproj.bot_dodge = this.bot_dodge;
288         newproj.bot_dodgerating = this.bot_dodgerating;
289
290         newproj.owner = this.owner;
291         newproj.realowner = this.realowner;
292         setorigin(newproj, this.origin);
293         setmodel(newproj, MDL_PROJECTILE_ELECTRO);
294         setsize(newproj, this.mins, this.maxs);
295         newproj.angles = vectoangles(-trace_plane_normal); // face against the surface
296
297         newproj.takedamage = this.takedamage;
298         newproj.damageforcescale = this.damageforcescale;
299         SetResourceExplicit(newproj, RES_HEALTH, GetResource(this, RES_HEALTH));
300         newproj.event_damage = this.event_damage;
301         newproj.spawnshieldtime = this.spawnshieldtime;
302         newproj.damagedbycontents = true;
303         IL_PUSH(g_damagedbycontents, newproj);
304
305         set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place
306         newproj.projectiledeathtype = this.projectiledeathtype;
307         newproj.weaponentity_fld = this.weaponentity_fld;
308
309         settouch(newproj, func_null);
310         newproj.death_time = this.death_time;
311         newproj.use = this.use;
312         newproj.flags = this.flags;
313         IL_PUSH(g_projectiles, newproj);
314         IL_PUSH(g_bot_dodge, newproj);
315
316         // check if limits are enabled (we can tell by checking if the original orb is listed) and push it to the list if so
317         if(LimitedElectroBallRubbleList && IL_CONTAINS(LimitedElectroBallRubbleList, this))
318         {
319                 ReplaceOldListedChildRubble(LimitedElectroBallRubbleList, newproj, this);
320         }
321
322         delete(this);
323
324         if(to)
325         {
326                 SetMovetypeFollow(newproj, to);
327
328                 setthink(newproj, W_Electro_Orb_Follow_Think);
329                 newproj.nextthink = time;
330         }
331         else
332         {
333                 setthink(newproj, adaptor_think2use_hittype_splash);
334                 newproj.nextthink = newproj.death_time;
335         }
336 }
337
338 void W_Electro_Orb_Touch(entity this, entity toucher)
339 {
340         PROJECTILE_TOUCH(this, toucher);
341         if(toucher.takedamage == DAMAGE_AIM && WEP_CVAR_SEC(electro, touchexplode))
342                 { W_Electro_Explode(this, toucher); }
343         else if(toucher.owner != this.owner && toucher.classname != this.classname) // don't stick to player's other projectiles!
344         {
345                 //UpdateCSQCProjectile(this);
346                 spamsound(this, CH_SHOTS, SND_ELECTRO_BOUNCE, VOL_BASE, ATTEN_NORM);
347                 this.projectiledeathtype |= HITTYPE_BOUNCE;
348
349                 if(WEP_CVAR_SEC(electro, stick))
350                         W_Electro_Orb_Stick(this, toucher);
351         }
352 }
353
354 void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
355 {
356         if(GetResource(this, RES_HEALTH) <= 0)
357                 return;
358
359         // note: combos are usually triggered by W_Electro_TriggerCombo, not damage
360         float is_combo = (inflictor.classname == "electro_orb_chain" || inflictor.classname == "electro_bolt");
361
362         if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_combo ? 1 : -1)))
363                 return; // g_projectiles_damage says to halt
364
365         TakeResource(this, RES_HEALTH, damage);
366         if(GetResource(this, RES_HEALTH) <= 0)
367         {
368                 this.takedamage = DAMAGE_NO;
369                 this.nextthink = time;
370                 if(is_combo)
371                 {
372                         // change owner to whoever caused the combo explosion
373                         this.realowner = inflictor.realowner;
374                         this.classname = "electro_orb_chain";
375                         setthink(this, W_Electro_ExplodeCombo);
376                         // delay combo chains, looks cooler
377                         // bound the length, inflictor may be in a galaxy far far away (warpzones)
378                         float len = min(WEP_CVAR(electro, combo_radius), vlen(this.origin - inflictor.origin));
379                         float delay = len / WEP_CVAR(electro, combo_speed);
380                         this.nextthink = time + delay;
381                 }
382                 else
383                 {
384                         this.use = W_Electro_Explode_use;
385                         setthink(this, adaptor_think2use); // not _hittype_splash, as this runs "immediately"
386                 }
387         }
388 }
389
390 void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity)
391 {
392         W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(electro, ammo), weaponentity);
393
394         W_SetupShot_ProjectileSize(
395                 actor,
396                 weaponentity,
397                 '-4 -4 -4',
398                 '4 4 4',
399                 false,
400                 2,
401                 SND_ELECTRO_FIRE2,
402                 CH_WEAPON_A,
403                 WEP_CVAR_SEC(electro, damage),
404                 thiswep.m_id | HITTYPE_SECONDARY
405         );
406
407         w_shotdir = v_forward; // no TrueAim for grenades please
408
409         W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
410
411         entity proj = new(electro_orb);
412         proj.owner = proj.realowner = actor;
413         proj.use = W_Electro_Explode_use;
414         setthink(proj, adaptor_think2use_hittype_splash);
415         proj.bot_dodge = true;
416         proj.bot_dodgerating = WEP_CVAR_SEC(electro, damage);
417         proj.nextthink = time + WEP_CVAR_SEC(electro, lifetime);
418         proj.death_time = time + WEP_CVAR_SEC(electro, lifetime);
419         PROJECTILE_MAKETRIGGER(proj);
420         proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
421         proj.weaponentity_fld = weaponentity;
422         setorigin(proj, w_shotorg);
423
424         //proj.glow_size = 50;
425         //proj.glow_color = 45;
426         set_movetype(proj, MOVETYPE_BOUNCE);
427         W_SetupProjVelocity_UP_SEC(proj, electro);
428         settouch(proj, W_Electro_Orb_Touch);
429         setsize(proj, '-4 -4 -4', '4 4 4');
430         proj.takedamage = DAMAGE_YES;
431         proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale);
432         SetResourceExplicit(proj, RES_HEALTH, WEP_CVAR_SEC(electro, health));
433         proj.event_damage = W_Electro_Orb_Damage;
434         proj.flags = FL_PROJECTILE;
435         IL_PUSH(g_projectiles, proj);
436         IL_PUSH(g_bot_dodge, proj);
437         proj.damagedbycontents = (WEP_CVAR_SEC(electro, damagedbycontents));
438         if(proj.damagedbycontents)
439                 IL_PUSH(g_damagedbycontents, proj);
440
441         proj.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
442         proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop);
443         proj.missile_flags = MIF_SPLASH | MIF_ARC;
444
445         if(WEP_CVAR_SEC(electro, limit) > 0)
446         {
447                 if (!LimitedElectroBallRubbleList)
448                         LimitedElectroBallRubbleList = IL_NEW();
449                 ListNewChildRubble(LimitedElectroBallRubbleList, proj);
450                 LimitedChildrenRubble(LimitedElectroBallRubbleList, "electro_orb", WEP_CVAR_SEC(electro, limit), adaptor_think2use_hittype_splash, actor);
451         }
452
453         CSQCProjectile(proj, true, PROJECTILE_ELECTRO, false); // no culling, it has sound
454
455         MUTATOR_CALLHOOK(EditProjectile, actor, proj);
456 }
457
458 void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, int fire)
459 {
460         if(actor.(weaponentity).electro_count > 1)
461         if(PHYS_INPUT_BUTTON_ATCK2(actor))
462         if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1))
463         {
464                 W_Electro_Attack_Orb(thiswep, actor, weaponentity);
465                 actor.(weaponentity).electro_count -= 1;
466                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
467                 return;
468         }
469         // WEAPONTODO: when the player releases the button, cut down the length of refire2?
470         w_ready(thiswep, actor, weaponentity, fire);
471 }
472
473 .float bot_secondary_electromooth;
474
475 METHOD(Electro, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
476 {
477     PHYS_INPUT_BUTTON_ATCK(actor) = PHYS_INPUT_BUTTON_ATCK2(actor) = false;
478     if(vdist(actor.origin - actor.enemy.origin, >, 1000)) { actor.bot_secondary_electromooth = 0; }
479     if(actor.bot_secondary_electromooth == 0)
480     {
481         float shoot;
482
483         if(WEP_CVAR_PRI(electro, speed))
484             shoot = bot_aim(actor, weaponentity, WEP_CVAR_PRI(electro, speed), 0, WEP_CVAR_PRI(electro, lifetime), false);
485         else
486             shoot = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
487
488         if(shoot)
489         {
490             PHYS_INPUT_BUTTON_ATCK(actor) = true;
491             if(random() < 0.01) actor.bot_secondary_electromooth = 1;
492         }
493     }
494     else
495     {
496         if(bot_aim(actor, weaponentity, WEP_CVAR_SEC(electro, speed), WEP_CVAR_SEC(electro, speed_up), WEP_CVAR_SEC(electro, lifetime), true))
497         {
498             PHYS_INPUT_BUTTON_ATCK2(actor) = true;
499             if(random() < 0.03) actor.bot_secondary_electromooth = 0;
500         }
501     }
502 }
503 METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
504 {
505     if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO
506     {
507         float ammo_amount = 0;
508         if(actor.(weaponentity).clip_load >= WEP_CVAR_PRI(electro, ammo))
509             ammo_amount = 1;
510         if(actor.(weaponentity).clip_load >= WEP_CVAR_SEC(electro, ammo))
511             ammo_amount += 1;
512
513         if(!ammo_amount)
514         {
515             thiswep.wr_reload(thiswep, actor, weaponentity);
516             return;
517         }
518     }
519
520     if(fire & 1)
521     {
522         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire)))
523         {
524                 W_Electro_Attack_Bolt(thiswep, actor, weaponentity);
525                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
526         }
527     }
528     else if(fire & 2)
529     {
530         if(time >= actor.(weaponentity).electro_secondarytime)
531         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(electro, refire)))
532         {
533             W_Electro_Attack_Orb(thiswep, actor, weaponentity);
534             actor.(weaponentity).electro_count = WEP_CVAR_SEC(electro, count);
535             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
536             actor.(weaponentity).electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor(actor);
537         }
538     }
539 }
540 METHOD(Electro, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
541 {
542     float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(electro, ammo);
543     ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(electro, ammo);
544     return ammo_amount;
545 }
546 METHOD(Electro, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
547 {
548     float ammo_amount;
549     if(WEP_CVAR(electro, combo_safeammocheck)) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
550     {
551         ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
552         ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
553     }
554     else
555     {
556         ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(electro, ammo);
557         ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(electro, ammo);
558     }
559     return ammo_amount;
560 }
561 METHOD(Electro, wr_resetplayer, void(entity thiswep, entity actor))
562 {
563     for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
564     {
565         .entity weaponentity = weaponentities[slot];
566         actor.(weaponentity).electro_secondarytime = time;
567     }
568 }
569 METHOD(Electro, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
570 {
571     W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND_RELOAD);
572 }
573 METHOD(Electro, wr_suicidemessage, Notification(entity thiswep))
574 {
575     if(w_deathtype & HITTYPE_SECONDARY)
576         return WEAPON_ELECTRO_SUICIDE_ORBS;
577     else
578         return WEAPON_ELECTRO_SUICIDE_BOLT;
579 }
580 METHOD(Electro, wr_killmessage, Notification(entity thiswep))
581 {
582     if(w_deathtype & HITTYPE_SECONDARY)
583     {
584         return WEAPON_ELECTRO_MURDER_ORBS;
585     }
586     else
587     {
588         if(w_deathtype & HITTYPE_BOUNCE)
589             return WEAPON_ELECTRO_MURDER_COMBO;
590         else
591             return WEAPON_ELECTRO_MURDER_BOLT;
592     }
593 }
594
595 #endif
596 #ifdef CSQC
597
598 METHOD(Electro, wr_impacteffect, void(entity thiswep, entity actor))
599 {
600     vector org2;
601     org2 = w_org + w_backoff * 6;
602     if(w_deathtype & HITTYPE_SECONDARY)
603     {
604         pointparticles(EFFECT_ELECTRO_BALLEXPLODE, org2, '0 0 0', 1);
605         if(!w_issilent)
606             sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
607     }
608     else
609     {
610         if(w_deathtype & HITTYPE_BOUNCE)
611         {
612             // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
613             pointparticles(EFFECT_ELECTRO_COMBO, org2, '0 0 0', 1);
614             if(!w_issilent)
615                 sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT_COMBO, VOL_BASE, ATTEN_NORM);
616         }
617         else
618         {
619             pointparticles(EFFECT_ELECTRO_IMPACT, org2, '0 0 0', 1);
620             if(!w_issilent)
621                 sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
622         }
623     }
624 }
625
626 #endif