15 float distance, impact_time = 0, i, mintime;
35 impact_time = distance / this.shot_speed;
37 prep = pre_pos + (this.
enemy.velocity * (impact_time + mintime));
45 vz = this.
enemy.velocity_z;
55 pre_pos = pre_pos + this.
enemy.velocity * mintime;
72 float s_score = 0, d_score;
74 if (_turret.enemy == _target) s_score = 1;
78 score = (d_score * _turret.target_select_rangebias) +
79 (s_score * _turret.target_select_samebias);
97 if (_turret.tur_defend)
100 ikr =
vlen(_turret.origin - _turret.tur_defend.origin);
101 d_score = 1 - d_dist / _turret.target_range;
106 ikr = _turret.target_range_optimal;
110 a_score = 1 -
tvt_thadf / _turret.aim_maxrot;
112 if ((_turret.target_select_missilebias > 0) && (_target.flags &
FL_PROJECTILE))
115 if ((_turret.target_select_playerbias > 0) &&
IS_CLIENT(_target))
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);
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);
128 if(
vdist((_turret.tur_shotorg -
real_origin(_target)), >, _turret.target_range))
135 string sd,sa,sm,sp,ss;
136 string sdt,sat,smt,spt;
139 d_score *= _turret.target_select_rangebias;
147 a_score *= _turret.target_select_anglebias;
151 m_score *= _turret.target_select_missilebias;
155 p_score *= _turret.target_select_playerbias;
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");
269 float true_limit = ((limit !=
RES_LIMIT_NONE) ? limit : targ.max_health);
297 this.
ammo = this.ammo_max;
307 setorigin(
this, this.
origin);
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;
397 ent.tur_head.avelocity =
'0 0 0';
399 ent.tur_head.angles =
'0 0 0';
402 string unitname = ent.netname;
403 #define X(class, prefix, fld, type) ent.fld = cvar(strcat("g_turrets_unit_", prefix, "_", #fld));
407 ent.ammo_max *= ent.turret_scale_ammo;
408 ent.ammo_recharge *= ent.turret_scale_ammo;
409 ent.aim_speed *= ent.turret_scale_aim;
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;
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;
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);
464 setorigin(proj, actor.tur_shotorg);
465 setsize(proj,
'-0.5 -0.5 -0.5' * _size,
'0.5 0.5 0.5' * _size);
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;
478 proj.enemy = actor.enemy;
479 proj.projectiledeathtype = _death;
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);
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);
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);
526 t_turret.tur_impacttime =
vlen(t_turret.tur_shotorg -
trace_endpos) / t_turret.shot_speed;
547 target_angle = this.
idle_aim - (
'1 0 0' * this.aim_maxpitch);
570 switch(this.track_type)
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;
580 if(this.
tur_head.angles_x < -
this.aim_maxpitch)
581 this.
tur_head.angles_x = this.aim_maxpitch;
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;
590 if(this.
tur_head.angles_y < -
this.aim_maxrot)
591 this.
tur_head.angles_y = this.aim_maxrot;
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));
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);
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)
621 this.
tur_head.angles_x = this.aim_maxpitch;
626 if((this.
tur_head.angles_x +
this.tur_head.avelocity_x *
frametime) < -
this.aim_maxpitch)
629 this.
tur_head.angles_x = -this.aim_maxpitch;
638 this.
tur_head.avelocity_y = move_angle_y;
640 if((this.
tur_head.angles_y +
this.tur_head.avelocity_y *
frametime) >
this.aim_maxrot)
643 this.
tur_head.angles_y = this.aim_maxrot;
648 if((this.
tur_head.angles_y +
this.tur_head.avelocity_y *
frametime) < -
this.aim_maxrot)
651 this.
tur_head.angles_y = -this.aim_maxrot;
697 if(e_target.owner == e_turret || e_target == e_turret.realowner)
700 if(!
checkpvs(e_target.origin, e_turret))
703 if(e_target.alpha != 0 && e_target.alpha <= 0.3)
706 if(
MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags))
739 if(e_target.owner.tur_head == e_target)
740 if(e_target.team != e_turret.team)
757 if (e_target.team != e_turret.team)
760 if (e_turret.team != e_target.owner.team)
763 if (e_turret.team != e_target.aiment.team)
768 if (e_target.team == e_turret.team)
771 if (e_turret.team == e_target.owner.team)
774 if (e_turret.team == e_target.aiment.team)
783 if (
tvt_dist < e_turret.target_range_min)
786 if (
tvt_dist > e_turret.target_range)
791 tvt_thadv =
angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target.origin);
805 if (
fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
808 if (
fabs(tvt_tadv_y) > e_turret.aim_maxrot)
815 v_tmp =
real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
817 traceline(e_turret.origin +
'0 0 16', v_tmp, 0, e_turret);
823 if (e_target.classname ==
"grapplinghook")
831#ifdef TURRET_DEBUG_TARGETSELECT
832 LOG_TRACE(
"Target:",e_target.netname,
" is a valid target for ",e_turret.netname);
848 e_enemy = this.
enemy;
849 m_score = this.turret_score_target(
this,e_enemy) * this.target_select_samebias;
854 e = findradius(this.
origin, this.target_range);
868 score = this.turret_score_target(
this,e);
869 if ((score > m_score) && (score > 0))
921 if(this.
ammo >= this.shot_dmg)
931 if (this.
ammo < this.shot_dmg)
936 if (this.
enemy.ammo >=
this.enemy.ammo_max)
968 if (this.shot_volly > 1)
970 if (this.
ammo < (this.shot_dmg * this.shot_volly))
985 return this.turret_firecheckfunc(
this);
1000 this.
ammo -= this.shot_dmg;
1010 if (this.shot_volly > 1)
1026 if (this.tur_debug_tmr1 <
time)
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;
1036 if (this.
ammo < this.ammo_max)
1119 float do_target_scan = 0;
1185 this.
team = actor.team;
1213 cvar_set(
"g_turrets_reloadcvars",
"0");
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);
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 ));
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);
1259 e.nextthink =
time + 2;
1263 if(targ.classname ==
"turret_checkpoint")
1269 LOG_TRACE(
"Turret has invalid defendpoint!");
1304 if(!this.shot_refire) { this.shot_refire = 1; }
1321 this.aim_speed =
bound(0.1, ((!this.aim_speed) ? 180 : this.aim_speed), 1000);
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; }
1351 _setmodel(
this, tur.model);
1360 this.
ammo = this.ammo_max;
1375 _setmodel(this.
tur_head, tur.head_model);
1376 setsize(this.
tur_head,
'0 0 0',
'0 0 0');
1378 setattachment(this.
tur_head,
this,
"tag_head");
1394 while(
vdist(this.tur_debug_rvec, <, 2))
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);
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)
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)
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)
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)
bool turret_send(entity this, entity to, float sf)
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_TARGETSELECT_ANGLELIMITS
const int TUR_FLAG_SUPPORT
const int TFL_FIRECHECK_DISTANCES
const int TFL_DMG_HEADSHAKE
const int TNSF_FULL_UPDATE
const int TFL_TARGETSELECT_TEAMCHECK
const int TFL_FIRECHECK_AMMO_OWN
const int TUR_FLAG_MISSILE
int target_validate_flags
const int TUR_FLAG_ISTURRET
const int TFL_SHOOT_VOLLYALWAYS
const int TFL_FIRECHECK_DEAD
const int TFL_TARGETSELECT_PLAYERS
const int TSF_NO_AMMO_REGEN
const int TUR_FLAG_PLAYER
const int TFL_FIRECHECK_AIMDIST
const int TFL_TARGETSELECT_RANGELIMITS
const int TFL_SHOOT_CLEARTARGET
const int TFL_AIM_ZPREDICT
const int TFL_FIRECHECK_AMMO_OTHER
const int TFL_TRACK_ROTATE
const int TFL_DMG_AIMSHAKE
const int TFL_TARGETSELECT_NO
const int TFL_FIRECHECK_REFIRE
const int TFL_TARGETSELECT_VEHICLES
const int TFL_AIM_SHOTTIMECOMPENSATE
const int TFL_AMMO_ENERGY
const int TFL_FIRECHECK_LOS
const int TFL_DMG_RETALIATE
const int TFL_TARGETSELECT_LOS
const int TFL_FIRECHECK_NO
const int TFL_AMMO_RECHARGE
const int TFL_DMG_DEATH_NORESPAWN
const int TFL_FIRECHECK_TEAMCHECK
const int TFL_TRACK_PITCH
const int TUR_FLAG_SPLASH
const int TFL_TARGETSELECT_MISSILES
const int TFL_TARGETSELECT_NOTURRETS
const int TFL_TARGETSELECT_OWNTEAM
const int TUR_FLAG_MEDPROJ
const int TFL_SHOOT_CUSTOM
const int TFL_TARGETSELECT_MISSILESONLY
const int TFL_FIRECHECK_AFF
const int TFL_SHOOT_HITALLVALID
#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)