Xonotic
crylink.qc
Go to the documentation of this file.
1 #include "crylink.qh"
2 
3 #ifdef SVQC
4 
5 void W_Crylink_CheckLinks(entity e)
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 
28 void W_Crylink_Dequeue_Raw(entity own, entity prev, entity me, entity next)
29 {
30  W_Crylink_CheckLinks(next);
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)
38  W_Crylink_CheckLinks(next);
39 }
40 
41 void W_Crylink_Dequeue(entity e)
42 {
43  W_Crylink_Dequeue_Raw(e.crylink_owner, e.queueprev, e, e.queuenext);
44 }
45 
46 void W_Crylink_DeleteLink(entity this)
47 {
48  if(this.classname != "spike_oktoremove")
49  W_Crylink_Dequeue(this);
50  delete_fn(this);
51 }
52 
53 void W_Crylink_Reset(entity this)
54 {
55  delete(this);
56 }
57 
58 // force projectile to explode
59 void 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(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius),
75  NULL, NULL, WEP_CVAR_BOTH(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)
89 float w_crylink_linkjoin_time;
90 vector W_Crylink_LinkJoin(entity e, float jspeed)
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
98  W_Crylink_CheckLinks(e);
99 
100  w_crylink_linkjoin_time = 0;
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 
166  W_Crylink_CheckLinks(e);
167 
168  return targ_origin;
169 }
170 
171 void W_Crylink_LinkJoinEffect_Think(entity this)
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(crylink, isprimary, joinexplode))
193  {
194  n /= WEP_CVAR_BOTH(crylink, isprimary, shots);
195  RadiusDamage(
196  e,
197  e.realowner,
198  WEP_CVAR_BOTH(crylink, isprimary, joinexplode_damage) * n,
199  WEP_CVAR_BOTH(crylink, isprimary, joinexplode_edgedamage) * n,
200  WEP_CVAR_BOTH(crylink, isprimary, joinexplode_radius) * n,
201  e.realowner,
202  NULL,
203  WEP_CVAR_BOTH(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 
215 float 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!
238 void W_Crylink_Touch(entity this, entity toucher)
239 {
240  float finalhit;
241  float f;
242  float isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
243  PROJECTILE_TOUCH(this, toucher);
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(crylink, isprimary, bouncedamagefactor);
253  if(a)
254  f *= a;
255 
256  float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius),
257  NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, this.weaponentity_fld, toucher);
258 
259  if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(this, WEP_CVAR_BOTH(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;
264  W_Crylink_LinkExplode(this.queuenext, this, toucher);
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 
284 void W_Crylink_Fadethink(entity this)
285 {
286  delete(this);
287 }
288 
289 void 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(crylink, ammo), weaponentity);
298 
299  maxdmg = WEP_CVAR_PRI(crylink, damage) * WEP_CVAR_PRI(crylink, shots);
300  maxdmg *= 1 + WEP_CVAR_PRI(crylink, bouncedamagefactor) * WEP_CVAR_PRI(crylink, bounces);
301  if(WEP_CVAR_PRI(crylink, joinexplode))
302  maxdmg += WEP_CVAR_PRI(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(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(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 
351  s = '0 0 0';
352  if(counter == 0)
353  s = '0 0 0';
354  else
355  {
356  makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
357  s.y = v_forward.x;
358  s.z = v_forward.y;
359  }
360  s = s * WEP_CVAR_PRI(crylink, spread) * autocvar_g_weaponspreadfactor;
361  W_SetupProjVelocity_Explicit(proj, w_shotdir + right * s.y + up * s.z, v_up, WEP_CVAR_PRI(crylink, speed), 0, 0, 0, false);
362  settouch(proj, W_Crylink_Touch);
363 
364  setthink(proj, W_Crylink_Fadethink);
365  if(counter == 0)
366  {
367  proj.fade_time = time + WEP_CVAR_PRI(crylink, middle_lifetime);
368  proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, middle_fadetime);
369  proj.nextthink = time + WEP_CVAR_PRI(crylink, middle_lifetime) + WEP_CVAR_PRI(crylink, middle_fadetime);
370  }
371  else
372  {
373  proj.fade_time = time + WEP_CVAR_PRI(crylink, other_lifetime);
374  proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, other_fadetime);
375  proj.nextthink = time + WEP_CVAR_PRI(crylink, other_lifetime) + WEP_CVAR_PRI(crylink, other_fadetime);
376  }
377  proj.teleport_time = time + WEP_CVAR_PRI(crylink, joindelay);
378  proj.cnt = WEP_CVAR_PRI(crylink, bounces);
379  //proj.scale = 1 + 1 * proj.cnt;
380 
381  proj.angles = vectoangles(proj.velocity);
382 
383  //proj.glow_size = 20;
384 
385  proj.flags = FL_PROJECTILE;
386  IL_PUSH(g_projectiles, proj);
387  IL_PUSH(g_bot_dodge, proj);
388  proj.missile_flags = MIF_SPLASH;
389 
390  CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
391 
392  MUTATOR_CALLHOOK(EditProjectile, actor, proj);
393  }
394  if(WEP_CVAR_PRI(crylink, joinspread) != 0)
395  {
396  actor.(weaponentity).crylink_lastgroup = proj;
397  W_Crylink_CheckLinks(proj);
398  actor.(weaponentity).crylink_waitrelease = 1;
399  }
400 }
401 
402 void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
403 {
404  float counter, shots;
405  entity proj, prevproj, firstproj;
406  vector s;
407  vector forward, right, up;
408  float maxdmg;
409 
410  W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(crylink, ammo), weaponentity);
411 
412  maxdmg = WEP_CVAR_SEC(crylink, damage) * WEP_CVAR_SEC(crylink, shots);
413  maxdmg *= 1 + WEP_CVAR_SEC(crylink, bouncedamagefactor) * WEP_CVAR_SEC(crylink, bounces);
414  if(WEP_CVAR_SEC(crylink, joinexplode))
415  maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage);
416 
417  W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg, thiswep.m_id | HITTYPE_SECONDARY);
418  forward = v_forward;
419  right = v_right;
420  up = v_up;
421 
422  shots = WEP_CVAR_SEC(crylink, shots);
423  W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
424  proj = prevproj = firstproj = NULL;
425  for(counter = 0; counter < shots; ++counter)
426  {
427  proj = new(spike);
428  proj.dtor = W_Crylink_DeleteLink;
429  proj.weaponentity_fld = weaponentity;
430  proj.reset = W_Crylink_Reset;
431  proj.realowner = proj.owner = actor;
432  proj.crylink_owner = actor;
433  proj.bot_dodge = true;
434  proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage);
435  if(shots == 1) {
436  proj.queuenext = proj;
437  proj.queueprev = proj;
438  }
439  else if(counter == 0) { // first projectile, store in firstproj for now
440  firstproj = proj;
441  }
442  else if(counter == shots - 1) { // last projectile, link up with first projectile
443  prevproj.queuenext = proj;
444  firstproj.queueprev = proj;
445  proj.queuenext = firstproj;
446  proj.queueprev = prevproj;
447  }
448  else { // else link up with previous projectile
449  prevproj.queuenext = proj;
450  proj.queueprev = prevproj;
451  }
452 
453  prevproj = proj;
454 
457  proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
458  //proj.gravity = 0.001;
459 
460  setorigin(proj, w_shotorg);
461  setsize(proj, '0 0 0', '0 0 0');
462 
463  if(WEP_CVAR_SEC(crylink, spreadtype) == 1)
464  {
465  s = '0 0 0';
466  if(counter == 0)
467  s = '0 0 0';
468  else
469  {
470  makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
471  s.y = v_forward.x;
472  s.z = v_forward.y;
473  }
474  s = s * WEP_CVAR_SEC(crylink, spread) * autocvar_g_weaponspreadfactor;
475  s = w_shotdir + right * s.y + up * s.z;
476  }
477  else
478  {
479  s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * WEP_CVAR_SEC(crylink, spread) * autocvar_g_weaponspreadfactor);
480  }
481 
482  W_SetupProjVelocity_Explicit(proj, s, v_up, WEP_CVAR_SEC(crylink, speed), 0, 0, 0, false);
483  settouch(proj, W_Crylink_Touch);
484  setthink(proj, W_Crylink_Fadethink);
485  if(counter == (shots - 1) / 2)
486  {
487  proj.fade_time = time + WEP_CVAR_SEC(crylink, middle_lifetime);
488  proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, middle_fadetime);
489  proj.nextthink = time + WEP_CVAR_SEC(crylink, middle_lifetime) + WEP_CVAR_SEC(crylink, middle_fadetime);
490  }
491  else
492  {
493  proj.fade_time = time + WEP_CVAR_SEC(crylink, other_lifetime);
494  proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, other_fadetime);
495  proj.nextthink = time + WEP_CVAR_SEC(crylink, other_lifetime) + WEP_CVAR_SEC(crylink, other_fadetime);
496  }
497  proj.teleport_time = time + WEP_CVAR_SEC(crylink, joindelay);
498  proj.cnt = WEP_CVAR_SEC(crylink, bounces);
499  //proj.scale = 1 + 1 * proj.cnt;
500 
501  proj.angles = vectoangles(proj.velocity);
502 
503  //proj.glow_size = 20;
504 
505  proj.flags = FL_PROJECTILE;
506  IL_PUSH(g_projectiles, proj);
507  IL_PUSH(g_bot_dodge, proj);
508  proj.missile_flags = MIF_SPLASH;
509 
510  CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
511 
512  MUTATOR_CALLHOOK(EditProjectile, actor, proj);
513  }
514  if(WEP_CVAR_SEC(crylink, joinspread) != 0)
515  {
516  actor.(weaponentity).crylink_lastgroup = proj;
517  W_Crylink_CheckLinks(proj);
518  actor.(weaponentity).crylink_waitrelease = 2;
519  }
520 }
521 
522 METHOD(Crylink, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
523 {
524  if(random() < 0.10)
525  PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), false);
526  else
527  PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false);
528 }
529 METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
530 {
531  if(autocvar_g_balance_crylink_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) { // forced reload
532  thiswep.wr_reload(thiswep, actor, weaponentity);
533  }
534 
535  if(fire & 1)
536  {
537  if(actor.(weaponentity).crylink_waitrelease != 1)
538  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire)))
539  {
540  W_Crylink_Attack(thiswep, actor, weaponentity);
541  weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
542  }
543  }
544 
545  if((fire & 2) && autocvar_g_balance_crylink_secondary)
546  {
547  if(actor.(weaponentity).crylink_waitrelease != 2)
548  if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire)))
549  {
550  W_Crylink_Attack2(thiswep, actor, weaponentity);
551  weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
552  }
553  }
554 
555  if((actor.(weaponentity).crylink_waitrelease == 1 && !(fire & 1)) || (actor.(weaponentity).crylink_waitrelease == 2 && !(fire & 2)))
556  {
557  if(!actor.(weaponentity).crylink_lastgroup || time > actor.(weaponentity).crylink_lastgroup.teleport_time)
558  {
559  // fired and released now!
560  if(actor.(weaponentity).crylink_lastgroup)
561  {
562  vector pos;
563  entity linkjoineffect;
564  float isprimary = (actor.(weaponentity).crylink_waitrelease == 1);
565 
566  pos = W_Crylink_LinkJoin(actor.(weaponentity).crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
567 
568  linkjoineffect = new(linkjoineffect);
569  linkjoineffect.weaponentity_fld = weaponentity;
570  setthink(linkjoineffect, W_Crylink_LinkJoinEffect_Think);
571  linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
572  linkjoineffect.owner = actor;
573  setorigin(linkjoineffect, pos);
574  }
575  actor.(weaponentity).crylink_waitrelease = 0;
576  if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !thiswep.wr_checkammo2(thiswep, actor, weaponentity))
577  if(!(actor.items & IT_UNLIMITED_AMMO))
578  {
579  // ran out of ammo!
580  actor.cnt = thiswep.m_id;
581  actor.(weaponentity).m_switchweapon = w_getbestweapon(actor, weaponentity);
582  }
583  }
584  }
585 }
586 METHOD(Crylink, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
587 {
588  // don't "run out of ammo" and switch weapons while waiting for release
589  if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
590  return true;
591 
592  float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(crylink, ammo);
593  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(crylink, ammo);
594  return ammo_amount;
595 }
596 METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
597 {
598  // don't "run out of ammo" and switch weapons while waiting for release
599  if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
600  return true;
601 
602  float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(crylink, ammo);
603  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(crylink, ammo);
604  return ammo_amount;
605 }
606 METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
607 {
608  W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD);
609 }
610 METHOD(Crylink, wr_suicidemessage, Notification(entity thiswep))
611 {
612  return WEAPON_CRYLINK_SUICIDE;
613 }
614 METHOD(Crylink, wr_killmessage, Notification(entity thiswep))
615 {
616  return WEAPON_CRYLINK_MURDER;
617 }
618 #endif
619 #ifdef CSQC
620 METHOD(Crylink, wr_impacteffect, void(entity thiswep, entity actor))
621 {
622  vector org2;
623  org2 = w_org + w_backoff * 2;
625  {
626  pointparticles(EFFECT_CRYLINK_IMPACT2, org2, '0 0 0', 1);
627  if(!w_issilent)
628  sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT2, VOL_BASE, ATTN_NORM);
629  }
630  else
631  {
632  pointparticles(EFFECT_CRYLINK_IMPACT, org2, '0 0 0', 1);
633  if(!w_issilent)
634  sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM);
635  }
636 }
637 #endif
#define WEP_CVAR_PRI(wepname, name)
Definition: all.qh:300
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition: player.qh:148
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity)
#define WEP_CVAR_SEC(wepname, name)
Definition: all.qh:301
float weapon_load[REGISTRY_MAX(Weapons)]
Definition: weaponsystem.qh:29
const int HITTYPE_BOUNCE
Definition: all.qh:28
float speed
Definition: subs.qh:41
vector w_shotorg
Definition: tracing.qh:18
const int PROJECTILE_CRYLINK
Definition: projectiles.qh:6
#define PROJECTILE_MAKETRIGGER(e)
Definition: common.qh:29
const int MIF_SPLASH
Definition: common.qh:34
#define w_getbestweapon(ent, wepent)
Definition: selection.qh:23
fade_rate
Definition: projectile.qh:14
entity() spawn
prev
Definition: all.qh:66
float MOVETYPE_BOUNCEMISSILE
Definition: progsdefs.qc:257
void w_ready(Weapon thiswep, entity actor,.entity weaponentity, int fire)
float radius
Definition: impulse.qh:11
var void delete_fn(entity e)
vector w_shotdir
Definition: tracing.qh:19
entity weaponentity_fld
Definition: weaponsystem.qh:27
origin
Definition: ent_cs.qc:114
string classname
Definition: csprogsdefs.qc:107
#define METHOD(cname, name, prototype)
Definition: oo.qh:257
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
Definition: common.qc:756
entity owner
Definition: main.qh:73
const int PROJECTILE_CRYLINK_BOUNCING
Definition: projectiles.qh:20
IntrusiveList g_bot_dodge
Definition: api.qh:150
#define PROJECTILE_TOUCH(e, t)
Definition: common.qh:27
const float ATTN_NORM
Definition: csprogsdefs.qc:226
float ammo
Definition: sv_turrets.qh:44
vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
Definition: common.qc:748
void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
Definition: tracing.qc:185
#define vlen2(v)
Definition: vector.qh:4
float cnt
Definition: powerups.qc:24
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void UpdateCSQCProjectile(entity e)
const int CH_WEAPON_A
Definition: sound.qh:7
vector v_up
Definition: csprogsdefs.qc:31
bool weapon_prepareattack(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
#define WEP_CVAR_BOTH(wepname, isprimary, name)
Definition: all.qh:302
#define pointparticles
Definition: csprogsdefs.qh:13
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
const float VOL_BASE
Definition: sound.qh:36
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
#define SAME_TEAM(a, b)
Definition: teams.qh:239
const int HITTYPE_SECONDARY
Definition: all.qh:25
#define W_SetupShot(ent, wepent, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition: tracing.qh:33
#define IS_DEAD(s)
Definition: utils.qh:26
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition: common.qc:669
const int CH_SHOTS
Definition: sound.qh:14
float w_deathtype
Definition: damage.qh:97
vector(float skel, float bonenum) _skel_get_boneabs_hidden
next
Definition: all.qh:88
float autocvar_g_weaponspreadfactor
Definition: weaponsystem.qh:5
IntrusiveList g_projectiles
Definition: common.qh:46
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float fade_time
Definition: common.qh:22
entity Notification
always last
Definition: all.qh:82
void weapon_thinkf(entity actor,.entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor,.entity weaponentity, int fire) func)
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:1057
vector v_right
Definition: csprogsdefs.qc:31
const float MAX_DAMAGEEXTRARADIUS
Definition: damage.qh:139
entity realowner
Definition: common.qh:25
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
setorigin(ent, v)
#define setthink(e, f)
vector angles
Definition: csprogsdefs.qc:104
void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use,.entity weaponentity)
#define sound(e, c, s, v, a)
Definition: sound.qh:52
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
float time
Definition: csprogsdefs.qc:16
vector velocity
Definition: csprogsdefs.qc:103
int m_id
Definition: weapon.qh:42
#define makevectors
Definition: post.qh:21
float DAMAGE_NO
Definition: progsdefs.qc:282
void set_movetype(entity this, int mt)
vector v_forward
Definition: csprogsdefs.qc:31
void W_Reload(entity actor,.entity weaponentity, float sent_ammo_min, Sound sent_sound)
int projectiledeathtype
Definition: common.qh:20