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