]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/turrets/sv_turrets.qc
Merge branch 'master' into Mario/monsters
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / turrets / sv_turrets.qc
index de6ff767e523e7a940a862a00bc7d819c8ef5795..1a51bcc63f60eba414d296fad9752599be6077a0 100644 (file)
@@ -1,3 +1,4 @@
+#include "sv_turrets.qh"
 #ifdef SVQC
 #include <server/autocvars.qh>
 
@@ -32,7 +33,7 @@ vector turret_aim_generic(entity this)
 
                        if(this.aim_flags & TFL_AIM_ZPREDICT)
                        if(!IS_ONGROUND(this.enemy))
-                       if(this.enemy.movetype == MOVETYPE_WALK || this.enemy.movetype == MOVETYPE_TOSS || this.enemy.movetype == MOVETYPE_BOUNCE)
+                       if(this.enemy.move_movetype == MOVETYPE_WALK || this.enemy.move_movetype == MOVETYPE_TOSS || this.enemy.move_movetype == MOVETYPE_BOUNCE)
                        {
                                float vz;
                                prep_z = pre_pos_z;
@@ -166,48 +167,53 @@ float turret_targetscore_generic(entity _turret, entity _target)
 // Generic damage handling
 void turret_hide(entity this)
 {
-       self.effects   |= EF_NODRAW;
-       self.nextthink = time + self.respawntime - 0.2;
-       setthink(self, turret_respawn);
+       this.effects   |= EF_NODRAW;
+       this.nextthink = time + this.respawntime - 0.2;
+       setthink(this, turret_respawn);
 }
 
 void turret_die(entity this)
 {
-       self.deadflag             = DEAD_DEAD;
-       self.tur_head.deadflag = self.deadflag;
+       this.deadflag             = DEAD_DEAD;
+       this.tur_head.deadflag = this.deadflag;
 
 // Unsolidify and hide real parts
-       self.solid                       = SOLID_NOT;
-       self.tur_head.solid      = self.solid;
+       this.solid                       = SOLID_NOT;
+       this.tur_head.solid      = this.solid;
 
-       self.event_damage                 = func_null;
-       self.takedamage                  = DAMAGE_NO;
+       this.event_damage                 = func_null;
+       this.event_heal = func_null;
+       this.takedamage                  = DAMAGE_NO;
 
-       self.health                      = 0;
+       SetResourceExplicit(this, RES_HEALTH, 0);
 
 // Go boom
-       //RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,DEATH_TURRET,world);
+       //RadiusDamage (this,this, min(this.ammo,50),min(this.ammo,50) * 0.25,250,NULL,min(this.ammo,50)*5,DEATH_TURRET,NULL);
 
-       Turret tur = get_turretinfo(self.m_id);
-       if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN)
+       Turret tur = get_turretinfo(this.m_id);
+       if(this.damage_flags & TFL_DMG_DEATH_NORESPAWN)
        {
-               tur.tr_death(tur, self);
+               // do a simple explosion effect here, since CSQC can't do it on a to-be-removed entity
+               sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+               Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
+
+               tur.tr_death(tur, this);
 
-               remove(self.tur_head);
-               remove(self);
+               delete(this.tur_head);
+               delete(this);
        }
        else
        {
                // Setup respawn
-               self.SendFlags    |= TNSF_STATUS;
-               self.nextthink   = time + 0.2;
-               setthink(self, turret_hide);
+               this.SendFlags    |= TNSF_STATUS;
+               this.nextthink   = time + 0.2;
+               setthink(this, turret_hide);
 
-               tur.tr_death(tur, self);
+               tur.tr_death(tur, this);
        }
 }
 
-void turret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
+void turret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce)
 {
        // Enough already!
        if(this.deadflag == DEAD_DEAD)
@@ -225,7 +231,7 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage,
                        return;
        }
 
-       this.health -= damage;
+       TakeResource(this, RES_HEALTH, damage);
 
        // thorw head slightly off aim when hit?
        if (this.damage_flags & TFL_DMG_HEADSHAKE)
@@ -239,10 +245,12 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage,
        if (this.turret_flags & TUR_FLAG_MOVE)
                this.velocity = this.velocity + vforce;
 
-       if (this.health <= 0)
+       if (GetResource(this, RES_HEALTH) <= 0)
        {
                this.event_damage                 = func_null;
                this.tur_head.event_damage = func_null;
+               this.event_heal = func_null;
+               this.tur_head.event_heal = func_null;
                this.takedamage                  = DAMAGE_NO;
                this.nextthink = time;
                setthink(this, turret_die);
@@ -251,33 +259,45 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage,
        this.SendFlags  |= TNSF_STATUS;
 }
 
+bool turret_heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RES_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResource(targ, RES_HEALTH) <= 0 || GetResource(targ, RES_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RES_HEALTH, amount, true_limit);
+       targ.SendFlags |= TNSF_STATUS;
+       return true;
+}
+
 void turret_think(entity this);
 void turret_respawn(entity this)
 {
        // Make sure all parts belong to the same team since
        // this function doubles as "teamchange" function.
-       self.tur_head.team      = self.team;
-       self.effects                       &= ~EF_NODRAW;
-       self.deadflag                           = DEAD_NO;
-       self.effects                            = EF_LOWPRECISION;
-       self.solid                                      = SOLID_BBOX;
-       self.takedamage                         = DAMAGE_AIM;
-       self.event_damage                       = turret_damage;
-       self.avelocity                          = '0 0 0';
-       self.tur_head.avelocity         = self.avelocity;
-       self.tur_head.angles            = self.idle_aim;
-       self.health                                     = self.max_health;
-       self.enemy                                      = world;
-       self.volly_counter                      = self.shot_volly;
-       self.ammo                                       = self.ammo_max;
-
-       self.nextthink = time + self.ticrate;
-       setthink(self, turret_think);
-
-       self.SendFlags = TNSF_FULL_UPDATE;
-
-       Turret tur = get_turretinfo(self.m_id);
-       tur.tr_setup(tur, self);
+       this.tur_head.team      = this.team;
+       this.effects                       &= ~EF_NODRAW;
+       this.deadflag                           = DEAD_NO;
+       this.effects                            = EF_LOWPRECISION;
+       this.solid                                      = SOLID_BBOX;
+       this.takedamage                         = DAMAGE_AIM;
+       this.event_damage                       = turret_damage;
+       this.event_heal                         = turret_heal;
+       this.avelocity                          = '0 0 0';
+       this.tur_head.avelocity         = this.avelocity;
+       this.tur_head.angles            = this.idle_aim;
+       SetResourceExplicit(this, RES_HEALTH, this.max_health);
+       this.enemy                                      = NULL;
+       this.volly_counter                      = this.shot_volly;
+       this.ammo                                       = this.ammo_max;
+
+       this.nextthink = time + this.ticrate;
+       setthink(this, turret_think);
+
+       this.SendFlags = TNSF_FULL_UPDATE;
+
+       Turret tur = get_turretinfo(this.m_id);
+       tur.tr_setup(tur, this);
 }
 
 
@@ -306,55 +326,49 @@ bool turret_send(entity this, entity to, float sf)
        WriteByte(MSG_ENTITY, sf);
        if(sf & TNSF_SETUP)
        {
-               WriteByte(MSG_ENTITY, self.m_id);
+               WriteByte(MSG_ENTITY, this.m_id);
 
-               WriteCoord(MSG_ENTITY, self.origin_x);
-               WriteCoord(MSG_ENTITY, self.origin_y);
-               WriteCoord(MSG_ENTITY, self.origin_z);
+               WriteVector(MSG_ENTITY, this.origin);
 
-               WriteAngle(MSG_ENTITY, self.angles_x);
-               WriteAngle(MSG_ENTITY, self.angles_y);
+               WriteAngle(MSG_ENTITY, this.angles_x);
+               WriteAngle(MSG_ENTITY, this.angles_y);
        }
 
        if(sf & TNSF_ANG)
        {
-               WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x));
-               WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y));
+               WriteShort(MSG_ENTITY, rint(this.tur_head.angles_x));
+               WriteShort(MSG_ENTITY, rint(this.tur_head.angles_y));
        }
 
        if(sf & TNSF_AVEL)
        {
-               WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x));
-               WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y));
+               WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_x));
+               WriteShort(MSG_ENTITY, rint(this.tur_head.avelocity_y));
        }
 
        if(sf & TNSF_MOVE)
        {
-               WriteShort(MSG_ENTITY, rint(self.origin_x));
-               WriteShort(MSG_ENTITY, rint(self.origin_y));
-               WriteShort(MSG_ENTITY, rint(self.origin_z));
+               WriteVector(MSG_ENTITY, this.origin);
 
-               WriteShort(MSG_ENTITY, rint(self.velocity_x));
-               WriteShort(MSG_ENTITY, rint(self.velocity_y));
-               WriteShort(MSG_ENTITY, rint(self.velocity_z));
+               WriteVector(MSG_ENTITY, this.velocity);
 
-               WriteShort(MSG_ENTITY, rint(self.angles_y));
+               WriteShort(MSG_ENTITY, rint(this.angles_y));
        }
 
        if(sf & TNSF_ANIM)
        {
-               WriteCoord(MSG_ENTITY, self.anim_start_time);
-               WriteByte(MSG_ENTITY, self.frame);
+               WriteCoord(MSG_ENTITY, this.anim_start_time);
+               WriteByte(MSG_ENTITY, this.frame);
        }
 
        if(sf & TNSF_STATUS)
        {
-               WriteByte(MSG_ENTITY, self.team);
+               WriteByte(MSG_ENTITY, this.team);
 
-               if(self.health <= 0)
+               if(GetResource(this, RES_HEALTH) <= 0)
                        WriteByte(MSG_ENTITY, 0);
                else
-                       WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
+                       WriteByte(MSG_ENTITY, ceil((GetResource(this, RES_HEALTH) / this.max_health) * 255));
        }
 
        return true;
@@ -365,7 +379,7 @@ void load_unit_settings(entity ent, bool is_reload)
        string unitname = ent.netname;
        string sbase;
 
-       if (ent == world)
+       if (ent == NULL)
                return;
 
        if(!ent.turret_scale_damage)    ent.turret_scale_damage = 1;
@@ -379,13 +393,13 @@ void load_unit_settings(entity ent, bool is_reload)
        sbase = strcat(cvar_base,unitname);
        if (is_reload)
        {
-               ent.enemy = world;
+               ent.enemy = NULL;
                ent.tur_head.avelocity = '0 0 0';
 
                ent.tur_head.angles = '0 0 0';
        }
 
-       ent.health       = cvar(strcat(sbase,"_health")) * ent.turret_scale_health;
+       SetResourceExplicit(ent, RES_HEALTH, cvar(strcat(sbase,"_health")) * ent.turret_scale_health);
        ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn;
 
        ent.shot_dmg             = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage;
@@ -430,37 +444,37 @@ void load_unit_settings(entity ent, bool is_reload)
 void turret_projectile_explode(entity this)
 {
 
-       self.takedamage = DAMAGE_NO;
-       self.event_damage = func_null;
+       this.takedamage = DAMAGE_NO;
+       this.event_damage = func_null;
 #ifdef TURRET_DEBUG
        float d;
-       d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
-       self.owner.tur_debug_dmg_t_h = self.owner.tur_debug_dmg_t_h + d;
-       self.owner.tur_debug_dmg_t_f = self.owner.tur_debug_dmg_t_f + self.owner.shot_dmg;
+       d = RadiusDamage (this, this.owner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, DMG_NOWEP, NULL);
+       this.owner.tur_debug_dmg_t_h = this.owner.tur_debug_dmg_t_h + d;
+       this.owner.tur_debug_dmg_t_f = this.owner.tur_debug_dmg_t_f + this.owner.shot_dmg;
 #else
-       RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+       RadiusDamage (this, this.realowner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, DMG_NOWEP, NULL);
 #endif
-       remove(self);
+       delete(this);
 }
 
-void turret_projectile_touch(entity this)
+void turret_projectile_touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
        turret_projectile_explode(this);
 }
 
-void turret_projectile_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
+void turret_projectile_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce)
 {
        this.velocity  += vforce;
-       this.health     -= damage;
+       TakeResource(this, RES_HEALTH, damage);
        //this.realowner = attacker; // Dont change realowner, it does not make much sense for turrets
-       if(this.health <= 0)
+       if(GetResource(this, RES_HEALTH) <= 0)
                W_PrepareExplosionByDamage(this, this.owner, turret_projectile_explode);
 }
 
 entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim)
 {
-    TC(Sound, _snd);
+       TC(Sound, _snd);
        entity proj;
 
        sound (actor, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM);
@@ -474,15 +488,17 @@ entity turret_projectile(entity actor, Sound _snd, float _size, float _health, f
        setthink(proj, turret_projectile_explode);
        settouch(proj, turret_projectile_touch);
        proj.nextthink    = time + 9;
-       proj.movetype           = MOVETYPE_FLYMISSILE;
+       set_movetype(proj, MOVETYPE_FLYMISSILE);
        proj.velocity           = normalize(actor.tur_shotdir_updated + randomvec() * actor.shot_spread) * actor.shot_speed;
-       proj.flags                = FL_PROJECTILE;
+       proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.enemy                = actor.enemy;
        proj.totalfrags  = _death;
        PROJECTILE_MAKETRIGGER(proj);
        if(_health)
        {
-               proj.health              = _health;
+               SetResourceExplicit(proj, RES_HEALTH, _health);
                proj.takedamage  = DAMAGE_YES;
                proj.event_damage  = turret_projectile_damage;
        }
@@ -547,7 +563,7 @@ void turret_track(entity this)
 
        if(!this.active)
                target_angle = this.idle_aim - ('1 0 0' * this.aim_maxpitch);
-       else if (this.enemy == world)
+       else if (this.enemy == NULL)
        {
                if(time > this.lip)
                        target_angle = this.idle_aim + this.angles;
@@ -672,6 +688,7 @@ void turret_track(entity this)
  + TFL_TARGETSELECT_LOS
  + TFL_TARGETSELECT_PLAYERS
  + TFL_TARGETSELECT_MISSILES
+ + TFL_TARGETSELECT_VEHICLES
  - TFL_TARGETSELECT_TRIGGERTARGET
  + TFL_TARGETSELECT_ANGLELIMITS
  + TFL_TARGETSELECT_RANGELIMITS
@@ -700,7 +717,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
        if(!checkpvs(e_target.origin, e_turret))
                return -1;
 
-       if(e_target.alpha <= 0.3)
+       if(e_target.alpha != 0 && e_target.alpha <= 0.3)
                return -1;
 
        if(MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags))
@@ -714,15 +731,17 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
                return -5;
 
        // Cant touch this
+       if (GetResource(e_target, RES_HEALTH) <= 0)
+               return -6;
+       else if (STAT(FROZEN, e_target))
+               return -6;
+
+       // vehicle
        if(IS_VEHICLE(e_target))
        {
-               if (e_target.vehicle_health <= 0)
-                       return -6;
+               if ((validate_flags & TFL_TARGETSELECT_VEHICLES) && !e_target.owner)
+                       return -7;
        }
-       else if (e_target.health <= 0)
-               return -6;
-       else if(STAT(FROZEN, e_target) > 0)
-               return -6;
 
        // player
        if (IS_CLIENT(e_target))
@@ -782,7 +801,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
        }
 
        // Can we even aim this thing?
-       tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target);
+       tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target.origin);
        tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles);
        tvt_thadf = vlen(tvt_thadv);
        tvt_tadf = vlen(tvt_tadv);
@@ -824,7 +843,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
        */
 
 #ifdef TURRET_DEBUG_TARGETSELECT
-       LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n");
+       LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname);
 #endif
 
        return 1;
@@ -844,13 +863,13 @@ entity turret_select_target(entity this)
                m_score = this.turret_score_target(this,e_enemy) * this.target_select_samebias;
        }
        else
-               e_enemy = this.enemy = world;
+               e_enemy = this.enemy = NULL;
 
        e = findradius(this.origin, this.target_range);
 
        // Nothing to aim at?
        if (!e)
-               return world;
+               return NULL;
 
        while (e)
        {
@@ -903,7 +922,7 @@ bool turret_firecheck(entity this)
        if (this.firecheck_flags & TFL_FIRECHECK_NO)
                return true;
 
-       if (this.enemy == world)
+       if (this.enemy == NULL)
                return false;
 
        // Ready?
@@ -942,10 +961,11 @@ bool turret_firecheck(entity this)
        {
                // To close?
                if (this.tur_dist_aimpos < this.target_range_min)
+               {
                        if(turret_validate_target(this, this.tur_impactent, this.target_validate_flags) > 0)
                                return true; // Target of opertunity?
-                       else
-                               return false;
+                       return false;
+               }
        }
 
        // Try to avoid FF?
@@ -999,7 +1019,7 @@ void turret_fire(entity this)
                this.volly_counter = this.shot_volly;
 
                if (this.shoot_flags & TFL_SHOOT_CLEARTARGET)
-                       this.enemy = world;
+                       this.enemy = NULL;
 
                if (this.shot_volly > 1)
                        this.attack_finished_single[0] = time + this.shot_volly_refire;
@@ -1061,7 +1081,7 @@ void turret_think(entity this)
 
                        e = e.chain;
                }
-               this.enemy = world;
+               this.enemy = NULL;
        }
        else if(this.shoot_flags & TFL_SHOOT_CUSTOM)
        {
@@ -1118,7 +1138,7 @@ void turret_think(entity this)
                if(this.target_validate_time < time)
                if (turret_validate_target(this, this.enemy, this.target_validate_flags) <= 0)
                {
-                       this.enemy = world;
+                       this.enemy = NULL;
                        this.target_validate_time = time + 0.5;
                        do_target_scan = 1;
                }
@@ -1134,7 +1154,7 @@ void turret_think(entity this)
                }
 
                // No target, just go to idle, do any custom stuff and bail.
-               if (this.enemy == world)
+               if (this.enemy == NULL)
                {
                        // Turn & pitch
                        if(!(this.track_flags & TFL_TRACK_NO))
@@ -1170,11 +1190,11 @@ void turret_think(entity this)
 
 /*
        When .used a turret switch team to activator.team.
-       If activator is world, the turret go inactive.
+       If activator is NULL, the turret go inactive.
 */
 void turret_use(entity this, entity actor, entity trigger)
 {
-       LOG_TRACE("Turret ",this.netname, " used by ", actor.classname, "\n");
+       LOG_TRACE("Turret ",this.netname, " used by ", actor.classname);
 
        this.team = actor.team;
 
@@ -1199,7 +1219,8 @@ void turrets_manager_think(entity this)
 
        if (autocvar_g_turrets_reloadcvars == 1)
        {
-               FOREACH_ENTITY(IS_TURRET(it), {
+               IL_EACH(g_turrets, true,
+               {
                        load_unit_settings(it, true);
                        Turret tur = get_turretinfo(it.m_id);
                        tur.tr_think(tur, it);
@@ -1220,13 +1241,13 @@ void turret_initparams(entity tur)
        tur.shot_force                  = bound(0.001,           (TRY(tur.shot_force)                :  tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000);
        tur.shot_volly                  = bound(1,               (TRY(tur.shot_volly)                :  1                                          ), floor(tur.ammo_max / tur.shot_dmg));
        tur.shot_volly_refire           = bound(tur.shot_refire, (TRY(tur.shot_volly_refire)         :  tur.shot_refire * tur.shot_volly           ), 60);
-       tur.target_range                = bound(0,               (TRY(tur.target_range)              :  tur.shot_speed * 0.5                       ), MAX_SHOT_DISTANCE);
-       tur.target_range_min            = bound(0,               (TRY(tur.target_range_min)          :  tur.shot_radius * 2                        ), MAX_SHOT_DISTANCE);
-       tur.target_range_optimal        = bound(0,               (TRY(tur.target_range_optimal)      :  tur.target_range * 0.5                     ), MAX_SHOT_DISTANCE);
+       tur.target_range                = bound(0,               (TRY(tur.target_range)              :  tur.shot_speed * 0.5                       ), max_shot_distance);
+       tur.target_range_min            = bound(0,               (TRY(tur.target_range_min)          :  tur.shot_radius * 2                        ), max_shot_distance);
+       tur.target_range_optimal        = bound(0,               (TRY(tur.target_range_optimal)      :  tur.target_range * 0.5                     ), max_shot_distance);
        tur.aim_maxrotate               = bound(0,               (TRY(tur.aim_maxrotate)             :  90                                         ), 360);
        tur.aim_maxpitch                = bound(0,               (TRY(tur.aim_maxpitch)              :  20                                         ), 90);
        tur.aim_speed                   = bound(0.1,             (TRY(tur.aim_speed)                 :  36                                         ), 1000);
-       tur.aim_firetolerance_dist      = bound(0.1,             (TRY(tur.aim_firetolerance_dist)    :  5 + (tur.shot_radius * 2)                  ), MAX_SHOT_DISTANCE);
+       tur.aim_firetolerance_dist      = bound(0.1,             (TRY(tur.aim_firetolerance_dist)    :  5 + (tur.shot_radius * 2)                  ), max_shot_distance);
        tur.target_select_rangebias     = bound(-10,             (TRY(tur.target_select_rangebias)   :  1                                          ), 10);
        tur.target_select_samebias      = bound(-10,             (TRY(tur.target_select_samebias)    :  1                                          ), 10);
        tur.target_select_anglebias     = bound(-10,             (TRY(tur.target_select_anglebias)   :  1                                          ), 10);
@@ -1237,6 +1258,36 @@ void turret_initparams(entity tur)
        #undef TRY
 }
 
+bool turret_closetotarget(entity this, vector targ, float range)
+{
+       vector path_extra_size = '1 1 1' * range;
+       return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size);
+}
+
+void turret_findtarget(entity this)
+{
+       entity e = find(NULL, classname, "turret_manager");
+       if(!e)
+       {
+               e = new(turret_manager);
+               setthink(e, turrets_manager_think);
+               e.nextthink = time + 2;
+       }
+
+       entity targ = find(NULL, targetname, this.target);
+       if(targ.classname == "turret_checkpoint")
+               return; // turrets don't defend checkpoints?
+
+       if (!targ)
+       {
+               this.target = "";
+               LOG_TRACE("Turret has invalid defendpoint!");
+       }
+
+       this.tur_defend = targ;
+       this.idle_aim = this.tur_head.angles + angleofs(this.tur_head, targ);
+}
+
 bool turret_initialize(entity this, Turret tur)
 {
        if(!autocvar_g_turrets)
@@ -1248,25 +1299,19 @@ bool turret_initialize(entity this, Turret tur)
        // if tur_head exists, we can assume this turret re-spawned
        if(!this.tur_head) {
                tur.tr_precache(tur);
-       }
-
-       entity e = find(world, classname, "turret_manager");
-       if(!e)
-       {
-               e = new(turret_manager);
-               setthink(e, turrets_manager_think);
-               e.nextthink = time + 2;
+               IL_PUSH(g_turrets, this);
+               IL_PUSH(g_bot_targets, this);
        }
 
        if(!(this.spawnflags & TSF_SUSPENDED))
-               WITHSELF(this, builtin_droptofloor());
+               droptofloor(this);
 
        this.netname = tur.netname;
        load_unit_settings(this, 0);
 
-       if(!this.team || !teamplay)             { this.team = MAX_SHOT_DISTANCE; }
+       if(!this.team || !teamplay)             { this.team = FLOAT_MAX; }
        if(!this.ticrate)                               { this.ticrate = ((this.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
-       if(!this.health)                                { this.health = 1000; }
+       if(!GetResource(this, RES_HEALTH)) { SetResourceExplicit(this, RES_HEALTH, 1000); }
        if(!this.shot_refire)                   { this.shot_refire = 1; }
        if(!this.tur_shotorg)                   { this.tur_shotorg = '50 0 50'; }
        if(!this.turret_flags)                  { this.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; }
@@ -1315,7 +1360,7 @@ bool turret_initialize(entity this, Turret tur)
        ++turret_count;
 
        _setmodel(this, tur.model);
-       setsize(this, tur.mins, tur.maxs);
+       setsize(this, tur.m_mins, tur.m_maxs);
 
        this.m_id                                       = tur.m_id;
        this.classname                          = "turret_main";
@@ -1323,16 +1368,18 @@ bool turret_initialize(entity this, Turret tur)
        this.effects                            = EF_NODRAW;
        this.netname                            = tur.turret_name;
        this.ticrate                            = bound(sys_frametime, this.ticrate, 60);
-       this.max_health                         = this.health;
+       this.max_health                         = GetResource(this, RES_HEALTH);
        this.target_validate_flags      = this.target_select_flags;
        this.ammo                                       = this.ammo_max;
        this.ammo_recharge                 *= this.ticrate;
        this.solid                                      = SOLID_BBOX;
        this.takedamage                         = DAMAGE_AIM;
-       this.movetype                           = MOVETYPE_NOCLIP;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.view_ofs                           = '0 0 0';
+       this.idle_aim                           = '0 0 0';
        this.turret_firecheckfunc       = turret_firecheck;
        this.event_damage                       = turret_damage;
+       this.event_heal                         = turret_heal;
        this.use                                        = turret_use;
        this.bot_attack                         = true;
        this.nextthink                          = time + 1;
@@ -1349,23 +1396,12 @@ bool turret_initialize(entity this, Turret tur)
        this.tur_head.owner                     = this;
        this.tur_head.takedamage        = DAMAGE_NO;
        this.tur_head.solid                     = SOLID_NOT;
-       this.tur_head.movetype          = this.movetype;
+       set_movetype(this.tur_head, this.move_movetype);
 
-       if(!this.tur_defend)
-       if(this.target != "")
-       {
-               this.tur_defend = find(world, targetname, this.target);
-               if (this.tur_defend == world)
-               {
-                       this.target = "";
-                       LOG_TRACE("Turret has invalid defendpoint!\n");
-               }
-       }
+       this.weaponentities[0] = this; // lol
 
-       if (this.tur_defend)
-               this.idle_aim = this.tur_head.angles + angleofs(this.tur_head, this.tur_defend);
-       else
-               this.idle_aim = '0 0 0';
+       if(!this.tur_defend && this.target != "")
+               InitializeEntity(this, turret_findtarget, INITPRIO_FINDTARGET);
 
 #ifdef TURRET_DEBUG
        this.tur_debug_start = this.nextthink;