Xonotic
sv_turrets.qc
Go to the documentation of this file.
1 #include "sv_turrets.qh"
2 
3 #ifdef SVQC
5 #include <server/bot/api.qh>
6 #include <server/damage.qh>
9 #include <server/world.qh>
10 
11 // Generic aiming
12 vector turret_aim_generic(entity this)
13 {
14 
15  vector pre_pos, prep;
16  float distance, impact_time = 0, i, mintime;
17 
18  turret_tag_fire_update(this);
19 
20  if(this.aim_flags & TFL_AIM_SIMPLE)
21  return real_origin(this.enemy);
22 
23  mintime = max(this.attack_finished_single[0] - time,0) + sys_frametime;
24 
25  // Baseline
26  pre_pos = real_origin(this.enemy);
27 
28  // Lead?
29  if (this.aim_flags & TFL_AIM_LEAD)
30  {
31  if (this.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime
32  {
33  prep = pre_pos;
34 
35  distance = vlen(prep - this.tur_shotorg);
36  impact_time = distance / this.shot_speed;
37 
38  prep = pre_pos + (this.enemy.velocity * (impact_time + mintime));
39 
40  if(this.aim_flags & TFL_AIM_ZPREDICT)
41  if(!IS_ONGROUND(this.enemy))
42  if(this.enemy.move_movetype == MOVETYPE_WALK || this.enemy.move_movetype == MOVETYPE_TOSS || this.enemy.move_movetype == MOVETYPE_BOUNCE)
43  {
44  float vz;
45  prep_z = pre_pos_z;
46  vz = this.enemy.velocity_z;
47  for(i = 0; i < impact_time; i += sys_frametime)
48  {
49  vz = vz - (autocvar_sv_gravity * sys_frametime);
50  prep_z = prep_z + vz * sys_frametime;
51  }
52  }
53  pre_pos = prep;
54  }
55  else
56  pre_pos = pre_pos + this.enemy.velocity * mintime;
57  }
58 
59  if(this.aim_flags & TFL_AIM_SPLASH)
60  {
61  //tracebox(pre_pos + '0 0 32',this.enemy.mins,this.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,this.enemy);
62  traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,this.enemy);
63  if(trace_fraction != 1.0)
64  pre_pos = trace_endpos;
65  }
66 
67  return pre_pos;
68 }
69 
70 float turret_targetscore_support(entity _turret,entity _target)
71 {
72  float score; // Total score
73  float s_score = 0, d_score;
74 
75  if (_turret.enemy == _target) s_score = 1;
76 
77  d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist);
78 
79  score = (d_score * _turret.target_select_rangebias) +
80  (s_score * _turret.target_select_samebias);
81 
82  return score;
83 }
84 
85 /*
86 * Generic bias aware score system.
87 */
88 float turret_targetscore_generic(entity _turret, entity _target)
89 {
90  float d_dist; // Defendmode Distance
91  float score; // Total score
92  float d_score; // Distance score
93  float a_score; // Angular score
94  float m_score = 0; // missile score
95  float p_score = 0; // player score
96  float ikr; // ideal kill range
97 
98  if (_turret.tur_defend)
99  {
100  d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin);
101  ikr = vlen(_turret.origin - _turret.tur_defend.origin);
102  d_score = 1 - d_dist / _turret.target_range;
103  }
104  else
105  {
106  // Make a normlized value base on the targets distance from our optimal killzone
107  ikr = _turret.target_range_optimal;
108  d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist);
109  }
110 
111  a_score = 1 - tvt_thadf / _turret.aim_maxrot;
112 
113  if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE))
114  m_score = 1;
115 
116  if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target))
117  p_score = 1;
118 
119  d_score = max(d_score, 0);
120  a_score = max(a_score, 0);
121  m_score = max(m_score, 0);
122  p_score = max(p_score, 0);
123 
124  score = (d_score * _turret.target_select_rangebias) +
125  (a_score * _turret.target_select_anglebias) +
126  (m_score * _turret.target_select_missilebias) +
127  (p_score * _turret.target_select_playerbias);
128 
129  if(vdist((_turret.tur_shotorg - real_origin(_target)), >, _turret.target_range))
130  {
131  //dprint("Wtf?\n");
132  score *= 0.001;
133  }
134 
135 #ifdef TURRET_DEBUG
136  string sd,sa,sm,sp,ss;
137  string sdt,sat,smt,spt;
138 
139  sd = ftos(d_score);
140  d_score *= _turret.target_select_rangebias;
141  sdt = ftos(d_score);
142 
143  //sv = ftos(v_score);
144  //v_score *= _turret.target_select_samebias;
145  //svt = ftos(v_score);
146 
147  sa = ftos(a_score);
148  a_score *= _turret.target_select_anglebias;
149  sat = ftos(a_score);
150 
151  sm = ftos(m_score);
152  m_score *= _turret.target_select_missilebias;
153  smt = ftos(m_score);
154 
155  sp = ftos(p_score);
156  p_score *= _turret.target_select_playerbias;
157  spt = ftos(p_score);
158 
159 
160  ss = ftos(score);
161  bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _target.netname," \]\n");
162  bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n");
163  bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n");
164  bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n");
165  bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n");
166  bprint("^3Total (w/bias):\[^1",ss,"\]\n");
167 
168 #endif
169 
170  return score;
171 }
172 
173 // Generic damage handling
174 void turret_hide(entity this)
175 {
176  this.effects |= EF_NODRAW;
177  this.nextthink = time + this.respawntime - 0.2;
178  setthink(this, turret_respawn);
179 }
180 
181 void turret_die(entity this)
182 {
183  this.deadflag = DEAD_DEAD;
184  this.tur_head.deadflag = this.deadflag;
185 
186 // Unsolidify and hide real parts
187  this.solid = SOLID_NOT;
188  this.tur_head.solid = this.solid;
189 
190  this.event_damage = func_null;
191  this.event_heal = func_null;
192  this.takedamage = DAMAGE_NO;
193 
195 
196 // Go boom
197  //RadiusDamage (this,this, min(this.ammo,50),min(this.ammo,50) * 0.25,250,NULL,min(this.ammo,50)*5,DEATH_TURRET,NULL);
198 
199  Turret tur = get_turretinfo(this.m_id);
201  {
202  // do a simple explosion effect here, since CSQC can't do it on a to-be-removed entity
203  sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
204  Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
205 
206  tur.tr_death(tur, this);
207 
208  delete(this.tur_head);
209  delete(this);
210  }
211  else
212  {
213  // Setup respawn
214  this.SendFlags |= TNSF_STATUS;
215  this.nextthink = time + 0.2;
216  setthink(this, turret_hide);
217 
218  tur.tr_death(tur, this);
219  }
220 }
221 
222 void turret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce)
223 {
224  // Enough already!
225  if(this.deadflag == DEAD_DEAD)
226  return;
227 
228  // Inactive turrets take no damage. (hm..)
229  if(!this.active)
230  return;
231 
232  if(SAME_TEAM(this, attacker))
233  {
235  damage = damage * autocvar_g_friendlyfire;
236  else
237  return;
238  }
239 
240  TakeResource(this, RES_HEALTH, damage);
241 
242  // thorw head slightly off aim when hit?
243  if (this.damage_flags & TFL_DMG_HEADSHAKE)
244  {
245  this.tur_head.angles_x = this.tur_head.angles_x + (-0.5 + random()) * damage;
246  this.tur_head.angles_y = this.tur_head.angles_y + (-0.5 + random()) * damage;
247 
248  this.SendFlags |= TNSF_ANG;
249  }
250 
251  if (this.turret_flags & TUR_FLAG_MOVE)
252  this.velocity = this.velocity + vforce;
253 
254  if (GetResource(this, RES_HEALTH) <= 0)
255  {
256  this.event_damage = func_null;
257  this.tur_head.event_damage = func_null;
258  this.event_heal = func_null;
259  this.tur_head.event_heal = func_null;
260  this.takedamage = DAMAGE_NO;
261  this.nextthink = time;
262  setthink(this, turret_die);
263  }
264 
265  this.SendFlags |= TNSF_STATUS;
266 }
267 
268 bool turret_heal(entity targ, entity inflictor, float amount, float limit)
269 {
270  float true_limit = ((limit != RES_LIMIT_NONE) ? limit : targ.max_health);
271  if(GetResource(targ, RES_HEALTH) <= 0 || GetResource(targ, RES_HEALTH) >= true_limit)
272  return false;
273 
274  GiveResourceWithLimit(targ, RES_HEALTH, amount, true_limit);
275  targ.SendFlags |= TNSF_STATUS;
276  return true;
277 }
278 
279 void turret_think(entity this);
280 void turret_respawn(entity this)
281 {
282  // Make sure all parts belong to the same team since
283  // this function doubles as "teamchange" function.
284  this.tur_head.team = this.team;
285  this.effects &= ~EF_NODRAW;
286  this.deadflag = DEAD_NO;
287  this.effects = EF_LOWPRECISION;
288  this.solid = SOLID_BBOX;
289  this.takedamage = DAMAGE_AIM;
290  this.event_damage = turret_damage;
291  this.event_heal = turret_heal;
292  this.avelocity = '0 0 0';
293  this.tur_head.avelocity = this.avelocity;
294  this.tur_head.angles = this.idle_aim;
296  this.enemy = NULL;
297  this.volly_counter = this.shot_volly;
298  this.ammo = this.ammo_max;
299 
300  this.nextthink = time + this.ticrate;
301  setthink(this, turret_think);
302 
303  this.SendFlags = TNSF_FULL_UPDATE;
304 
305  Turret tur = get_turretinfo(this.m_id);
306  tur.tr_setup(tur, this);
307 }
308 
309 
310 // Main functions
311 .float clientframe;
312 void turrets_setframe(entity this, float _frame, float client_only)
313 {
314  if((client_only ? this.clientframe : this.frame ) != _frame)
315  {
316  this.SendFlags |= TNSF_ANIM;
317  this.anim_start_time = time;
318  }
319 
320  if(client_only)
321  this.clientframe = _frame;
322  else
323  this.frame = _frame;
324 
325 }
326 
327 bool turret_send(entity this, entity to, float sf)
328 {
329  WriteHeader(MSG_ENTITY, ENT_CLIENT_TURRET);
330  WriteByte(MSG_ENTITY, sf);
331  if(sf & TNSF_SETUP)
332  {
333  WriteByte(MSG_ENTITY, this.m_id);
334 
335  WriteVector(MSG_ENTITY, this.origin);
336 
337  WriteAngleVector2D(MSG_ENTITY, this.angles);
338  }
339 
340  if(sf & TNSF_ANG)
341  {
342  WriteShort(MSG_ENTITY, rint(this.tur_head.angles_x));
343  WriteShort(MSG_ENTITY, rint(this.tur_head.angles_y));
344  }
345 
346  if(sf & TNSF_AVEL)
347  {
348  WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_x));
349  WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_y));
350  }
351 
352  if(sf & TNSF_MOVE)
353  {
354  WriteVector(MSG_ENTITY, this.origin);
355 
356  WriteVector(MSG_ENTITY, this.velocity);
357 
358  WriteShort(MSG_ENTITY, rint(this.angles_y));
359  }
360 
361  if(sf & TNSF_ANIM)
362  {
363  WriteCoord(MSG_ENTITY, this.anim_start_time);
364  WriteByte(MSG_ENTITY, this.frame);
365  }
366 
367  if(sf & TNSF_STATUS)
368  {
369  WriteByte(MSG_ENTITY, this.team);
370 
371  if(GetResource(this, RES_HEALTH) <= 0)
372  WriteByte(MSG_ENTITY, 0);
373  else
374  WriteByte(MSG_ENTITY, ceil((GetResource(this, RES_HEALTH) / this.max_health) * 255));
375  }
376 
377  return true;
378 }
379 
380 void load_unit_settings(entity ent, bool is_reload)
381 {
382  if (ent == NULL)
383  return;
384 
385  if(!ent.turret_scale_damage) ent.turret_scale_damage = 1;
386  if(!ent.turret_scale_range) ent.turret_scale_range = 1;
387  if(!ent.turret_scale_refire) ent.turret_scale_refire = 1;
388  if(!ent.turret_scale_ammo) ent.turret_scale_ammo = 1;
389  if(!ent.turret_scale_aim) ent.turret_scale_aim = 1;
390  if(!ent.turret_scale_health) ent.turret_scale_health = 1;
391  if(!ent.turret_scale_respawn) ent.turret_scale_respawn = 1;
392 
393  if (is_reload)
394  {
395  ent.enemy = NULL;
396  ent.tur_head.avelocity = '0 0 0';
397 
398  ent.tur_head.angles = '0 0 0';
399  }
400 
401  string unitname = ent.netname;
402  #define X(class, prefix, fld, type) ent.fld = cvar(strcat("g_turrets_unit_", prefix, "_", #fld));
403  TR_PROPS_COMMON(X, , unitname)
404  #undef X
405 
406  ent.ammo_max *= ent.turret_scale_ammo;
407  ent.ammo_recharge *= ent.turret_scale_ammo;
408  ent.aim_speed *= ent.turret_scale_aim;
409  SetResourceExplicit(ent, RES_HEALTH, GetResource(ent, RES_HEALTH) * ent.turret_scale_health);
410  ent.respawntime *= ent.turret_scale_respawn;
411  ent.shot_dmg *= ent.turret_scale_damage;
412  ent.shot_refire *= ent.turret_scale_refire;
413  ent.shot_radius *= ent.turret_scale_damage;
414  ent.shot_force *= ent.turret_scale_damage;
415  ent.shot_volly_refire *= ent.turret_scale_refire;
416  ent.target_range *= ent.turret_scale_range;
417  ent.target_range_min *= ent.turret_scale_range;
418  ent.target_range_optimal *= ent.turret_scale_range;
419 
420  if(is_reload) {
421  Turret tur = get_turretinfo(ent.m_id);
422  tur.tr_setup(tur, ent);
423  }
424 }
425 
427 {
428 
429  this.takedamage = DAMAGE_NO;
430  this.event_damage = func_null;
431 #ifdef TURRET_DEBUG
432  float d;
433  d = RadiusDamage (this, this.owner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.projectiledeathtype, DMG_NOWEP, NULL);
434  this.owner.tur_debug_dmg_t_h = this.owner.tur_debug_dmg_t_h + d;
435  this.owner.tur_debug_dmg_t_f = this.owner.tur_debug_dmg_t_f + this.owner.shot_dmg;
436 #else
437  RadiusDamage (this, this.realowner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.projectiledeathtype, DMG_NOWEP, NULL);
438 #endif
439  delete(this);
440 }
441 
442 void turret_projectile_touch(entity this, entity toucher)
443 {
444  PROJECTILE_TOUCH(this, toucher);
446 }
447 
448 void turret_projectile_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce)
449 {
450  this.velocity += vforce;
451  TakeResource(this, RES_HEALTH, damage);
452  //this.realowner = attacker; // Dont change realowner, it does not make much sense for turrets
453  if(GetResource(this, RES_HEALTH) <= 0)
455 }
456 
457 entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim)
458 {
459  TC(Sound, _snd);
460  entity proj;
461 
462  sound (actor, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM);
463  proj = spawn ();
464  setorigin(proj, actor.tur_shotorg);
465  setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size);
466  proj.owner = actor;
467  proj.realowner = actor;
468  proj.bot_dodge = true;
469  proj.bot_dodgerating = actor.shot_dmg;
471  settouch(proj, turret_projectile_touch);
472  proj.nextthink = time + 9;
474  proj.velocity = normalize(actor.tur_shotdir_updated + randomvec() * actor.shot_spread) * actor.shot_speed;
475  proj.flags = FL_PROJECTILE;
476  IL_PUSH(g_projectiles, proj);
477  IL_PUSH(g_bot_dodge, proj);
478  proj.enemy = actor.enemy;
479  proj.projectiledeathtype = _death;
481  if(_health)
482  {
483  SetResourceExplicit(proj, RES_HEALTH, _health);
484  proj.takedamage = DAMAGE_YES;
485  proj.event_damage = turret_projectile_damage;
486  }
487  else
488  proj.flags |= FL_NOTARGET;
489 
490  CSQCProjectile(proj, _cli_anim, _proj_type, _cull);
491 
492  return proj;
493 }
494 
499 void turret_do_updates(entity t_turret)
500 {
501  vector enemy_pos = real_origin(t_turret.enemy);
502 
503  turret_tag_fire_update(t_turret);
504 
505  t_turret.tur_shotdir_updated = v_forward;
506  t_turret.tur_dist_enemy = vlen(t_turret.tur_shotorg - enemy_pos);
507  t_turret.tur_dist_aimpos = vlen(t_turret.tur_shotorg - t_turret.tur_aimpos);
508 
509  /*if((t_turret.firecheck_flags & TFL_FIRECHECK_VERIFIED) && (t_turret.enemy))
510  {
511  oldpos = t_turret.enemy.origin;
512  setorigin(t_turret.enemy, t_turret.tur_aimpos);
513  tracebox(t_turret.tur_shotorg, '-1 -1 -1', '1 1 1', t_turret.tur_shotorg + (t_turret.tur_shotdir_updated * t_turret.tur_dist_aimpos), MOVE_NORMAL,t_turret);
514  setorigin(t_turret.enemy, oldpos);
515 
516  if(trace_ent == t_turret.enemy)
517  t_turret.tur_dist_impact_to_aimpos = 0;
518  else
519  t_turret.tur_dist_impact_to_aimpos = vlen(trace_endpos - t_turret.tur_aimpos);
520  }
521  else*/
522  tracebox(t_turret.tur_shotorg, '-1 -1 -1','1 1 1', t_turret.tur_shotorg + (t_turret.tur_shotdir_updated * t_turret.tur_dist_aimpos), MOVE_NORMAL,t_turret);
523 
524  t_turret.tur_dist_impact_to_aimpos = vlen(trace_endpos - t_turret.tur_aimpos) - (vlen(t_turret.enemy.maxs - t_turret.enemy.mins) * 0.5);
525  t_turret.tur_impactent = trace_ent;
526  t_turret.tur_impacttime = vlen(t_turret.tur_shotorg - trace_endpos) / t_turret.shot_speed;
527 }
528 
533 .float turret_framecounter;
534 void turret_track(entity this)
535 {
536  vector target_angle; // This is where we want to aim
537  vector move_angle; // This is where we can aim
538  float f_tmp;
539  vector v1, v2;
540  v1 = this.tur_head.angles;
541  v2 = this.tur_head.avelocity;
542 
543  if (this.track_flags == TFL_TRACK_NO)
544  return;
545 
546  if(!this.active)
547  target_angle = this.idle_aim - ('1 0 0' * this.aim_maxpitch);
548  else if (this.enemy == NULL)
549  {
550  if(time > this.lip)
551  target_angle = this.idle_aim + this.angles;
552  else
553  target_angle = vectoangles(normalize(this.tur_aimpos - this.tur_shotorg));
554  }
555  else
556  {
557  target_angle = vectoangles(normalize(this.tur_aimpos - this.tur_shotorg));
558  }
559 
560  this.tur_head.angles_x = anglemods(this.tur_head.angles_x);
561  this.tur_head.angles_y = anglemods(this.tur_head.angles_y);
562 
563  // Find the diffrence between where we currently aim and where we want to aim
564  //move_angle = target_angle - (this.angles + this.tur_head.angles);
565  //move_angle = shortangle_vxy(move_angle,(this.angles + this.tur_head.angles));
566 
568  move_angle = shortangle_vxy(move_angle, this.tur_head.angles);
569 
570  switch(this.track_type)
571  {
573  f_tmp = this.aim_speed * this.ticrate; // dgr/sec -> dgr/tic
574  if (this.track_flags & TFL_TRACK_PITCH)
575  {
576  this.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp);
577  if(this.tur_head.angles_x > this.aim_maxpitch)
578  this.tur_head.angles_x = this.aim_maxpitch;
579 
580  if(this.tur_head.angles_x < -this.aim_maxpitch)
581  this.tur_head.angles_x = this.aim_maxpitch;
582  }
583 
584  if (this.track_flags & TFL_TRACK_ROTATE)
585  {
586  this.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp);
587  if(this.tur_head.angles_y > this.aim_maxrot)
588  this.tur_head.angles_y = this.aim_maxrot;
589 
590  if(this.tur_head.angles_y < -this.aim_maxrot)
591  this.tur_head.angles_y = this.aim_maxrot;
592  }
593 
594  // CSQC
595  this.SendFlags |= TNSF_ANG;
596 
597  return;
598 
600  f_tmp = this.aim_speed * this.ticrate; // dgr/sec -> dgr/tic
601  move_angle_x = bound(-this.aim_speed, move_angle_x * this.track_accel_pitch * f_tmp, this.aim_speed);
602  move_angle_y = bound(-this.aim_speed, move_angle_y * this.track_accel_rot * f_tmp, this.aim_speed);
603  move_angle = (this.tur_head.avelocity * this.track_blendrate) + (move_angle * (1 - this.track_blendrate));
604  break;
605 
607 
608  move_angle_y = bound(-this.aim_speed, move_angle_y, this.aim_speed);
609  move_angle_x = bound(-this.aim_speed, move_angle_x, this.aim_speed);
610 
611  break;
612  }
613 
614  // pitch
615  if (this.track_flags & TFL_TRACK_PITCH)
616  {
617  this.tur_head.avelocity_x = move_angle_x;
618  if((this.tur_head.angles_x + this.tur_head.avelocity_x * this.ticrate) > this.aim_maxpitch)
619  {
620  this.tur_head.avelocity_x = 0;
621  this.tur_head.angles_x = this.aim_maxpitch;
622 
623  this.SendFlags |= TNSF_ANG;
624  }
625 
626  if((this.tur_head.angles_x + this.tur_head.avelocity_x * this.ticrate) < -this.aim_maxpitch)
627  {
628  this.tur_head.avelocity_x = 0;
629  this.tur_head.angles_x = -this.aim_maxpitch;
630 
631  this.SendFlags |= TNSF_ANG;
632  }
633  }
634 
635  // rot
636  if (this.track_flags & TFL_TRACK_ROTATE)
637  {
638  this.tur_head.avelocity_y = move_angle_y;
639 
640  if((this.tur_head.angles_y + this.tur_head.avelocity_y * this.ticrate) > this.aim_maxrot)
641  {
642  this.tur_head.avelocity_y = 0;
643  this.tur_head.angles_y = this.aim_maxrot;
644 
645  this.SendFlags |= TNSF_ANG;
646  }
647 
648  if((this.tur_head.angles_y + this.tur_head.avelocity_y * this.ticrate) < -this.aim_maxrot)
649  {
650  this.tur_head.avelocity_y = 0;
651  this.tur_head.angles_y = -this.aim_maxrot;
652 
653  this.SendFlags |= TNSF_ANG;
654  }
655  }
656 
657  this.SendFlags |= TNSF_AVEL;
658 
659  // Force a angle update every 10'th frame
660  this.turret_framecounter += 1;
661  if(this.turret_framecounter >= 10)
662  {
663  this.SendFlags |= TNSF_ANG;
664  this.turret_framecounter = 0;
665  }
666 }
667 
668 /*
669  + TFL_TARGETSELECT_NO
670  + TFL_TARGETSELECT_LOS
671  + TFL_TARGETSELECT_PLAYERS
672  + TFL_TARGETSELECT_MISSILES
673  + TFL_TARGETSELECT_VEHICLES
674  - TFL_TARGETSELECT_TRIGGERTARGET
675  + TFL_TARGETSELECT_ANGLELIMITS
676  + TFL_TARGETSELECT_RANGELIMITS
677  + TFL_TARGETSELECT_TEAMCHECK
678  - TFL_TARGETSELECT_NOBUILTIN
679  + TFL_TARGETSELECT_OWNTEAM
680 */
681 
686 float turret_validate_target(entity e_turret, entity e_target, float validate_flags)
687 {
688  vector v_tmp;
689 
690  //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)
691  // return -0.5;
692 
693  if(!e_target)
694  return -2;
695 
696  if(e_target.owner == e_turret)
697  return -0.5;
698 
699  if(!checkpvs(e_target.origin, e_turret))
700  return -1;
701 
702  if(e_target.alpha != 0 && e_target.alpha <= 0.3)
703  return -1;
704 
705  if(MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags))
706  return M_ARGV(3, float);
707 
708  if (validate_flags & TFL_TARGETSELECT_NO)
709  return -4;
710 
711  // If only this was used more..
712  if (e_target.flags & FL_NOTARGET)
713  return -5;
714 
715  // Cant touch this
716  if (GetResource(e_target, RES_HEALTH) <= 0)
717  return -6;
718  else if (STAT(FROZEN, e_target))
719  return -6;
720 
721  // vehicle
722  if(IS_VEHICLE(e_target))
723  {
724  if ((validate_flags & TFL_TARGETSELECT_VEHICLES) && !e_target.owner)
725  return -7;
726  }
727 
728  // player
729  if (IS_CLIENT(e_target))
730  {
731  if(!(validate_flags & TFL_TARGETSELECT_PLAYERS))
732  return -7;
733 
734  if (IS_DEAD(e_target))
735  return -8;
736  }
737 
738  // enemy turrets
739  if(validate_flags & TFL_TARGETSELECT_NOTURRETS)
740  if(e_target.owner.tur_head == e_target)
741  if(e_target.team != e_turret.team) // Dont break support units.
742  return -9;
743 
744  // Missile
745  if (e_target.flags & FL_PROJECTILE)
746  if(!(validate_flags & TFL_TARGETSELECT_MISSILES))
747  return -10;
748 
749  if (validate_flags & TFL_TARGETSELECT_MISSILESONLY)
750  if(!(e_target.flags & FL_PROJECTILE))
751  return -10.5;
752 
753  // Team check
754  if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)
755  {
756  if (validate_flags & TFL_TARGETSELECT_OWNTEAM)
757  {
758  if (e_target.team != e_turret.team)
759  return -11;
760 
761  if (e_turret.team != e_target.owner.team)
762  return -12;
763 
764  if (e_turret.team != e_target.aiment.team)
765  return -12; // portals
766  }
767  else
768  {
769  if (e_target.team == e_turret.team)
770  return -13;
771 
772  if (e_turret.team == e_target.owner.team)
773  return -14;
774 
775  if (e_turret.team == e_target.aiment.team)
776  return -14; // portals
777  }
778  }
779 
780  // Range limits?
781  tvt_dist = vlen(e_turret.origin - real_origin(e_target));
782  if (validate_flags & TFL_TARGETSELECT_RANGELIMITS)
783  {
784  if (tvt_dist < e_turret.target_range_min)
785  return -15;
786 
787  if (tvt_dist > e_turret.target_range)
788  return -16;
789  }
790 
791  // Can we even aim this thing?
792  tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target.origin);
793  tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles);
795 
796  /*
797  if(validate_flags & TFL_TARGETSELECT_FOV)
798  {
799  if(e_turret.target_select_fov < tvt_thadf)
800  return -21;
801  }
802  */
803 
804  if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)
805  {
806  if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
807  return -17;
808 
809  if (fabs(tvt_tadv_y) > e_turret.aim_maxrot)
810  return -18;
811  }
812 
813  // Line of sight?
814  if (validate_flags & TFL_TARGETSELECT_LOS)
815  {
816  v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
817 
818  traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret);
819 
820  if(vdist(v_tmp - trace_endpos, >, e_turret.aim_firetolerance_dist))
821  return -19;
822  }
823 
824  if (e_target.classname == "grapplinghook")
825  return -20;
826 
827  /*
828  if (e_target.classname == "func_button")
829  return -21;
830  */
831 
832 #ifdef TURRET_DEBUG_TARGETSELECT
833  LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname);
834 #endif
835 
836  return 1;
837 }
838 
840 {
841  entity e; // target looper entity
842  float score; // target looper entity score
843  entity e_enemy; // currently best scoreing target
844  float m_score; // currently best scoreing target's score
845 
846  m_score = 0;
847  if(this.enemy && this.enemy.takedamage && turret_validate_target(this,this.enemy,this.target_validate_flags) > 0)
848  {
849  e_enemy = this.enemy;
850  m_score = this.turret_score_target(this,e_enemy) * this.target_select_samebias;
851  }
852  else
853  e_enemy = this.enemy = NULL;
854 
855  e = findradius(this.origin, this.target_range);
856 
857  // Nothing to aim at?
858  if (!e)
859  return NULL;
860 
861  while (e)
862  {
863  if(e.takedamage)
864  {
865  float f = turret_validate_target(this, e, this.target_select_flags);
866  //dprint("F is: ", ftos(f), "\n");
867  if ( f > 0)
868  {
869  score = this.turret_score_target(this,e);
870  if ((score > m_score) && (score > 0))
871  {
872  e_enemy = e;
873  m_score = score;
874  }
875  }
876  }
877  e = e.chain;
878  }
879 
880  return e_enemy;
881 }
882 
883 
884 /*
885  + = implemented
886  - = not implemented
887 
888  + TFL_FIRECHECK_NO
889  + TFL_FIRECHECK_WORLD
890  + TFL_FIRECHECK_DEAD
891  + TFL_FIRECHECK_DISTANCES
892  - TFL_FIRECHECK_LOS
893  + TFL_FIRECHECK_AIMDIST
894  + TFL_FIRECHECK_REALDIST
895  - TFL_FIRECHECK_ANGLEDIST
896  - TFL_FIRECHECK_TEAMCECK
897  + TFL_FIRECHECK_AFF
898  + TFL_FIRECHECK_AMMO_OWN
899  + TFL_FIRECHECK_AMMO_OTHER
900  + TFL_FIRECHECK_REFIRE
901 */
902 
906 bool turret_firecheck(entity this)
907 {
908  // This one just dont care =)
910  return true;
911 
912  if (this.enemy == NULL)
913  return false;
914 
915  // Ready?
917  if (this.attack_finished_single[0] > time) return false;
918 
919  // Special case: volly fire turret that has to fire a full volly if a shot was fired.
921  if (this.volly_counter != this.shot_volly)
922  if(this.ammo >= this.shot_dmg)
923  return true;
924 
925  // Lack of zombies makes shooting dead things unnecessary :P
927  if (IS_DEAD(this.enemy))
928  return false;
929 
930  // Own ammo?
932  if (this.ammo < this.shot_dmg)
933  return false;
934 
935  // Other's ammo? (support-supply units)
937  if (this.enemy.ammo >= this.enemy.ammo_max)
938  return false;
939 
940  // Target of opertunity?
942  {
943  this.enemy = this.tur_impactent;
944  return true;
945  }
946 
948  {
949  // To close?
950  if (this.tur_dist_aimpos < this.target_range_min)
951  {
953  return true; // Target of opertunity?
954  return false;
955  }
956  }
957 
958  // Try to avoid FF?
960  if (this.tur_impactent.team == this.team)
961  return false;
962 
963  // aim<->predicted impact
965  if (this.tur_dist_impact_to_aimpos > this.aim_firetolerance_dist)
966  return false;
967 
968  // Volly status
969  if (this.shot_volly > 1)
970  if (this.volly_counter == this.shot_volly)
971  if (this.ammo < (this.shot_dmg * this.shot_volly))
972  return false;
973 
974  /*if(this.firecheck_flags & TFL_FIRECHECK_VERIFIED)
975  if(this.tur_impactent != this.enemy)
976  return false;*/
977 
978  return true;
979 }
980 
981 bool turret_checkfire(entity this)
982 {
983  if(MUTATOR_CALLHOOK(Turret_CheckFire, this))
984  return M_ARGV(1, bool);
985 
986  return this.turret_firecheckfunc(this);
987 }
988 
989 void turret_fire(entity this)
990 {
991  if (autocvar_g_turrets_nofire != 0)
992  return;
993 
994  if(MUTATOR_CALLHOOK(TurretFire, this))
995  return;
996 
997  Turret info = get_turretinfo(this.m_id);
998  info.tr_attack(info, this);
999 
1000  this.attack_finished_single[0] = time + this.shot_refire;
1001  this.ammo -= this.shot_dmg;
1002  this.volly_counter = this.volly_counter - 1;
1003 
1004  if (this.volly_counter <= 0)
1005  {
1006  this.volly_counter = this.shot_volly;
1007 
1009  this.enemy = NULL;
1010 
1011  if (this.shot_volly > 1)
1012  this.attack_finished_single[0] = time + this.shot_volly_refire;
1013  }
1014 
1015 #ifdef TURRET_DEBUG
1016  if (this.enemy) paint_target3(this.tur_aimpos, 64, this.tur_debug_rvec, this.tur_impacttime + 0.25);
1017 #endif
1018 }
1019 
1020 void turret_think(entity this)
1021 {
1022  this.nextthink = time + this.ticrate;
1023 
1024  MUTATOR_CALLHOOK(TurretThink, this);
1025 
1026 #ifdef TURRET_DEBUG
1027  if (this.tur_debug_tmr1 < time)
1028  {
1029  if (this.enemy) paint_target (this.enemy,128,this.tur_debug_rvec,0.9);
1030  paint_target(this,256,this.tur_debug_rvec,0.9);
1031  this.tur_debug_tmr1 = time + 1;
1032  }
1033 #endif
1034 
1035  // Handle ammo
1036  if (!(this.spawnflags & TSF_NO_AMMO_REGEN))
1037  if (this.ammo < this.ammo_max)
1038  this.ammo = min(this.ammo + this.ammo_recharge, this.ammo_max);
1039 
1040  // Inactive turrets needs to run the think loop,
1041  // So they can handle animation and wake up if need be.
1042  if(!this.active)
1043  {
1044  turret_track(this);
1045  return;
1046  }
1047 
1048  // This is typicaly used for zaping every target in range
1049  // turret_fusionreactor uses this to recharge friendlys.
1051  {
1052  // Do a this.turret_fire for every valid target.
1053  entity e = findradius(this.origin,this.target_range);
1054  while (e)
1055  {
1056  if(e.takedamage)
1057  {
1059  {
1060  this.enemy = e;
1061 
1062  turret_do_updates(this);
1063 
1064  if (turret_checkfire(this))
1065  turret_fire(this);
1066  }
1067  }
1068 
1069  e = e.chain;
1070  }
1071  this.enemy = NULL;
1072  }
1073  else if(this.shoot_flags & TFL_SHOOT_CUSTOM)
1074  {
1075  // This one is doing something.. oddball. assume its handles what needs to be handled.
1076 
1077  // Predict?
1078  if(!(this.aim_flags & TFL_AIM_NO))
1079  this.tur_aimpos = turret_aim_generic(this);
1080 
1081  // Turn & pitch?
1082  if(!(this.track_flags & TFL_TRACK_NO))
1083  turret_track(this);
1084 
1085  turret_do_updates(this);
1086 
1087  // Fire?
1088  if (turret_checkfire(this))
1089  turret_fire(this);
1090  }
1091  else
1092  {
1093  // Special case for volly always. if it fired once it must compleate the volly.
1094  if(this.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
1095  if(this.volly_counter != this.shot_volly)
1096  {
1097  // Predict or whatnot
1098  if(!(this.aim_flags & TFL_AIM_NO))
1099  this.tur_aimpos = turret_aim_generic(this);
1100 
1101  // Turn & pitch
1102  if(!(this.track_flags & TFL_TRACK_NO))
1103  turret_track(this);
1104 
1105  turret_do_updates(this);
1106 
1107  // Fire!
1108  if (turret_checkfire(this))
1109  turret_fire(this);
1110 
1111  Turret tur = get_turretinfo(this.m_id);
1112  tur.tr_think(tur, this);
1113 
1114  return;
1115  }
1116 
1117  // Check if we have a vailid enemy, and try to find one if we dont.
1118 
1119  // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
1120  float do_target_scan = 0;
1122  do_target_scan = 1;
1123 
1124  // Old target (if any) invalid?
1125  if(this.target_validate_time < time)
1126  if (turret_validate_target(this, this.enemy, this.target_validate_flags) <= 0)
1127  {
1128  this.enemy = NULL;
1129  this.target_validate_time = time + 0.5;
1130  do_target_scan = 1;
1131  }
1132 
1133  // But never more often then g_turrets_targetscan_mindelay!
1135  do_target_scan = 0;
1136 
1137  if(do_target_scan)
1138  {
1139  this.enemy = turret_select_target(this);
1140  this.target_select_time = time;
1141  }
1142 
1143  // No target, just go to idle, do any custom stuff and bail.
1144  if (this.enemy == NULL)
1145  {
1146  // Turn & pitch
1147  if(!(this.track_flags & TFL_TRACK_NO))
1148  turret_track(this);
1149 
1150  Turret tur = get_turretinfo(this.m_id);
1151  tur.tr_think(tur, this);
1152 
1153  // And bail.
1154  return;
1155  }
1156  else
1157  this.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target.
1158 
1159  // Predict?
1160  if(!(this.aim_flags & TFL_AIM_NO))
1161  this.tur_aimpos = turret_aim_generic(this);
1162 
1163  // Turn & pitch?
1164  if(!(this.track_flags & TFL_TRACK_NO))
1165  turret_track(this);
1166 
1167  turret_do_updates(this);
1168 
1169  // Fire?
1170  if (turret_checkfire(this))
1171  turret_fire(this);
1172  }
1173 
1174  Turret tur = get_turretinfo(this.m_id);
1175  tur.tr_think(tur, this);
1176 }
1177 
1178 /*
1179  When .used a turret switch team to activator.team.
1180  If activator is NULL, the turret go inactive.
1181 */
1182 void turret_use(entity this, entity actor, entity trigger)
1183 {
1184  LOG_TRACE("Turret ",this.netname, " used by ", actor.classname);
1185 
1186  this.team = actor.team;
1187 
1188  if(this.team == 0)
1189  this.active = ACTIVE_NOT;
1190  else
1191  this.active = ACTIVE_ACTIVE;
1192 
1193 }
1194 
1195 void turret_link(entity this)
1196 {
1197  Net_LinkEntity(this, true, 0, turret_send);
1198  setthink(this, turret_think);
1199  this.nextthink = time;
1200  this.tur_head.effects = EF_NODRAW;
1201 }
1202 
1203 void turrets_manager_think(entity this)
1204 {
1205  this.nextthink = time + 1;
1206 
1208  {
1209  IL_EACH(g_turrets, true,
1210  {
1211  load_unit_settings(it, true);
1212  Turret tur = get_turretinfo(it.m_id);
1213  tur.tr_think(tur, it);
1214  });
1215  cvar_set("g_turrets_reloadcvars", "0");
1216  }
1217 }
1218 
1219 void turret_initparams(entity tur)
1220 {
1221  #define TRY(x) (x) ? (x)
1222  tur.respawntime = max (-1, (TRY(tur.respawntime) : 60 ));
1223  tur.shot_refire = bound(0.01, (TRY(tur.shot_refire) : 1 ), 9999);
1224  tur.shot_dmg = max (1, (TRY(tur.shot_dmg) : tur.shot_refire * 50 ));
1225  tur.shot_radius = max (1, (TRY(tur.shot_radius) : tur.shot_dmg * 0.5 ));
1226  tur.shot_speed = max (1, (TRY(tur.shot_speed) : 2500 ));
1227  tur.shot_spread = bound(0.0001, (TRY(tur.shot_spread) : 0.0125 ), 500);
1228  tur.shot_force = bound(0.001, (TRY(tur.shot_force) : tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000);
1229  tur.shot_volly = bound(1, (TRY(tur.shot_volly) : 1 ), floor(tur.ammo_max / tur.shot_dmg));
1230  tur.shot_volly_refire = bound(tur.shot_refire, (TRY(tur.shot_volly_refire) : tur.shot_refire * tur.shot_volly ), 60);
1231  tur.target_range = bound(0, (TRY(tur.target_range) : tur.shot_speed * 0.5 ), max_shot_distance);
1232  tur.target_range_min = bound(0, (TRY(tur.target_range_min) : tur.shot_radius * 2 ), max_shot_distance);
1233  tur.target_range_optimal = bound(0, (TRY(tur.target_range_optimal) : tur.target_range * 0.5 ), max_shot_distance);
1234  tur.aim_maxrot = bound(0, (TRY(tur.aim_maxrot) : 90 ), 360);
1235  tur.aim_maxpitch = bound(0, (TRY(tur.aim_maxpitch) : 20 ), 90);
1236  tur.aim_speed = bound(0.1, (TRY(tur.aim_speed) : 36 ), 1000);
1237  tur.aim_firetolerance_dist = bound(0.1, (TRY(tur.aim_firetolerance_dist) : 5 + (tur.shot_radius * 2) ), max_shot_distance);
1238  tur.target_select_rangebias = bound(-10, (TRY(tur.target_select_rangebias) : 1 ), 10);
1239  tur.target_select_samebias = bound(-10, (TRY(tur.target_select_samebias) : 1 ), 10);
1240  tur.target_select_anglebias = bound(-10, (TRY(tur.target_select_anglebias) : 1 ), 10);
1241  tur.target_select_missilebias = bound(-10, (TRY(tur.target_select_missilebias) : 1 ), 10);
1242  tur.target_select_playerbias = bound(-10, (TRY(tur.target_select_playerbias) : 1 ), 10);
1243  tur.ammo_max = max (tur.shot_dmg, (TRY(tur.ammo_max) : tur.shot_dmg * 10 ));
1244  tur.ammo_recharge = max (0, (TRY(tur.ammo_recharge) : tur.shot_dmg * 0.5 ));
1245  #undef TRY
1246 }
1247 
1248 bool turret_closetotarget(entity this, vector targ)
1249 {
1250  vector path_extra_size = '64 64 64';
1251  return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size);
1252 }
1253 
1254 void turret_findtarget(entity this)
1255 {
1256  entity e = find(NULL, classname, "turret_manager");
1257  if(!e)
1258  {
1259  e = new_pure(turret_manager);
1260  setthink(e, turrets_manager_think);
1261  e.nextthink = time + 2;
1262  }
1263 
1264  entity targ = find(NULL, targetname, this.target);
1265  if(targ.classname == "turret_checkpoint")
1266  return; // turrets don't defend checkpoints?
1267 
1268  if (!targ)
1269  {
1270  this.target = "";
1271  LOG_TRACE("Turret has invalid defendpoint!");
1272  }
1273 
1274  this.tur_defend = targ;
1275  this.idle_aim = this.tur_head.angles + angleofs(this.tur_head, targ);
1276 }
1277 
1278 void turret_reset(entity this)
1279 {
1280  turret_respawn(this);
1281 }
1282 
1283 bool turret_initialize(entity this, Turret tur)
1284 {
1285  if(!autocvar_g_turrets)
1286  return false;
1287 
1288  if(tur.m_id == 0)
1289  return false; // invalid turret
1290 
1291  // if tur_head exists, we can assume this turret re-spawned
1292  if(!this.tur_head) {
1293  tur.tr_precache(tur);
1294  IL_PUSH(g_turrets, this);
1295  IL_PUSH(g_bot_targets, this);
1296  }
1297 
1298  if(!(this.spawnflags & TSF_SUSPENDED))
1299  droptofloor(this);
1300 
1301  this.netname = tur.netname;
1302  load_unit_settings(this, 0);
1303 
1304  if(!this.team || !teamplay) { this.team = FLOAT_MAX; }
1305  if(!this.ticrate) { this.ticrate = ((this.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
1306  if(!GetResource(this, RES_HEALTH)) { SetResourceExplicit(this, RES_HEALTH, 1000); }
1307  if(!this.shot_refire) { this.shot_refire = 1; }
1308  if(!this.tur_shotorg) { this.tur_shotorg = '50 0 50'; }
1311  if(!this.aim_flags) { this.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; }
1312  if(!this.track_type) { this.track_type = TFL_TRACKTYPE_STEPMOTOR; }
1313  if(!this.track_flags) { this.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; }
1314  if(!this.ammo_flags) { this.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; }
1315  if(!this.target_select_flags) { this.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; }
1316  if(!this.firecheck_flags) { this.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_LOS
1317  | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_REFIRE; }
1318 
1319  if(this.track_type != TFL_TRACKTYPE_STEPMOTOR)
1320  {
1321  // Fluid / Ineria mode. Looks mutch nicer.
1322  // Can reduce aim preformance alot, needs a bit diffrent aimspeed
1323 
1324  this.aim_speed = bound(0.1, ((!this.aim_speed) ? 180 : this.aim_speed), 1000);
1325 
1326  if(!this.track_accel_pitch) { this.track_accel_pitch = 0.5; }
1327  if(!this.track_accel_rot) { this.track_accel_rot = 0.5; }
1328  if(!this.track_blendrate) { this.track_blendrate = 0.35; }
1329  }
1330 
1331  turret_initparams(this);
1332 
1333  this.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags);
1334 
1335  if(this.turret_flags & TUR_FLAG_SPLASH)
1336  this.aim_flags |= TFL_AIM_SPLASH;
1337 
1338  if(this.turret_flags & TUR_FLAG_MISSILE)
1340 
1341  if(this.turret_flags & TUR_FLAG_PLAYER)
1343 
1344  if(this.spawnflags & TSL_NO_RESPAWN)
1346 
1347  if (this.turret_flags & TUR_FLAG_SUPPORT)
1348  this.turret_score_target = turret_targetscore_support;
1349  else
1350  this.turret_score_target = turret_targetscore_generic;
1351 
1352  ++turret_count;
1353 
1354  _setmodel(this, tur.model);
1355  setsize(this, tur.m_mins, tur.m_maxs);
1356 
1357  this.m_id = tur.m_id;
1358  this.active = ACTIVE_ACTIVE;
1359  this.effects = EF_NODRAW;
1360  this.netname = tur.turret_name;
1361  this.ticrate = bound(sys_frametime, this.ticrate, 60);
1362  this.max_health = GetResource(this, RES_HEALTH);
1364  this.ammo = this.ammo_max;
1365  this.ammo_recharge *= this.ticrate;
1366  this.solid = SOLID_BBOX;
1367  this.takedamage = DAMAGE_AIM;
1369  this.view_ofs = '0 0 0';
1370  this.idle_aim = '0 0 0';
1371  this.turret_firecheckfunc = turret_firecheck;
1372  this.event_damage = turret_damage;
1373  this.event_heal = turret_heal;
1374  this.use = turret_use;
1375  this.bot_attack = true;
1376  this.nextthink = time + 1 + turret_count * sys_frametime;
1377  this.reset = turret_reset;
1378 
1379  this.tur_head = new(turret_head);
1380  _setmodel(this.tur_head, tur.head_model);
1381  setsize(this.tur_head, '0 0 0', '0 0 0');
1382  setorigin(this.tur_head, '0 0 0');
1383  setattachment(this.tur_head, this, "tag_head");
1384 
1385  this.tur_head.netname = this.tur_head.classname;
1386  this.tur_head.team = this.team;
1387  this.tur_head.owner = this;
1388  this.tur_head.takedamage = DAMAGE_NO;
1389  this.tur_head.solid = SOLID_NOT;
1390  set_movetype(this.tur_head, this.move_movetype);
1391 
1392  this.weaponentities[0] = this; // lol
1393 
1394  if(!this.tur_defend && this.target != "")
1395  InitializeEntity(this, turret_findtarget, INITPRIO_FINDTARGET);
1396 
1397 #ifdef TURRET_DEBUG
1398  this.tur_debug_start = this.nextthink;
1399  while(vdist(this.tur_debug_rvec, <, 2))
1400  this.tur_debug_rvec = randomvec() * 4;
1401 
1402  this.tur_debug_rvec_x = fabs(this.tur_debug_rvec_x);
1403  this.tur_debug_rvec_y = fabs(this.tur_debug_rvec_y);
1404  this.tur_debug_rvec_z = fabs(this.tur_debug_rvec_z);
1405 #endif
1406 
1407  turret_link(this);
1408  turret_respawn(this);
1409  turret_tag_fire_update(this);
1410 
1411  tur.tr_setup(tur, this);
1412 
1413  if(MUTATOR_CALLHOOK(TurretSpawn, this))
1414  return false;
1415 
1416  return true;
1417 }
1418 #endif
const float SOLID_NOT
Definition: csprogsdefs.qc:244
#define get_turretinfo(i)
Definition: all.qh:9
entity turret_select_target(entity this)
#define IL_EACH(this, cond, body)
int spawnflags
Definition: turret.qh:19
vector AnglesTransform_ToAngles(vector v)
vector tvt_thadv
Definition: sv_turrets.qh:109
vector tur_shotorg
Definition: sv_turrets.qh:31
float MOVETYPE_WALK
Definition: progsdefs.qc:249
const int TFL_TARGETSELECT_ANGLELIMITS
Definition: turret.qh:68
const int TFL_DMG_HEADSHAKE
Definition: turret.qh:155
float turret_validate_target(entity e_turret, entity e_target, float validate_flags)
void turret_respawn(entity this)
ERASEABLE float anglemods(float v)
Definition: angle.qc:13
float respawntime
Definition: items.qh:36
vector AnglesTransform_FromAngles(vector v)
#define PROJECTILE_MAKETRIGGER(e)
Definition: common.qh:29
const int TFL_TARGETSELECT_NOTURRETS
Definition: turret.qh:73
const int TUR_FLAG_SPLASH
Definition: turret.qh:121
const int TFL_FIRECHECK_AFF
Definition: turret.qh:102
vector view_ofs
Definition: progsdefs.qc:151
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
#define X(team)
const int TNSF_STATUS
Definition: turret.qh:168
vector idle_aim
Definition: sv_turrets.qh:45
const int TSF_SUSPENDED
Definition: turret.qh:159
const int TUR_FLAG_MISSILE
Definition: turret.qh:129
const int TNSF_ANIM
Definition: turret.qh:174
const int TFL_AIM_ZPREDICT
Definition: turret.qh:84
#define IS_CLIENT(v)
Definition: utils.qh:13
float tvt_dist
Definition: sv_turrets.qh:112
ERASEABLE vector angleofs3(vector from, vector ang, vector to)
Definition: angle.qc:73
int team
Definition: main.qh:157
float tur_dist_aimpos
Definition: sv_turrets.qh:36
float MOVETYPE_TOSS
Definition: progsdefs.qc:252
string turret_name
human readable name
Definition: turret.qh:11
int m_id
Definition: turret.qh:6
entity tur_head
Definition: sv_turrets.qh:29
entity() spawn
float DAMAGE_AIM
Definition: progsdefs.qc:284
int target_validate_flags
Definition: turret.qh:62
float bot_attack
Definition: api.qh:38
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
float target_validate_time
Definition: sv_turrets.qh:41
const int TSL_NO_RESPAWN
Definition: turret.qh:163
#define IS_ONGROUND(s)
Definition: movetypes.qh:16
const int TFL_FIRECHECK_AMMO_OWN
Definition: turret.qh:103
#define TR_PROPS_COMMON(P, class, prefix)
Definition: all.qh:13
float tur_impacttime
Definition: sv_turrets.qh:33
bool turret_initialize(entity this, Turret tur)
float checkpvs(vector viewpos, entity viewee)
const int TUR_FLAG_SUPPORT
Definition: turret.qh:130
string netname
Definition: powerups.qc:20
const int TFL_TRACK_ROTATE
Definition: turret.qh:91
#define ammo_flags
Definition: turret.qh:138
const int TUR_FLAG_PLAYER
Definition: turret.qh:128
entity to
Definition: self.qh:96
entity tur_defend
Definition: sv_turrets.qh:30
const int TFL_SHOOT_VOLLYALWAYS
Definition: turret.qh:112
origin
Definition: ent_cs.qc:114
void turrets_setframe(entity this, float _frame, float client_only)
#define droptofloor
Definition: pre.qh:5
string classname
Definition: csprogsdefs.qc:107
int target_select_flags
Definition: turret.qh:61
vector avelocity
Definition: csprogsdefs.qc:105
float MOVETYPE_BOUNCE
Definition: progsdefs.qc:256
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
float effects
Definition: csprogsdefs.qc:111
void turret_do_updates(entity e_turret)
updates aim org, shot org, shot dir and enemy org for selected turret
entity tur_impactent
Definition: sv_turrets.qh:34
void turret_projectile_explode(entity this)
float spawnflags
Definition: progsdefs.qc:191
#define DMG_NOWEP
Definition: damage.qh:126
entity owner
Definition: main.qh:73
const int TNSF_AVEL
Definition: turret.qh:171
float autocvar_g_turrets_targetscan_maxdelay
Definition: sv_turrets.qh:9
const int TFL_AMMO_RECHARGE
Definition: turret.qh:143
float move_movetype
Definition: movetypes.qh:76
float DEAD_DEAD
Definition: progsdefs.qc:276
IntrusiveList g_bot_dodge
Definition: api.qh:150
IntrusiveList g_bot_targets
Definition: api.qh:149
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
const int TFL_TARGETSELECT_PLAYERS
Definition: turret.qh:65
IntrusiveList g_turrets
Definition: sv_turrets.qh:114
const int TFL_TARGETSELECT_LOS
Definition: turret.qh:64
#define PROJECTILE_TOUCH(e, t)
Definition: common.qh:27
float volly_counter
Definition: sv_turrets.qh:38
const int TFL_DMG_DEATH_NORESPAWN
Definition: turret.qh:156
const int TUR_FLAG_MEDPROJ
Definition: turret.qh:126
const int TFL_DMG_YES
Definition: turret.qh:149
vector absmax
Definition: csprogsdefs.qc:92
RES_HEALTH
Definition: ent_cs.qc:126
const float EF_NODRAW
Definition: csprogsdefs.qc:305
float ammo
Definition: sv_turrets.qh:44
bool autocvar_g_turrets_nofire
Definition: sv_turrets.qh:7
float sys_frametime
Definition: common.qh:57
int m_id
Definition: effect.qh:19
entity enemy
Definition: sv_ctf.qh:143
int damage_flags
Definition: turret.qh:147
const int TFL_TARGETSELECT_OWNTEAM
Definition: turret.qh:72
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
const int TFL_FIRECHECK_NO
Definition: turret.qh:106
float autocvar_g_turrets_targetscan_mindelay
Definition: sv_turrets.qh:10
void GiveResourceWithLimit(entity receiver, Resource res_type, float amount, float limit)
Gives an entity some resource but not more than a limit.
const int CH_WEAPON_A
Definition: sound.qh:7
const int TFL_FIRECHECK_AMMO_OTHER
Definition: turret.qh:104
ERASEABLE float boxesoverlap(vector m1, vector m2, vector m3, vector m4)
requires that m2>m1 in all coordinates, and that m4>m3
Definition: vector.qh:73
#define angleofs(from, to)
Definition: angle.qc:90
bool autocvar_g_turrets_reloadcvars
Definition: sv_turrets.qh:8
int firecheck_flags
Definition: turret.qh:94
const int RES_LIMIT_NONE
Definition: resources.qh:46
const int TFL_FIRECHECK_REFIRE
Definition: turret.qh:105
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
Definition: common.qc:91
const int TFL_FIRECHECK_DISTANCES
Definition: turret.qh:96
const int TFL_AIM_LEAD
Definition: turret.qh:82
int shoot_flags
Definition: turret.qh:109
bool turret_firecheck(entity this)
const int ACTIVE_ACTIVE
Definition: defs.qh:37
const int TFL_SHOOT_CUSTOM
Definition: turret.qh:115
#define NULL
Definition: post.qh:17
float max_health
float anim_start_time
Definition: turret.qh:173
vector m_mins
turret hitbox size
Definition: turret.qh:21
const float VOL_BASE
Definition: sound.qh:36
float autocvar_g_turrets_aimidle_delay
Definition: sv_turrets.qh:6
const int TNSF_SETUP
Definition: turret.qh:169
#define TC(T, sym)
Definition: _all.inc:82
const int TFL_SHOOT_HITALLVALID
Definition: turret.qh:113
vector trace_endpos
Definition: csprogsdefs.qc:37
const int TFL_TRACK_PITCH
Definition: turret.qh:90
float takedamage
Definition: progsdefs.qc:147
float autocvar_g_friendlyfire
Definition: damage.qh:25
entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim)
const int TFL_FIRECHECK_AIMDIST
Definition: turret.qh:98
#define SAME_TEAM(a, b)
Definition: teams.qh:239
const int TFL_AIM_NO
Definition: turret.qh:80
Definition: sound.qh:119
float teamplay
Definition: progsdefs.qc:31
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
const float ATTEN_NORM
Definition: sound.qh:30
const float TFL_TRACKTYPE_STEPMOTOR
Definition: sv_turrets.qh:63
float nextthink
Definition: csprogsdefs.qc:121
const int CH_SHOTS
Definition: sound.qh:14
vector tur_aimpos
Definition: sv_turrets.qh:32
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define IS_VEHICLE(v)
Definition: utils.qh:22
const int TSF_NO_AMMO_REGEN
Definition: turret.qh:161
float MOVETYPE_FLYMISSILE
Definition: progsdefs.qc:255
IntrusiveList g_projectiles
Definition: common.qh:46
const int TFL_TARGETSELECT_TEAMCHECK
Definition: turret.qh:70
vector AnglesTransform_LeftDivide(vector from_transform, vector to_transform)
float MOVETYPE_NOCLIP
Definition: progsdefs.qc:254
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float deadflag
Definition: progsdefs.qc:149
bool autocvar_g_turrets
Definition: sv_turrets.qh:5
const int TFL_TARGETSELECT_NO
Definition: turret.qh:63
void InitializeEntity(entity e, void(entity this) func, int order)
Definition: world.qc:2146
const int TFL_AIM_SPLASH
Definition: turret.qh:81
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
float lip
Definition: subs.qh:40
#define LOG_TRACE(...)
Definition: log.qh:81
const int TFL_FIRECHECK_DEAD
Definition: turret.qh:95
const float TFL_TRACKTYPE_FLUIDPRECISE
Definition: sv_turrets.qh:64
const int TFL_DMG_AIMSHAKE
Definition: turret.qh:154
bool turret_closetotarget(entity this, vector targ)
const int TUR_FLAG_MOVE
Definition: turret.qh:133
const int TFL_AMMO_ENERGY
Definition: turret.qh:140
const int TFL_TARGETSELECT_VEHICLES
Definition: turret.qh:76
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
const int TFL_TRACK_NO
Definition: turret.qh:89
string targetname
Definition: progsdefs.qc:194
entity realowner
Definition: common.qh:25
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
const float SOLID_BBOX
Definition: csprogsdefs.qc:246
const int TNSF_ANG
Definition: turret.qh:170
const int TFL_TARGETSELECT_MISSILES
Definition: turret.qh:66
int active
Definition: defs.qh:34
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
Definition: anim.qh:6
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
setorigin(ent, v)
int turret_flags
Definition: turret.qh:118
const int TFL_FIRECHECK_TEAMCHECK
Definition: turret.qh:101
float DEAD_NO
Definition: progsdefs.qc:274
const int TUR_FLAG_ISTURRET
Definition: turret.qh:135
#define setthink(e, f)
vector tvt_tadv
Definition: sv_turrets.qh:110
vector angles
Definition: csprogsdefs.qc:104
void turret_die(entity this)
Definition: cl_turrets.qc:329
const float TFL_TRACKTYPE_FLUIDINERTIA
Definition: sv_turrets.qh:65
#define use
Definition: csprogsdefs.qh:50
ERASEABLE vector shortangle_vxy(vector ang1, vector ang2)
Definition: angle.qc:58
float FL_NOTARGET
Definition: progsdefs.qc:238
const int TFL_FIRECHECK_LOS
Definition: turret.qh:97
string target
Definition: progsdefs.qc:193
#define sound(e, c, s, v, a)
Definition: sound.qh:52
vector absmin
Definition: csprogsdefs.qc:92
int track_flags
Definition: turret.qh:88
float MOVE_WORLDONLY
const int ACTIVE_NOT
Definition: defs.qh:36
float time
Definition: csprogsdefs.qc:16
const int TFL_AIM_SHOTTIMECOMPENSATE
Definition: turret.qh:83
vector velocity
Definition: csprogsdefs.qc:103
const int TNSF_MOVE
Definition: turret.qh:172
const int TFL_AIM_SIMPLE
Definition: turret.qh:85
vector m_maxs
turret hitbox size
Definition: turret.qh:23
float trace_fraction
Definition: csprogsdefs.qc:36
float target_select_time
Definition: sv_turrets.qh:40
float tur_dist_impact_to_aimpos
Definition: sv_turrets.qh:37
float DAMAGE_NO
Definition: progsdefs.qc:282
int aim_flags
Definition: turret.qh:79
const int TNSF_FULL_UPDATE
Definition: turret.qh:176
float tvt_thadf
Definition: sv_turrets.qh:111
void set_movetype(entity this, int mt)
float EF_LOWPRECISION
var void func_null()
float turret_count
Definition: sv_turrets.qh:91
const int TFL_TARGETSELECT_RANGELIMITS
Definition: turret.qh:69
const float FLOAT_MAX
Definition: float.qh:3
float ticrate
Definition: main.qh:182
const int TFL_DMG_RETALIATE
Definition: turret.qh:151
float attack_finished_single[MAX_WEAPONSLOTS]
Definition: weaponsystem.qh:36
const int TFL_SHOOT_CLEARTARGET
Definition: turret.qh:114
vector v_forward
Definition: csprogsdefs.qc:31
float solid
Definition: csprogsdefs.qc:99
float DAMAGE_YES
Definition: progsdefs.qc:283
Definition: turret.qh:5
const int TFL_TARGETSELECT_MISSILESONLY
Definition: turret.qh:75