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