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{
14 vector pre_pos, prep;
15 float distance, impact_time = 0, i, mintime;
16
18
19 if(this.aim_flags & TFL_AIM_SIMPLE)
20 return real_origin(this.enemy);
21
22 mintime = max(this.attack_finished_single[0] - time,0) + sys_frametime;
23
24 // Baseline
25 pre_pos = real_origin(this.enemy);
26
27 // Lead?
28 if (this.aim_flags & TFL_AIM_LEAD)
29 {
30 if (this.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime
31 {
32 prep = pre_pos;
33
34 distance = vlen(prep - this.tur_shotorg);
35 impact_time = distance / this.shot_speed;
36
37 prep = pre_pos + (this.enemy.velocity * (impact_time + mintime));
38
40 if(!IS_ONGROUND(this.enemy))
41 if(this.enemy.move_movetype == MOVETYPE_WALK || this.enemy.move_movetype == MOVETYPE_TOSS || this.enemy.move_movetype == MOVETYPE_BOUNCE)
42 {
43 float vz;
44 prep_z = pre_pos_z;
45 vz = this.enemy.velocity_z;
46 for(i = 0; i < impact_time; i += sys_frametime)
47 {
49 prep_z = prep_z + vz * sys_frametime;
50 }
51 }
52 pre_pos = prep;
53 }
54 else
55 pre_pos = pre_pos + this.enemy.velocity * mintime;
56 }
57
58 if(this.aim_flags & TFL_AIM_SPLASH)
59 {
60 //tracebox(pre_pos + '0 0 32',this.enemy.mins,this.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,this.enemy);
61 traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,this.enemy);
62 if(trace_fraction != 1.0)
63 pre_pos = trace_endpos;
64 }
65
66 return pre_pos;
67}
68
70{
71 float score; // Total score
72 float s_score = 0, d_score;
73
74 if (_turret.enemy == _target) s_score = 1;
75
76 d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist);
77
78 score = (d_score * _turret.target_select_rangebias) +
79 (s_score * _turret.target_select_samebias);
80
81 return score;
82}
83
84/*
85* Generic bias aware score system.
86*/
88{
89 float d_dist; // Defendmode Distance
90 float score; // Total score
91 float d_score; // Distance score
92 float a_score; // Angular score
93 float m_score = 0; // missile score
94 float p_score = 0; // player score
95 float ikr; // ideal kill range
96
97 if (_turret.tur_defend)
98 {
99 d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin);
100 ikr = vlen(_turret.origin - _turret.tur_defend.origin);
101 d_score = 1 - d_dist / _turret.target_range;
102 }
103 else
104 {
105 // Make a normlized value base on the targets distance from our optimal killzone
106 ikr = _turret.target_range_optimal;
107 d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist);
108 }
109
110 a_score = 1 - tvt_thadf / _turret.aim_maxrot;
111
112 if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE))
113 m_score = 1;
114
115 if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target))
116 p_score = 1;
117
118 d_score = max(d_score, 0);
119 a_score = max(a_score, 0);
120 m_score = max(m_score, 0);
121 p_score = max(p_score, 0);
122
123 score = (d_score * _turret.target_select_rangebias) +
124 (a_score * _turret.target_select_anglebias) +
125 (m_score * _turret.target_select_missilebias) +
126 (p_score * _turret.target_select_playerbias);
127
128 if(vdist((_turret.tur_shotorg - real_origin(_target)), >, _turret.target_range))
129 {
130 //dprint("Wtf?\n");
131 score *= 0.001;
132 }
133
134#ifdef TURRET_DEBUG
135 string sd,sa,sm,sp,ss;
136 string sdt,sat,smt,spt;
137
138 sd = ftos(d_score);
139 d_score *= _turret.target_select_rangebias;
140 sdt = ftos(d_score);
141
142 //sv = ftos(v_score);
143 //v_score *= _turret.target_select_samebias;
144 //svt = ftos(v_score);
145
146 sa = ftos(a_score);
147 a_score *= _turret.target_select_anglebias;
148 sat = ftos(a_score);
149
150 sm = ftos(m_score);
151 m_score *= _turret.target_select_missilebias;
152 smt = ftos(m_score);
153
154 sp = ftos(p_score);
155 p_score *= _turret.target_select_playerbias;
156 spt = ftos(p_score);
157
158
159 ss = ftos(score);
160 bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _target.netname," \]\n");
161 bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n");
162 bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n");
163 bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n");
164 bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n");
165 bprint("^3Total (w/bias):\[^1",ss,"\]\n");
166
167#endif
168
169 return score;
170}
171
172// Generic damage handling
174{
175 this.effects |= EF_NODRAW;
176 this.nextthink = time + this.respawntime - 0.2;
178}
179
181{
182 this.deadflag = DEAD_DEAD;
183 this.tur_head.deadflag = this.deadflag;
184
185 // Unsolidify and hide real parts
186 this.solid = SOLID_NOT;
187 this.tur_head.solid = this.solid;
188
189 this.event_damage = func_null;
190 this.event_heal = func_null;
191 this.takedamage = DAMAGE_NO;
192
193 SetResourceExplicit(this, RES_HEALTH, 0);
194
195 // Go boom
196 //RadiusDamage (this,this, min(this.ammo,50),min(this.ammo,50) * 0.25,250,NULL,min(this.ammo,50)*5,DEATH_TURRET,NULL);
197
198 Turret tur = get_turretinfo(this.m_id);
200 {
201 // do a simple explosion effect here, since CSQC can't do it on a to-be-removed entity
202 sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
203 Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
204
205 tur.tr_death(tur, this);
206
207 delete(this.tur_head);
208 delete(this);
209 }
210 else
211 {
212 // Setup respawn
213 this.SendFlags |= TNSF_STATUS;
214 this.nextthink = time + 0.2;
215 setthink(this, turret_hide);
216
217 tur.tr_death(tur, this);
218 }
219}
220
221void turret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce)
222{
223 // Enough already!
224 if(this.deadflag == DEAD_DEAD)
225 return;
226
227 // Inactive turrets take no damage. (hm..)
228 if(!this.active)
229 return;
230
231 if(SAME_TEAM(this, attacker))
232 {
234 damage = damage * autocvar_g_friendlyfire;
235 else
236 return;
237 }
238
239 TakeResource(this, RES_HEALTH, damage);
240
241 // thorw head slightly off aim when hit?
243 {
244 this.tur_head.angles_x = this.tur_head.angles_x + (-0.5 + random()) * damage;
245 this.tur_head.angles_y = this.tur_head.angles_y + (-0.5 + random()) * damage;
246
247 this.SendFlags |= TNSF_ANG;
248 }
249
250 if (this.turret_flags & TUR_FLAG_MOVE)
251 this.velocity = this.velocity + vforce;
252
253 if (GetResource(this, RES_HEALTH) <= 0)
254 {
255 this.event_damage = func_null;
256 this.tur_head.event_damage = func_null;
257 this.event_heal = func_null;
258 this.tur_head.event_heal = func_null;
259 this.takedamage = DAMAGE_NO;
260 this.nextthink = time;
261 setthink(this, turret_die);
262 }
263
264 this.SendFlags |= TNSF_STATUS;
265}
266
267bool turret_heal(entity targ, entity inflictor, float amount, float limit)
268{
269 float true_limit = ((limit != RES_LIMIT_NONE) ? limit : targ.max_health);
270 if(GetResource(targ, RES_HEALTH) <= 0 || GetResource(targ, RES_HEALTH) >= true_limit)
271 return false;
272
273 GiveResourceWithLimit(targ, RES_HEALTH, amount, true_limit);
274 targ.SendFlags |= TNSF_STATUS;
275 return true;
276}
277
278void turret_think(entity this);
280{
281 // Make sure all parts belong to the same team since
282 // this function doubles as "teamchange" function.
283 this.tur_head.team = this.team;
284 this.effects &= ~EF_NODRAW;
285 this.deadflag = DEAD_NO;
287 this.solid = SOLID_BBOX;
288 this.takedamage = DAMAGE_AIM;
289 this.event_damage = turret_damage;
290 this.event_heal = turret_heal;
291 this.avelocity = '0 0 0';
292 this.tur_head.avelocity = this.avelocity;
293 this.tur_head.angles = this.idle_aim;
294 SetResourceExplicit(this, RES_HEALTH, this.max_health);
295 this.enemy = NULL;
296 this.volly_counter = this.shot_volly;
297 this.ammo = this.ammo_max;
298
299 this.nextthink = time;
300 setthink(this, turret_think);
301
303
304 Turret tur = get_turretinfo(this.m_id);
305 tur.tr_setup(tur, this);
306
307 setorigin(this, this.origin); // make sure it's linked to the area grid
308}
309
310
311// Main functions
313void turrets_setframe(entity this, float _frame, float client_only)
314{
315 if((client_only ? this.clientframe : this.frame ) != _frame)
316 {
317 this.SendFlags |= TNSF_ANIM;
318 this.anim_start_time = time;
319 }
320
321 if(client_only)
322 this.clientframe = _frame;
323 else
324 this.frame = _frame;
325
326}
327
328bool turret_send(entity this, entity to, float sf)
329{
330 WriteHeader(MSG_ENTITY, ENT_CLIENT_TURRET);
332 if(sf & TNSF_SETUP)
333 {
335
336 WriteVector(MSG_ENTITY, this.origin);
337
338 WriteAngleVector2D(MSG_ENTITY, this.angles);
339 }
340
341 if(sf & TNSF_ANG)
342 {
343 WriteShort(MSG_ENTITY, rint(this.tur_head.angles_x));
344 WriteShort(MSG_ENTITY, rint(this.tur_head.angles_y));
345 }
346
347 if(sf & TNSF_AVEL)
348 {
349 WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_x));
350 WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_y));
351 }
352
353 if(sf & TNSF_MOVE)
354 {
355 WriteVector(MSG_ENTITY, this.origin);
356
357 WriteVector(MSG_ENTITY, this.velocity);
358
360 }
361
362 if(sf & TNSF_ANIM)
363 {
366 }
367
368 if(sf & TNSF_STATUS)
369 {
371
372 if(GetResource(this, RES_HEALTH) <= 0)
374 else
375 WriteByte(MSG_ENTITY, ceil((GetResource(this, RES_HEALTH) / this.max_health) * 255));
376 }
377
378 return true;
379}
380
381void load_unit_settings(entity ent, bool is_reload)
382{
383 if (ent == NULL)
384 return;
385
386 if(!ent.turret_scale_damage) ent.turret_scale_damage = 1;
387 if(!ent.turret_scale_range) ent.turret_scale_range = 1;
388 if(!ent.turret_scale_refire) ent.turret_scale_refire = 1;
389 if(!ent.turret_scale_ammo) ent.turret_scale_ammo = 1;
390 if(!ent.turret_scale_aim) ent.turret_scale_aim = 1;
391 if(!ent.turret_scale_health) ent.turret_scale_health = 1;
392 if(!ent.turret_scale_respawn) ent.turret_scale_respawn = 1;
393
394 if (is_reload)
395 {
396 ent.enemy = NULL;
397 ent.tur_head.avelocity = '0 0 0';
398
399 ent.tur_head.angles = '0 0 0';
400 }
401
402 string unitname = ent.netname;
403 #define X(class, prefix, fld, type) ent.fld = cvar(strcat("g_turrets_unit_", prefix, "_", #fld));
404 TR_PROPS_COMMON(X, , unitname)
405 #undef X
406
407 ent.ammo_max *= ent.turret_scale_ammo;
408 ent.ammo_recharge *= ent.turret_scale_ammo;
409 ent.aim_speed *= ent.turret_scale_aim;
410 SetResourceExplicit(ent, RES_HEALTH, GetResource(ent, RES_HEALTH) * ent.turret_scale_health);
411 ent.respawntime *= ent.turret_scale_respawn;
412 ent.shot_dmg *= ent.turret_scale_damage;
413 ent.shot_refire *= ent.turret_scale_refire;
414 ent.shot_radius *= ent.turret_scale_damage;
415 ent.shot_force *= ent.turret_scale_damage;
416 ent.shot_volly_refire *= ent.turret_scale_refire;
417 ent.target_range *= ent.turret_scale_range;
418 ent.target_range_min *= ent.turret_scale_range;
419 ent.target_range_optimal *= ent.turret_scale_range;
420
421 if(is_reload) {
422 Turret tur = get_turretinfo(ent.m_id);
423 tur.tr_setup(tur, ent);
424 }
425}
426
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
447
448void 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
457entity 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;
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
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
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 * frametime;
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 * frametime;
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 * frametime) > 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 * frametime) < -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 * frametime) > 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 * frametime) < -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
686float 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 // Don't attack against owner
697 if(e_target.owner == e_turret || e_target == e_turret.realowner)
698 return -0.5;
699
700 if(!checkpvs(e_target.origin, e_turret))
701 return -1;
702
703 if(e_target.alpha != 0 && e_target.alpha <= 0.3)
704 return -1;
705
706 if(MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags))
707 return M_ARGV(3, float);
708
709 if (validate_flags & TFL_TARGETSELECT_NO)
710 return -4;
711
712 // If only this was used more..
713 if (e_target.flags & FL_NOTARGET)
714 return -5;
715
716 // Cant touch this
717 if (GetResource(e_target, RES_HEALTH) <= 0)
718 return -6;
719
720 // vehicle
721 if(IS_VEHICLE(e_target))
722 {
723 if ((validate_flags & TFL_TARGETSELECT_VEHICLES) && !e_target.owner)
724 return -7;
725 }
726
727 // player
728 if (IS_CLIENT(e_target))
729 {
730 if(!(validate_flags & TFL_TARGETSELECT_PLAYERS))
731 return -7;
732
733 if (IS_DEAD(e_target))
734 return -8;
735 }
736
737 // enemy turrets
738 if(validate_flags & TFL_TARGETSELECT_NOTURRETS)
739 if(e_target.owner.tur_head == e_target)
740 if(e_target.team != e_turret.team) // Dont break support units.
741 return -9;
742
743 // Missile
744 if (e_target.flags & FL_PROJECTILE)
745 if(!(validate_flags & TFL_TARGETSELECT_MISSILES))
746 return -10;
747
748 if (validate_flags & TFL_TARGETSELECT_MISSILESONLY)
749 if(!(e_target.flags & FL_PROJECTILE))
750 return -10.5;
751
752 // Team check
753 if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)
754 {
755 if (validate_flags & TFL_TARGETSELECT_OWNTEAM)
756 {
757 if (e_target.team != e_turret.team)
758 return -11;
759
760 if (e_turret.team != e_target.owner.team)
761 return -12;
762
763 if (e_turret.team != e_target.aiment.team)
764 return -12; // portals
765 }
766 else
767 {
768 if (e_target.team == e_turret.team)
769 return -13;
770
771 if (e_turret.team == e_target.owner.team)
772 return -14;
773
774 if (e_turret.team == e_target.aiment.team)
775 return -14; // portals
776 }
777 }
778
779 // Range limits?
780 tvt_dist = vlen(e_turret.origin - real_origin(e_target));
781 if (validate_flags & TFL_TARGETSELECT_RANGELIMITS)
782 {
783 if (tvt_dist < e_turret.target_range_min)
784 return -15;
785
786 if (tvt_dist > e_turret.target_range)
787 return -16;
788 }
789
790 // Can we even aim this thing?
791 tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target.origin);
792 tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles);
794
795 /*
796 if(validate_flags & TFL_TARGETSELECT_FOV)
797 {
798 if(e_turret.target_select_fov < tvt_thadf)
799 return -21;
800 }
801 */
802
803 if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)
804 {
805 if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
806 return -17;
807
808 if (fabs(tvt_tadv_y) > e_turret.aim_maxrot)
809 return -18;
810 }
811
812 // Line of sight?
813 if (validate_flags & TFL_TARGETSELECT_LOS)
814 {
815 v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
816
817 traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret);
818
819 if(vdist(v_tmp - trace_endpos, >, e_turret.aim_firetolerance_dist))
820 return -19;
821 }
822
823 if (e_target.classname == "grapplinghook")
824 return -20;
825
826 /*
827 if (e_target.classname == "func_button")
828 return -21;
829 */
830
831#ifdef TURRET_DEBUG_TARGETSELECT
832 LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname);
833#endif
834
835 return 1;
836}
837
839{
840 entity e; // target looper entity
841 float score; // target looper entity score
842 entity e_enemy; // currently best scoreing target
843 float m_score; // currently best scoreing target's score
844
845 m_score = 0;
846 if(this.enemy && this.enemy.takedamage && turret_validate_target(this,this.enemy,this.target_validate_flags) > 0)
847 {
848 e_enemy = this.enemy;
849 m_score = this.turret_score_target(this,e_enemy) * this.target_select_samebias;
850 }
851 else
852 e_enemy = this.enemy = NULL;
853
854 e = findradius(this.origin, this.target_range);
855
856 // Nothing to aim at?
857 if (!e)
858 return NULL;
859
860 while (e)
861 {
862 if(e.takedamage)
863 {
864 float f = turret_validate_target(this, e, this.target_select_flags);
865 //dprint("F is: ", ftos(f), "\n");
866 if ( f > 0)
867 {
868 score = this.turret_score_target(this,e);
869 if ((score > m_score) && (score > 0))
870 {
871 e_enemy = e;
872 m_score = score;
873 }
874 }
875 }
876 e = e.chain;
877 }
878
879 return e_enemy;
880}
881
882
883/*
884 + = implemented
885 - = not implemented
886
887 + TFL_FIRECHECK_NO
888 + TFL_FIRECHECK_WORLD
889 + TFL_FIRECHECK_DEAD
890 + TFL_FIRECHECK_DISTANCES
891 - TFL_FIRECHECK_LOS
892 + TFL_FIRECHECK_AIMDIST
893 + TFL_FIRECHECK_REALDIST
894 - TFL_FIRECHECK_ANGLEDIST
895 - TFL_FIRECHECK_TEAMCECK
896 + TFL_FIRECHECK_AFF
897 + TFL_FIRECHECK_AMMO_OWN
898 + TFL_FIRECHECK_AMMO_OTHER
899 + TFL_FIRECHECK_REFIRE
900*/
901
906{
907 // This one just dont care =)
909 return true;
910
911 if (this.enemy == NULL)
912 return false;
913
914 // Ready?
916 if (this.attack_finished_single[0] > time) return false;
917
918 // Special case: volly fire turret that has to fire a full volly if a shot was fired.
920 if (this.volly_counter != this.shot_volly)
921 if(this.ammo >= this.shot_dmg)
922 return true;
923
924 // Lack of zombies makes shooting dead things unnecessary :P
926 if (IS_DEAD(this.enemy))
927 return false;
928
929 // Own ammo?
931 if (this.ammo < this.shot_dmg)
932 return false;
933
934 // Other's ammo? (support-supply units)
936 if (this.enemy.ammo >= this.enemy.ammo_max)
937 return false;
938
939 // Target of opertunity?
941 {
942 this.enemy = this.tur_impactent;
943 return true;
944 }
945
947 {
948 // To close?
949 if (this.tur_dist_aimpos < this.target_range_min)
950 {
952 return true; // Target of opertunity?
953 return false;
954 }
955 }
956
957 // Try to avoid FF?
959 if (this.tur_impactent.team == this.team)
960 return false;
961
962 // aim<->predicted impact
964 if (this.tur_dist_impact_to_aimpos > this.aim_firetolerance_dist)
965 return false;
966
967 // Volly status
968 if (this.shot_volly > 1)
969 if (this.volly_counter == this.shot_volly)
970 if (this.ammo < (this.shot_dmg * this.shot_volly))
971 return false;
972
973 /*if(this.firecheck_flags & TFL_FIRECHECK_VERIFIED)
974 if(this.tur_impactent != this.enemy)
975 return false;*/
976
977 return true;
978}
979
981{
982 if(MUTATOR_CALLHOOK(Turret_CheckFire, this))
983 return M_ARGV(1, bool);
984
985 return this.turret_firecheckfunc(this);
986}
987
989{
991 return;
992
993 if(MUTATOR_CALLHOOK(TurretFire, this))
994 return;
995
996 Turret info = get_turretinfo(this.m_id);
997 info.tr_attack(info, this);
998
999 this.attack_finished_single[0] = time + this.shot_refire;
1000 this.ammo -= this.shot_dmg;
1001 this.volly_counter = this.volly_counter - 1;
1002
1003 if (this.volly_counter <= 0)
1004 {
1005 this.volly_counter = this.shot_volly;
1006
1008 this.enemy = NULL;
1009
1010 if (this.shot_volly > 1)
1011 this.attack_finished_single[0] = time + this.shot_volly_refire;
1012 }
1013
1014#ifdef TURRET_DEBUG
1015 if (this.enemy) paint_target3(this.tur_aimpos, 64, this.tur_debug_rvec, this.tur_impacttime + 0.25);
1016#endif
1017}
1018
1020{
1021 this.nextthink = time;
1022
1023 MUTATOR_CALLHOOK(TurretThink, this);
1024
1025#ifdef TURRET_DEBUG
1026 if (this.tur_debug_tmr1 < time)
1027 {
1028 if (this.enemy) paint_target (this.enemy,128,this.tur_debug_rvec,0.9);
1029 paint_target(this,256,this.tur_debug_rvec,0.9);
1030 this.tur_debug_tmr1 = time + 1;
1031 }
1032#endif
1033
1034 // Handle ammo
1035 if (!(this.spawnflags & TSF_NO_AMMO_REGEN))
1036 if (this.ammo < this.ammo_max)
1037 this.ammo = min(this.ammo + this.ammo_recharge * frametime, this.ammo_max);
1038
1039 // Inactive turrets needs to run the think loop,
1040 // So they can handle animation and wake up if need be.
1041 if(!this.active)
1042 {
1043 turret_track(this);
1044 return;
1045 }
1046
1047 // This is typicaly used for zaping every target in range
1048 // turret_fusionreactor uses this to recharge friendlys.
1050 {
1051 // Do a this.turret_fire for every valid target.
1052 entity e = findradius(this.origin,this.target_range);
1053 while (e)
1054 {
1055 if(e.takedamage)
1056 {
1058 {
1059 this.enemy = e;
1060
1061 turret_do_updates(this);
1062
1063 if (turret_checkfire(this))
1064 turret_fire(this);
1065 }
1066 }
1067
1068 e = e.chain;
1069 }
1070 this.enemy = NULL;
1071 }
1072 else if(this.shoot_flags & TFL_SHOOT_CUSTOM)
1073 {
1074 // This one is doing something.. oddball. assume its handles what needs to be handled.
1075
1076 // Predict?
1077 if(!(this.aim_flags & TFL_AIM_NO))
1078 this.tur_aimpos = turret_aim_generic(this);
1079
1080 // Turn & pitch?
1081 if(!(this.track_flags & TFL_TRACK_NO))
1082 turret_track(this);
1083
1084 turret_do_updates(this);
1085
1086 // Fire?
1087 if (turret_checkfire(this))
1088 turret_fire(this);
1089 }
1090 else
1091 {
1092 // Special case for volly always. if it fired once it must compleate the volly.
1094 if(this.volly_counter != this.shot_volly)
1095 {
1096 // Predict or whatnot
1097 if(!(this.aim_flags & TFL_AIM_NO))
1098 this.tur_aimpos = turret_aim_generic(this);
1099
1100 // Turn & pitch
1101 if(!(this.track_flags & TFL_TRACK_NO))
1102 turret_track(this);
1103
1104 turret_do_updates(this);
1105
1106 // Fire!
1107 if (turret_checkfire(this))
1108 turret_fire(this);
1109
1110 Turret tur = get_turretinfo(this.m_id);
1111 tur.tr_think(tur, this);
1112
1113 return;
1114 }
1115
1116 // Check if we have a vailid enemy, and try to find one if we dont.
1117
1118 // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
1119 float do_target_scan = 0;
1121 do_target_scan = 1;
1122
1123 // Old target (if any) invalid?
1124 if(this.target_validate_time < time)
1125 if (turret_validate_target(this, this.enemy, this.target_validate_flags) <= 0)
1126 {
1127 this.enemy = NULL;
1128 this.target_validate_time = time + 0.5;
1129 do_target_scan = 1;
1130 }
1131
1132 // But never more often then g_turrets_targetscan_mindelay!
1134 do_target_scan = 0;
1135
1136 if(do_target_scan)
1137 {
1138 this.enemy = turret_select_target(this);
1139 this.target_select_time = time;
1140 }
1141
1142 // No target, just go to idle, do any custom stuff and bail.
1143 if (this.enemy == NULL)
1144 {
1145 // Turn & pitch
1146 if(!(this.track_flags & TFL_TRACK_NO))
1147 turret_track(this);
1148
1149 Turret tur = get_turretinfo(this.m_id);
1150 tur.tr_think(tur, this);
1151
1152 // And bail.
1153 return;
1154 }
1155 else
1156 this.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target.
1157
1158 // Predict?
1159 if(!(this.aim_flags & TFL_AIM_NO))
1160 this.tur_aimpos = turret_aim_generic(this);
1161
1162 // Turn & pitch?
1163 if(!(this.track_flags & TFL_TRACK_NO))
1164 turret_track(this);
1165
1166 turret_do_updates(this);
1167
1168 // Fire?
1169 if (turret_checkfire(this))
1170 turret_fire(this);
1171 }
1172
1173 Turret tur = get_turretinfo(this.m_id);
1174 tur.tr_think(tur, this);
1175}
1176
1177/*
1178 When .used a turret switch team to activator.team.
1179 If activator is NULL, the turret go inactive.
1180*/
1181void turret_use(entity this, entity actor, entity trigger)
1182{
1183 LOG_TRACE("Turret ", this.netname, " used by ", actor.classname);
1184
1185 this.team = actor.team;
1186
1187 if(this.team == 0)
1188 this.active = ACTIVE_NOT;
1189 else
1190 this.active = ACTIVE_ACTIVE;
1191}
1192
1194{
1195 Net_LinkEntity(this, true, 0, turret_send);
1196 setthink(this, turret_think);
1197 this.nextthink = time;
1198 this.tur_head.effects = EF_NODRAW;
1199}
1200
1202{
1203 this.nextthink = time + 1;
1204
1206 {
1207 IL_EACH(g_turrets, true,
1208 {
1209 load_unit_settings(it, true);
1210 Turret tur = get_turretinfo(it.m_id);
1211 tur.tr_think(tur, it);
1212 });
1213 cvar_set("g_turrets_reloadcvars", "0");
1214 }
1215}
1216
1218{
1219 #define TRY(x) (x) ? (x)
1220 tur.respawntime = max (-1, (TRY(tur.respawntime) : 60 ));
1221 tur.shot_refire = bound(0.01, (TRY(tur.shot_refire) : 1 ), 9999);
1222 tur.shot_dmg = max (1, (TRY(tur.shot_dmg) : tur.shot_refire * 50 ));
1223 tur.shot_radius = max (1, (TRY(tur.shot_radius) : tur.shot_dmg * 0.5 ));
1224 tur.shot_speed = max (1, (TRY(tur.shot_speed) : 2500 ));
1225 tur.shot_spread = bound(0.0001, (TRY(tur.shot_spread) : 0.0125 ), 500);
1226 tur.shot_force = bound(0.001, (TRY(tur.shot_force) : tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000);
1227 tur.shot_volly = bound(1, (TRY(tur.shot_volly) : 1 ), floor(tur.ammo_max / tur.shot_dmg));
1228 tur.shot_volly_refire = bound(tur.shot_refire, (TRY(tur.shot_volly_refire) : tur.shot_refire * tur.shot_volly ), 60);
1229 tur.target_range = bound(0, (TRY(tur.target_range) : tur.shot_speed * 0.5 ), max_shot_distance);
1230 tur.target_range_min = bound(0, (TRY(tur.target_range_min) : tur.shot_radius * 2 ), max_shot_distance);
1231 tur.target_range_optimal = bound(0, (TRY(tur.target_range_optimal) : tur.target_range * 0.5 ), max_shot_distance);
1232 tur.aim_maxrot = bound(0, (TRY(tur.aim_maxrot) : 90 ), 360);
1233 tur.aim_maxpitch = bound(0, (TRY(tur.aim_maxpitch) : 20 ), 90);
1234 tur.aim_speed = bound(0.1, (TRY(tur.aim_speed) : 36 ), 1000);
1235 tur.aim_firetolerance_dist = bound(0.1, (TRY(tur.aim_firetolerance_dist) : 5 + (tur.shot_radius * 2) ), max_shot_distance);
1236 tur.target_select_rangebias = bound(-10, (TRY(tur.target_select_rangebias) : 1 ), 10);
1237 tur.target_select_samebias = bound(-10, (TRY(tur.target_select_samebias) : 1 ), 10);
1238 tur.target_select_anglebias = bound(-10, (TRY(tur.target_select_anglebias) : 1 ), 10);
1239 tur.target_select_missilebias = bound(-10, (TRY(tur.target_select_missilebias) : 1 ), 10);
1240 tur.target_select_playerbias = bound(-10, (TRY(tur.target_select_playerbias) : 1 ), 10);
1241 tur.ammo_max = max (tur.shot_dmg, (TRY(tur.ammo_max) : tur.shot_dmg * 10 ));
1242 tur.ammo_recharge = max (0, (TRY(tur.ammo_recharge) : tur.shot_dmg * 0.5 ));
1243 #undef TRY
1244}
1245
1246bool turret_closetotarget(entity this, vector targ, float range)
1247{
1248 vector path_extra_size = '1 1 1' * range;
1249 return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size);
1250}
1251
1253{
1254 entity e = find(NULL, classname, "turret_manager");
1255 if(!e)
1256 {
1257 e = new_pure(turret_manager);
1259 e.nextthink = time + 2;
1260 }
1261
1262 entity targ = find(NULL, targetname, this.target);
1263 if(targ.classname == "turret_checkpoint")
1264 return; // turrets don't defend checkpoints?
1265
1266 if (!targ)
1267 {
1268 this.target = "";
1269 LOG_TRACE("Turret has invalid defendpoint!");
1270 }
1271
1272 this.tur_defend = targ;
1273 this.idle_aim = this.tur_head.angles + angleofs(this.tur_head, targ);
1274}
1275
1277{
1278 turret_respawn(this);
1279}
1280
1282{
1284 return false;
1285
1286 if(tur.m_id == 0)
1287 return false; // invalid turret
1288
1289 // if tur_head exists, we can assume this turret re-spawned
1290 if(!this.tur_head) {
1291 tur.tr_precache(tur);
1292 IL_PUSH(g_turrets, this);
1293 IL_PUSH(g_bot_targets, this);
1294 }
1295
1296 if(!(this.spawnflags & TSF_SUSPENDED))
1298
1299 this.netname = tur.netname;
1300 load_unit_settings(this, 0);
1301
1302 if(!this.team || !teamplay) { this.team = FLOAT_MAX; }
1303 if(!GetResource(this, RES_HEALTH)) { SetResourceExplicit(this, RES_HEALTH, 1000); }
1304 if(!this.shot_refire) { this.shot_refire = 1; }
1305 if(!this.tur_shotorg) { this.tur_shotorg = '50 0 50'; }
1309 if(!this.track_type) { this.track_type = TFL_TRACKTYPE_STEPMOTOR; }
1315
1316 if(this.track_type != TFL_TRACKTYPE_STEPMOTOR)
1317 {
1318 // Fluid / Ineria mode. Looks mutch nicer.
1319 // Can reduce aim preformance alot, needs a bit diffrent aimspeed
1320
1321 this.aim_speed = bound(0.1, ((!this.aim_speed) ? 180 : this.aim_speed), 1000);
1322
1323 if(!this.track_accel_pitch) { this.track_accel_pitch = 0.5; }
1324 if(!this.track_accel_rot) { this.track_accel_rot = 0.5; }
1325 if(!this.track_blendrate) { this.track_blendrate = 0.35; }
1326 }
1327
1328 turret_initparams(this);
1329
1331
1332 if(this.turret_flags & TUR_FLAG_SPLASH)
1333 this.aim_flags |= TFL_AIM_SPLASH;
1334
1337
1338 if(this.turret_flags & TUR_FLAG_PLAYER)
1340
1341 if(this.spawnflags & TSL_NO_RESPAWN)
1343
1344 if (this.turret_flags & TUR_FLAG_SUPPORT)
1345 this.turret_score_target = turret_targetscore_support;
1346 else
1347 this.turret_score_target = turret_targetscore_generic;
1348
1349 ++turret_count;
1350
1351 _setmodel(this, tur.model);
1352 setsize(this, tur.m_mins, tur.m_maxs);
1353
1354 this.m_id = tur.m_id;
1355 this.active = ACTIVE_ACTIVE;
1356 this.effects = EF_NODRAW;
1357 this.netname = tur.m_name;
1358 this.max_health = GetResource(this, RES_HEALTH);
1360 this.ammo = this.ammo_max;
1361 this.solid = SOLID_BBOX;
1362 this.takedamage = DAMAGE_AIM;
1364 this.view_ofs = '0 0 0';
1365 this.idle_aim = '0 0 0';
1366 this.turret_firecheckfunc = turret_firecheck;
1367 this.event_damage = turret_damage;
1368 this.event_heal = turret_heal;
1369 this.use = turret_use;
1370 this.bot_attack = true;
1371 this.nextthink = time + 1;
1372 this.reset = turret_reset;
1373
1374 this.tur_head = new(turret_head);
1375 _setmodel(this.tur_head, tur.head_model);
1376 setsize(this.tur_head, '0 0 0', '0 0 0');
1377 setorigin(this.tur_head, '0 0 0');
1378 setattachment(this.tur_head, this, "tag_head");
1379
1380 this.tur_head.netname = this.tur_head.classname;
1381 this.tur_head.team = this.team;
1382 this.tur_head.owner = this;
1383 this.tur_head.takedamage = DAMAGE_NO;
1384 this.tur_head.solid = SOLID_NOT;
1386
1387 this.weaponentities[0] = this; // lol
1388
1389 if(!this.tur_defend && this.target != "")
1391
1392#ifdef TURRET_DEBUG
1393 this.tur_debug_start = this.nextthink;
1394 while(vdist(this.tur_debug_rvec, <, 2))
1395 this.tur_debug_rvec = randomvec() * 4;
1396
1397 this.tur_debug_rvec_x = fabs(this.tur_debug_rvec_x);
1398 this.tur_debug_rvec_y = fabs(this.tur_debug_rvec_y);
1399 this.tur_debug_rvec_z = fabs(this.tur_debug_rvec_z);
1400#endif
1401
1402 turret_link(this);
1403 turret_respawn(this);
1405
1406 tur.tr_setup(tur, this);
1407
1408 if(MUTATOR_CALLHOOK(TurretSpawn, this))
1409 return false;
1410
1411 return true;
1412}
1413#endif
ERASEABLE float anglemods(float v)
Definition angle.qc:13
ERASEABLE vector angleofs3(vector from, vector ang, vector to)
Definition angle.qc:73
#define angleofs(from, to)
Definition angle.qc:90
ERASEABLE vector shortangle_vxy(vector ang1, vector ang2)
Definition angle.qc:58
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.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
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:30
virtual void tr_death()
(SERVER) called when turret dies
Definition turret.qh:41
virtual void tr_setup()
(BOTH) setup turret data
Definition turret.qh:33
virtual void tr_precache()
(BOTH) precaches models/sounds used by this turret
Definition turret.qh:45
vector m_mins
turret hitbox size
Definition turret.qh:28
virtual void tr_think()
(SERVER) logic to run every frame
Definition turret.qh:37
int spawnflags
Definition turret.qh:26
string netname
Definition powerups.qc:20
float anim_start_time
Definition items.qc:15
entity owner
Definition main.qh:87
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:242
#define IS_DEAD(s)
Definition player.qh:245
#define autocvar_sv_gravity
Definition stats.qh:421
float turret_tag_fire_update(entity this)
Definition util.qc:9
vector real_origin(entity ent)
Definition util.qc:148
const int FL_PROJECTILE
Definition constants.qh:85
const int FL_NOTARGET
Definition constants.qh:76
const int INITPRIO_FINDTARGET
Definition constants.qh:96
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
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:981
#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:19
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:124
ent angles
Definition ent_cs.qc:121
angles_y
Definition ent_cs.qc:119
solid
Definition ent_cs.qc:165
const float FLOAT_MAX
Definition float.qh:3
#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:118
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define LOG_TRACE(...)
Definition log.qh:76
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)
void WriteByte(float data, float dest, float desto)
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_WALK
Definition movetypes.qh:132
const int MOVETYPE_FLYMISSILE
Definition movetypes.qh:138
float move_movetype
Definition movetypes.qh:76
const int MOVETYPE_NOCLIP
Definition movetypes.qh:137
const int MOVETYPE_TOSS
Definition movetypes.qh:135
const int MOVETYPE_BOUNCE
Definition movetypes.qh:139
#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:67
#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:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
float sys_frametime
Definition common.qh:57
float respawntime
Definition items.qh:30
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
Definition common.qc:87
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:28
IntrusiveList g_projectiles
Definition common.qh:58
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:34
const float VOL_BASE
Definition sound.qh:36
const int CH_SHOTS
Definition sound.qh:14
const int CH_WEAPON_A
Definition sound.qh:7
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:153
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:69
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)
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:87
void turret_think(entity this)
bool turret_send(entity this, entity to, float sf)
#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
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
#define SAME_TEAM(a, b)
Definition teams.qh:241
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
Definition turret.qh:106
const int TFL_TARGETSELECT_ANGLELIMITS
Definition turret.qh:85
const int TUR_FLAG_SUPPORT
Definition turret.qh:147
const int TFL_FIRECHECK_DISTANCES
Definition turret.qh:113
const int TFL_DMG_HEADSHAKE
Definition turret.qh:172
const int TSF_SUSPENDED
Definition turret.qh:176
const int TNSF_FULL_UPDATE
Definition turret.qh:193
const int TFL_TARGETSELECT_TEAMCHECK
Definition turret.qh:87
const int TFL_FIRECHECK_AMMO_OWN
Definition turret.qh:120
const int TUR_FLAG_MISSILE
Definition turret.qh:146
int target_validate_flags
Definition turret.qh:79
const int TFL_DMG_YES
Definition turret.qh:166
const int TUR_FLAG_ISTURRET
Definition turret.qh:152
const int TFL_SHOOT_VOLLYALWAYS
Definition turret.qh:129
const int TFL_FIRECHECK_DEAD
Definition turret.qh:112
const int TFL_AIM_LEAD
Definition turret.qh:99
const int TFL_TARGETSELECT_PLAYERS
Definition turret.qh:82
const int TFL_AIM_SPLASH
Definition turret.qh:98
int track_flags
Definition turret.qh:105
const int TNSF_ANG
Definition turret.qh:187
const int TSF_NO_AMMO_REGEN
Definition turret.qh:178
const int TUR_FLAG_PLAYER
Definition turret.qh:145
int turret_flags
Definition turret.qh:135
const int TFL_FIRECHECK_AIMDIST
Definition turret.qh:115
const int TFL_TARGETSELECT_RANGELIMITS
Definition turret.qh:86
const int TNSF_AVEL
Definition turret.qh:188
const int TFL_SHOOT_CLEARTARGET
Definition turret.qh:131
const int TFL_AIM_ZPREDICT
Definition turret.qh:101
const int TFL_FIRECHECK_AMMO_OTHER
Definition turret.qh:121
const int TFL_TRACK_ROTATE
Definition turret.qh:108
int shoot_flags
Definition turret.qh:126
int target_select_flags
Definition turret.qh:78
const int TFL_DMG_AIMSHAKE
Definition turret.qh:171
const int TFL_TARGETSELECT_NO
Definition turret.qh:80
const int TFL_FIRECHECK_REFIRE
Definition turret.qh:122
const int TSL_NO_RESPAWN
Definition turret.qh:180
const int TFL_TARGETSELECT_VEHICLES
Definition turret.qh:93
const int TNSF_SETUP
Definition turret.qh:186
const int TFL_AIM_SHOTTIMECOMPENSATE
Definition turret.qh:100
const int TFL_AMMO_ENERGY
Definition turret.qh:157
const int TNSF_MOVE
Definition turret.qh:189
const int TFL_FIRECHECK_LOS
Definition turret.qh:114
const int TFL_AIM_NO
Definition turret.qh:97
const int TFL_DMG_RETALIATE
Definition turret.qh:168
const int TFL_TARGETSELECT_LOS
Definition turret.qh:81
const int TFL_FIRECHECK_NO
Definition turret.qh:123
const int TNSF_ANIM
Definition turret.qh:191
const int TFL_AMMO_RECHARGE
Definition turret.qh:160
int damage_flags
Definition turret.qh:164
const int TFL_DMG_DEATH_NORESPAWN
Definition turret.qh:173
const int TNSF_STATUS
Definition turret.qh:185
const int TFL_FIRECHECK_TEAMCHECK
Definition turret.qh:118
const int TFL_TRACK_PITCH
Definition turret.qh:107
const int TUR_FLAG_SPLASH
Definition turret.qh:138
const int TUR_FLAG_MOVE
Definition turret.qh:150
int firecheck_flags
Definition turret.qh:111
int aim_flags
Definition turret.qh:96
const int TFL_TARGETSELECT_MISSILES
Definition turret.qh:83
const int TFL_AIM_SIMPLE
Definition turret.qh:102
const int TFL_TARGETSELECT_NOTURRETS
Definition turret.qh:90
const int TFL_TARGETSELECT_OWNTEAM
Definition turret.qh:89
const int TUR_FLAG_MEDPROJ
Definition turret.qh:143
const int TFL_SHOOT_CUSTOM
Definition turret.qh:132
const int TFL_TARGETSELECT_MISSILESONLY
Definition turret.qh:92
const int TFL_FIRECHECK_AFF
Definition turret.qh:119
#define ammo_flags
Definition turret.qh:155
const int TFL_SHOOT_HITALLVALID
Definition turret.qh:130
#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:22
#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:73
int max_shot_distance
Definition weapon.qh:203
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
float attack_finished_single[MAX_WEAPONSLOTS]
void DropToFloor_QC_DelayedInit(entity this)
Definition world.qc:2407
void InitializeEntity(entity e, void(entity this) func, int order)
Definition world.qc:2209