Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
crylink.qc
Go to the documentation of this file.
1#include "crylink.qh"
2
3#ifdef SVQC
4
6{
7 float i;
8 entity p;
9
10 if(e == NULL)
11 error("W_Crylink_CheckLinks: entity is NULL");
12 if(e.classname != "spike" || wasfreed(e))
13 error(sprintf("W_Crylink_CheckLinks: entity is not a spike but a %s (freed: %d)", e.classname, wasfreed(e)));
14
15 p = e;
16 for(i = 0; i < 1000; ++i)
17 {
18 if(p.queuenext.queueprev != p || p.queueprev.queuenext != p)
19 error("W_Crylink_CheckLinks: queue is inconsistent");
20 p = p.queuenext;
21 if(p == e)
22 break;
23 }
24 if(i >= 1000)
25 error("W_Crylink_CheckLinks: infinite chain");
26}
27
29{
31 .entity weaponentity = me.weaponentity_fld;
32 if(me == own.(weaponentity).crylink_lastgroup)
33 own.(weaponentity).crylink_lastgroup = ((me == next) ? NULL : next);
34 prev.queuenext = next;
35 next.queueprev = prev;
36 me.classname = "spike_oktoremove";
37 if(me != next)
39}
40
42{
43 W_Crylink_Dequeue_Raw(e.crylink_owner, e.queueprev, e, e.queuenext);
44}
45
47{
48 if(this.classname != "spike_oktoremove")
50 delete_fn(this);
51}
52
54{
55 delete(this);
56}
57
58// force projectile to explode
59void W_Crylink_LinkExplode(entity e, entity e2, entity directhitentity)
60{
61 float a;
62
63 if(e == e2)
64 return;
65
66 a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1);
67
68 .entity weaponentity = e.weaponentity_fld;
69 if(e == e.crylink_owner.(weaponentity).crylink_lastgroup)
70 e.crylink_owner.(weaponentity).crylink_lastgroup = NULL;
71
72 float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
73
74 RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, damage) * a, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, edgedamage) * a, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, radius),
75 NULL, NULL, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, force) * a, e.projectiledeathtype, e.weaponentity_fld, directhitentity);
76
77 W_Crylink_LinkExplode(e.queuenext, e2, directhitentity);
78
79 e.classname = "spike_oktoremove";
80 delete(e);
81}
82
83// adjust towards center
84// returns the origin where they will meet... and the time till the meeting is
85// stored in w_crylink_linkjoin_time.
86// could possibly network this origin and time, and display a special particle
87// effect when projectiles meet there :P
88// jspeed: joining speed (calculate this as join spread * initial speed)
91{
92 vector avg_origin, avg_velocity;
93 vector targ_origin;
94 float avg_dist, n;
95 entity p;
96
97 // FIXME remove this debug code
99
101
102 avg_origin = e.origin;
103 avg_velocity = e.velocity;
104 n = 1;
105 for(p = e; (p = p.queuenext) != e; )
106 {
107 avg_origin += WarpZone_RefSys_TransformOrigin(p, e, p.origin);
108 avg_velocity += WarpZone_RefSys_TransformVelocity(p, e, p.velocity);
109 ++n;
110 }
111 avg_origin *= (1.0 / n);
112 avg_velocity *= (1.0 / n);
113
114 if(n < 2)
115 return avg_origin; // nothing to do
116
117 // yes, mathematically we can do this in ONE step, but beware of 32bit floats...
118 avg_dist = (vlen(e.origin - avg_origin) ** 2);
119 for(p = e; (p = p.queuenext) != e; )
120 avg_dist += (vlen(WarpZone_RefSys_TransformOrigin(p, e, p.origin) - avg_origin) ** 2);
121 avg_dist *= (1.0 / n);
122 avg_dist = sqrt(avg_dist);
123
124 if(avg_dist == 0)
125 return avg_origin; // no change needed
126
127 if(jspeed == 0)
128 {
129 e.velocity = avg_velocity;
131 for(p = e; (p = p.queuenext) != e; )
132 {
133 p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity);
135 }
136 targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE
137 }
138 else
139 {
140 w_crylink_linkjoin_time = avg_dist / jspeed;
141 targ_origin = avg_origin + w_crylink_linkjoin_time * avg_velocity;
142
143 e.velocity = (targ_origin - e.origin) * (1.0 / w_crylink_linkjoin_time);
145 for(p = e; (p = p.queuenext) != e; )
146 {
147 p.velocity = WarpZone_RefSys_TransformVelocity(e, p, (targ_origin - WarpZone_RefSys_TransformOrigin(p, e, p.origin)) * (1.0 / w_crylink_linkjoin_time));
149 }
150
151 // analysis:
152 // jspeed -> +infinity:
153 // w_crylink_linkjoin_time -> +0
154 // targ_origin -> avg_origin
155 // p->velocity -> HUEG towards center
156 // jspeed -> 0:
157 // w_crylink_linkjoin_time -> +/- infinity
158 // targ_origin -> avg_velocity * +/- infinity
159 // p->velocity -> avg_velocity
160 // jspeed -> -infinity:
161 // w_crylink_linkjoin_time -> -0
162 // targ_origin -> avg_origin
163 // p->velocity -> HUEG away from center
164 }
165
167
168 return targ_origin;
169}
170
172{
173 // is there at least 2 projectiles very close?
174 entity e, p;
175 float n;
176 .entity weaponentity = this.weaponentity_fld;
177 e = this.owner.(weaponentity).crylink_lastgroup;
178 n = 0;
179 if(e)
180 {
181 if(vlen2(e.origin - this.origin) < vlen2(e.velocity) * frametime)
182 ++n;
183 for(p = e; (p = p.queuenext) != e; )
184 {
185 if(vlen2(p.origin - this.origin) < vlen2(p.velocity) * frametime)
186 ++n;
187 }
188 if(n >= 2)
189 {
190 float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
191
192 if(WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinexplode))
193 {
194 n /= WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, shots);
196 e,
197 e.realowner,
198 WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinexplode_damage) * n,
199 WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinexplode_edgedamage) * n,
200 WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinexplode_radius) * n,
201 e.realowner,
202 NULL,
203 WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinexplode_force) * n,
204 e.projectiledeathtype,
205 e.weaponentity_fld,
206 NULL
207 );
208 Send_Effect(EFFECT_CRYLINK_JOINEXPLODE, this.origin, '0 0 0', n);
209 }
210 }
211 }
212 delete(this);
213}
214
215float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad)
216{
217 entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, false);
218 float hit_friendly = 0;
219 float hit_enemy = 0;
220
221 while(head)
222 {
223 if((head.takedamage != DAMAGE_NO) && (!IS_DEAD(head)))
224 {
225 if(SAME_TEAM(head, projectile.realowner))
226 ++hit_friendly;
227 else
228 ++hit_enemy;
229 }
230
231 head = head.chain;
232 }
233
234 return (hit_enemy ? false : hit_friendly);
235}
236
237// NO bounce protection, as bounces are limited!
239{
240 float finalhit;
241 float f;
242 float isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
244
245 float a;
246 a = bound(0, 1 - (time - this.fade_time) * this.fade_rate, 1);
247
248 finalhit = ((this.cnt <= 0) || (toucher.takedamage != DAMAGE_NO));
249 if(finalhit)
250 f = 1;
251 else
252 f = WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, bouncedamagefactor);
253 if(a)
254 f *= a;
255
256 float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, damage) * f, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, edgedamage) * f, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, radius),
257 NULL, NULL, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, force) * f, this.projectiledeathtype, this.weaponentity_fld, toucher);
258
259 if(totaldamage && ((WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(this, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, radius)))))
260 {
261 .entity weaponentity = this.weaponentity_fld;
262 if(this == this.crylink_owner.(weaponentity).crylink_lastgroup)
263 this.crylink_owner.(weaponentity).crylink_lastgroup = NULL;
265 this.classname = "spike_oktoremove";
266 delete(this);
267 return;
268 }
269 else if(finalhit)
270 {
271 // just unlink
272 delete(this);
273 return;
274 }
275 this.cnt = this.cnt - 1;
276 this.angles = vectoangles(this.velocity);
277 this.owner = NULL;
279 // commented out as it causes a little hitch...
280 //if(proj.cnt == 0)
281 // CSQCProjectile(proj, true, PROJECTILE_CRYLINK, true);
282}
283
285{
286 delete(this);
287}
288
289void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity)
290{
291 float counter, shots;
292 entity proj, prevproj, firstproj;
293 vector s;
294 vector forward, right, up;
295 float maxdmg;
296
297 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(WEP_CRYLINK, ammo), weaponentity);
298
299 maxdmg = WEP_CVAR_PRI(WEP_CRYLINK, damage) * WEP_CVAR_PRI(WEP_CRYLINK, shots);
300 maxdmg *= 1 + WEP_CVAR_PRI(WEP_CRYLINK, bouncedamagefactor) * WEP_CVAR_PRI(WEP_CRYLINK, bounces);
301 if(WEP_CVAR_PRI(WEP_CRYLINK, joinexplode))
302 maxdmg += WEP_CVAR_PRI(WEP_CRYLINK, joinexplode_damage);
303
304 W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg, thiswep.m_id);
305 forward = v_forward;
306 right = v_right;
307 up = v_up;
308
309 shots = WEP_CVAR_PRI(WEP_CRYLINK, shots);
310 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
311 proj = prevproj = firstproj = NULL;
312 for(counter = 0; counter < shots; ++counter)
313 {
314 proj = new(spike);
315 proj.dtor = W_Crylink_DeleteLink;
316 proj.reset = W_Crylink_Reset;
317 proj.realowner = proj.owner = actor;
318 proj.crylink_owner = actor;
319 proj.weaponentity_fld = weaponentity;
320 proj.bot_dodge = true;
321 proj.bot_dodgerating = WEP_CVAR_PRI(WEP_CRYLINK, damage);
322 if(shots == 1) {
323 proj.queuenext = proj;
324 proj.queueprev = proj;
325 }
326 else if(counter == 0) { // first projectile, store in firstproj for now
327 firstproj = proj;
328 }
329 else if(counter == shots - 1) { // last projectile, link up with first projectile
330 prevproj.queuenext = proj;
331 firstproj.queueprev = proj;
332 proj.queuenext = firstproj;
333 proj.queueprev = prevproj;
334 }
335 else { // else link up with previous projectile
336 prevproj.queuenext = proj;
337 proj.queueprev = prevproj;
338 }
339
340 prevproj = proj;
341
344 proj.projectiledeathtype = thiswep.m_id;
345 //proj.gravity = 0.001;
346
347 setorigin(proj, w_shotorg);
348 setsize(proj, '0 0 0', '0 0 0');
349
350 s = W_CalculateSpreadPattern(1, 0, counter, shots);
351 s = s * WEP_CVAR_PRI(WEP_CRYLINK, spread) * autocvar_g_weaponspreadfactor;
352 W_SetupProjVelocity_Explicit(proj, w_shotdir + right * s.y + up * s.z, v_up, WEP_CVAR_PRI(WEP_CRYLINK, speed), 0, 0, 0, false);
354
356 if(counter == 0)
357 {
358 proj.fade_time = time + WEP_CVAR_PRI(WEP_CRYLINK, middle_lifetime);
359 proj.fade_rate = 1 / WEP_CVAR_PRI(WEP_CRYLINK, middle_fadetime);
360 proj.nextthink = time + WEP_CVAR_PRI(WEP_CRYLINK, middle_lifetime) + WEP_CVAR_PRI(WEP_CRYLINK, middle_fadetime);
361 }
362 else
363 {
364 proj.fade_time = time + WEP_CVAR_PRI(WEP_CRYLINK, other_lifetime);
365 proj.fade_rate = 1 / WEP_CVAR_PRI(WEP_CRYLINK, other_fadetime);
366 proj.nextthink = time + WEP_CVAR_PRI(WEP_CRYLINK, other_lifetime) + WEP_CVAR_PRI(WEP_CRYLINK, other_fadetime);
367 }
368 proj.teleport_time = time + WEP_CVAR_PRI(WEP_CRYLINK, joindelay);
369 proj.cnt = WEP_CVAR_PRI(WEP_CRYLINK, bounces);
370 //proj.scale = 1 + 1 * proj.cnt;
371
372 proj.angles = vectoangles(proj.velocity);
373
374 //proj.glow_size = 20;
375
376 proj.flags = FL_PROJECTILE;
377 IL_PUSH(g_projectiles, proj);
378 IL_PUSH(g_bot_dodge, proj);
379 proj.missile_flags = MIF_SPLASH;
380
381 CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
382
383 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
384 }
385 if(WEP_CVAR_PRI(WEP_CRYLINK, joinspread) != 0 && WEP_CVAR_PRI(WEP_CRYLINK, shots) > 1)
386 {
387 actor.(weaponentity).crylink_lastgroup = proj;
389 actor.(weaponentity).crylink_waitrelease = 1;
390 }
391}
392
393void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
394{
395 float counter, shots;
396 entity proj, prevproj, firstproj;
397 vector s;
398 vector forward, right, up;
399 float maxdmg;
400
401 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(WEP_CRYLINK, ammo), weaponentity);
402
403 maxdmg = WEP_CVAR_SEC(WEP_CRYLINK, damage) * WEP_CVAR_SEC(WEP_CRYLINK, shots);
404 maxdmg *= 1 + WEP_CVAR_SEC(WEP_CRYLINK, bouncedamagefactor) * WEP_CVAR_SEC(WEP_CRYLINK, bounces);
405 if(WEP_CVAR_SEC(WEP_CRYLINK, joinexplode))
406 maxdmg += WEP_CVAR_SEC(WEP_CRYLINK, joinexplode_damage);
407
408 W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg, thiswep.m_id | HITTYPE_SECONDARY);
409 forward = v_forward;
410 right = v_right;
411 up = v_up;
412
413 shots = WEP_CVAR_SEC(WEP_CRYLINK, shots);
414 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
415 proj = prevproj = firstproj = NULL;
416 for(counter = 0; counter < shots; ++counter)
417 {
418 proj = new(spike);
419 proj.dtor = W_Crylink_DeleteLink;
420 proj.weaponentity_fld = weaponentity;
421 proj.reset = W_Crylink_Reset;
422 proj.realowner = proj.owner = actor;
423 proj.crylink_owner = actor;
424 proj.bot_dodge = true;
425 proj.bot_dodgerating = WEP_CVAR_SEC(WEP_CRYLINK, damage);
426 if(shots == 1) {
427 proj.queuenext = proj;
428 proj.queueprev = proj;
429 }
430 else if(counter == 0) { // first projectile, store in firstproj for now
431 firstproj = proj;
432 }
433 else if(counter == shots - 1) { // last projectile, link up with first projectile
434 prevproj.queuenext = proj;
435 firstproj.queueprev = proj;
436 proj.queuenext = firstproj;
437 proj.queueprev = prevproj;
438 }
439 else { // else link up with previous projectile
440 prevproj.queuenext = proj;
441 proj.queueprev = prevproj;
442 }
443
444 prevproj = proj;
445
448 proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
449 //proj.gravity = 0.001;
450
451 setorigin(proj, w_shotorg);
452 setsize(proj, '0 0 0', '0 0 0');
453
454 if(WEP_CVAR_SEC(WEP_CRYLINK, spreadtype) == 1)
455 {
456 s = W_CalculateSpreadPattern(1, 0, counter, shots);
457 s = s * WEP_CVAR_SEC(WEP_CRYLINK, spread) * autocvar_g_weaponspreadfactor;
458 s = w_shotdir + right * s.y + up * s.z;
459 }
460 else
461 {
462 s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * WEP_CVAR_SEC(WEP_CRYLINK, spread) * autocvar_g_weaponspreadfactor);
463 }
464
465 W_SetupProjVelocity_Explicit(proj, s, v_up, WEP_CVAR_SEC(WEP_CRYLINK, speed), 0, 0, 0, false);
468 if(counter == (shots - 1) / 2)
469 {
470 proj.fade_time = time + WEP_CVAR_SEC(WEP_CRYLINK, middle_lifetime);
471 proj.fade_rate = 1 / WEP_CVAR_SEC(WEP_CRYLINK, middle_fadetime);
472 proj.nextthink = time + WEP_CVAR_SEC(WEP_CRYLINK, middle_lifetime) + WEP_CVAR_SEC(WEP_CRYLINK, middle_fadetime);
473 }
474 else
475 {
476 proj.fade_time = time + WEP_CVAR_SEC(WEP_CRYLINK, other_lifetime);
477 proj.fade_rate = 1 / WEP_CVAR_SEC(WEP_CRYLINK, other_fadetime);
478 proj.nextthink = time + WEP_CVAR_SEC(WEP_CRYLINK, other_lifetime) + WEP_CVAR_SEC(WEP_CRYLINK, other_fadetime);
479 }
480 proj.teleport_time = time + WEP_CVAR_SEC(WEP_CRYLINK, joindelay);
481 proj.cnt = WEP_CVAR_SEC(WEP_CRYLINK, bounces);
482 //proj.scale = 1 + 1 * proj.cnt;
483
484 proj.angles = vectoangles(proj.velocity);
485
486 //proj.glow_size = 20;
487
488 proj.flags = FL_PROJECTILE;
489 IL_PUSH(g_projectiles, proj);
490 IL_PUSH(g_bot_dodge, proj);
491 proj.missile_flags = MIF_SPLASH;
492
493 CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
494
495 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
496 }
497 if(WEP_CVAR_SEC(WEP_CRYLINK, joinspread) != 0 && WEP_CVAR_SEC(WEP_CRYLINK, shots) > 1)
498 {
499 actor.(weaponentity).crylink_lastgroup = proj;
501 actor.(weaponentity).crylink_waitrelease = 2;
502 }
503}
504
505METHOD(Crylink, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
506{
507 if(random() < 0.10)
508 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(WEP_CRYLINK, speed), 0, WEP_CVAR_PRI(WEP_CRYLINK, middle_lifetime), false, true);
509 else
510 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(WEP_CRYLINK, speed), 0, WEP_CVAR_SEC(WEP_CRYLINK, middle_lifetime), false, true);
511}
512
513METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
514{
515 if (autocvar_g_balance_crylink_reload_ammo
516 && actor.(weaponentity).clip_load < min(WEP_CVAR_PRI(WEP_CRYLINK, ammo), WEP_CVAR_SEC(WEP_CRYLINK, ammo)))
517 { // forced reload
518 thiswep.wr_reload(thiswep, actor, weaponentity);
519 }
520
521 else if(fire & 1)
522 {
523 if(actor.(weaponentity).crylink_waitrelease != 1)
524 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(WEP_CRYLINK, refire)))
525 {
526 W_Crylink_Attack(thiswep, actor, weaponentity);
527 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_CRYLINK, animtime), w_ready);
528 }
529 }
530
531 else if((fire & 2) && autocvar_g_balance_crylink_secondary)
532 {
533 if(actor.(weaponentity).crylink_waitrelease != 2)
534 if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(WEP_CRYLINK, refire)))
535 {
536 W_Crylink_Attack2(thiswep, actor, weaponentity);
537 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_CRYLINK, animtime), w_ready);
538 }
539 }
540
541 if((actor.(weaponentity).crylink_waitrelease == 1 && !(fire & 1)) || (actor.(weaponentity).crylink_waitrelease == 2 && !(fire & 2)))
542 {
543 if(!actor.(weaponentity).crylink_lastgroup || time > actor.(weaponentity).crylink_lastgroup.teleport_time)
544 {
545 // fired and released now!
546 if(actor.(weaponentity).crylink_lastgroup)
547 {
548 vector pos;
549 entity linkjoineffect;
550 float isprimary = (actor.(weaponentity).crylink_waitrelease == 1);
551
552 pos = W_Crylink_LinkJoin(actor.(weaponentity).crylink_lastgroup, WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, joinspread) * WEP_CVAR_BOTH(WEP_CRYLINK, isprimary, speed));
553
554 linkjoineffect = new(linkjoineffect);
555 linkjoineffect.weaponentity_fld = weaponentity;
557 linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
558 linkjoineffect.owner = actor;
559 setorigin(linkjoineffect, pos);
560 }
561 actor.(weaponentity).crylink_waitrelease = 0;
562 if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !thiswep.wr_checkammo2(thiswep, actor, weaponentity))
563 if(!(actor.items & IT_UNLIMITED_AMMO))
564 {
565 // ran out of ammo!
566 actor.cnt = thiswep.m_id;
567 actor.(weaponentity).m_switchweapon = w_getbestweapon(actor, weaponentity);
568 }
569 }
570 }
571}
572
573METHOD(Crylink, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
574{
575 // don't "run out of ammo" and switch weapons while waiting for release
576 if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
577 return true;
578
579 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(WEP_CRYLINK, ammo);
580 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(WEP_CRYLINK, ammo);
581 return ammo_amount;
582}
583
584METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
585{
586 // don't "run out of ammo" and switch weapons while waiting for release
587 if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
588 return true;
589
590 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(WEP_CRYLINK, ammo);
591 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(WEP_CRYLINK, ammo);
592 return ammo_amount;
593}
594
595METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
596{
597 W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(WEP_CRYLINK, ammo), WEP_CVAR_SEC(WEP_CRYLINK, ammo)), SND_RELOAD);
598}
599
600METHOD(Crylink, wr_suicidemessage, Notification(entity thiswep))
601{
602 return WEAPON_CRYLINK_SUICIDE;
603}
604
605METHOD(Crylink, wr_killmessage, Notification(entity thiswep))
606{
607 return WEAPON_CRYLINK_MURDER;
608}
609
610#endif
611#ifdef CSQC
612
613METHOD(Crylink, wr_impacteffect, void(entity thiswep, entity actor))
614{
615 vector org2 = w_org + w_backoff * 2;
617 {
618 pointparticles(EFFECT_CRYLINK_IMPACT2, org2, '0 0 0', 1);
619 if(!w_issilent)
620 sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT2, VOL_BASE, ATTN_NORM);
621 }
622 else
623 {
624 pointparticles(EFFECT_CRYLINK_IMPACT, org2, '0 0 0', 1);
625 if(!w_issilent)
626 sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM);
627 }
628}
629
630#endif
631#ifdef MENUQC
633
634METHOD(Crylink, describe, string(Crylink this))
635{
636 TC(Crylink, this);
638 PAR(_("The %s fires bursts of laser-like projectiles, spreading out as they travel away and deflecting off walls. "
639 "If the primary fire is held, when it's released the projectiles will converge before spreading out again, "
640 "which can be utilized to deal more damage to an enemy by making the projectiles converge on them."), COLORED_NAME(this));
641 PAR(_("Projectiles deal damage on collision but also when they bounce, so with good positioning they can sometimes deal double damage."));
642 PAR(_("The secondary fire shoots the projectiles together in a tight group, so it is often the better option in situations where the enemy is an easy target to hit."));
643 PAR(_("It consumes %s ammo for each projectile, which is shared by several weapons."), COLORED_NAME(ITEM_Cells));
644 PAR(_("Close to medium range is the ideal time to use the %s, although the secondary fire can be useful for long range combat sometimes."), COLORED_NAME(this));
645 PAR(_("The %s deals knockback in a unique way, pulling the player from their center to the point of impact. "
646 "This makes it one of the best weapons to slow someone down if you are chasing them, particularly with the secondary fire. "
647 "Another common use of the %s is \"crylink running,\" where you partially angle down and use the secondary fire to pull yourself forwards, in order to gain speed."), COLORED_NAME(this), COLORED_NAME(this));
648 PAR(W_Guide_Keybinds(this));
649 PAR(W_Guide_DPS_bothMultishot(this.netname, "primary", "secondary"));
650 return PAGE_TEXT;
651}
652
653#endif
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
vector W_CalculateSpreadPattern(int pattern, float bias, int counter, int total)
float autocvar_g_weaponspreadfactor
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.
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 cnt
Definition powerups.qc:24
entity owner
Definition main.qh:87
#define COLORED_NAME(this)
Definition color.qh:195
const int IT_UNLIMITED_AMMO
Definition item.qh:23
float radius
Definition impulse.qh:11
#define IS_DEAD(s)
Definition player.qh:245
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:150
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:152
const int FL_PROJECTILE
Definition constants.qh:85
vector v_up
string classname
float frametime
vector velocity
float time
vector v_right
vector v_forward
vector origin
const float ATTN_NORM
void UpdateCSQCProjectile(entity e)
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
vector w_org
const float MAX_DAMAGEEXTRARADIUS
int w_deathtype
vector w_backoff
float w_issilent
const int HITTYPE_BOUNCE
Definition all.qh:31
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 Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:124
ent angles
Definition ent_cs.qc:121
prev
Definition all.qh:71
next
Definition all.qh:93
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define TC(T, sym)
Definition _all.inc:82
vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
Definition common.qc:773
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:686
vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
Definition common.qc:765
float bound(float min, float value, float max)
float random(void)
float vlen(vector v)
vector vectoangles(vector v)
float sqrt(float f)
float min(float f,...)
vector normalize(vector v)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_BOUNCEMISSILE
Definition movetypes.qh:140
entity Notification
always last
Definition all.qh:81
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
fade_rate
Definition projectile.qh:14
const int PROJECTILE_CRYLINK_BOUNCING
const int PROJECTILE_CRYLINK
Definition projectiles.qh:6
#define w_getbestweapon(ent, wepent)
Definition selection.qh:23
#define setthink(e, f)
vector
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
const int MIF_SPLASH
Definition common.qh:46
int projectiledeathtype
Definition common.qh:21
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:28
float fade_time
Definition common.qh:23
IntrusiveList g_projectiles
Definition common.qh:58
#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
#define sound(e, c, s, v, a)
Definition sound.qh:52
#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_NO
Definition subs.qh:79
float ammo
Definition sv_turrets.qh:43
var void delete_fn(entity e)
#define SAME_TEAM(a, b)
Definition teams.qh:241
entity realowner
void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
Definition tracing.qc:192
vector w_shotdir
Definition tracing.qh:20
vector w_shotorg
Definition tracing.qh:19
#define W_SetupShot(ent, wepent, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition tracing.qh:34
#define vlen2(v)
Definition vector.qh:4
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:836
string W_Guide_DPS_bothMultishot(string name, string pri, string sec)
Definition all.qc:944
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:728
#define WEP_CVAR_PRI(wep, name)
Definition all.qh:322
#define WEP_CVAR_BOTH(wep, isprimary, name)
Definition all.qh:324
#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)]
Weapon m_switchweapon
Definition wepent.qh:25