Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
electro.qc
Go to the documentation of this file.
1#include "electro.qh"
2
3#ifdef GAMEQC
4
5#ifdef CSQC
6
7.float ltime;
9{
10 float dt = time - this.move_time;
11 this.move_time = time;
12 if(dt <= 0)
13 return;
14
15 float myscale = bound(0, (this.ltime - time) * 4, 1);
16 this.scale = (WEP_CVAR(WEP_ELECTRO, combo_radius) * 0.05) * myscale;
17 this.angles = this.angles + dt * this.avelocity;
18}
19
21{
22 setmodel(e, MDL_PROJECTILE_ELECTRO);
23 setsize(e, '-4 -4 -4', '4 4 4');
24
25 setorigin(e, e.origin);
26
27 e.draw = electro_orb_draw;
29 SetResourceExplicit(e, RES_HEALTH, 255);
31 e.solid = SOLID_NOT;
32 e.avelocity = '7 0 11';
33 e.drawmask = MASK_NORMAL;
34 e.alpha = 0.7;
35}
36#endif
37
38REGISTER_NET_LINKED(Electro_Orb)
39
40#ifdef CSQC
41NET_HANDLE(Electro_Orb, bool isNew)
42{
43 Net_Accept(Electro_Orb);
44 int sf = ReadByte();
45 if (sf & 1) {
46 this.origin = ReadVector();
47 setorigin(this, this.origin);
48 this.ltime = time + ReadByte()/10.0;
49 // this.ltime = time + this.orb_lifetime;
51 }
52 return true;
53}
54#endif
55
56#ifdef SVQC
57bool electro_orb_send(entity this, entity to, int sf)
58{
59 int channel = MSG_ENTITY;
60 WriteHeader(channel, Electro_Orb);
61 WriteByte(channel, sf);
62 if (sf & 1) {
63 WriteVector(channel, this.origin);
64
65 // round time delta to a 1/10th of a second
66 WriteByte(channel, (this.ltime - time)*10.0+0.5);
67 }
68 return true;
69}
70#endif
71
72#endif
73
74#ifdef SVQC
76
78{
79 entity e = WarpZone_FindRadius(org, rad, !WEP_CVAR(WEP_ELECTRO, combo_comboradius_thruwall));
80 while(e)
81 {
82 if(e.classname == "electro_orb")
83 {
84 // check if the ball we are exploding is not owned by an
85 // independent player which is not the player who shot the ball
86 if(IS_INDEPENDENT_PLAYER(e.realowner) && own != e.realowner)
87 {
88 e = e.chain;
89 continue;
90 }
91 // do we allow thruwall triggering?
92 if(WEP_CVAR(WEP_ELECTRO, combo_comboradius_thruwall))
93 {
94 // if distance is greater than thruwall distance, check to make sure it's not through a wall
95 if(vdist(e.WarpZone_findradius_dist, >, WEP_CVAR(WEP_ELECTRO, combo_comboradius_thruwall)))
96 {
98 if(trace_fraction != 1)
99 {
100 // trigger is through a wall and outside of thruwall range, abort
101 e = e.chain;
102 continue;
103 }
104 }
105 }
106
107 // change owner to whoever caused the combo explosion
108 e.realowner = own;
109 e.takedamage = DAMAGE_NO;
110 e.classname = "electro_orb_chain";
111
112 // now set the next one to trigger as well
114
115 // delay combo chains, looks cooler
116 float delay = 0;
117 if (WEP_CVAR(WEP_ELECTRO, combo_speed))
118 delay = vlen(e.WarpZone_findradius_dist) / WEP_CVAR(WEP_ELECTRO, combo_speed);
119 e.nextthink = time + delay;
120 }
121 e = e.chain;
122 }
123}
124
126{
127 if(time >= this.ltime)
128 {
129 delete(this);
130 return;
131 }
132
133 this.nextthink = time;
134
135 float damage = WEP_CVAR(WEP_ELECTRO, combo_damage) * PHYS_INPUT_TIMELENGTH;
136 float edgedamage = WEP_CVAR(WEP_ELECTRO, combo_edgedamage) * PHYS_INPUT_TIMELENGTH;
137
138 RadiusDamage(this, this.realowner, damage, edgedamage, WEP_CVAR(WEP_ELECTRO, combo_radius), NULL, NULL, 0, this.projectiledeathtype, this.weaponentity_fld, NULL);
139 this.projectiledeathtype |= HITTYPE_SPAM; // ensure it doesn't spam its effect
140}
141
143{
144 entity newproj = spawn();
145 newproj.classname = this.classname;
146 newproj.solid = this.solid;
147 setorigin(newproj, this.origin);
148 setmodel(newproj, MDL_PROJECTILE_ELECTRO);
149 setsize(newproj, this.mins, this.maxs);
150 newproj.owner = this.owner;
151 newproj.realowner = this.realowner;
152 newproj.weaponentity_fld = this.weaponentity_fld;
153 newproj.projectiledeathtype = WEP_ELECTRO.m_id | HITTYPE_BOUNCE; // use THIS type for a combo because primary can't bounce
154
156 newproj.nextthink = time;
157 newproj.ltime = time + WEP_CVAR(WEP_ELECTRO, combo_duration);
158 set_movetype(newproj, MOVETYPE_NONE);
159
160 Net_LinkEntity(newproj, true, 0, electro_orb_send);
161 newproj.SendFlags |= 1;
162
163 // fire the first damage tick immediately
164 getthink(newproj)(newproj);
165}
166
168{
169 W_Electro_TriggerCombo(this.origin, WEP_CVAR(WEP_ELECTRO, combo_comboradius), this.realowner);
170
171 this.event_damage = func_null;
172 if (!this.velocity)
173 this.velocity = this.movedir; // .velocity must be != '0 0 0' for particle fx and decal to work
174
175 if(WEP_CVAR(WEP_ELECTRO, combo_duration))
176 {
178
179 delete(this);
180 return;
181 }
182
184 this,
185 this.realowner,
186 WEP_CVAR(WEP_ELECTRO, combo_damage),
187 WEP_CVAR(WEP_ELECTRO, combo_edgedamage),
188 WEP_CVAR(WEP_ELECTRO, combo_radius),
189 NULL,
190 NULL,
191 WEP_CVAR(WEP_ELECTRO, combo_force),
192 WEP_ELECTRO.m_id | HITTYPE_BOUNCE, // use THIS type for a combo because primary can't bounce
193 this.weaponentity_fld,
194 NULL
195 );
196
197 delete(this);
198}
199
200void W_Electro_Explode(entity this, entity directhitentity)
201{
202 if(directhitentity.takedamage == DAMAGE_AIM)
203 if(IS_PLAYER(directhitentity))
204 if(DIFF_TEAM(this.realowner, directhitentity))
205 if(!IS_DEAD(directhitentity))
206 if(IsFlying(directhitentity))
207 Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
208
209 this.event_damage = func_null;
210 this.takedamage = DAMAGE_NO;
211 if (!this.velocity)
212 this.velocity = this.movedir; // .velocity must be != '0 0 0' for particle fx and decal to work
213
214 if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
215 {
217 this,
218 this.realowner,
219 WEP_CVAR_SEC(WEP_ELECTRO, damage),
220 WEP_CVAR_SEC(WEP_ELECTRO, edgedamage),
221 WEP_CVAR_SEC(WEP_ELECTRO, radius),
222 NULL,
223 NULL,
224 WEP_CVAR_SEC(WEP_ELECTRO, force),
226 this.weaponentity_fld,
227 directhitentity
228 );
229 }
230 else
231 {
232 W_Electro_TriggerCombo(this.origin, WEP_CVAR_PRI(WEP_ELECTRO, comboradius), this.realowner);
234 this,
235 this.realowner,
236 WEP_CVAR_PRI(WEP_ELECTRO, damage),
237 WEP_CVAR_PRI(WEP_ELECTRO, edgedamage),
238 WEP_CVAR_PRI(WEP_ELECTRO, radius),
239 NULL,
240 NULL,
241 WEP_CVAR_PRI(WEP_ELECTRO, force),
243 this.weaponentity_fld,
244 directhitentity
245 );
246 }
247
248 delete(this);
249}
250
251void W_Electro_Explode_use(entity this, entity actor, entity trigger)
252{
253 W_Electro_Explode(this, trigger);
254}
255
261
262
263//void sys_phys_update_single(entity this);
264
266{
267 // sys_phys_update_single(this);
268 if(time >= this.ltime)
269 {
270 this.use(this, NULL, NULL);
271 return;
272 }
273
274 if(WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_radius))
275 {
276 float found = 0;
277 entity e = WarpZone_FindRadius(this.origin, WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_radius), true);
278
279 // loop through nearby orbs and trigger them
280 while(e)
281 {
282 if(e.classname == "electro_orb")
283 {
284 // check if the ball we are exploding is not owned by an
285 // independent player which is not the player who shot the ball
286 if(IS_INDEPENDENT_PLAYER(e.realowner) && this.realowner != e.realowner)
287 {
288 e = e.chain;
289 continue;
290 }
291 bool explode;
292 if (this.owner == e.owner)
293 {
294 explode = WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_own);
295 }
296 else if (SAME_TEAM(this.owner, e.owner))
297 {
298 explode = WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_teammate);
299 }
300 else
301 {
302 explode = WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_enemy);
303 }
304
305 if (explode)
306 {
307 // change owner to whoever caused the combo explosion
308 e.realowner = this.realowner;
309 e.takedamage = DAMAGE_NO;
310 e.classname = "electro_orb_chain";
311
312 // Only first orb explosion uses midaircombo_speed, others use the normal combo_speed.
313 // This allows to avoid the delay on the first explosion which looks better
314 // (the bolt and orb should explode together because they interacted together)
315 // while keeping the chaining delay.
317 float delay = 0;
318 if (WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_speed))
319 delay = vlen(e.WarpZone_findradius_dist) / WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_speed);
320 e.nextthink = time + delay;
321
322 ++found;
323 }
324 }
325 e = e.chain;
326 }
327
328 // if we triggered an orb, should we explode? if not, lets try again next time
329 if(found && WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_explode))
330 { this.use(this, NULL, NULL); }
331 else
332 { this.nextthink = min(time + WEP_CVAR_PRI(WEP_ELECTRO, midaircombo_interval), this.ltime); }
333 }
334 else { this.nextthink = this.ltime; }
335 // this.nextthink = time;
336}
337
338void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity)
339{
340 entity proj;
341
342 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(WEP_ELECTRO, ammo), weaponentity);
343
345 actor,
346 weaponentity,
347 '0 0 -3',
348 '0 0 -3',
349 false,
350 2,
351 SND_ELECTRO_FIRE,
353 WEP_CVAR_PRI(WEP_ELECTRO, damage),
354 thiswep.m_id
355 );
356
357 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
358
359 proj = new(electro_bolt);
360 proj.owner = proj.realowner = actor;
361 proj.bot_dodge = true;
362 proj.bot_dodgerating = WEP_CVAR_PRI(WEP_ELECTRO, damage);
363 proj.use = W_Electro_Explode_use;
365 proj.nextthink = time;
366 proj.ltime = time + WEP_CVAR_PRI(WEP_ELECTRO, lifetime);
368 proj.projectiledeathtype = thiswep.m_id;
369 proj.weaponentity_fld = weaponentity;
370 setorigin(proj, w_shotorg);
371
372 // if (IS_CSQC)
374 W_SetupProjVelocity_PRI(proj, WEP_ELECTRO);
375 proj.angles = vectoangles(proj.velocity);
377 setsize(proj, '0 0 -3', '0 0 -3');
378 proj.flags = FL_PROJECTILE;
379 IL_PUSH(g_projectiles, proj);
380 IL_PUSH(g_bot_dodge, proj);
381 proj.missile_flags = MIF_SPLASH;
382
383 CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true);
384
385 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
386 // proj.com_phys_pos = proj.origin;
387 // proj.com_phys_vel = proj.velocity;
388}
389
391{
392 if (time > this.death_time)
393 {
395 return;
396 }
397 if (this.move_movetype == MOVETYPE_FOLLOW)
398 {
399 int lost = LostMovetypeFollow(this);
400 if (lost == 2)
401 {
402 // FIXME if player disconnected, it isn't possible to drop the orb at player's origin
403 // see comment in LostMovetypeFollow implementation
404 delete(this);
405 return;
406 }
407 if (lost)
408 {
409 // drop the orb at the corpse's location
412
414 this.nextthink = this.death_time;
415 return;
416 }
417 }
418 this.nextthink = time;
419}
420
422{
423 entity newproj = spawn();
424 newproj.classname = this.classname;
425
426 newproj.bot_dodge = this.bot_dodge;
427 newproj.bot_dodgerating = this.bot_dodgerating;
428
429 newproj.owner = this.owner;
430 newproj.realowner = this.realowner;
431 PROJECTILE_MAKETRIGGER(newproj);
432 setorigin(newproj, this.origin);
433 setmodel(newproj, MDL_PROJECTILE_ELECTRO);
434 setsize(newproj, this.mins, this.maxs);
435 newproj.angles = vectoangles(-trace_plane_normal); // face against the surface
436 newproj.traileffectnum = _particleeffectnum(EFFECT_TR_NEXUIZPLASMA.eent_eff_name);
437
438 newproj.movedir = -trace_plane_normal;
439
440 newproj.takedamage = this.takedamage;
441 newproj.damageforcescale = this.damageforcescale;
442 SetResourceExplicit(newproj, RES_HEALTH, GetResource(this, RES_HEALTH));
443 newproj.event_damage = this.event_damage;
444 newproj.spawnshieldtime = this.spawnshieldtime;
445 newproj.damagedbycontents = true;
447
448 set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place
449 newproj.projectiledeathtype = this.projectiledeathtype;
450 newproj.weaponentity_fld = this.weaponentity_fld;
451
452 settouch(newproj, func_null);
453 if(WEP_CVAR_SEC(WEP_ELECTRO, stick_lifetime) > 0)
454 newproj.death_time = time + WEP_CVAR_SEC(WEP_ELECTRO, stick_lifetime);
455 else
456 newproj.death_time = this.death_time;
457 newproj.use = this.use;
458 newproj.flags = this.flags;
459 IL_PUSH(g_projectiles, newproj);
460 IL_PUSH(g_bot_dodge, newproj);
461
462 // check if limits are enabled (we can tell by checking if the original orb is listed) and push it to the list if so
464 {
466 }
467
468 delete(this);
469
470 if(to)
471 {
472 SetMovetypeFollow(newproj, to);
473
475 newproj.nextthink = time;
476 }
477 else
478 {
480 newproj.nextthink = newproj.death_time;
481 }
482}
483
485{
487 if(toucher.takedamage == DAMAGE_AIM && WEP_CVAR_SEC(WEP_ELECTRO, touchexplode))
488 {
490 }
491 else if(toucher.owner != this.owner && toucher.classname != this.classname) // don't stick to player's other projectiles!
492 {
493 //UpdateCSQCProjectile(this);
494 spamsound(this, CH_SHOTS, SND_ELECTRO_BOUNCE, VOL_BASE, ATTEN_NORM);
496
497 if(WEP_CVAR_SEC(WEP_ELECTRO, stick))
498 {
499 if(WEP_CVAR_SEC(WEP_ELECTRO, stick_lifetime) == 0)
501 else
503 }
504 }
505}
506
507void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
508{
509 if(GetResource(this, RES_HEALTH) <= 0)
510 return;
511
512 // note: combos are usually triggered by W_Electro_TriggerCombo, not damage
513 float is_combo = (inflictor.classname == "electro_orb_chain" || inflictor.classname == "electro_bolt");
514
515 if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_combo ? 1 : -1)))
516 return; // g_projectiles_damage says to halt
517
518 TakeResource(this, RES_HEALTH, damage);
519 if(GetResource(this, RES_HEALTH) <= 0)
520 {
521 this.takedamage = DAMAGE_NO;
522 this.nextthink = time;
523 if(is_combo)
524 {
525 // change owner to whoever caused the combo explosion
526 this.realowner = inflictor.realowner;
527 this.classname = "electro_orb_chain";
529 // delay combo chains, looks cooler
530 // bound the length, inflictor may be in a galaxy far far away (warpzones)
531 float len = min(WEP_CVAR(WEP_ELECTRO, combo_radius), vlen(this.origin - inflictor.origin));
532 float delay = len / WEP_CVAR(WEP_ELECTRO, combo_speed);
533 this.nextthink = time + delay;
534 }
535 else
536 {
538 setthink(this, adaptor_think2use); // not _hittype_splash, as this runs "immediately"
539 }
540 }
541}
542
543void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity)
544{
545 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(WEP_ELECTRO, ammo), weaponentity);
546
548 actor,
549 weaponentity,
550 '-4 -4 -4',
551 '4 4 4',
552 false,
553 2,
554 SND_ELECTRO_FIRE2,
556 WEP_CVAR_SEC(WEP_ELECTRO, damage),
557 thiswep.m_id | HITTYPE_SECONDARY
558 );
559
560 w_shotdir = v_forward; // no TrueAim for grenades please
561
562 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
563
564 entity proj = new(electro_orb);
565 proj.owner = proj.realowner = actor;
566 proj.use = W_Electro_Explode_use;
568 proj.bot_dodge = true;
569 proj.bot_dodgerating = WEP_CVAR_SEC(WEP_ELECTRO, damage);
570 proj.nextthink = time + WEP_CVAR_SEC(WEP_ELECTRO, lifetime);
571 proj.death_time = time + WEP_CVAR_SEC(WEP_ELECTRO, lifetime);
573 proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
574 proj.weaponentity_fld = weaponentity;
575 setorigin(proj, w_shotorg);
576
577 //proj.glow_size = 50;
578 //proj.glow_color = 45;
580 W_SetupProjVelocity_UP_SEC(proj, WEP_ELECTRO);
582 setsize(proj, '-4 -4 -4', '4 4 4');
583 proj.takedamage = DAMAGE_YES;
584 proj.damageforcescale = WEP_CVAR_SEC(WEP_ELECTRO, damageforcescale);
585 SetResourceExplicit(proj, RES_HEALTH, WEP_CVAR_SEC(WEP_ELECTRO, health));
586 proj.event_damage = W_Electro_Orb_Damage;
587 proj.flags = FL_PROJECTILE;
588 IL_PUSH(g_projectiles, proj);
589 IL_PUSH(g_bot_dodge, proj);
590 proj.damagedbycontents = (WEP_CVAR_SEC(WEP_ELECTRO, damagedbycontents));
591 if(proj.damagedbycontents)
593
594 proj.bouncefactor = WEP_CVAR_SEC(WEP_ELECTRO, bouncefactor);
595 proj.bouncestop = WEP_CVAR_SEC(WEP_ELECTRO, bouncestop);
596 proj.missile_flags = MIF_SPLASH | MIF_ARC;
597
598 if(WEP_CVAR_SEC(WEP_ELECTRO, limit) > 0)
599 {
604 }
605
606 CSQCProjectile(proj, true, PROJECTILE_ELECTRO, false); // no culling, it has sound
607
608 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
609}
610
611void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, int fire)
612{
613 if(actor.(weaponentity).electro_count > 1)
614 if(PHYS_INPUT_BUTTON_ATCK2(actor))
615 if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1))
616 {
617 W_Electro_Attack_Orb(thiswep, actor, weaponentity);
618 actor.(weaponentity).electro_count -= 1;
619 actor.(weaponentity).electro_secondarytime = time;
620 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_ELECTRO, animtime), W_Electro_CheckAttack);
621 return;
622 }
623 w_ready(thiswep, actor, weaponentity, fire);
624}
625
627
628METHOD(Electro, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
629{
630 PHYS_INPUT_BUTTON_ATCK(actor) = PHYS_INPUT_BUTTON_ATCK2(actor) = false;
631 if(vdist(actor.origin - actor.enemy.origin, >, 1000)) { actor.bot_secondary_electromooth = 0; }
632 if(actor.bot_secondary_electromooth == 0)
633 {
634 float shoot;
635
636 if(WEP_CVAR_PRI(WEP_ELECTRO, speed))
637 shoot = bot_aim(actor, weaponentity, WEP_CVAR_PRI(WEP_ELECTRO, speed), 0, WEP_CVAR_PRI(WEP_ELECTRO, lifetime), false, true);
638 else
639 shoot = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false, true);
640
641 if(shoot)
642 {
643 PHYS_INPUT_BUTTON_ATCK(actor) = true;
644 if(random() < 0.01) actor.bot_secondary_electromooth = 1;
645 }
646 }
647 else
648 {
649 if(bot_aim(actor, weaponentity, WEP_CVAR_SEC(WEP_ELECTRO, speed), WEP_CVAR_SEC(WEP_ELECTRO, speed_up), WEP_CVAR_SEC(WEP_ELECTRO, lifetime), true, true))
650 {
651 PHYS_INPUT_BUTTON_ATCK2(actor) = true;
652 if(random() < 0.03) actor.bot_secondary_electromooth = 0;
653 }
654 }
655}
656
657METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
658{
659 if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO
660 {
661 float ammo_amount = 0;
662 if(actor.(weaponentity).clip_load >= WEP_CVAR_PRI(WEP_ELECTRO, ammo))
663 ammo_amount = 1;
664 if(actor.(weaponentity).clip_load >= WEP_CVAR_SEC(WEP_ELECTRO, ammo))
665 ammo_amount += 1;
666
667 if(!ammo_amount)
668 {
669 thiswep.wr_reload(thiswep, actor, weaponentity);
670 return;
671 }
672 }
673
674 if(fire & 1)
675 {
676 if(time >= actor.(weaponentity).electro_secondarytime + WEP_CVAR_SEC(WEP_ELECTRO, refire2) * W_WeaponRateFactor(actor))
677 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(WEP_ELECTRO, refire)))
678 {
679 W_Electro_Attack_Bolt(thiswep, actor, weaponentity);
680 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_ELECTRO, animtime), w_ready);
681 }
682 }
683 else if(fire & 2)
684 {
685 if(time >= actor.(weaponentity).electro_secondarytime + WEP_CVAR_SEC(WEP_ELECTRO, refire) * W_WeaponRateFactor(actor))
686 if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1))
687 {
688 W_Electro_Attack_Orb(thiswep, actor, weaponentity);
689 actor.(weaponentity).electro_count = WEP_CVAR_SEC(WEP_ELECTRO, count);
690 actor.(weaponentity).electro_secondarytime = time;
691 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_ELECTRO, animtime), W_Electro_CheckAttack);
692 }
693 }
694}
695
696METHOD(Electro, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
697{
698 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(WEP_ELECTRO, ammo);
699 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(WEP_ELECTRO, ammo);
700 return ammo_amount;
701}
702
703METHOD(Electro, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
704{
705 float ammo_amount;
706 if(WEP_CVAR(WEP_ELECTRO, combo_safeammocheck)) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
707 {
708 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(WEP_ELECTRO, ammo) + WEP_CVAR_PRI(WEP_ELECTRO, ammo);
709 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(WEP_ELECTRO, ammo) + WEP_CVAR_PRI(WEP_ELECTRO, ammo);
710 }
711 else
712 {
713 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(WEP_ELECTRO, ammo);
714 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(WEP_ELECTRO, ammo);
715 }
716 return ammo_amount;
717}
718
719METHOD(Electro, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
720{
721 W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(WEP_ELECTRO, ammo), WEP_CVAR_SEC(WEP_ELECTRO, ammo)), SND_RELOAD);
722}
723
724METHOD(Electro, wr_suicidemessage, Notification(entity thiswep))
725{
727 return WEAPON_ELECTRO_SUICIDE_ORBS;
728 else
729 return WEAPON_ELECTRO_SUICIDE_BOLT;
730}
731
732METHOD(Electro, wr_killmessage, Notification(entity thiswep))
733{
735 {
736 return WEAPON_ELECTRO_MURDER_ORBS;
737 }
738 else
739 {
741 return WEAPON_ELECTRO_MURDER_COMBO;
742 else
743 return WEAPON_ELECTRO_MURDER_BOLT;
744 }
745}
746
747#endif
748#ifdef CSQC
749
750METHOD(Electro, wr_impacteffect, void(entity thiswep, entity actor))
751{
752 vector org2 = w_org + w_backoff * 2;
754 {
755 pointparticles(EFFECT_ELECTRO_BALLEXPLODE, org2, '0 0 0', 1);
756 if(!w_issilent)
757 sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
758 }
759 else
760 {
762 {
763 // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
764 pointparticles(EFFECT_ELECTRO_COMBO, org2, '0 0 0', 1);
765 if(!w_issilent)
766 sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT_COMBO, VOL_BASE, ATTEN_NORM);
767 }
768 else
769 {
770 pointparticles(EFFECT_ELECTRO_IMPACT, org2, '0 0 0', 1);
771 if(!w_issilent)
772 sound(actor, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
773 }
774 }
775}
776
777#endif
778#ifdef MENUQC
781
782METHOD(Electro, describe, string(Electro this))
783{
784 TC(Electro, this);
786 PAR(_("The %s shoots electric balls forwards, dealing some splash damage when they burst on impact."), COLORED_NAME(this));
787 PAR(_("The secondary fire launches orbs that are influenced by gravity, "
788 "so they can be laid around the map at high traffic locations (like at %s flag bases) to damage enemies that walk by. "
789 "The orbs burst after some time, and can be forced to burst in a \"combo\" if a primary fire ball bursts near them."), COLORED_NAME(MAPINFO_TYPE_CTF));
790 PAR(_("It consumes some %s ammo for each ball / orb."), COLORED_NAME(ITEM_Cells));
791 PAR(_("The %s is one of the best spam weapons to use in crowded areas, since combos can deal tons of damage, if the enemy is close enough. "
792 "Since the primary fire doesn't travel particularly fast, the %s is not useful in many other situations."), COLORED_NAME(this), COLORED_NAME(this));
793 PAR(W_Guide_Keybinds(this));
794 PAR(W_Guide_DPS_secondaryMultishotWithCombo(this.netname, "primary", "secondary", "secondary_count", "secondary_refire2", "combo", true));
795 return PAGE_TEXT;
796}
797
798#endif
float bot_dodge
Definition api.qh:40
float bot_dodgerating
Definition api.qh:39
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity, bool shot_accurate)
IntrusiveList g_bot_dodge
Definition api.qh:150
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:44
int m_id
Definition weapon.qh:45
string netname
Definition powerups.qc:20
float lifetime
Definition powerups.qc:23
float count
Definition powerups.qc:22
float delay
Definition items.qc:17
IntrusiveList g_drawables
Definition main.qh:91
entity owner
Definition main.qh:87
#define COLORED_NAME(this)
Definition color.qh:195
float radius
Definition impulse.qh:11
#define setmodel(this, m)
Definition model.qh:26
bool IsFlying(entity this)
Definition player.qc:836
#define PHYS_INPUT_TIMELENGTH
Definition player.qh:254
#define IS_DEAD(s)
Definition player.qh:245
#define IS_PLAYER(s)
Definition player.qh:243
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:150
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:152
float W_WeaponRateFactor(entity this)
void SetMovetypeFollow(entity ent, entity e)
Definition util.qc:2033
int LostMovetypeFollow(entity ent)
Definition util.qc:2063
const int FL_PROJECTILE
Definition constants.qh:85
string classname
float flags
const float MOVE_NOMONSTERS
vector avelocity
const float MASK_NORMAL
vector mins
vector velocity
const float SOLID_NOT
float time
vector maxs
float nextthink
vector v_forward
vector origin
float trace_fraction
vector trace_plane_normal
#define spawn
#define use
float death_time
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype,.entity weaponentity, entity directhitentity)
Definition damage.qc:981
float damagedbycontents
Definition damage.qh:45
IntrusiveList g_damagedbycontents
Definition damage.qh:135
float spawnshieldtime
Definition damage.qh:61
vector w_org
int w_deathtype
float damageforcescale
vector w_backoff
float w_issilent
const int HITTYPE_BOUNCE
Definition all.qh:31
const int HITTYPE_SPAM
Definition all.qh:34
const int HITTYPE_SECONDARY
Definition all.qh:29
float speed
Definition dynlight.qc:9
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
void W_Electro_TouchExplode(entity this, entity toucher)
Definition electro.qc:256
void electro_orb_draw(entity this)
Definition electro.qc:8
void W_Electro_Explode_use(entity this, entity actor, entity trigger)
Definition electro.qc:251
void W_Electro_Attack_Bolt(Weapon thiswep, entity actor,.entity weaponentity)
Definition electro.qc:338
void W_Electro_Orb_ExplodeOverTime(entity this)
Definition electro.qc:142
void W_Electro_Orb_Touch(entity this, entity toucher)
Definition electro.qc:484
bool electro_orb_send(entity this, entity to, int sf)
Definition electro.qc:57
void W_Electro_Bolt_Think(entity this)
Definition electro.qc:265
void W_Electro_Explode(entity this, entity directhitentity)
Definition electro.qc:200
float bot_secondary_electromooth
Definition electro.qc:626
void W_Electro_ExplodeComboThink(entity this)
Definition electro.qc:125
void W_Electro_Attack_Orb(Weapon thiswep, entity actor,.entity weaponentity)
Definition electro.qc:543
void electro_orb_setup(entity e)
Definition electro.qc:20
void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition electro.qc:507
void W_Electro_CheckAttack(Weapon thiswep, entity actor,.entity weaponentity, int fire)
Definition electro.qc:611
void W_Electro_Orb_Follow_Think(entity this)
Definition electro.qc:390
void W_Electro_Orb_Stick(entity this, entity to)
Definition electro.qc:421
void W_Electro_TriggerCombo(vector org, float rad, entity own)
Definition electro.qc:77
void W_Electro_ExplodeCombo(entity this)
Definition electro.qc:167
IntrusiveList LimitedElectroBallRubbleList
Definition electro.qh:96
float electro_count
Definition electro.qh:97
float electro_secondarytime
Definition electro.qh:98
ent angles
Definition ent_cs.qc:121
solid
Definition ent_cs.qc:165
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
ERASEABLE bool IL_CONTAINS(IntrusiveList this, entity it)
#define IL_NEW()
#define TC(T, sym)
Definition _all.inc:82
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
#define Net_Accept(classname)
Definition net.qh:201
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:686
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition common.qc:348
vector movedir
Definition viewloc.qh:18
float bound(float min, float value, float max)
float random(void)
float vlen(vector v)
vector vectoangles(vector v)
float min(float f,...)
void WriteByte(float data, float dest, float desto)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_NONE
Definition movetypes.qh:129
const int MOVETYPE_FOLLOW
Definition movetypes.qh:141
float bouncefactor
Definition movetypes.qh:47
float move_time
Definition movetypes.qh:77
float move_movetype
Definition movetypes.qh:76
const int MOVETYPE_FLY
Definition movetypes.qh:134
const int MOVETYPE_TOSS
Definition movetypes.qh:135
float bouncestop
Definition movetypes.qh:46
const int MOVETYPE_BOUNCE
Definition movetypes.qh:139
float ltime
Definition net.qc:10
var void func_null()
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
entity Notification
always last
Definition all.qh:81
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define NULL
Definition post.qh:14
float scale
Definition projectile.qc:14
const int PROJECTILE_ELECTRO
Definition projectiles.qh:3
const int PROJECTILE_ELECTRO_BEAM
Definition projectiles.qh:7
float health
Legacy fields for the resources. To be removed.
Definition resources.qh:9
void LimitedChildrenRubble(IntrusiveList list, string cname, int limit, void(entity) deleteproc, entity parent)
Definition rubble.qc:5
entity ListNewChildRubble(IntrusiveList list, entity child)
Definition rubble.qc:46
entity ReplaceOldListedChildRubble(IntrusiveList list, entity child, entity oldChild)
Definition rubble.qc:39
#define setthink(e, f)
#define getthink(e)
vector
Definition self.qh:92
vector org
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
#define IS_INDEPENDENT_PLAYER(e)
Definition client.qh:312
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition common.qc:45
void adaptor_think2use_hittype_splash(entity this)
Definition common.qc:106
const int MIF_SPLASH
Definition common.qh:46
int projectiledeathtype
Definition common.qh:21
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:28
IntrusiveList g_projectiles
Definition common.qh:58
const int MIF_ARC
Definition common.qh:47
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:34
const float VOL_BASE
Definition sound.qh:36
const int CH_SHOTS
Definition sound.qh:14
const int CH_WEAPON_A
Definition sound.qh:7
const float ATTEN_NORM
Definition sound.qh:30
#define sound(e, c, s, v, a)
Definition sound.qh:52
float spamsound(entity e, int chan, Sound samp, float vol, float _atten)
use this one if you might be causing spam (e.g.
Definition all.qc:124
#define PAGE_TEXT
Definition string.qh:643
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:649
#define PAGE_TEXT_INIT()
Definition string.qh:642
const int DAMAGE_YES
Definition subs.qh:80
const int DAMAGE_NO
Definition subs.qh:79
const int DAMAGE_AIM
Definition subs.qh:81
float takedamage
Definition subs.qh:78
float ammo
Definition sv_turrets.qh:43
#define SAME_TEAM(a, b)
Definition teams.qh:241
#define DIFF_TEAM(a, b)
Definition teams.qh:242
entity realowner
vector w_shotdir
Definition tracing.qh:20
#define W_SetupShot_ProjectileSize(ent, wepent, mi, ma, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition tracing.qh:30
#define W_SetupProjVelocity_PRI(ent, wep)
Definition tracing.qh:65
vector w_shotorg
Definition tracing.qh:19
#define W_SetupProjVelocity_UP_SEC(ent, wep)
Definition tracing.qh:56
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:836
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:728
string W_Guide_DPS_secondaryMultishotWithCombo(string name, string pri, string sec, string shots, string refire2, string combo, bool sec_variable)
Definition all.qc:1014
#define WEP_CVAR_PRI(wep, name)
Definition all.qh:322
#define WEP_CVAR(wep, name)
Definition all.qh:321
#define WEP_CVAR_SEC(wep, name)
Definition all.qh:323
void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use,.entity weaponentity)
void W_Reload(entity actor,.entity weaponentity, float sent_ammo_min, Sound sent_sound)
void weapon_thinkf(entity actor,.entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor,.entity weaponentity, int fire) func)
bool weapon_prepareattack(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
void w_ready(Weapon thiswep, entity actor,.entity weaponentity, int fire)
entity weaponentity_fld
float weapon_load[REGISTRY_MAX(Weapons)]