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