Xonotic
seeker.qc
Go to the documentation of this file.
1 #include "seeker.qh"
2 
3 #ifdef SVQC
4 
5 // ============================
6 // Begin: Missile functions, these are general functions to be manipulated by other code
7 // ============================
8 void W_Seeker_Missile_Explode(entity this, entity directhitentity)
9 {
10  this.event_damage = func_null;
11  RadiusDamage(this, this.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), NULL, NULL, WEP_CVAR(seeker, missile_force), this.projectiledeathtype, this.weaponentity_fld, directhitentity);
12 
13  delete(this);
14 }
15 
16 void W_Seeker_Missile_Explode_think(entity this)
17 {
18  W_Seeker_Missile_Explode(this, NULL);
19 }
20 
21 void W_Seeker_Missile_Touch(entity this, entity toucher)
22 {
23  PROJECTILE_TOUCH(this, toucher);
24 
25  W_Seeker_Missile_Explode(this, toucher);
26 }
27 
28 void W_Seeker_Missile_Think(entity this)
29 {
30  entity e;
31  vector desireddir, olddir, newdir, eorg;
32  float turnrate;
33  float dist;
34  float spd;
35 
36  if(time > this.cnt)
37  {
39  W_Seeker_Missile_Explode(this, NULL);
40  }
41 
42  spd = vlen(this.velocity);
43  spd = bound(
44  spd - WEP_CVAR(seeker, missile_decel) * frametime,
45  WEP_CVAR(seeker, missile_speed_max),
46  spd + WEP_CVAR(seeker, missile_accel) * frametime
47  );
48 
49  if(this.enemy != NULL)
50  if(this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy))
51  this.enemy = NULL;
52 
53  if(this.enemy != NULL)
54  {
55  e = this.enemy;
56  eorg = 0.5 * (e.absmin + e.absmax);
57  turnrate = WEP_CVAR(seeker, missile_turnrate); // how fast to turn
58  desireddir = normalize(eorg - this.origin);
59  olddir = normalize(this.velocity); // get my current direction
60  dist = vlen(eorg - this.origin);
61 
62  // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
63  if(WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist)))
64  {
65  // Is it a better idea (shorter distance) to trace to the target itself?
66  if( vdist(this.origin + olddir * this.wait, <, dist))
67  traceline(this.origin, this.origin + olddir * this.wait, false, this);
68  else
69  traceline(this.origin, eorg, false, this);
70 
71  // Setup adaptive tracelength
72  this.wait = bound(WEP_CVAR(seeker, missile_smart_trace_min), vlen(this.origin - trace_endpos), this.wait = WEP_CVAR(seeker, missile_smart_trace_max));
73 
74  // Calc how important it is that we turn and add this to the desierd (enemy) dir.
75  desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
76  }
77 
78  newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
79  this.velocity = newdir * spd; // make me fly in the new direction at my flight speed
80  }
81  else
82  dist = 0;
83 
84  // Proxy
85  if(WEP_CVAR(seeker, missile_proxy))
86  {
87  if(dist <= WEP_CVAR(seeker, missile_proxy_maxrange))
88  {
89  if(this.cvar_cl_autoswitch == 0)
90  {
91  this.cvar_cl_autoswitch = time + WEP_CVAR(seeker, missile_proxy_delay);
92  }
93  else
94  {
95  if(this.cvar_cl_autoswitch <= time)
96  {
97  W_Seeker_Missile_Explode(this, NULL);
98  this.cvar_cl_autoswitch = 0;
99  }
100  }
101  }
102  else
103  {
104  if(this.cvar_cl_autoswitch != 0)
105  this.cvar_cl_autoswitch = 0;
106  }
107  }
109 
110  if(IS_DEAD(this.enemy))
111  {
112  this.enemy = NULL;
113  this.cnt = time + 1 + (random() * 4);
114  this.nextthink = this.cnt;
115  return;
116  }
117 
118  //this.angles = vectoangles(this.velocity); // turn model in the new flight direction
119  this.nextthink = time;// + 0.05; // csqc projectiles
120  UpdateCSQCProjectile(this);
121 }
122 
123 
124 
125 void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
126 {
127  if(GetResource(this, RES_HEALTH) <= 0)
128  return;
129 
130  if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
131  return; // g_projectiles_damage says to halt
132 
133  if(this.realowner == attacker)
134  TakeResource(this, RES_HEALTH, (damage * 0.25));
135  else
136  TakeResource(this, RES_HEALTH, damage);
137 
138  if(GetResource(this, RES_HEALTH) <= 0)
139  W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode_think);
140 }
141 
142 /*
143 void W_Seeker_Missile_Animate(entity this)
144 {
145  this.frame = this.frame +1;
146  this.nextthink = time + 0.05;
147 
148  if(this.enemy != NULL)
149  if(this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy))
150  this.enemy = NULL;
151 
152  if(this.frame == 5)
153  {
154  this.think = W_Seeker_Missile_Think;
155  this.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
156 
157  if(autocvar_g_balance_seeker_missile_proxy)
158  this.move_movetype = MOVETYPE_BOUNCEMISSILE;
159  else
160  this.move_movetype = MOVETYPE_FLYMISSILE;
161  }
162 
163  UpdateCSQCProjectile(this);
164 }
165 */
166 
167 void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, vector f_diff, entity m_target)
168 {
169  W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, missile_ammo), weaponentity);
170 
171  makevectors(actor.v_angle);
172  W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0, ((m_target != NULL) ? thiswep.m_id | HITTYPE_SECONDARY : thiswep.m_id));
173  w_shotorg += f_diff;
174  W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
175 
176  //actor.detornator = false;
177 
178  entity missile = new(seeker_missile);
179  missile.owner = missile.realowner = actor;
180  missile.bot_dodge = true;
181  missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage);
182 
183  setthink(missile, W_Seeker_Missile_Think);
184  settouch(missile, W_Seeker_Missile_Touch);
185  missile.event_damage = W_Seeker_Missile_Damage;
186  missile.nextthink = time;// + 0.2;// + cvar("g_balance_seeker_missile_activate_delay");
187  missile.cnt = time + WEP_CVAR(seeker, missile_lifetime);
188  missile.enemy = m_target;
189  missile.solid = SOLID_BBOX;
190  missile.scale = 2;
191  missile.takedamage = DAMAGE_YES;
192  missile.weaponentity_fld = weaponentity;
193  SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(seeker, missile_health));
194  missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale);
195  missile.damagedbycontents = true;
196  IL_PUSH(g_damagedbycontents, missile);
197  //missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
198 
199  if(missile.enemy != NULL)
200  missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
201  else
202  missile.projectiledeathtype = thiswep.m_id;
203 
204 
205  setorigin(missile, w_shotorg);
206  setsize(missile, '-4 -4 -4', '4 4 4');
208  missile.flags = FL_PROJECTILE;
209  IL_PUSH(g_projectiles, missile);
210  IL_PUSH(g_bot_dodge, missile);
211  missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
212 
213  W_SetupProjVelocity_UP_PRE(missile, seeker, missile_);
214 
215  missile.angles = vectoangles(missile.velocity);
216 
217  CSQCProjectile(missile, false, PROJECTILE_SEEKER, true);
218 
219  MUTATOR_CALLHOOK(EditProjectile, actor, missile);
220 }
221 
222 // ============================
223 // Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
224 // ============================
225 void W_Seeker_Flac_Explode(entity this, entity directhitentity)
226 {
227  this.event_damage = func_null;
228 
229  RadiusDamage(this, this.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), NULL, NULL, WEP_CVAR(seeker, flac_force), this.projectiledeathtype, this.weaponentity_fld, directhitentity);
230 
231  delete(this);
232 }
233 
234 void W_Seeker_Flac_Touch(entity this, entity toucher)
235 {
236  W_Seeker_Flac_Explode(this, toucher);
237 }
238 
239 void W_Seeker_Flac_Explode_use(entity this, entity actor, entity trigger)
240 {
241  W_Seeker_Flac_Explode(this, trigger);
242 }
243 
244 void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
245 {
246  entity missile;
247  vector f_diff;
248  float c;
249 
250  W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, flac_ammo), weaponentity);
251 
252  c = actor.(weaponentity).bulletcounter % 4;
253  switch(c)
254  {
255  case 0:
256  f_diff = '-1.25 -3.75 0';
257  break;
258  case 1:
259  f_diff = '+1.25 -3.75 0';
260  break;
261  case 2:
262  f_diff = '-1.25 +3.75 0';
263  break;
264  case 3:
265  default:
266  f_diff = '+1.25 +3.75 0';
267  break;
268  }
269  W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage), thiswep.m_id | HITTYPE_SECONDARY);
270  w_shotorg += f_diff;
271 
272  // uses hagar effects!
273  W_MuzzleFlash(WEP_HAGAR, actor, weaponentity, w_shotorg, w_shotdir);
274 
275  missile = new(missile);
276  missile.owner = missile.realowner = actor;
277  missile.bot_dodge = true;
278  missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage);
279  settouch(missile, W_Seeker_Flac_Touch);
280  missile.use = W_Seeker_Flac_Explode_use;
282  missile.nextthink = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand);
283  missile.solid = SOLID_BBOX;
284  set_movetype(missile, MOVETYPE_FLY);
285  missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
286  missile.weaponentity_fld = weaponentity;
287  missile.flags = FL_PROJECTILE;
288  IL_PUSH(g_projectiles, missile);
289  IL_PUSH(g_bot_dodge, missile);
290  missile.missile_flags = MIF_SPLASH;
291 
292  // csqc projectiles
293  //missile.angles = vectoangles(missile.velocity);
294  //missile.scale = 0.4; // BUG: the model is too big
295 
296  setorigin(missile, w_shotorg);
297  setsize(missile, '-2 -2 -2', '2 2 2');
298 
299  W_SetupProjVelocity_UP_PRE(missile, seeker, flac_);
300  CSQCProjectile(missile, true, PROJECTILE_FLAC, true);
301 
302  MUTATOR_CALLHOOK(EditProjectile, actor, missile);
303 }
304 
305 // ============================
306 // Begin: Tag and rocket controllers
307 // ============================
308 entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarget)
309 {
310  IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == isowner,
311  {
312  if(it.tag_target == istarget && it.weaponentity_fld == weaponentity)
313  return it;
314  });
315 
316  return NULL;
317 }
318 
319 void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity)
320 {
321  entity closest_target = NULL;
322 
323  IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == actor,
324  {
325  if(closest_target)
326  {
327  if(vlen2(actor.origin - it.tag_target.origin) < vlen2(actor.origin - closest_target.origin))
328  closest_target = it.tag_target;
329  }
330  else
331  closest_target = it.tag_target;
332  });
333 
334  if(closest_target)
335  {
336  traceline(actor.origin + actor.view_ofs, closest_target.origin, MOVE_NOMONSTERS, actor);
337  if(!closest_target || (trace_fraction < 1 && trace_ent != closest_target))
338  closest_target = NULL;
339  }
340 
341  W_Seeker_Fire_Missile(thiswep, actor, weaponentity, '0 0 0', closest_target);
342 }
343 
344 void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
345 {
346  this.cnt = this.cnt - 1;
347 
348  Weapon thiswep = WEP_SEEKER;
349  .entity weaponentity = this.weaponentity_fld;
350  if((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResource(this.realowner, thiswep.ammo_type) < WEP_CVAR(seeker, missile_ammo)) || (this.cnt <= -1) || (IS_DEAD(this.realowner)) || (this.realowner.(weaponentity).m_switchweapon != thiswep))
351  {
352  delete(this);
353  return;
354  }
355 
356  this.nextthink = time + WEP_CVAR(seeker, missile_delay) * W_WeaponRateFactor(this.realowner);
357 
358  entity own = this.realowner;
359 
360  entity oldenemy = own.enemy;
361  own.enemy = this.enemy;
362 
363  switch(own.cnt % 4)
364  {
365  case 0:
366  W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO
367  break;
368  case 1:
369  W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO
370  break;
371  case 2:
372  W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO
373  break;
374  case 3:
375  default:
376  W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO
377  break;
378  }
379 
380  own.enemy = oldenemy;
381 }
382 
383 void W_Seeker_Tracker_Think(entity this)
384 {
385  .entity weaponentity = this.weaponentity_fld;
386  // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
387  if((IS_DEAD(this.realowner)) || (IS_DEAD(this.tag_target)) || (this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER)
388  || (time > this.tag_time + WEP_CVAR(seeker, tag_tracker_lifetime)))
389  {
390  if(this)
391  {
392  WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
393  delete(this);
394  }
395  return;
396  }
397 
398  // Update the think method information
399  this.nextthink = time;
400 }
401 
402 // ============================
403 // Begin: Tag projectile
404 // ============================
405 void W_Seeker_Tag_Explode(entity this)
406 {
407  //if(other==this.realowner)
408  // return;
409  Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
410 
411  delete(this);
412 }
413 
414 void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
415 {
416  if(GetResource(this, RES_HEALTH) <= 0)
417  return;
418  TakeResource(this, RES_HEALTH, damage);
419  if(GetResource(this, RES_HEALTH) <= 0)
420  W_Seeker_Tag_Explode(this);
421 }
422 
423 void W_Seeker_Tag_Touch(entity this, entity toucher)
424 {
425  vector dir;
426  vector org2;
427  entity e;
428 
429  PROJECTILE_TOUCH(this, toucher);
430 
431  dir = normalize(this.realowner.origin - this.origin);
432  org2 = findbetterlocation(this.origin, 8);
433 
434  te_knightspike(org2);
435 
436  this.event_damage = func_null;
437  Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
438 
439  if(toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
440  {
441  // check to see if this person is already tagged by me
442  .entity weaponentity = this.weaponentity_fld;
443  entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
444 
445  if(tag != NULL)
446  {
447  if(toucher.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first
448  WaypointSprite_Kill(toucher.wps_tag_tracker);
449 
450  tag.tag_time = time;
451  }
452  else
453  {
454  //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
455  e = new(tag_tracker);
456  e.weaponentity_fld = this.weaponentity_fld;
457  e.cnt = WEP_CVAR(seeker, missile_count);
458  e.owner = this.owner;
459  e.realowner = this.realowner;
460  IL_PUSH(g_seeker_trackers, e);
461 
462  if(WEP_CVAR(seeker, type) == 1)
463  {
464  e.tag_target = toucher;
465  e.tag_time = time;
466  setthink(e, W_Seeker_Tracker_Think);
467  }
468  else
469  {
470  e.enemy = toucher;
471  setthink(e, W_Seeker_Vollycontroller_Think);
472  }
473 
474  e.nextthink = time;
475  }
476 
477  if(WEP_CVAR(seeker, type) == 1)
478  {
479  WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(seeker, tag_tracker_lifetime), 0, toucher, '0 0 64', this.realowner, 0, toucher, wps_tag_tracker, true, RADARICON_TAGGED);
480  WaypointSprite_UpdateRule(toucher.wps_tag_tracker, 0, SPRITERULE_DEFAULT);
481  }
482  }
483 
484  delete(this);
485  return;
486 }
487 
488 void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
489 {
490  W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, tag_ammo), weaponentity);
491 
492  W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count), thiswep.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY);
493 
494  entity missile = new(seeker_tag);
495  missile.weaponentity_fld = weaponentity;
496  missile.owner = missile.realowner = actor;
497  missile.bot_dodge = true;
498  missile.bot_dodgerating = 50;
499  settouch(missile, W_Seeker_Tag_Touch);
500  setthink(missile, SUB_Remove);
501  missile.nextthink = time + WEP_CVAR(seeker, tag_lifetime);
502  set_movetype(missile, MOVETYPE_FLY);
503  missile.solid = SOLID_BBOX;
504 
505  missile.takedamage = DAMAGE_YES;
506  missile.event_damage = W_Seeker_Tag_Damage;
507  SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(seeker, tag_health));
508  missile.damageforcescale = WEP_CVAR(seeker, tag_damageforcescale);
509 
510  setorigin(missile, w_shotorg);
511  setsize(missile, '-2 -2 -2', '2 2 2');
512 
513  missile.flags = FL_PROJECTILE;
514  IL_PUSH(g_projectiles, missile);
515  IL_PUSH(g_bot_dodge, missile);
516  //missile.missile_flags = MIF_..?;
517 
518  set_movetype(missile, MOVETYPE_FLY);
519  W_SetupProjVelocity_PRE(missile, seeker, tag_);
520  missile.angles = vectoangles(missile.velocity);
521 
522  CSQCProjectile(missile, true, PROJECTILE_TAG, false); // has sound
523 
524  MUTATOR_CALLHOOK(EditProjectile, actor, missile);
525 }
526 
527 // ============================
528 // Begin: Genereal weapon functions
529 // ============================
530 
531 METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
532 {
533  if(WEP_CVAR(seeker, type) == 1)
534  {
535  if(W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
536  PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(seeker, missile_speed_max), 0, WEP_CVAR(seeker, missile_lifetime), false);
537  else
538  PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false);
539  }
540  else
541  PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false);
542 }
543 METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
544 {
545  if(autocvar_g_balance_seeker_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) { // forced reload
546  thiswep.wr_reload(thiswep, actor, weaponentity);
547  } else if(fire & 1)
548  {
549  if(WEP_CVAR(seeker, type) == 1)
550  {
551  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, missile_refire)))
552  {
553  W_Seeker_Attack(thiswep, actor, weaponentity);
554  weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
555  }
556  }
557  else
558  {
559  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
560  {
561  W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
562  weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
563  }
564  }
565  }
566 
567  else if(fire & 2)
568  {
569  if(WEP_CVAR(seeker, type) == 1)
570  {
571  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
572  {
573  W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
574  weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
575  }
576  }
577  else
578  {
579  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, flac_refire)))
580  {
581  W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
582  weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
583  }
584  }
585  }
586 }
587 METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
588 {
589  float ammo_amount;
590  if(WEP_CVAR(seeker, type) == 1)
591  {
592  ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, missile_ammo);
593  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, missile_ammo);
594  }
595  else
596  {
597  ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, tag_ammo);
598  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, tag_ammo);
599  }
600  return ammo_amount;
601 }
602 METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
603 {
604  float ammo_amount;
605  if(WEP_CVAR(seeker, type) == 1)
606  {
607  ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, tag_ammo);
608  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, tag_ammo);
609  }
610  else
611  {
612  ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, flac_ammo);
613  ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, flac_ammo);
614  }
615  return ammo_amount;
616 }
617 METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
618 {
619  W_Reload(actor, weaponentity, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND_RELOAD);
620 }
621 METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
622 {
623  return WEAPON_SEEKER_SUICIDE;
624 }
625 METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
626 {
628  return WEAPON_SEEKER_MURDER_TAG;
629  else
630  return WEAPON_SEEKER_MURDER_SPRAY;
631 }
632 
633 #endif
634 #ifdef CSQC
635 
636 METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
637 {
638  vector org2;
639  org2 = w_org + w_backoff * 6;
641  {
643  {
644  if(!w_issilent)
645  sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
646  }
647  else
648  {
649  pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
650  if(!w_issilent)
651  {
652  if(w_random<0.15)
653  sound(actor, CH_SHOTS, SND_TAGEXP1, 1, ATTEN_NORM);
654  else if(w_random<0.7)
655  sound(actor, CH_SHOTS, SND_TAGEXP2, 1, ATTEN_NORM);
656  else
657  sound(actor, CH_SHOTS, SND_TAGEXP3, 1, ATTEN_NORM);
658  }
659  }
660  }
661  else
662  {
663  pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
664  if(!w_issilent)
665  {
666  if(w_random<0.15)
667  sound(actor, CH_SHOTS, SND_SEEKEREXP1, 1, ATTEN_NORM);
668  else if(w_random<0.7)
669  sound(actor, CH_SHOTS, SND_SEEKEREXP2, 1, ATTEN_NORM);
670  else
671  sound(actor, CH_SHOTS, SND_SEEKEREXP3, 1, ATTEN_NORM);
672  }
673  }
674 }
675 
676 #endif
const int HITTYPE_SPLASH
automatically set by RadiusDamage
Definition: all.qh:27
const int PROJECTILE_TAG
Definition: projectiles.qh:5
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition: player.qh:148
#define IL_EACH(this, cond, body)
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity)
float weapon_load[REGISTRY_MAX(Weapons)]
Definition: weaponsystem.qh:29
const int HITTYPE_BOUNCE
Definition: all.qh:28
vector w_shotorg
Definition: tracing.qh:18
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
Definition: cl_resources.qc:15
Definition: seeker.qh:3
const int MIF_SPLASH
Definition: common.qh:34
const int SPRITERULE_DEFAULT
#define W_SetupShot_ProjectileSize(ent, wepent, mi, ma, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition: tracing.qh:29
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition: common.qc:49
IntrusiveList g_damagedbycontents
Definition: damage.qh:155
entity() spawn
float DAMAGE_AIM
Definition: progsdefs.qc:284
const int MIF_GUIDED_TAG
Definition: common.qh:41
void w_ready(Weapon thiswep, entity actor,.entity weaponentity, int fire)
#define WEP_CVAR(wepname, name)
Definition: all.qh:299
vector w_shotdir
Definition: tracing.qh:19
entity weaponentity_fld
Definition: weaponsystem.qh:27
origin
Definition: ent_cs.qc:114
#define W_SetupProjVelocity_PRE(ent, wepname, prefix)
Definition: tracing.qh:62
#define METHOD(cname, name, prototype)
Definition: oo.qh:257
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
#define W_SetupProjVelocity_UP_PRE(ent, wepname, prefix)
Definition: tracing.qh:51
entity owner
Definition: main.qh:73
IntrusiveList g_bot_dodge
Definition: api.qh:150
float bulletcounter
Definition: weaponsystem.qh:25
entity trace_ent
Definition: csprogsdefs.qc:40
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
Definition: cl_resources.qc:31
#define PROJECTILE_TOUCH(e, t)
Definition: common.qh:27
RES_HEALTH
Definition: ent_cs.qc:126
entity enemy
Definition: sv_ctf.qh:143
const float MOVE_NOMONSTERS
Definition: csprogsdefs.qc:253
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
Resource ammo_type
M: ammotype : main ammo type.
Definition: weapon.qh:48
bool weapon_prepareattack(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
Definition: common.qc:91
float wait
Definition: subs.qh:39
#define pointparticles
Definition: csprogsdefs.qh:13
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
vector trace_endpos
Definition: csprogsdefs.qc:37
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
const int HITTYPE_SECONDARY
Definition: all.qh:25
#define IS_DEAD(s)
Definition: utils.qh:26
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
const int CH_SHOTS
Definition: sound.qh:14
float w_deathtype
Definition: damage.qh:97
vector(float skel, float bonenum) _skel_get_boneabs_hidden
float MOVETYPE_FLYMISSILE
Definition: progsdefs.qc:255
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
const int PROJECTILE_SEEKER
Definition: projectiles.qh:13
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
const int PROJECTILE_FLAC
Definition: projectiles.qh:14
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
entity realowner
Definition: common.qh:25
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
const float SOLID_BBOX
Definition: csprogsdefs.qc:246
void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
setorigin(ent, v)
#define setthink(e, f)
vector trace_plane_normal
Definition: csprogsdefs.qc:38
void adaptor_think2use_hittype_splash(entity this)
Definition: common.qc:110
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
int dir
Definition: impulse.qc:89
#define makevectors
Definition: post.qh:21
float trace_fraction
Definition: csprogsdefs.qc:36
float W_WeaponRateFactor(entity this)
Definition: weaponsystem.qc:33
void set_movetype(entity this, int mt)
float MOVETYPE_FLY
Definition: progsdefs.qc:251
var void func_null()
float DAMAGE_YES
Definition: progsdefs.qc:283
void W_Reload(entity actor,.entity weaponentity, float sent_ammo_min, Sound sent_sound)
int projectiledeathtype
Definition: common.qh:20