33 prep += this.
enemy.velocity * (impact_time + mintime);
40 float vz = this.
enemy.velocity.z;
50 pre_pos += this.
enemy.velocity * mintime;
66 float s_score = (_turret.enemy == _target);
70 float score = (d_score * _turret.target_select_rangebias)
71 + (s_score * _turret.target_select_samebias);
88 if (_turret.tur_defend)
91 ikr =
vlen(_turret.origin - _turret.tur_defend.origin);
92 d_score = 1 - d_dist / _turret.target_range;
97 ikr = _turret.target_range_optimal;
101 a_score = 1 -
tvt_thadf / _turret.aim_maxrot;
103 if (_turret.target_select_missilebias > 0 && (_target.flags &
FL_PROJECTILE))
106 if (_turret.target_select_playerbias > 0 &&
IS_CLIENT(_target))
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);
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);
119 if (
vdist(_turret.tur_shotorg -
real_origin(_target), >, _turret.target_range))
126 string sd =
ftos(d_score);
127 d_score *= _turret.target_select_rangebias;
128 string sdt =
ftos(d_score);
134 string sa =
ftos(a_score);
135 a_score *= _turret.target_select_anglebias;
136 string sat =
ftos(a_score);
138 string sm =
ftos(m_score);
139 m_score *= _turret.target_select_missilebias;
140 string smt =
ftos(m_score);
142 string sp =
ftos(p_score);
143 p_score *= _turret.target_select_playerbias;
144 string spt =
ftos(p_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");
255 float true_limit = (limit !=
RES_LIMIT_NONE) ? limit : targ.max_health;
283 this.
ammo = this.ammo_max;
293 setorigin(
this, this.
origin);
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;
381 ent.tur_head.avelocity =
'0 0 0';
383 ent.tur_head.angles =
'0 0 0';
386 string unitname = ent.netname;
387 #define X(class, prefix, fld, type) ent.fld = cvar(strcat("g_turrets_unit_", prefix, "_", #fld));
391 ent.ammo_max *= ent.turret_scale_ammo;
392 ent.ammo_recharge *= ent.turret_scale_ammo;
393 ent.aim_speed *= ent.turret_scale_aim;
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;
424 this.owner.shot_radius,
427 this.owner.shot_force,
428 this.projectiledeathtype,
433 this.owner.tur_debug_dmg_t_h += d;
434 this.owner.tur_debug_dmg_t_f +=
this.owner.shot_dmg;
461 setorigin(proj, actor.tur_shotorg);
462 setsize(proj,
'-0.5 -0.5 -0.5' * _size,
'0.5 0.5 0.5' * _size);
464 proj.realowner = actor;
465 proj.bot_dodge =
true;
466 proj.bot_dodgerating = actor.shot_dmg;
469 proj.nextthink =
time + 9;
471 proj.velocity =
normalize(actor.tur_shotdir_updated +
randomvec() * actor.shot_spread) * actor.shot_speed;
475 proj.enemy = actor.enemy;
476 proj.projectiledeathtype = _death;
502 t_turret.tur_shotdir_updated =
v_forward;
503 t_turret.tur_dist_enemy =
vlen(t_turret.tur_shotorg - enemy_pos);
504 t_turret.tur_dist_aimpos =
vlen(t_turret.tur_shotorg - t_turret.tur_aimpos);
519 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);
521 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);
523 t_turret.tur_impacttime =
vlen(t_turret.tur_shotorg -
trace_endpos) / t_turret.shot_speed;
541 target_angle = this.
idle_aim - (
'1 0 0' * this.aim_maxpitch);
564 switch (this.track_type)
570 this.
tur_head.angles.x +=
bound(-f_tmp, move_angle.x, f_tmp);
571 if (this.
tur_head.angles.x >
this.aim_maxpitch)
572 this.
tur_head.angles.x = this.aim_maxpitch;
574 if (this.
tur_head.angles.x < -
this.aim_maxpitch)
575 this.
tur_head.angles.x = this.aim_maxpitch;
580 this.
tur_head.angles.y +=
bound(-f_tmp, move_angle.y, f_tmp);
581 if (this.
tur_head.angles.y >
this.aim_maxrot)
582 this.
tur_head.angles.y = this.aim_maxrot;
584 if (this.
tur_head.angles.y < -
this.aim_maxrot)
585 this.
tur_head.angles.y = this.aim_maxrot;
594 move_angle.x =
bound(-this.aim_speed, move_angle.x *
this.track_accel_pitch * f_tmp,
this.aim_speed);
595 move_angle.y =
bound(-this.aim_speed, move_angle.y *
this.track_accel_rot * f_tmp,
this.aim_speed);
596 move_angle = this.
tur_head.avelocity * this.track_blendrate + move_angle * (1 - this.track_blendrate);
600 move_angle.y =
bound(-this.aim_speed, move_angle.y,
this.aim_speed);
601 move_angle.x =
bound(-this.aim_speed, move_angle.x,
this.aim_speed);
608 this.
tur_head.avelocity.x = move_angle.x;
609 if (this.
tur_head.angles.x +
this.tur_head.avelocity.x *
frametime >
this.aim_maxpitch)
612 this.
tur_head.angles.x = this.aim_maxpitch;
617 if (this.
tur_head.angles.x +
this.tur_head.avelocity.x *
frametime < -
this.aim_maxpitch)
620 this.
tur_head.angles.x = -this.aim_maxpitch;
629 this.
tur_head.avelocity.y = move_angle.y;
631 if (this.
tur_head.angles.y +
this.tur_head.avelocity.y *
frametime >
this.aim_maxrot)
634 this.
tur_head.angles.y = this.aim_maxrot;
639 if (this.
tur_head.angles.y +
this.tur_head.avelocity.y *
frametime < -
this.aim_maxrot)
642 this.
tur_head.angles.y = -this.aim_maxrot;
685 if (e_target.owner == e_turret || e_target == e_turret.realowner)
688 if (!
checkpvs(e_target.origin, e_turret))
690 if (e_target.alpha != 0 && e_target.alpha <= 0.3)
693 if (
MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags))
720 && e_target.owner.tur_head == e_target
742 if (
DIFF_TEAM(e_turret, e_target.aiment))
751 if (
SAME_TEAM(e_turret, e_target.aiment))
760 if (
tvt_dist < e_turret.target_range_min)
762 if (
tvt_dist > e_turret.target_range)
767 tvt_thadv =
angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target.origin);
790 traceline(e_turret.origin +
'0 0 16', v_tmp, 0, e_turret);
795 if (e_target.classname ==
"grapplinghook")
802#ifdef TURRET_DEBUG_TARGETSELECT
803 LOG_TRACE(
"Target:", e_target.netname,
" is a valid target for ", e_turret.netname);
819 e_enemy = this.
enemy;
820 m_score = this.turret_score_target(
this, e_enemy) * this.target_select_samebias;
825 e = findradius(this.
origin, this.target_range);
832 for (; e; e = e.chain)
839 score = this.turret_score_target(
this, e);
840 if (score > m_score && score > 0)
891 && this.
ammo >= this.shot_dmg)
901 && this.
ammo < this.shot_dmg)
906 && this.
enemy.ammo >=
this.enemy.ammo_max)
938 if (this.shot_volly > 1
940 && this.
ammo < this.shot_dmg * this.shot_volly)
957 return this.turret_firecheckfunc(
this);
971 this.
ammo -= this.shot_dmg;
981 if (this.shot_volly > 1)
998 if (this.tur_debug_tmr1 <
time)
1001 paint_target(this.
enemy, 128, this.tur_debug_rvec, 0.9);
1002 paint_target(
this, 256, this.tur_debug_rvec, 0.9);
1003 this.tur_debug_tmr1 =
time + 1;
1009 && this.
ammo < this.ammo_max)
1025 for (
entity e = findradius(this.
origin, this.target_range); e; e = e.chain)
1085 float do_target_scan = 0;
1151 this.
team = actor.team;
1179 cvar_set(
"g_turrets_reloadcvars",
"0");
1185 #define TRY(x) (x) ? (x)
1186 tur.respawntime =
max (-1, (
TRY(tur.respawntime) : 60 ));
1187 tur.shot_refire =
bound(0.01, (
TRY(tur.shot_refire) : 1 ), 9999);
1188 tur.shot_dmg =
max (1, (
TRY(tur.shot_dmg) : tur.shot_refire * 50 ));
1189 tur.shot_radius =
max (1, (
TRY(tur.shot_radius) : tur.shot_dmg * 0.5 ));
1190 tur.shot_speed =
max (1, (
TRY(tur.shot_speed) : 2500 ));
1191 tur.shot_spread =
bound(0.0001, (
TRY(tur.shot_spread) : 0.0125 ), 500);
1192 tur.shot_force =
bound(0.001, (
TRY(tur.shot_force) : tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000);
1193 tur.shot_volly =
bound(1, (
TRY(tur.shot_volly) : 1 ),
floor(tur.ammo_max / tur.shot_dmg));
1194 tur.shot_volly_refire =
bound(tur.shot_refire, (
TRY(tur.shot_volly_refire) : tur.shot_refire * tur.shot_volly ), 60);
1198 tur.aim_maxrot =
bound(0, (
TRY(tur.aim_maxrot) : 90 ), 360);
1199 tur.aim_maxpitch =
bound(0, (
TRY(tur.aim_maxpitch) : 20 ), 90);
1200 tur.aim_speed =
bound(0.1, (
TRY(tur.aim_speed) : 36 ), 1000);
1201 tur.aim_firetolerance_dist =
bound(0.1, (
TRY(tur.aim_firetolerance_dist) : 5 + (tur.shot_radius * 2) ),
max_shot_distance);
1202 tur.target_select_rangebias =
bound(-10, (
TRY(tur.target_select_rangebias) : 1 ), 10);
1203 tur.target_select_samebias =
bound(-10, (
TRY(tur.target_select_samebias) : 1 ), 10);
1204 tur.target_select_anglebias =
bound(-10, (
TRY(tur.target_select_anglebias) : 1 ), 10);
1205 tur.target_select_missilebias =
bound(-10, (
TRY(tur.target_select_missilebias) : 1 ), 10);
1206 tur.target_select_playerbias =
bound(-10, (
TRY(tur.target_select_playerbias) : 1 ), 10);
1207 tur.ammo_max =
max (tur.shot_dmg, (
TRY(tur.ammo_max) : tur.shot_dmg * 10 ));
1208 tur.ammo_recharge =
max (0, (
TRY(tur.ammo_recharge) : tur.shot_dmg * 0.5 ));
1214 vector path_extra_size =
'1 1 1' * range;
1215 return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.
absmin - path_extra_size, this.
absmax + path_extra_size);
1225 e.nextthink =
time + 2;
1229 if (targ.classname ==
"turret_checkpoint")
1235 LOG_TRACE(
"Turret has invalid defendpoint!");
1271 if (!this.shot_refire) this.shot_refire = 1;
1288 this.aim_speed =
bound(0.1, (!this.aim_speed ? 180 : this.aim_speed), 1000);
1290 if (!this.track_accel_pitch)
1291 this.track_accel_pitch = 0.5;
1292 if (!this.track_accel_rot)
1293 this.track_accel_rot = 0.5;
1294 if (!this.track_blendrate)
1295 this.track_blendrate = 0.35;
1321 _setmodel(
this, tur.model);
1330 this.
ammo = this.ammo_max;
1345 _setmodel(this.
tur_head, tur.head_model);
1346 setsize(this.
tur_head,
'0 0 0',
'0 0 0');
1348 setattachment(this.
tur_head,
this,
"tag_head");
1364 while (
vdist(this.tur_debug_rvec, <, 2))
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);
ERASEABLE float anglemods(float v)
ERASEABLE vector angleofs3(vector from, vector ang, vector to)
#define angleofs(from, to)
ERASEABLE vector shortangle_vxy(vector ang1, vector ang2)
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
IntrusiveList g_bot_targets
IntrusiveList g_bot_dodge
#define MUTATOR_CALLHOOK(id,...)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
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
string m_name
human readable name
vector m_maxs
turret hitbox size
virtual void tr_death()
(SERVER) called when turret dies
virtual void tr_setup()
(BOTH) setup turret data
virtual void tr_precache()
(BOTH) precaches models/sounds used by this turret
vector m_mins
turret hitbox size
virtual void tr_think()
(SERVER) logic to run every frame
#define autocvar_sv_gravity
float turret_tag_fire_update(entity this)
Update this.tur_shotorg by getting up2date bone info NOTICE this func overwrites the global v_forward...
vector real_origin(entity ent)
const int INITPRIO_FINDTARGET
float checkpvs(vector viewpos, entity viewee)
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)
float autocvar_g_friendlyfire
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_EACH(this, cond, body)
#define WriteHeader(to, id)
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
#define WriteRegistered(r, to, it)
void set_movetype(entity this, int mt)
const int MOVETYPE_FLYMISSILE
const int MOVETYPE_NOCLIP
const int MOVETYPE_BOUNCE
#define new_pure(class)
purely logical entities (not linked to the area grid)
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
#define PROJECTILE_TOUCH(e, t)
IntrusiveList g_projectiles
#define PROJECTILE_MAKETRIGGER(e)
#define sound(e, c, s, v, a)
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)
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)
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 turret_targetscore_generic(entity _turret, entity _target)
void turret_think(entity this)
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)
float target_validate_time
float autocvar_g_turrets_targetscan_maxdelay
bool autocvar_g_turrets_nofire
float autocvar_g_turrets_targetscan_mindelay
const float TFL_TRACKTYPE_FLUIDINERTIA
bool autocvar_g_turrets_reloadcvars
const float TFL_TRACKTYPE_FLUIDPRECISE
const float TFL_TRACKTYPE_STEPMOTOR
float autocvar_g_turrets_aimidle_delay
float tur_dist_impact_to_aimpos
const int TFL_TRACK_NO
don't move head
const int TFL_TARGETSELECT_ANGLELIMITS
apply extra angular limits to target selection
const int TUR_FLAG_SUPPORT
supports other units
const int TFL_FIRECHECK_DISTANCES
another range check
const int TFL_DMG_HEADSHAKE
damage shakes head
const int TNSF_FULL_UPDATE
const int TFL_TARGETSELECT_TEAMCHECK
don't attack teammates
const int TFL_FIRECHECK_AMMO_OWN
own ammo needs to be larger than damage dealt
const int TUR_FLAG_MISSILE
can damage missiles
int target_validate_flags
const int TFL_DMG_YES
can be damaged
const int TUR_FLAG_ISTURRET
identifies this unit as a turret
const int TFL_SHOOT_VOLLYALWAYS
always do a full volly, even if target is lost
const int TFL_FIRECHECK_DEAD
don't attack dead targets (zombies?)
const int TFL_AIM_LEAD
try to predict target movement
const int TFL_TARGETSELECT_PLAYERS
target players
const int TFL_AIM_SPLASH
aim for ground around the target's feet
const int TSF_NO_AMMO_REGEN
disable builtin ammo regeneration
const int TUR_FLAG_PLAYER
can damage players
const int TFL_FIRECHECK_AIMDIST
consider distance impactpoint<->aimspot
const int TFL_TARGETSELECT_RANGELIMITS
limit target selection range
const int TFL_SHOOT_CLEARTARGET
lose target after attack (after volly is done if in volly mode)
const int TFL_AIM_ZPREDICT
predict target's z position at impact
const int TFL_FIRECHECK_AMMO_OTHER
target's ammo needs to be less than max
const int TFL_TRACK_ROTATE
rotate head
const int TFL_DMG_AIMSHAKE
damage throws off aim
const int TFL_TARGETSELECT_NO
don't automatically find targets
const int TFL_FIRECHECK_REFIRE
check single attack finished delays
const int TSL_NO_RESPAWN
don't re-spawn
const int TFL_TARGETSELECT_VEHICLES
target manned vehicles
const int TFL_AIM_SHOTTIMECOMPENSATE
compensate for shot traveltime when leading
const int TFL_AMMO_ENERGY
uses power
const int TFL_FIRECHECK_LOS
line of sight
const int TFL_AIM_NO
no aiming
const int TFL_DMG_RETALIATE
target attackers
const int TFL_TARGETSELECT_LOS
require line of sight to find targets
const int TFL_FIRECHECK_NO
no prefire checks
const int TFL_AMMO_RECHARGE
regenerates ammo
const int TFL_DMG_DEATH_NORESPAWN
no re-spawning
const int TFL_FIRECHECK_TEAMCHECK
don't attack teammates
const int TFL_TRACK_PITCH
pitch head
const int TUR_FLAG_SPLASH
can deal splash damage
const int TUR_FLAG_MOVE
can move
const int TFL_TARGETSELECT_MISSILES
target projectiles
const int TFL_AIM_SIMPLE
aim at player's current location
const int TFL_TARGETSELECT_NOTURRETS
don't attack other turrets
const int TFL_TARGETSELECT_OWNTEAM
only attack teammates
const int TUR_FLAG_MEDPROJ
turret fires medium projectiles
const int TFL_SHOOT_CUSTOM
custom attacking
const int TFL_TARGETSELECT_MISSILESONLY
only attack missiles
const int TFL_FIRECHECK_AFF
try to avoid any friendly fire
const int TFL_SHOOT_HITALLVALID
loop through all valid targets
#define get_turretinfo(i)
#define TR_PROPS_COMMON(P, class, prefix)
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
ERASEABLE float boxesoverlap(vector m1, vector m2, vector m3, vector m4)
requires that m2>m1 in all coordinates, and that m4>m3
entity weaponentities[MAX_WEAPONSLOTS]
float attack_finished_single[MAX_WEAPONSLOTS]
void DropToFloor_QC_DelayedInit(entity this)
void InitializeEntity(entity e, void(entity this) func, int order)