Merge branch 'master' into Mario/turrets
authorMario <zacjardine@y7mail.com>
Tue, 14 Apr 2015 22:22:35 +0000 (08:22 +1000)
committerMario <zacjardine@y7mail.com>
Tue, 14 Apr 2015 22:22:35 +0000 (08:22 +1000)
Conflicts:
qcsrc/client/progs.src
qcsrc/client/tturrets.qh
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/progs.src
qcsrc/server/tturrets/include/turrets.qh
qcsrc/server/tturrets/include/turrets_early.qh
qcsrc/server/tturrets/system/system_aimprocs.qc
qcsrc/server/tturrets/system/system_damage.qc
qcsrc/server/tturrets/system/system_misc.qc
qcsrc/server/tturrets/units/unit_hellion.qc
qcsrc/server/tturrets/units/unit_hk.qc
qcsrc/server/tturrets/units/unit_mlrs.qc
qcsrc/server/tturrets/units/unit_phaser.qc

82 files changed:
qcsrc/client/damage.qc
qcsrc/client/main.qc
qcsrc/client/progs.src
qcsrc/client/tturrets.qc [deleted file]
qcsrc/client/tturrets.qh [deleted file]
qcsrc/common/command/generic.qc
qcsrc/common/deathtypes.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/nades.qc
qcsrc/common/notifications.qh
qcsrc/common/turrets/all.qh [new file with mode: 0644]
qcsrc/common/turrets/checkpoint.qc [new file with mode: 0644]
qcsrc/common/turrets/cl_turrets.qc [new file with mode: 0644]
qcsrc/common/turrets/cl_turrets.qh [new file with mode: 0644]
qcsrc/common/turrets/config.qc [new file with mode: 0644]
qcsrc/common/turrets/config.qh [new file with mode: 0644]
qcsrc/common/turrets/sv_turrets.qc [new file with mode: 0644]
qcsrc/common/turrets/sv_turrets.qh [new file with mode: 0644]
qcsrc/common/turrets/targettrigger.qc [new file with mode: 0644]
qcsrc/common/turrets/turrets.qc [new file with mode: 0644]
qcsrc/common/turrets/turrets.qh [new file with mode: 0644]
qcsrc/common/turrets/unit/ewheel.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/flac.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/fusionreactor.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/hellion.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/hk.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/machinegun.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/mlrs.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/phaser.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/plasma.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/plasma_dual.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/tesla.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/walker.qc [new file with mode: 0644]
qcsrc/common/turrets/util.qc [new file with mode: 0644]
qcsrc/common/turrets/util.qh [new file with mode: 0644]
qcsrc/server/autocvars.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_damage.qh
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_assault.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutators_include.qc
qcsrc/server/progs.src
qcsrc/server/t_teleporters.qc
qcsrc/server/tturrets/include/turrets.qh [deleted file]
qcsrc/server/tturrets/include/turrets_early.qh [deleted file]
qcsrc/server/tturrets/system/system_aimprocs.qc [deleted file]
qcsrc/server/tturrets/system/system_damage.qc [deleted file]
qcsrc/server/tturrets/system/system_main.qc [deleted file]
qcsrc/server/tturrets/system/system_misc.qc [deleted file]
qcsrc/server/tturrets/system/system_scoreprocs.qc [deleted file]
qcsrc/server/tturrets/units/unit_checkpoint.qc [deleted file]
qcsrc/server/tturrets/units/unit_ewheel.qc [deleted file]
qcsrc/server/tturrets/units/unit_flac.qc [deleted file]
qcsrc/server/tturrets/units/unit_fusionreactor.qc [deleted file]
qcsrc/server/tturrets/units/unit_hellion.qc [deleted file]
qcsrc/server/tturrets/units/unit_hk.qc [deleted file]
qcsrc/server/tturrets/units/unit_machinegun.qc [deleted file]
qcsrc/server/tturrets/units/unit_mlrs.qc [deleted file]
qcsrc/server/tturrets/units/unit_phaser.qc [deleted file]
qcsrc/server/tturrets/units/unit_plasma.qc [deleted file]
qcsrc/server/tturrets/units/unit_targettrigger.qc [deleted file]
qcsrc/server/tturrets/units/unit_tessla.qc [deleted file]
qcsrc/server/tturrets/units/unit_walker.qc [deleted file]
qcsrc/server/vehicles/bumblebee.qc
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/vehicles/vehicles_def.qh
turrets.cfg
unit_ewheel.cfg [deleted file]
unit_flac.cfg [deleted file]
unit_fusreac.cfg [deleted file]
unit_hellion.cfg [deleted file]
unit_hk.cfg [deleted file]
unit_machinegun.cfg [deleted file]
unit_mlrs.cfg [deleted file]
unit_phaser.cfg [deleted file]
unit_plasma.cfg [deleted file]
unit_plasma2.cfg [deleted file]
unit_tesla.cfg [deleted file]
unit_walker.cfg [deleted file]

index 000ef8b..e3660b2 100644 (file)
@@ -329,7 +329,7 @@ void Ent_DamageInfo(float isNew)
                                pointparticles(particleeffectnum("electro_impact"), self.origin, w_backoff * 1000, 1);
                                break;
 
-                        case DEATH_TURRET_WALK_MEELE:
+                        case DEATH_TURRET_WALK_MELEE:
                                sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_MIN);
                                pointparticles(particleeffectnum("TE_SPARK"), self.origin, w_backoff * 1000, 1);
                                break;
index 36ec4f7..b753139 100644 (file)
@@ -4,7 +4,6 @@
 #include "scoreboard.qh"
 #include "shownames.qh"
 #include "target_music.qh"
-#include "tturrets.qh"
 #include "tuba.qh"
 #include "wall.qh"
 #include "waypointsprites.qh"
@@ -17,6 +16,9 @@
 
 #include "../common/monsters/monsters.qh"
 
+#include "../common/turrets/cl_turrets.qh"
+#include "../common/turrets/turrets.qh"
+
 #include "../warpzonelib/client.qh"
 
 // --------------------------------------------------------------------------
@@ -108,6 +110,7 @@ void CSQC_Init(void)
 
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterMonsters);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
@@ -127,7 +130,6 @@ void CSQC_Init(void)
        GibSplash_Precache();
        Casings_Precache();
        Vehicles_Precache();
-       turrets_precache();
        Tuba_Precache();
        CSQCPlayer_Precache();
 
index f80da18..cdd0b09 100644 (file)
@@ -29,7 +29,6 @@ shownames.qc
 sortlist.qc
 target_music.qc
 teamradar.qc
-tturrets.qc
 tuba.qc
 vehicles/vehicles.qc
 view.qc
@@ -57,6 +56,9 @@ weapons/projectile.qc // TODO
 
 ../common/monsters/monsters.qc
 
+../common/turrets/cl_turrets.qc
+../common/turrets/turrets.qc
+
 ../common/weapons/weapons.qc // TODO
 
 ../csqcmodellib/cl_model.qc
@@ -69,5 +71,6 @@ weapons/projectile.qc // TODO
 
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/client.qc
+
 ../warpzonelib/common.qc
 ../warpzonelib/mathlib.qc
diff --git a/qcsrc/client/tturrets.qc b/qcsrc/client/tturrets.qc
deleted file mode 100644 (file)
index d246985..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-#include "tturrets.qh"
-#include "waypointsprites.qh"
-
-#include "../server/movelib.qh"
-
-string tid2info_base;
-string tid2info_head;
-string tid2info_name;
-vector  tid2info_min;
-vector  tid2info_max;
-
-void turret_tid2info(float _tid);
-void turret_precache(float _tid);
-float turret_is_precache[TID_LAST];
-
-void turrets_precache()
-{
-    turret_precache(TID_COMMON);
-}
-
-void turret_precache(int _tid)
-{
-    if (!turret_is_precache[TID_COMMON])
-    {
-        precache_sound ("weapons/rocket_impact.wav");
-        precache_model ("models/turrets/base-gib1.md3");
-        precache_model ("models/turrets/base-gib2.md3");
-        precache_model ("models/turrets/base-gib3.md3");
-        precache_model ("models/turrets/base-gib4.md3");
-        precache_model ("models/turrets/head-gib1.md3");
-        precache_model ("models/turrets/head-gib2.md3");
-        precache_model ("models/turrets/head-gib3.md3");
-        precache_model ("models/turrets/head-gib4.md3");
-        precache_model ("models/turrets/terrainbase.md3");
-        precache_model ("models/turrets/base.md3");
-        precache_model ("models/turrets/rocket.md3");
-    }
-    turret_tid2info(_tid);
-    if(turret_is_precache[_tid])
-        return;
-
-    switch(_tid)
-    {
-        case TID_EWHEEL:
-            precache_model ("models/turrets/ewheel-base2.md3");
-            precache_model ("models/turrets/ewheel-gun1.md3");
-            break;
-        case TID_FLAC:
-            precache_model ("models/turrets/flac.md3");
-            break;
-        case TID_FUSION:
-            precache_model ("models/turrets/reactor.md3");
-            break;
-        case TID_HELLION:
-            precache_model ("models/turrets/hellion.md3");
-            break;
-        case TID_HK:
-            precache_model ("models/turrets/hk.md3");
-            break;
-        case TID_MACHINEGUN:
-            precache_model ("models/turrets/machinegun.md3");
-            precache_sound ("weapons/uzi_fire.wav");
-            break;
-        case TID_MLRS:
-            precache_model ("models/turrets/mlrs.md3");
-            break;
-        case TID_PHASER:
-            precache_model ("models/turrets/phaser.md3");
-            precache_model ("models/turrets/phaser_beam.md3");
-            precache_sound ("turrets/phaser.wav");
-            break;
-        case TID_PLASMA:
-            precache_model ("models/turrets/plasma.md3");
-            break;
-        case TID_PLASMA_DUAL:
-            precache_model ("models/turrets/plasmad.md3");
-            break;
-        case TID_TESLA:
-            precache_model ("models/turrets/tesla_head.md3");
-            precache_model ("models/turrets/tesla_base.md3");
-            break;
-        case TID_WALKER:
-            precache_model ("models/turrets/walker_head_minigun.md3");
-            precache_model ("models/turrets/walker_body.md3");
-            precache_sound ("weapons/uzi_fire.wav");
-            break;
-    }
-    turret_is_precache[_tid] = true;
-}
-
-void turret_tid2info(float _tid)
-{
-    tid2info_base = "models/turrets/base.md3";
-    tid2info_min = '-32 -32 0';
-    tid2info_max = '32 32 64';
-
-    switch(_tid)
-    {
-        case TID_EWHEEL:
-            tid2info_base = "models/turrets/ewheel-base2.md3";
-            tid2info_head = "models/turrets/ewheel-gun1.md3";
-            tid2info_name = "eWheel";
-            break;
-        case TID_FLAC:
-            tid2info_head = "models/turrets/flac.md3";
-            tid2info_name = "Flac Cannon";
-            break;
-        case TID_FUSION:
-            tid2info_head = "models/turrets/reactor.md3";
-            tid2info_name = "Fusion Reactor";
-            tid2info_min = '-34 -34 0';
-            tid2info_max = '34 34 90';
-            break;
-        case TID_HELLION:
-            tid2info_head = "models/turrets/hellion.md3";
-            tid2info_name = "Hellion";
-            break;
-        case TID_HK:
-            tid2info_head = "models/turrets/hk.md3";
-            tid2info_name = "Hunter-Killer";
-            break;
-        case TID_MACHINEGUN:
-            tid2info_head = "models/turrets/machinegun.md3";
-            tid2info_name = "Machinegun";
-            break;
-        case TID_MLRS:
-            tid2info_head = "models/turrets/mlrs.md3";
-            tid2info_name = "MLRS";
-            break;
-        case TID_PHASER:
-            tid2info_head = "models/turrets/phaser.md3";
-            tid2info_name = "Phaser";
-            break;
-        case TID_PLASMA:
-            tid2info_head = "models/turrets/plasma.md3";
-            tid2info_name = "Plasma";
-            break;
-        case TID_PLASMA_DUAL:
-            tid2info_head = "models/turrets/plasmad.md3";
-            tid2info_name = "Dual Plasma";
-            break;
-        case TID_TESLA:
-            tid2info_base = "models/turrets/tesla_base.md3";
-            tid2info_head = "models/turrets/tesla_head.md3";
-            tid2info_name = "Tesla coil";
-            tid2info_min = '-60 -60 0';
-            tid2info_max  ='60 60 128';
-            break;
-        case TID_WALKER:
-            tid2info_base = "models/turrets/walker_body.md3";
-            tid2info_head = "models/turrets/walker_head_minigun.md3";
-            tid2info_name = "Walker";
-            tid2info_min = '-70 -70 0';
-            tid2info_max = '70 70 95';
-            break;
-    }
-}
-
-void turret_remove()
-{
-    remove(self.tur_head);
-    //remove(self.enemy);
-    self.tur_head = world;
-}
-
-.vector glowmod;
-void turret_changeteam()
-{
-       switch(self.team - 1)
-       {
-        case NUM_TEAM_1: // Red
-            self.glowmod = '2 0 0';
-            self.teamradar_color = '1 0 0';
-            break;
-
-        case NUM_TEAM_2: // Blue
-            self.glowmod = '0 0 2';
-            self.teamradar_color = '0 0 1';
-            break;
-
-        case NUM_TEAM_3: // Yellow
-            self.glowmod = '1 1 0';
-            self.teamradar_color = '1 1 0';
-            break;
-
-        case NUM_TEAM_4: // Pink
-            self.glowmod = '1 0 1';
-            self.teamradar_color = '1 0 1';
-            break;
-       }
-
-       if(self.team)
-        self.colormap = 1024 + (self.team - 1) * 17;
-
-       self.tur_head.colormap = self.colormap;
-       self.tur_head.glowmod = self.glowmod;
-
-}
-
-void turret_head_draw()
-{
-    self.drawmask = MASK_NORMAL;
-}
-
-void turret_draw()
-{
-    float dt;
-
-    dt = time - self.move_time;
-    self.move_time = time;
-    if(dt <= 0)
-        return;
-
-    self.tur_head.angles += dt * self.tur_head.move_avelocity;
-
-    if (self.health < 127)
-    {
-        dt = random();
-
-        if(dt < 0.03)
-            te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
-    }
-
-    if(self.health < 85)
-    if(dt < 0.01)
-        pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
-
-    if(self.health < 32)
-    if(dt < 0.015)
-        pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
-
-}
-
-void turret_draw2d()
-{
-       if(self.netname == "")
-           return;
-
-       if(!autocvar_g_waypointsprite_turrets)
-               return;
-
-    if(autocvar_cl_hidewaypoints)
-        return;
-
-       float dist = vlen(self.origin - view_origin);
-    float t = (GetPlayerColor(player_localnum) + 1);
-
-       vector o;
-       string txt;
-
-       if(autocvar_cl_vehicles_hud_tactical)
-       if(dist < 10240 && t != self.team)
-       {
-        // TODO: Vehicle tactical hud
-        o = project_3d_to_2d(self.origin + '0 0 32');
-        if(o.z < 0
-        || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
-        || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
-        || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
-        || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
-            return; // Dont draw wp's for turrets out of view
-        o.z = 0;
-        if(hud != HUD_NORMAL)
-        {
-            switch(hud)
-            {
-                case HUD_SPIDERBOT:
-                case HUD_WAKIZASHI:
-                case HUD_RAPTOR:
-                case HUD_BUMBLEBEE:
-                    if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER)
-                        txt = "gfx/vehicles/vth-mover.tga";
-                    else
-                        txt = "gfx/vehicles/vth-stationary.tga";
-
-                    vector pz = drawgetimagesize(txt) * 0.25;
-                    drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL);
-                    break;
-            }
-        }
-       }
-
-       if(dist > self.maxdistance)
-        return;
-
-       string spriteimage = self.netname;
-       float a = self.alpha * autocvar_hud_panel_fg_alpha;
-       vector rgb = spritelookupcolor(spriteimage, self.teamradar_color);
-
-
-       if(self.maxdistance > waypointsprite_normdistance)
-               a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
-       else if(self.maxdistance > 0)
-               a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
-
-       if(rgb == '0 0 0')
-       {
-               self.teamradar_color = '1 0 1';
-               printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
-       }
-
-       txt = self.netname;
-       if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
-               txt = _("Spam");
-       else
-               txt = spritelookuptext(spriteimage);
-
-       if(time - floor(time) > 0.5 && t == self.team)
-       {
-               if(self.helpme && time < self.helpme)
-               {
-                   a *= SPRITE_HELPME_BLINK;
-                   txt = sprintf(_("%s under attack!"), txt);
-               }
-               else
-                       a *= spritelookupblinkvalue(spriteimage);
-       }
-
-       if(autocvar_g_waypointsprite_uppercase)
-               txt = strtoupper(txt);
-
-       if(a > 1)
-       {
-               rgb *= a;
-               a = 1;
-       }
-
-       if(a <= 0)
-           return;
-
-       rgb = fixrgbexcess(rgb);
-
-       o = project_3d_to_2d(self.origin + '0 0 64');
-       if(o.z < 0
-       || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
-       || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
-       || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
-       || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
-           return; // Dont draw wp's for turrets out of view
-
-       o.z = 0;
-
-       float edgedistance_min, crosshairdistance;
-               edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
-       (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
-       (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
-       (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
-
-       float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
-
-       crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
-
-       t = waypointsprite_scale * vidscale;
-       a *= waypointsprite_alpha;
-
-       {
-               a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
-               t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
-       }
-       if (edgedistance_min < waypointsprite_edgefadedistance) {
-               a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
-               t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
-       }
-       if(crosshairdistance < waypointsprite_crosshairfadedistance) {
-               a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
-               t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
-       }
-
-       o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t);
-    o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
-    drawhealthbar(
-            o,
-            0,
-            self.health / 255,
-            '0 0 0',
-            '0 0 0',
-            0.5 * SPRITE_HEALTHBAR_WIDTH * t,
-            0.5 * SPRITE_HEALTHBAR_HEIGHT * t,
-            SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize,
-            SPRITE_HEALTHBAR_BORDER * t,
-            0,
-            rgb,
-            a * SPRITE_HEALTHBAR_BORDERALPHA,
-            rgb,
-            a * SPRITE_HEALTHBAR_HEALTHALPHA,
-            DRAWFLAG_NORMAL
-            );
-}
-
-void turret_walker_draw()
-{
-    float dt;
-
-    dt = time - self.move_time;
-    self.move_time = time;
-    if(dt <= 0)
-        return;
-
-    fixedmakevectors(self.angles);
-    movelib_groundalign4point(300, 100, 0.25, 45);
-    setorigin(self, self.origin + self.velocity * dt);
-    self.tur_head.angles += dt * self.tur_head.move_avelocity;
-    self.angles_y = self.move_angles.y;
-
-    if (self.health < 127)
-    if(random() < 0.15)
-        te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
-}
-
-void turret_ewheel_draw()
-{
-    float dt;
-
-    dt = time - self.move_time;
-    self.move_time = time;
-    if(dt <= 0)
-        return;
-
-    fixedmakevectors(self.angles);
-    setorigin(self, self.origin + self.velocity * dt);
-    self.tur_head.angles += dt * self.tur_head.move_avelocity;
-    self.angles_y = self.move_angles.y;
-
-    if (self.health < 127)
-    if(random() < 0.05)
-        te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
-}
-
-void turret_construct()
-{
-    if(self.tur_head == world)
-        self.tur_head = spawn();
-
-    turret_tid2info(self.turret_type);
-    self.netname = tid2info_name;
-
-    setorigin(self, self.origin);
-    setmodel(self, tid2info_base);
-    setmodel(self.tur_head, tid2info_head);
-    setsize(self, tid2info_min, tid2info_max);
-    setsize(self.tur_head, '0 0 0', '0 0 0');
-
-    if(self.turret_type == TID_EWHEEL)
-        setattachment(self.tur_head, self, "");
-    else
-        setattachment(self.tur_head, self, "tag_head");
-
-    self.tur_head.classname     = "turret_head";
-    self.tur_head.owner         = self;
-    self.tur_head.move_movetype = MOVETYPE_NOCLIP;
-    self.move_movetype          = MOVETYPE_NOCLIP;
-    self.tur_head.angles        = self.angles;
-    self.health                 = 255;
-    self.solid                  = SOLID_BBOX;
-    self.tur_head.solid         = SOLID_NOT;
-    self.movetype               = MOVETYPE_NOCLIP;
-    self.tur_head.movetype      = MOVETYPE_NOCLIP;
-    self.draw                   = turret_draw;
-    self.entremove              = turret_remove;
-    self.drawmask               = MASK_NORMAL;
-    self.tur_head.drawmask      = MASK_NORMAL;
-    self.anim_start_time        = 0;
-    self.draw2d = turret_draw2d;
-    self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
-    self.teamradar_color = '1 0 0';
-    self.alpha = 1;
-
-    if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER)
-    {
-        self.gravity            = 1;
-        self.movetype           = MOVETYPE_BOUNCE;
-        self.move_movetype      = MOVETYPE_BOUNCE;
-        self.move_origin        = self.origin;
-        self.move_time          = time;
-        switch(self.turret_type)
-        {
-            case TID_EWHEEL:
-                self.draw               = turret_ewheel_draw;
-                break;
-            case TID_WALKER:
-                self.draw               = turret_walker_draw;
-                break;
-
-        }
-    }
-}
-
-entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode);
-void turret_gibboom();
-void turret_gib_draw()
-{
-    Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
-
-    self.drawmask = MASK_NORMAL;
-
-       if(self.cnt)
-       {
-           if(time >= self.nextthink)
-           {
-            turret_gibboom();
-            remove(self);
-           }
-       }
-       else
-       {
-        self.alpha = bound(0, self.nextthink - time, 1);
-        if(self.alpha < ALPHA_MIN_VISIBLE)
-            remove(self);
-       }
-}
-
-void turret_gibboom()
-{
-    float i;
-
-    sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
-    pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
-
-    for (i = 1; i < 5; i = i + 1)
-        turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false);
-}
-
-entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode)
-{
-    entity gib;
-
-    traceline(_from, _to, MOVE_NOMONSTERS, world);
-    if(trace_startsolid)
-        return world;
-
-    gib = spawn();
-    setorigin(gib, _from);
-    setmodel(gib, _model);
-    gib.colormod    = _cmod;
-       gib.solid       = SOLID_CORPSE;
-    gib.draw        = turret_gib_draw;
-    gib.cnt         = _explode;
-    setsize(gib, '-1 -1 -1', '1 1 1');
-    if(_explode)
-    {
-        gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15));
-        gib.effects = EF_FLAME;
-    }
-    else
-        gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
-
-    gib.gravity         = 1;
-       gib.move_movetype   = MOVETYPE_BOUNCE;
-       gib.move_origin     = _from;
-       setorigin(gib,        _from);
-       gib.move_velocity   = _to;
-       gib.move_avelocity  = prandomvec() * 32;
-       gib.move_time       = time;
-       gib.damageforcescale = 1;
-       gib.classname = "turret_gib";
-
-       return gib;
-}
-
-void turret_die()
-{
-
-    sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
-    pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
-    turret_tid2info(self.turret_type);
-    if (!autocvar_cl_nogibs)
-    {
-        // Base
-        if(self.turret_type == TID_EWHEEL)
-            turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true);
-        else if (self.turret_type == TID_WALKER)
-            turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true);
-        else if (self.turret_type == TID_TESLA)
-            turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false);
-        else
-        {
-            if (random() > 0.5)
-            {
-                turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
-                turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
-                turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
-            }
-            else
-                turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true);
-
-            entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true);
-            if(headgib)
-            {
-                headgib.angles = headgib.move_angles = self.tur_head.angles;
-                headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45;
-                headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity.y * 5;
-                headgib.gravity = 0.5;
-            }
-        }
-    }
-
-    setmodel(self, "null");
-    setmodel(self.tur_head, "null");
-}
-
-void ent_turret()
-{
-    int sf = ReadByte();
-
-       if(sf & TNSF_SETUP)
-       {
-           self.turret_type = ReadByte();
-
-           self.origin_x = ReadCoord();
-           self.origin_y = ReadCoord();
-           self.origin_z = ReadCoord();
-           setorigin(self, self.origin);
-
-           self.angles_x = ReadAngle();
-           self.angles_y = ReadAngle();
-
-           turret_precache(self.turret_type);
-           turret_construct();
-           self.colormap = 1024;
-           self.glowmod = '0 1 1';
-           self.tur_head.colormap = self.colormap;
-           self.tur_head.glowmod = self.glowmod;
-    }
-
-    if(sf & TNSF_ANG)
-    {
-        if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
-            self.tur_head = spawn();
-
-        self.tur_head.move_angles_x = ReadShort();
-        self.tur_head.move_angles_y = ReadShort();
-        //self.tur_head.angles = self.angles + self.tur_head.move_angles;
-        self.tur_head.angles = self.tur_head.move_angles;
-    }
-
-    if(sf & TNSF_AVEL)
-    {
-        if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
-            self.tur_head = spawn();
-
-        self.tur_head.move_avelocity_x = ReadShort();
-        self.tur_head.move_avelocity_y = ReadShort();
-    }
-
-    if(sf & TNSF_MOVE)
-    {
-        self.origin_x = ReadShort();
-        self.origin_y = ReadShort();
-        self.origin_z = ReadShort();
-        setorigin(self, self.origin);
-
-        self.velocity_x = ReadShort();
-        self.velocity_y = ReadShort();
-        self.velocity_z = ReadShort();
-
-        self.move_angles_y = ReadShort();
-
-        self.move_time     = time;
-        self.move_velocity = self.velocity;
-        self.move_origin   = self.origin;
-    }
-
-    if(sf & TNSF_ANIM)
-    {
-        self.frame1time = ReadCoord();
-        self.frame      = ReadByte();
-    }
-
-    if(sf & TNSF_STATUS)
-    {
-        int _tmp = ReadByte();
-        if(_tmp != self.team)
-        {
-            self.team = _tmp;
-            turret_changeteam();
-        }
-
-        _tmp = ReadByte();
-        if(_tmp == 0 && self.health != 0)
-            turret_die();
-        else if(self.health && self.health != _tmp)
-            self.helpme = servertime + 10;
-
-        self.health = _tmp;
-    }
-    //self.enemy.health = self.health / 255;
-}
diff --git a/qcsrc/client/tturrets.qh b/qcsrc/client/tturrets.qh
deleted file mode 100644 (file)
index 6316d79..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef TTURRETS_H
-#define TTURRETS_H
-
-#include "../server/tturrets/include/turrets_early.qh"
-
-void ent_turret();
-void turrets_precache();
-.entity tur_head;
-#endif
index 7f302e0..460410c 100644 (file)
@@ -1,5 +1,6 @@
 #include "generic.qh"
 #include "shared_defs.qh"
+#include "../turrets/config.qh"
 
 // =========================================================
 //  Generic program common command code, written by Samual
@@ -321,6 +322,59 @@ void GenericCommand_dumpweapons(float request) // WEAPONTODO: make this work wit
        }
 }
 
+void GenericCommand_dumpturrets(float request)
+{
+       switch(request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       #ifdef SVQC
+                       tur_config_file = -1;
+                       tur_config_alsoprint = -1;
+                       string filename = argv(1);
+                       
+                       if(filename == "")
+                       {
+                               filename = "turrets_dump.cfg";
+                               tur_config_alsoprint = FALSE;
+                       }
+                       else if(filename == "-")
+                       {
+                               filename = "turrets_dump.cfg";
+                               tur_config_alsoprint = TRUE;
+                       }
+                       tur_config_file = fopen(filename, FILE_WRITE);
+                       
+                       if(tur_config_file >= 0)
+                       {
+                               Dump_Turret_Settings();
+                               print(sprintf("Dumping turrets... File located in ^2data/data/%s^7.\n", filename));
+                               fclose(tur_config_file);
+                               tur_config_file = -1;
+                               tur_config_alsoprint = -1;
+                       }
+                       else
+                       {
+                               print(sprintf("^1Error: ^7Could not open file '%s'!\n", filename));
+                       }
+                       #else
+                       print(_("Turrets dump command only works with sv_cmd.\n"));
+                       #endif
+                       return;
+               }
+                       
+               default:
+               case CMD_REQUEST_USAGE:
+               {
+                       print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpturrets [filename]"));
+                       print("  Where 'filename' is the file to write (default is turrets_dump.cfg),\n");
+                       print("  if supplied with '-' output to console as well as default,\n");
+                       print("  if left blank, it will only write to default.\n");
+                       return;
+               }
+       }
+}
+
 void GenericCommand_maplist(float request, float argc)
 {
        switch(request)
@@ -629,6 +683,7 @@ void GenericCommand_(float request)
        GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \
        GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \
        GENERIC_COMMAND("dumpnotifs", GenericCommand_dumpnotifs(request), "Dump all notifications into notifications_dump.txt") \
+       GENERIC_COMMAND("dumpturrets", GenericCommand_dumpturrets(request), "Dump all turrets into turrets_dump.txt") \
        GENERIC_COMMAND("dumpweapons", GenericCommand_dumpweapons(request), "Dump all weapons into weapons_dump.txt") \
        GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \
        GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \
index 301d665..89afc0d 100644 (file)
@@ -62,7 +62,7 @@ int DEATH_VHLAST;
        DEATHTYPE(DEATH_TURRET_PLASMA,          DEATH_SELF_TURRET_PLASMA,           NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_TURRET_TESLA,           DEATH_SELF_TURRET_TESLA,            NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_TURRET_WALK_GUN,        DEATH_SELF_TURRET_WALK_GUN,         NO_MSG,                        NORMAL_POS) \
-       DEATHTYPE(DEATH_TURRET_WALK_MEELE,      DEATH_SELF_TURRET_WALK_MEELE,       NO_MSG,                        NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_WALK_MELEE,      DEATH_SELF_TURRET_WALK_MELEE,       NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_TURRET_WALK_ROCKET,     DEATH_SELF_TURRET_WALK_ROCKET,      NO_MSG,                        DEATH_TURRET_LAST) \
        DEATHTYPE(DEATH_VH_BUMB_DEATH,          DEATH_SELF_VH_BUMB_DEATH,           DEATH_MURDER_VH_BUMB_DEATH,    DEATH_VHFIRST) \
        DEATHTYPE(DEATH_VH_BUMB_GUN,            NO_MSG,                             DEATH_MURDER_VH_BUMB_GUN,      NORMAL_POS) \
index 28430e9..4c44349 100644 (file)
     #include "../../server/defs.qh"
     #include "../deathtypes.qh"
     #include "../../server/mutators/mutators_include.qh"
-    #include "../../server/tturrets/include/turrets_early.qh"
     #include "../../server/vehicles/vehicles_def.qh"
     #include "../../server/campaign.qh"
     #include "../../server/command/common.qh"
     #include "../../server/command/cmd.qh"
     #include "../../csqcmodellib/sv_model.qh"
     #include "../../server/round_handler.qh"
-    #include "../../server/tturrets/include/turrets.qh"
 #endif
 
 // =========================
index 4225a19..07a91a8 100644 (file)
@@ -4,7 +4,6 @@
        #include "nades.qh"
        #include "buffs.qh"
        #include "../client/movetypes.qh"
-       #include "../server/tturrets/include/turrets_early.qh"
        #include "../client/main.qh"
        #include "../csqcmodellib/cl_model.qh"
 #elif defined(MENUQC)
index 1cb1adf..dd43575 100644 (file)
@@ -437,7 +437,7 @@ void Send_Notification_WOCOVA(
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_PLASMA,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 got served some superheated plasma from a turret%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_TESLA,        2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 was electrocuted by a Tesla turret%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_GUN,     2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 got served a lead enrichment by a Walker turret%s%s"), "") \
-    MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MEELE,   2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \
+    MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MELEE,   2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_ROCKET,  2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 was blasted away by a Walker turret%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_BUMB_DEATH,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_CRUSH,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 was crushed by a vehicle%s%s"), "") \
@@ -824,7 +824,7 @@ void Send_Notification_WOCOVA(
     MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PLASMA,             NO_MSG,        INFO_DEATH_SELF_TURRET_PLASMA,             CENTER_DEATH_SELF_TURRET) \
     MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_TESLA,              NO_MSG,        INFO_DEATH_SELF_TURRET_TESLA,              CENTER_DEATH_SELF_TURRET) \
     MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_GUN,           NO_MSG,        INFO_DEATH_SELF_TURRET_WALK_GUN,           CENTER_DEATH_SELF_TURRET_WALK) \
-    MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MEELE,         NO_MSG,        INFO_DEATH_SELF_TURRET_WALK_MEELE,         CENTER_DEATH_SELF_TURRET_WALK) \
+    MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MELEE,         NO_MSG,        INFO_DEATH_SELF_TURRET_WALK_MELEE,         CENTER_DEATH_SELF_TURRET_WALK) \
     MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_ROCKET,        NO_MSG,        INFO_DEATH_SELF_TURRET_WALK_ROCKET,        CENTER_DEATH_SELF_TURRET_WALK) \
     MSG_MULTI_NOTIF(1, DEATH_SELF_VH_BUMB_DEATH,             NO_MSG,        INFO_DEATH_SELF_VH_BUMB_DEATH,             CENTER_DEATH_SELF_VH_BUMB_DEATH) \
     MSG_MULTI_NOTIF(1, DEATH_SELF_VH_CRUSH,                  NO_MSG,        INFO_DEATH_SELF_VH_CRUSH,                  CENTER_DEATH_SELF_VH_CRUSH) \
diff --git a/qcsrc/common/turrets/all.qh b/qcsrc/common/turrets/all.qh
new file mode 100644 (file)
index 0000000..04bb10f
--- /dev/null
@@ -0,0 +1,12 @@
+#include "unit/ewheel.qc"
+#include "unit/flac.qc"
+#include "unit/fusionreactor.qc"
+#include "unit/hellion.qc"
+#include "unit/hk.qc"
+#include "unit/machinegun.qc"
+#include "unit/mlrs.qc"
+#include "unit/phaser.qc"
+#include "unit/plasma.qc"
+#include "unit/plasma_dual.qc"
+#include "unit/tesla.qc"
+#include "unit/walker.qc"
diff --git a/qcsrc/common/turrets/checkpoint.qc b/qcsrc/common/turrets/checkpoint.qc
new file mode 100644 (file)
index 0000000..f2fc3a8
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+    turret_checkpoint
+**/
+
+
+//.entity checkpoint_target;
+
+/*
+#define checkpoint_cache_who  flagcarried
+#define checkpoint_cache_from lastrocket
+#define checkpoint_cache_to   selected_player
+*/
+
+.entity pathgoal;
+
+/*
+entity path_makeorcache(entity forwho,entity start, entity end)
+{
+    entity oldself;
+    entity pth;
+    oldself = self;
+    self = forwho;
+
+    //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR);
+
+    self = oldself;
+    return pth;
+}
+*/
+
+void turret_checkpoint_use()
+{
+}
+
+#if 0
+void turret_checkpoint_think()
+{
+    if(self.enemy)
+        te_lightning1(self,self.origin, self.enemy.origin);
+
+    self.nextthink = time + 0.25;
+}
+#endif
+/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32)
+-----------KEYS------------
+target: .targetname of next waypoint in chain.
+wait:   Pause at this point # seconds.
+-----------SPAWNFLAGS-----------
+---------NOTES----------
+If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly.
+If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached.
+*/
+//float tc_acum;
+void turret_checkpoint_init()
+{
+    traceline(self.origin + '0 0 16', self.origin - '0 0 1024', MOVE_WORLDONLY, self);
+    setorigin(self, trace_endpos + '0 0 32');
+
+    if(self.target != "")
+    {
+        self.enemy = find(world, targetname, self.target);
+        if(self.enemy == world)
+            dprint("A turret_checkpoint faild to find its target!\n");
+    }
+    //self.think = turret_checkpoint_think;
+    //self.nextthink = time + tc_acum + 0.25;
+    //tc_acum += 0.25;
+}
+
+void spawnfunc_turret_checkpoint()
+{
+    setorigin(self,self.origin);
+    self.think = turret_checkpoint_init;
+    self.nextthink = time + 0.2;
+}
+
+// Compat.
+void spawnfunc_walker_checkpoint()
+{
+    self.classname = "turret_checkpoint";
+    spawnfunc_turret_checkpoint();
+}
diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc
new file mode 100644 (file)
index 0000000..2f8e77d
--- /dev/null
@@ -0,0 +1,445 @@
+void turret_remove()
+{
+       remove(self.tur_head);
+       //remove(self.enemy);
+       self.tur_head = world;
+}
+
+.vector glowmod;
+void turret_changeteam()
+{
+       self.glowmod = Team_ColorRGB(self.team - 1) * 2;
+       self.teamradar_color = Team_ColorRGB(self.team - 1);
+
+       if(self.team)
+               self.colormap = 1024 + (self.team - 1) * 17;
+
+       self.tur_head.colormap = self.colormap;
+       self.tur_head.glowmod = self.glowmod;
+
+}
+
+void turret_head_draw()
+{
+       self.drawmask = MASK_NORMAL;
+}
+
+void turret_draw()
+{
+       float dt;
+
+       dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0)
+               return;
+
+       self.tur_head.angles += dt * self.tur_head.move_avelocity;
+
+       if (self.health < 127)
+       {
+               dt = random();
+
+               if(dt < 0.03)
+                       te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
+       }
+
+       if(self.health < 85)
+       if(dt < 0.01)
+               pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
+
+       if(self.health < 32)
+       if(dt < 0.015)
+               pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
+
+}
+
+void turret_draw2d()
+{
+       if(self.netname == "")
+               return;
+
+       if(!autocvar_g_waypointsprite_turrets)
+               return;
+
+       if(autocvar_cl_hidewaypoints)
+               return;
+
+       float dist = vlen(self.origin - view_origin);
+       float t = (GetPlayerColor(player_localnum) + 1);
+
+       vector o;
+       string txt;
+
+       if(autocvar_cl_vehicles_hud_tactical)
+       if(dist < 10240 && t != self.team)
+       {
+               // TODO: Vehicle tactical hud
+               o = project_3d_to_2d(self.origin + '0 0 32');
+               if(o_z < 0
+               || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
+               || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
+               || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
+               || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
+                       return; // Dont draw wp's for turrets out of view
+               o_z = 0;
+               if(hud != HUD_NORMAL)
+               {
+                       if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE)
+                               txt = "gfx/vehicles/vth-mover.tga";
+                       else
+                               txt = "gfx/vehicles/vth-stationary.tga";
+
+                       vector pz = drawgetimagesize(txt) * 0.25;
+                       drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL);
+               }
+       }
+
+       if(dist > self.maxdistance)
+               return;
+
+       string spriteimage = self.netname;
+       float a = self.alpha * autocvar_hud_panel_fg_alpha;
+       vector rgb = spritelookupcolor(spriteimage, self.teamradar_color);
+
+
+       if(self.maxdistance > waypointsprite_normdistance)
+               a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
+       else if(self.maxdistance > 0)
+               a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
+
+       if(rgb == '0 0 0')
+       {
+               self.teamradar_color = '1 0 1';
+               printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
+       }
+
+       txt = self.netname;
+       if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
+               txt = _("Spam");
+       else
+               txt = spritelookuptext(spriteimage);
+
+       if(time - floor(time) > 0.5 && t == self.team)
+       {
+               if(self.helpme && time < self.helpme)
+               {
+                       a *= SPRITE_HELPME_BLINK;
+                       txt = sprintf(_("%s under attack!"), txt);
+               }
+               else
+                       a *= spritelookupblinkvalue(spriteimage);
+       }
+
+       if(autocvar_g_waypointsprite_uppercase)
+               txt = strtoupper(txt);
+
+       if(a > 1)
+       {
+               rgb *= a;
+               a = 1;
+       }
+
+       if(a <= 0)
+               return;
+
+       rgb = fixrgbexcess(rgb);
+
+       o = project_3d_to_2d(self.origin + '0 0 64');
+       if(o_z < 0
+       || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
+       || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
+       || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
+       || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
+               return; // Dont draw wp's for turrets out of view
+
+       o_z = 0;
+
+       float edgedistance_min, crosshairdistance;
+               edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
+       (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
+       (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
+       (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
+
+       float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
+
+       crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
+
+       t = waypointsprite_scale * vidscale;
+       a *= waypointsprite_alpha;
+
+       {
+               a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
+               t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
+       }
+       if (edgedistance_min < waypointsprite_edgefadedistance) {
+               a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
+               t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
+       }
+       if(crosshairdistance < waypointsprite_crosshairfadedistance) {
+               a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
+               t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
+       }
+
+       o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t);
+       o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
+       drawhealthbar(
+                       o,
+                       0,
+                       self.health / 255,
+                       '0 0 0',
+                       '0 0 0',
+                       0.5 * SPRITE_HEALTHBAR_WIDTH * t,
+                       0.5 * SPRITE_HEALTHBAR_HEIGHT * t,
+                       SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize,
+                       SPRITE_HEALTHBAR_BORDER * t,
+                       0,
+                       rgb,
+                       a * SPRITE_HEALTHBAR_BORDERALPHA,
+                       rgb,
+                       a * SPRITE_HEALTHBAR_HEALTHALPHA,
+                       DRAWFLAG_NORMAL
+                       );
+}
+
+void turret_construct()
+{
+       entity tur = get_turretinfo(self.turretid);
+
+       if(self.tur_head == world)
+               self.tur_head = spawn();
+
+       self.netname = TUR_NAME(self.turretid);
+
+       setorigin(self, self.origin);
+       setmodel(self, tur.model);
+       setmodel(self.tur_head, tur.head_model);
+       setsize(self, tur.mins, tur.maxs);
+       setsize(self.tur_head, '0 0 0', '0 0 0');
+
+       if(self.turretid == TUR_EWHEEL)
+               setattachment(self.tur_head, self, "");
+       else
+               setattachment(self.tur_head, self, "tag_head");
+
+       self.tur_head.classname                 = "turret_head";
+       self.tur_head.owner                             = self;
+       self.tur_head.move_movetype             = MOVETYPE_NOCLIP;
+       self.move_movetype                              = MOVETYPE_NOCLIP;
+       self.tur_head.angles                    = self.angles;
+       self.health                                             = 255;
+       self.solid                                              = SOLID_BBOX;
+       self.tur_head.solid                             = SOLID_NOT;
+       self.movetype                                   = MOVETYPE_NOCLIP;
+       self.tur_head.movetype                  = MOVETYPE_NOCLIP;
+       self.draw                                               = turret_draw;
+       self.entremove                                  = turret_remove;
+       self.drawmask                                   = MASK_NORMAL;
+       self.tur_head.drawmask                  = MASK_NORMAL;
+       self.anim_start_time                    = 0;
+       self.draw2d = turret_draw2d;
+       self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
+       self.teamradar_color = '1 0 0';
+       self.alpha = 1;
+       
+       TUR_ACTION(self.turretid, TR_SETUP);
+}
+
+entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode);
+void turret_gibboom();
+void turret_gib_draw()
+{
+       Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
+
+       self.drawmask = MASK_NORMAL;
+
+       if(self.cnt)
+       {
+               if(time >= self.nextthink)
+               {
+                       turret_gibboom();
+                       remove(self);
+               }
+       }
+       else
+       {
+               self.alpha = bound(0, self.nextthink - time, 1);
+               if(self.alpha < ALPHA_MIN_VISIBLE)
+                       remove(self);
+       }
+}
+
+void turret_gibboom()
+{
+       float i;
+
+       sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+       pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+
+       for (i = 1; i < 5; i = i + 1)
+               turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false);
+}
+
+entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode)
+{
+       entity gib;
+
+       traceline(_from, _to, MOVE_NOMONSTERS, world);
+       if(trace_startsolid)
+               return world;
+
+       gib = spawn();
+       setorigin(gib, _from);
+       setmodel(gib, _model);
+       gib.colormod    = _cmod;
+       gib.solid          = SOLID_CORPSE;
+       gib.draw                = turret_gib_draw;
+       gib.cnt          = _explode;
+       setsize(gib, '-1 -1 -1', '1 1 1');
+       if(_explode)
+       {
+               gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15));
+               gib.effects = EF_FLAME;
+       }
+       else
+               gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
+
+       gib.gravity              = 1;
+       gib.move_movetype   = MOVETYPE_BOUNCE;
+       gib.move_origin  = _from;
+       setorigin(gib,          _from);
+       gib.move_velocity   = _to;
+       gib.move_avelocity  = prandomvec() * 32;
+       gib.move_time      = time;
+       gib.damageforcescale = 1;
+       gib.classname = "turret_gib";
+
+       return gib;
+}
+
+void turret_die()
+{
+       sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+       pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+       if (!autocvar_cl_nogibs)
+       {
+               // Base
+               if(self.turretid == TUR_EWHEEL)
+                       turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true);
+               else if (self.turretid == TUR_WALKER)
+                       turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true);
+               else if (self.turretid == TUR_TESLA)
+                       turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false);
+               else
+               {
+                       if (random() > 0.5)
+                       {
+                               turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
+                               turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
+                               turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false);
+                       }
+                       else
+                               turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true);
+
+                       entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true);
+                       if(headgib)
+                       {
+                               headgib.angles = headgib.move_angles = self.tur_head.angles;
+                               headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45;
+                               headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5;
+                               headgib.gravity = 0.5;
+                       }
+               }
+       }
+
+       setmodel(self, "null");
+       setmodel(self.tur_head, "null");
+}
+
+void ent_turret()
+{
+       float sf;
+       sf = ReadByte();
+
+       if(sf & TNSF_SETUP)
+       {
+               self.turretid = ReadByte();
+
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.angles_x = ReadAngle();
+               self.angles_y = ReadAngle();
+               
+               turret_construct();
+               self.colormap = 1024;
+               self.glowmod = '0 1 1';
+               self.tur_head.colormap = self.colormap;
+               self.tur_head.glowmod = self.glowmod;
+       }
+
+       if(sf & TNSF_ANG)
+       {
+               if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
+                       self.tur_head = spawn();
+
+               self.tur_head.move_angles_x = ReadShort();
+               self.tur_head.move_angles_y = ReadShort();
+               //self.tur_head.angles = self.angles + self.tur_head.move_angles;
+               self.tur_head.angles = self.tur_head.move_angles;
+       }
+
+       if(sf & TNSF_AVEL)
+       {
+               if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
+                       self.tur_head = spawn();
+
+               self.tur_head.move_avelocity_x = ReadShort();
+               self.tur_head.move_avelocity_y = ReadShort();
+       }
+
+       if(sf & TNSF_MOVE)
+       {
+               self.origin_x = ReadShort();
+               self.origin_y = ReadShort();
+               self.origin_z = ReadShort();
+               setorigin(self, self.origin);
+
+               self.velocity_x = ReadShort();
+               self.velocity_y = ReadShort();
+               self.velocity_z = ReadShort();
+
+               self.move_angles_y = ReadShort();
+
+               self.move_time   = time;
+               self.move_velocity = self.velocity;
+               self.move_origin   = self.origin;
+       }
+
+       if(sf & TNSF_ANIM)
+       {
+               self.frame1time = ReadCoord();
+               self.frame        = ReadByte();
+       }
+
+       if(sf & TNSF_STATUS)
+       {
+               float _tmp;
+               _tmp = ReadByte();
+               if(_tmp != self.team)
+               {
+                       self.team = _tmp;
+                       turret_changeteam();
+               }
+
+               _tmp = ReadByte();
+               if(_tmp == 0 && self.health != 0)
+                       turret_die();
+               else if(self.health && self.health != _tmp)
+                       self.helpme = servertime + 10;
+
+               self.health = _tmp;
+       }
+       //self.enemy.health = self.health / 255;
+}
diff --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh
new file mode 100644 (file)
index 0000000..0f8ff94
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef CL_TURRETS_H
+#define CL_TURRETS_H
+
+void ent_turret();
+
+#endif
diff --git a/qcsrc/common/turrets/config.qc b/qcsrc/common/turrets/config.qc
new file mode 100644 (file)
index 0000000..895b469
--- /dev/null
@@ -0,0 +1,62 @@
+// ==========================
+//  Turret Config Generator
+// ==========================
+
+void T_Config_Queue_Swap(float root, float child, entity pass)
+{
+       string oldroot = tur_config_queue[root];
+       tur_config_queue[root] = tur_config_queue[child];
+       tur_config_queue[child] = oldroot;
+}
+
+float T_Config_Queue_Compare(float root, float child, entity pass)
+{
+       float i, r, c;
+
+       for(i = 1; i <= 100; ++i)
+       {
+               r = str2chr(tur_config_queue[root], i);
+               c = str2chr(tur_config_queue[child], i);
+               if(r == c) { continue; }
+               else if(c > r) { return -1; }
+               else { return 1; }
+       }
+       
+       return 0;
+}
+
+void Dump_Turret_Settings(void)
+{
+       float i, x, totalsettings = 0;
+       for(i = TUR_FIRST; i <= TUR_LAST; ++i)
+       {
+               // step 1: clear the queue
+               TUR_CONFIG_COUNT = 0;
+               for(x = 0; x <= MAX_TUR_CONFIG; ++x)
+                       { tur_config_queue[x] = string_null; }
+
+               // step 2: build new queue
+               TUR_ACTION(i, TR_CONFIG);
+
+               // step 3: sort queue
+               heapsort(TUR_CONFIG_COUNT, T_Config_Queue_Swap, T_Config_Queue_Compare, world);
+               
+               // step 4: write queue
+               TUR_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, TUR_NAME(i)))
+               for(x = 0; x <= TUR_CONFIG_COUNT; ++x)
+                       { TUR_CONFIG_WRITETOFILE(tur_config_queue[x]) }
+               TUR_CONFIG_WRITETOFILE("// }}}\n")
+
+               // step 5: debug info
+               print(sprintf("#%d: %s: %d settings...\n", i, TUR_NAME(i), TUR_CONFIG_COUNT));
+               totalsettings += TUR_CONFIG_COUNT;
+       }
+
+       // clear queue now that we're finished
+       TUR_CONFIG_COUNT = 0;
+       for(x = 0; x <= MAX_TUR_CONFIG; ++x)
+               { tur_config_queue[x] = string_null; }
+
+       // extra information
+       print(sprintf("Totals: %d turrets, %d settings\n", (i - 1), totalsettings));
+}
diff --git a/qcsrc/common/turrets/config.qh b/qcsrc/common/turrets/config.qh
new file mode 100644 (file)
index 0000000..85cbd20
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef TURRETS_CONFIG_H
+#define TURRETS_CONFIG_H
+
+#ifdef SVQC
+
+void Dump_Turret_Settings(void);
+float tur_config_file;
+float tur_config_alsoprint;
+
+#define MAX_TUR_CONFIG 256
+float TUR_CONFIG_COUNT;
+string tur_config_queue[MAX_TUR_CONFIG];
+
+#define TUR_CONFIG_QUEUE(a) { \
+       tur_config_queue[TUR_CONFIG_COUNT] = a; \
+       ++TUR_CONFIG_COUNT; }
+
+#define TUR_CONFIG_WRITETOFILE(a) { \
+       fputs(tur_config_file, a); \
+       if(tur_config_alsoprint) { print(a); } }
+
+#define TUR_CONFIG_WRITE_CVARS(turret,name) \
+               { TUR_CONFIG_QUEUE( \
+                       sprintf("set g_turrets_unit_%s_%s %g\n", #turret, #name, \
+                       cvar(sprintf("g_turrets_unit_%s_%s", #turret, #name)))) } \
+
+#define TUR_CONFIG_SETTINGS(tursettings) \
+       #define TUR_ADD_CVAR(turret,name) TUR_CONFIG_WRITE_CVARS(turret,name) \
+       tursettings \
+       #undef TUR_ADD_CVAR
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc
new file mode 100644 (file)
index 0000000..91d427f
--- /dev/null
@@ -0,0 +1,1394 @@
+#ifdef SVQC
+#include "../../server/autocvars.qh"
+
+// Generic aiming
+vector turret_aim_generic()
+{
+
+       vector pre_pos, prep;
+       float distance, impact_time = 0, i, mintime;
+
+       turret_tag_fire_update();
+
+       if(self.aim_flags & TFL_AIM_SIMPLE)
+               return real_origin(self.enemy);
+
+       mintime = max(self.attack_finished_single - time,0) + sys_frametime;
+
+       // Baseline
+       pre_pos = real_origin(self.enemy);
+
+       // Lead?
+       if (self.aim_flags & TFL_AIM_LEAD)
+       {
+               if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE)           // Need to conpensate for shot traveltime
+               {
+                       prep = pre_pos;
+                       
+                       distance = vlen(prep - self.tur_shotorg);
+                       impact_time = distance / self.shot_speed;
+
+                       prep = pre_pos + (self.enemy.velocity * (impact_time + mintime));
+
+                       if(self.aim_flags & TFL_AIM_ZPREDICT)
+                       if(!(self.enemy.flags & FL_ONGROUND))
+                       if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE)
+                       {
+                               float vz;
+                               prep_z = pre_pos_z;
+                               vz = self.enemy.velocity_z;
+                               for(i = 0; i < impact_time; i += sys_frametime)
+                               {
+                                       vz = vz - (autocvar_sv_gravity * sys_frametime);
+                                       prep_z = prep_z + vz * sys_frametime;
+                               }
+                       }
+                       pre_pos = prep;
+               }
+               else
+                       pre_pos = pre_pos + self.enemy.velocity * mintime;
+       }
+
+       if(self.aim_flags & TFL_AIM_SPLASH)
+       {
+               //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
+               traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
+               if(trace_fraction != 1.0)
+                       pre_pos = trace_endpos;
+       }
+
+       return pre_pos;
+}
+
+float turret_targetscore_support(entity _turret,entity _target)
+{
+       float score;            // Total score
+       float s_score = 0, d_score;
+
+       if (_turret.enemy == _target) s_score = 1;
+
+       d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist);
+
+       score = (d_score * _turret.target_select_rangebias) +
+                       (s_score * _turret.target_select_samebias);
+
+       return score;
+}
+
+/*
+* Generic bias aware score system.
+*/
+float turret_targetscore_generic(entity _turret, entity _target)
+{
+       float d_dist;      // Defendmode Distance
+       float score;            // Total score
+       float d_score;    // Distance score
+       float a_score;    // Angular score
+       float m_score = 0;  // missile score
+       float p_score = 0;  // player score
+       float ikr;                // ideal kill range
+
+       if (_turret.tur_defend)
+       {
+               d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin);
+               ikr = vlen(_turret.origin - _turret.tur_defend.origin);
+               d_score = 1 - d_dist / _turret.target_range;
+       }
+       else
+       {
+               // Make a normlized value base on the targets distance from our optimal killzone
+               ikr = _turret.target_range_optimal;
+               d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist);
+       }
+
+       a_score = 1 - tvt_thadf / _turret.aim_maxrotate;
+
+       if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE))
+               m_score = 1;
+
+       if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target))
+               p_score = 1;
+
+       d_score = max(d_score, 0);
+       a_score = max(a_score, 0);
+       m_score = max(m_score, 0);
+       p_score = max(p_score, 0);
+
+       score = (d_score * _turret.target_select_rangebias) +
+                       (a_score * _turret.target_select_anglebias) +
+                       (m_score * _turret.target_select_missilebias) +
+                       (p_score * _turret.target_select_playerbias);
+
+       if(_turret.target_range < vlen(_turret.tur_shotorg - real_origin(_target)))
+       {
+               //dprint("Wtf?\n");
+               score *= 0.001;
+       }
+
+#ifdef TURRET_DEBUG
+       string sd,sa,sm,sp,ss;
+       string sdt,sat,smt,spt;
+
+       sd = ftos(d_score);
+       d_score *= _turret.target_select_rangebias;
+       sdt = ftos(d_score);
+
+       //sv = ftos(v_score);
+       //v_score *= _turret.target_select_samebias;
+       //svt = ftos(v_score);
+
+       sa = ftos(a_score);
+       a_score *= _turret.target_select_anglebias;
+       sat = ftos(a_score);
+
+       sm = ftos(m_score);
+       m_score *= _turret.target_select_missilebias;
+       smt = ftos(m_score);
+
+       sp = ftos(p_score);
+       p_score *= _turret.target_select_playerbias;
+       spt = ftos(p_score);
+
+
+       ss = ftos(score);
+       bprint("^3Target scores^7 \[  ",_turret.netname, "  \] ^3for^7 \[  ", _target.netname,"  \]\n");
+       bprint("^5Range:\[  ",sd,  "  \]^2+bias:\[  ",sdt,"  \]\n");
+       bprint("^5Angle:\[  ",sa,  "  \]^2+bias:\[  ",sat,"  \]\n");
+       bprint("^5Missile:\[  ",sm,"  \]^2+bias:\[  ",smt,"  \]\n");
+       bprint("^5Player:\[  ",sp, "  \]^2+bias:\[  ",spt,"  \]\n");
+       bprint("^3Total (w/bias):\[^1",ss,"\]\n");
+
+#endif
+
+       return score;
+}
+
+// Generic damage handling
+void turret_hide()
+{
+       self.effects   |= EF_NODRAW;
+       self.nextthink = time + self.respawntime - 0.2;
+       self.think       = turret_respawn;
+}
+
+void turret_die()
+{
+       self.deadflag             = DEAD_DEAD;
+       self.tur_head.deadflag = self.deadflag;
+
+// Unsolidify and hide real parts
+       self.solid                       = SOLID_NOT;
+       self.tur_head.solid      = self.solid;
+
+       self.event_damage                 = func_null;
+       self.takedamage                  = DAMAGE_NO;
+
+       self.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);
+
+       if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN)
+       {
+               TUR_ACTION(self.turretid, TR_DEATH);
+
+               remove(self.tur_head);
+               remove(self);
+       }
+       else
+       {
+               // Setup respawn
+               self.SendFlags    |= TNSF_STATUS;
+               self.nextthink   = time + 0.2;
+               self.think               = turret_hide;
+
+               TUR_ACTION(self.turretid, TR_DEATH);
+       }
+}
+
+void turret_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+       // Enough already!
+       if(self.deadflag == DEAD_DEAD)
+               return;
+
+       // Inactive turrets take no damage. (hm..)
+       if(!self.active)
+               return;
+
+       if(SAME_TEAM(self, attacker))
+       {
+               if(autocvar_g_friendlyfire)
+                       damage = damage * autocvar_g_friendlyfire;
+               else
+                       return;
+       }
+
+       self.health -= damage;
+
+       // thorw head slightly off aim when hit?
+       if (self.damage_flags & TFL_DMG_HEADSHAKE)
+       {
+               self.tur_head.angles_x = self.tur_head.angles_x + (-0.5 + random()) * damage;
+               self.tur_head.angles_y = self.tur_head.angles_y + (-0.5 + random()) * damage;
+
+               self.SendFlags  |= TNSF_ANG;
+       }
+
+       if (self.turret_flags & TUR_FLAG_MOVE)
+               self.velocity = self.velocity + vforce;
+
+       if (self.health <= 0)
+       {
+               self.event_damage                 = func_null;
+               self.tur_head.event_damage = func_null;
+               self.takedamage                  = DAMAGE_NO;
+               self.nextthink = time;
+               self.think = turret_die;
+       }
+
+       self.SendFlags  |= TNSF_STATUS;
+}
+
+void() turret_think;
+void turret_respawn()
+{
+       // 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;
+       self.think       = turret_think;
+
+       self.SendFlags = TNSF_FULL_UPDATE;
+       
+       TUR_ACTION(self.turretid, TR_SETUP);
+}
+
+
+// Main functions
+#define cvar_base "g_turrets_unit_"
+.float clientframe;
+void turrets_setframe(float _frame, float client_only)
+{
+       if((client_only ? self.clientframe : self.frame ) != _frame)
+       {
+               self.SendFlags |= TNSF_ANIM;
+               self.anim_start_time = time;
+       }
+
+        if(client_only)
+               self.clientframe = _frame;
+       else
+               self.frame = _frame;
+
+}
+
+float turret_send(entity to, float sf)
+{
+
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET);
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & TNSF_SETUP)
+       {
+               WriteByte(MSG_ENTITY, self.turretid);
+
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteAngle(MSG_ENTITY, self.angles_x);
+               WriteAngle(MSG_ENTITY, self.angles_y);
+       }
+
+       if(sf & TNSF_ANG)
+       {
+               WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x));
+               WriteShort(MSG_ENTITY, rint(self.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));
+       }
+
+       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));
+
+               WriteShort(MSG_ENTITY, rint(self.velocity_x));
+               WriteShort(MSG_ENTITY, rint(self.velocity_y));
+               WriteShort(MSG_ENTITY, rint(self.velocity_z));
+
+               WriteShort(MSG_ENTITY, rint(self.angles_y));
+       }
+
+       if(sf & TNSF_ANIM)
+       {
+               WriteCoord(MSG_ENTITY, self.anim_start_time);
+               WriteByte(MSG_ENTITY, self.frame);
+       }
+
+       if(sf & TNSF_STATUS)
+       {
+               WriteByte(MSG_ENTITY, self.team);
+
+               if(self.health <= 0)
+                       WriteByte(MSG_ENTITY, 0);
+               else
+                       WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
+       }
+
+       return true;
+}
+
+void load_unit_settings(entity ent, string unitname, float is_reload)
+{
+       string sbase;
+
+       if (ent == world)
+               return;
+
+       if(!ent.turret_scale_damage)    ent.turret_scale_damage = 1;
+       if(!ent.turret_scale_range)             ent.turret_scale_range  = 1;
+       if(!ent.turret_scale_refire)    ent.turret_scale_refire = 1;
+       if(!ent.turret_scale_ammo)              ent.turret_scale_ammo   = 1;
+       if(!ent.turret_scale_aim)               ent.turret_scale_aim     = 1;
+       if(!ent.turret_scale_health)    ent.turret_scale_health = 1;
+       if(!ent.turret_scale_respawn)   ent.turret_scale_respawn = 1;
+
+       sbase = strcat(cvar_base,unitname);
+       if (is_reload)
+       {
+               ent.enemy = world;
+               ent.tur_head.avelocity = '0 0 0';
+
+               ent.tur_head.angles = '0 0 0';
+       }
+
+       ent.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;
+       ent.shot_refire   = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire;
+       ent.shot_radius   = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage;
+       ent.shot_speed          = cvar(strcat(sbase,"_shot_speed"));
+       ent.shot_spread   = cvar(strcat(sbase,"_shot_spread"));
+       ent.shot_force          = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage;
+       ent.shot_volly          = cvar(strcat(sbase,"_shot_volly"));
+       ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire;
+
+       ent.target_range                 = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range;
+       ent.target_range_min     = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range;
+       ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range;
+       //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range;
+
+       ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias"));
+       ent.target_select_samebias  = cvar(strcat(sbase,"_target_select_samebias"));
+       ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias"));
+       ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias"));
+       //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov"));
+
+       ent.ammo_max     = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo;
+       ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo;
+
+       ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist"));
+       ent.aim_speed   = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim;
+       ent.aim_maxrotate  = cvar(strcat(sbase,"_aim_maxrot"));
+       ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch"));
+
+       ent.track_type          = cvar(strcat(sbase,"_track_type"));
+       ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch"));
+       ent.track_accel_rotate  = cvar(strcat(sbase,"_track_accel_rot"));
+       ent.track_blendrate  = cvar(strcat(sbase,"_track_blendrate"));
+
+       if(is_reload)
+               TUR_ACTION(self.turretid, TR_SETUP);
+}
+
+void turret_projectile_explode()
+{
+
+       self.takedamage = DAMAGE_NO;
+       self.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;
+#else
+       RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+#endif
+       remove(self);
+}
+
+void turret_projectile_touch()
+{
+       PROJECTILE_TOUCH;
+       turret_projectile_explode();
+}
+
+void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+       self.velocity  += vforce;
+       self.health     -= damage;
+       //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets
+       if(self.health <= 0)
+               W_PrepareExplosionByDamage(self.owner, turret_projectile_explode);
+}
+
+entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim)
+{
+       entity proj;
+
+       sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM);
+       proj                             = spawn ();
+       setorigin(proj, self.tur_shotorg);
+       setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size);
+       proj.owner                = self;
+       proj.realowner    = self;
+       proj.bot_dodge    = true;
+       proj.bot_dodgerating = self.shot_dmg;
+       proj.think                = turret_projectile_explode;
+       proj.touch                = turret_projectile_touch;
+       proj.nextthink    = time + 9;
+       proj.movetype           = MOVETYPE_FLYMISSILE;
+       proj.velocity           = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
+       proj.flags                = FL_PROJECTILE;
+       proj.enemy                = self.enemy;
+       proj.totalfrags  = _death;
+       PROJECTILE_MAKETRIGGER(proj);
+       if(_health)
+       {
+               proj.health              = _health;
+               proj.takedamage  = DAMAGE_YES;
+               proj.event_damage  = turret_projectile_damage;
+       }
+       else
+               proj.flags |= FL_NOTARGET;
+
+       CSQCProjectile(proj, _cli_anim, _proj_type, _cull);
+
+       return proj;
+}
+
+/**
+** updates enemy distances, predicted impact point/time
+** and updated aim<->predict impact distance.
+**/
+void turret_do_updates(entity t_turret)
+{
+       vector enemy_pos;
+       entity oldself;
+
+       oldself = self;
+       self = t_turret;
+
+       enemy_pos = real_origin(self.enemy);
+
+       turret_tag_fire_update();
+
+       self.tur_shotdir_updated = v_forward;
+       self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos);
+       self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos);
+
+       /*if((self.firecheck_flags & TFL_FIRECHECK_VERIFIED) && (self.enemy))
+       {
+               oldpos = self.enemy.origin;
+               setorigin(self.enemy, self.tur_aimpos);
+               tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self);
+               setorigin(self.enemy, oldpos);
+
+               if(trace_ent == self.enemy)
+                       self.tur_dist_impact_to_aimpos = 0;
+               else
+                       self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos);
+       }
+       else*/
+               tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self);
+
+       self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5);
+       self.tur_impactent                       = trace_ent;
+       self.tur_impacttime                     = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed;
+
+       self = oldself;
+}
+
+/**
+** Handles head rotation according to
+** the units .track_type and .track_flags
+**/
+.float turret_framecounter;
+void turret_track()
+{
+       vector target_angle; // This is where we want to aim
+       vector move_angle;   // This is where we can aim
+       float f_tmp;
+       vector v1, v2;
+       v1 = self.tur_head.angles;
+       v2 = self.tur_head.avelocity;
+
+       if (self.track_flags == TFL_TRACK_NO)
+               return;
+
+       if(!self.active)
+               target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch);
+       else if (self.enemy == world)
+       {
+               if(time > self.lip)
+                       target_angle = self.idle_aim + self.angles;
+               else
+                       target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg));
+       }
+       else
+       {
+               target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg));
+       }
+
+       self.tur_head.angles_x = anglemods(self.tur_head.angles_x);
+       self.tur_head.angles_y = anglemods(self.tur_head.angles_y);
+
+       // Find the diffrence between where we currently aim and where we want to aim
+       //move_angle = target_angle - (self.angles + self.tur_head.angles);
+       //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles));
+
+       move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles;
+       move_angle = shortangle_vxy(move_angle, self.tur_head.angles);
+
+       switch(self.track_type)
+       {
+               case TFL_TRACKTYPE_STEPMOTOR:
+                       f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic
+                       if (self.track_flags & TFL_TRACK_PITCH)
+                       {
+                               self.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp);
+                               if(self.tur_head.angles_x > self.aim_maxpitch)
+                                       self.tur_head.angles_x = self.aim_maxpitch;
+
+                               if(self.tur_head.angles_x  < -self.aim_maxpitch)
+                                       self.tur_head.angles_x = self.aim_maxpitch;
+                       }
+
+                       if (self.track_flags & TFL_TRACK_ROTATE)
+                       {
+                               self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp);
+                               if(self.tur_head.angles_y > self.aim_maxrotate)
+                                       self.tur_head.angles_y = self.aim_maxrotate;
+
+                               if(self.tur_head.angles_y  < -self.aim_maxrotate)
+                                       self.tur_head.angles_y = self.aim_maxrotate;
+                       }
+
+                       // CSQC
+                       self.SendFlags  |= TNSF_ANG;
+
+                       return;
+
+               case TFL_TRACKTYPE_FLUIDINERTIA:
+                       f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic
+                       move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp, self.aim_speed);
+                       move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rotate * f_tmp, self.aim_speed);
+                       move_angle = (self.tur_head.avelocity * self.track_blendrate) + (move_angle * (1 - self.track_blendrate));
+                       break;
+
+               case TFL_TRACKTYPE_FLUIDPRECISE:
+
+                       move_angle_y = bound(-self.aim_speed, move_angle_y, self.aim_speed);
+                       move_angle_x = bound(-self.aim_speed, move_angle_x, self.aim_speed);
+
+                       break;
+       }
+
+       //  pitch
+       if (self.track_flags & TFL_TRACK_PITCH)
+       {
+               self.tur_head.avelocity_x = move_angle_x;
+               if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) > self.aim_maxpitch)
+               {
+                       self.tur_head.avelocity_x = 0;
+                       self.tur_head.angles_x = self.aim_maxpitch;
+
+                       self.SendFlags  |= TNSF_ANG;
+               }
+
+               if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch)
+               {
+                       self.tur_head.avelocity_x = 0;
+                       self.tur_head.angles_x = -self.aim_maxpitch;
+
+                       self.SendFlags  |= TNSF_ANG;
+               }
+       }
+
+       //  rot
+       if (self.track_flags & TFL_TRACK_ROTATE)
+       {
+               self.tur_head.avelocity_y = move_angle_y;
+
+               if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrotate)
+               {
+                       self.tur_head.avelocity_y = 0;
+                       self.tur_head.angles_y = self.aim_maxrotate;
+
+                       self.SendFlags  |= TNSF_ANG;
+               }
+
+               if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrotate)
+               {
+                       self.tur_head.avelocity_y = 0;
+                       self.tur_head.angles_y = -self.aim_maxrotate;
+
+                       self.SendFlags  |= TNSF_ANG;
+               }
+       }
+
+       self.SendFlags  |= TNSF_AVEL;
+
+       // Force a angle update every 10'th frame
+       self.turret_framecounter += 1;
+       if(self.turret_framecounter >= 10)
+       {
+               self.SendFlags |= TNSF_ANG;
+               self.turret_framecounter = 0;
+       }
+}
+
+/*
+ + TFL_TARGETSELECT_NO
+ + TFL_TARGETSELECT_LOS
+ + TFL_TARGETSELECT_PLAYERS
+ + TFL_TARGETSELECT_MISSILES
+ - TFL_TARGETSELECT_TRIGGERTARGET
+ + TFL_TARGETSELECT_ANGLELIMITS
+ + TFL_TARGETSELECT_RANGELIMITS
+ + TFL_TARGETSELECT_TEAMCHECK
+ - TFL_TARGETSELECT_NOBUILTIN
+ + TFL_TARGETSELECT_OWNTEAM
+*/
+
+/**
+** Evaluate a entity for target valitity based on validate_flags
+** NOTE: the caller must check takedamage before calling this, to inline this check.
+**/
+float turret_validate_target(entity e_turret, entity e_target, float validate_flags)
+{
+       vector v_tmp;
+
+       //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)
+       //      return -0.5;
+
+       if(!e_target)
+               return -2;
+
+       if(e_target.owner == e_turret)
+               return -0.5;
+
+       if(!checkpvs(e_target.origin, e_turret))
+               return -1;
+
+       if(e_target.alpha <= 0.3)
+               return -1;
+
+       if(g_onslaught)
+               if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
+                       return - 3;
+
+       if (validate_flags & TFL_TARGETSELECT_NO)
+               return -4;
+
+       // If only this was used more..
+       if (e_target.flags & FL_NOTARGET)
+               return -5;
+
+       // Cant touch this
+       if(e_target.vehicle_flags & VHF_ISVEHICLE)
+       {
+               if (e_target.vehicle_health <= 0)
+                       return -6;
+       }
+       else if (e_target.health <= 0)
+               return -6;
+       else if(e_target.frozen > 0)
+               return -6;
+
+       // player
+       if (IS_CLIENT(e_target))
+       {
+               if(!(validate_flags & TFL_TARGETSELECT_PLAYERS))
+                       return -7;
+
+               if (e_target.deadflag != DEAD_NO)
+                       return -8;
+       }
+
+       // enemy turrets
+       if(validate_flags & TFL_TARGETSELECT_NOTURRETS)
+       if(e_target.owner.tur_head == e_target)
+       if(e_target.team != e_turret.team) // Dont break support units.
+               return -9;
+
+       // Missile
+       if (e_target.flags & FL_PROJECTILE)
+       if(!(validate_flags & TFL_TARGETSELECT_MISSILES))
+               return -10;
+
+       if (validate_flags & TFL_TARGETSELECT_MISSILESONLY)
+       if(!(e_target.flags & FL_PROJECTILE))
+               return -10.5;
+
+       // Team check
+       if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)
+       {
+               if (validate_flags & TFL_TARGETSELECT_OWNTEAM)
+               {
+                       if (e_target.team != e_turret.team)
+                               return -11;
+
+                       if (e_turret.team != e_target.owner.team)
+                               return -12;
+               }
+               else
+               {
+                       if (e_target.team == e_turret.team)
+                               return -13;
+
+                       if (e_turret.team == e_target.owner.team)
+                               return -14;
+               }
+       }
+
+       // Range limits?
+       tvt_dist = vlen(e_turret.origin - real_origin(e_target));
+       if (validate_flags & TFL_TARGETSELECT_RANGELIMITS)
+       {
+               if (tvt_dist < e_turret.target_range_min)
+                       return -15;
+
+               if (tvt_dist > e_turret.target_range)
+                       return -16;
+       }
+
+       // 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_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles);
+       tvt_thadf = vlen(tvt_thadv);
+       tvt_tadf = vlen(tvt_tadv);
+
+       /*
+       if(validate_flags & TFL_TARGETSELECT_FOV)
+       {
+               if(e_turret.target_select_fov < tvt_thadf)
+                       return -21;
+       }
+       */
+
+       if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)
+       {
+               if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
+                       return -17;
+
+               if (fabs(tvt_tadv_y) > e_turret.aim_maxrotate)
+                       return -18;
+       }
+
+       // Line of sight?
+       if (validate_flags & TFL_TARGETSELECT_LOS)
+       {
+               v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
+
+               traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret);
+
+               if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos))
+                       return -19;
+       }
+
+       if (e_target.classname == "grapplinghook")
+               return -20;
+
+       /*
+       if (e_target.classname == "func_button")
+               return -21;
+       */
+
+#ifdef TURRET_DEBUG_TARGETSELECT
+       dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n");
+#endif
+
+       return 1;
+}
+
+entity turret_select_target()
+{
+       entity e;               // target looper entity
+       float  score;   // target looper entity score
+       entity e_enemy;  // currently best scoreing target
+       float  m_score;  // currently best scoreing target's score
+
+       m_score = 0;
+       if(self.enemy && self.enemy.takedamage && turret_validate_target(self,self.enemy,self.target_validate_flags) > 0)
+       {
+               e_enemy = self.enemy;
+               m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias;
+       }
+       else
+               e_enemy = self.enemy = world;
+
+       e = findradius(self.origin, self.target_range);
+
+       // Nothing to aim at?
+       if (!e)
+               return world;
+
+       while (e)
+       {
+               if(e.takedamage)
+               {
+                       float f = turret_validate_target(self, e, self.target_select_flags);
+                       //dprint("F is: ", ftos(f), "\n");
+                       if ( f > 0)
+                       {
+                               score = self.turret_score_target(self,e);
+                               if ((score > m_score) && (score > 0))
+                               {
+                                       e_enemy = e;
+                                       m_score = score;
+                               }
+                       }
+               }
+               e = e.chain;
+       }
+
+       return e_enemy;
+}
+
+
+/*
+ + = implemented
+ - = not implemented
+
+ + TFL_FIRECHECK_NO
+ + TFL_FIRECHECK_WORLD
+ + TFL_FIRECHECK_DEAD
+ + TFL_FIRECHECK_DISTANCES
+ - TFL_FIRECHECK_LOS
+ + TFL_FIRECHECK_AIMDIST
+ + TFL_FIRECHECK_REALDIST
+ - TFL_FIRECHECK_ANGLEDIST
+ - TFL_FIRECHECK_TEAMCECK
+ + TFL_FIRECHECK_AFF
+ + TFL_FIRECHECK_AMMO_OWN
+ + TFL_FIRECHECK_AMMO_OTHER
+ + TFL_FIRECHECK_REFIRE
+*/
+
+/**
+** Preforms pre-fire checks based on the uints firecheck_flags
+**/
+float turret_firecheck()
+{
+       // This one just dont care =)
+       if (self.firecheck_flags & TFL_FIRECHECK_NO)
+               return 1;
+
+       if (self.enemy == world)
+               return 0;
+
+       // Ready?
+       if (self.firecheck_flags & TFL_FIRECHECK_REFIRE)
+               if (self.attack_finished_single > time) return 0;
+
+       // Special case: volly fire turret that has to fire a full volly if a shot was fired.
+       if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
+               if (self.volly_counter != self.shot_volly)
+                       if(self.ammo >= self.shot_dmg)
+                               return 1;
+
+       // Lack of zombies makes shooting dead things unnecessary :P
+       if (self.firecheck_flags & TFL_FIRECHECK_DEAD)
+               if (self.enemy.deadflag != DEAD_NO)
+                       return 0;
+
+       // Own ammo?
+       if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OWN)
+               if (self.ammo < self.shot_dmg)
+                       return 0;
+
+       // Other's ammo? (support-supply units)
+       if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OTHER)
+               if (self.enemy.ammo >= self.enemy.ammo_max)
+                       return 0;
+
+       // Target of opertunity?
+       if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
+       {
+               self.enemy = self.tur_impactent;
+               return 1;
+       }
+
+       if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES)
+       {
+               // To close?
+               if (self.tur_dist_aimpos < self.target_range_min)
+                       if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
+                               return 1; // Target of opertunity?
+                       else
+                               return 0;
+       }
+
+       // Try to avoid FF?
+       if (self.firecheck_flags & TFL_FIRECHECK_AFF)
+               if (self.tur_impactent.team == self.team)
+                       return 0;
+
+       // aim<->predicted impact
+       if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST)
+               if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist)
+                       return 0;
+
+       // Volly status
+       if (self.shot_volly > 1)
+               if (self.volly_counter == self.shot_volly)
+                       if (self.ammo < (self.shot_dmg * self.shot_volly))
+                               return 0;
+
+       /*if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED)
+               if(self.tur_impactent != self.enemy)
+                       return 0;*/
+
+       return 1;
+}
+
+void turret_fire()
+{
+       if (autocvar_g_turrets_nofire != 0)
+               return;
+               
+       TUR_ACTION(self.turretid, TR_ATTACK);
+
+       self.attack_finished_single = time + self.shot_refire;
+       self.ammo -= self.shot_dmg;
+       self.volly_counter = self.volly_counter - 1;
+
+       if (self.volly_counter <= 0)
+       {
+               self.volly_counter = self.shot_volly;
+
+               if (self.shoot_flags & TFL_SHOOT_CLEARTARGET)
+                       self.enemy = world;
+
+               if (self.shot_volly > 1)
+                       self.attack_finished_single = time + self.shot_volly_refire;
+       }
+
+#ifdef TURRET_DEBUG
+       if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_debug_rvec, self.tur_impacttime + 0.25);
+#endif
+}
+
+void turret_think()
+{
+       entity e;
+
+       self.nextthink = time + self.ticrate;
+
+       // ONS uses somewhat backwards linking.
+       if (teamplay)
+       {
+               if (g_onslaught)
+                       if (self.target)
+                       {
+                               e = find(world, targetname,self.target);
+                               if (e != world)
+                                       self.team = e.team;
+                       }
+
+               if (self.team != self.tur_head.team)
+                       turret_respawn();
+       }
+
+#ifdef TURRET_DEBUG
+       if (self.tur_debug_tmr1 < time)
+       {
+               if (self.enemy) paint_target (self.enemy,128,self.tur_debug_rvec,0.9);
+               paint_target(self,256,self.tur_debug_rvec,0.9);
+               self.tur_debug_tmr1 = time + 1;
+       }
+#endif
+
+       // Handle ammo
+       if (!(self.spawnflags & TSF_NO_AMMO_REGEN))
+       if (self.ammo < self.ammo_max)
+               self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max);
+
+       // Inactive turrets needs to run the think loop,
+       // So they can handle animation and wake up if need be.
+       if(!self.active)
+       {
+               turret_track();
+               return;
+       }
+
+       // This is typicaly used for zaping every target in range
+       // turret_fusionreactor uses this to recharge friendlys.
+       if (self.shoot_flags & TFL_SHOOT_HITALLVALID)
+       {
+               // Do a self.turret_fire for every valid target.
+               e = findradius(self.origin,self.target_range);
+               while (e)
+               {
+                       if(e.takedamage)
+                       {
+                               if (turret_validate_target(self,e,self.target_validate_flags))
+                               {
+                                       self.enemy = e;
+
+                                       turret_do_updates(self);
+
+                                       if (self.turret_firecheckfunc())
+                                               turret_fire();
+                               }
+                       }
+
+                       e = e.chain;
+               }
+               self.enemy = world;
+       }
+       else if(self.shoot_flags & TFL_SHOOT_CUSTOM)
+       {
+               // This one is doing something.. oddball. assume its handles what needs to be handled.
+
+               // Predict?
+               if(!(self.aim_flags & TFL_AIM_NO))
+                       self.tur_aimpos = turret_aim_generic();
+
+               // Turn & pitch?
+               if(!(self.track_flags & TFL_TRACK_NO))
+                       turret_track();
+
+               turret_do_updates(self);
+
+               // Fire?
+               if (self.turret_firecheckfunc())
+                       turret_fire();
+       }
+       else
+       {
+               // Special case for volly always. if it fired once it must compleate the volly.
+               if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
+                       if(self.volly_counter != self.shot_volly)
+                       {
+                               // Predict or whatnot
+                               if(!(self.aim_flags & TFL_AIM_NO))
+                                       self.tur_aimpos = turret_aim_generic();
+
+                               // Turn & pitch
+                               if(!(self.track_flags & TFL_TRACK_NO))
+                                       turret_track();
+
+                               turret_do_updates(self);
+
+                               // Fire!
+                               if (self.turret_firecheckfunc() != 0)
+                                       turret_fire();
+                                       
+                               TUR_ACTION(self.turretid, TR_THINK);
+
+                               return;
+                       }
+
+               // Check if we have a vailid enemy, and try to find one if we dont.
+
+               // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+               float do_target_scan = 0;
+               if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+                       do_target_scan = 1;
+
+               // Old target (if any) invalid?
+               if(self.target_validate_time < time)
+               if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+               {
+                       self.enemy = world;
+                       self.target_validate_time = time + 0.5;
+                       do_target_scan = 1;
+               }
+
+               // But never more often then g_turrets_targetscan_mindelay!
+               if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+                       do_target_scan = 0;
+
+               if(do_target_scan)
+               {
+                       self.enemy = turret_select_target();
+                       self.target_select_time = time;
+               }
+
+               // No target, just go to idle, do any custom stuff and bail.
+               if (self.enemy == world)
+               {
+                       // Turn & pitch
+                       if(!(self.track_flags & TFL_TRACK_NO))
+                               turret_track();
+                               
+                       TUR_ACTION(self.turretid, TR_THINK);
+
+                       // And bail.
+                       return;
+               }
+               else
+                       self.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target.
+
+               // Predict?
+               if(!(self.aim_flags & TFL_AIM_NO))
+                       self.tur_aimpos = turret_aim_generic();
+
+               // Turn & pitch?
+               if(!(self.track_flags & TFL_TRACK_NO))
+                       turret_track();
+
+               turret_do_updates(self);
+
+               // Fire?
+               if (self.turret_firecheckfunc())
+                       turret_fire();
+       }
+
+       TUR_ACTION(self.turretid, TR_THINK);
+}
+
+/*
+       When .used a turret switch team to activator.team.
+       If activator is world, the turret go inactive.
+*/
+void turret_use()
+{
+       dprint("Turret ",self.netname, " used by ", activator.classname, "\n");
+
+       self.team = activator.team;
+
+       if(self.team == 0)
+               self.active = ACTIVE_NOT;
+       else
+               self.active = ACTIVE_ACTIVE;
+
+}
+
+void turret_link()
+{
+       Net_LinkEntity(self, true, 0, turret_send);
+       self.think       = turret_think;
+       self.nextthink = time;
+       self.tur_head.effects = EF_NODRAW;
+}
+
+void turrets_manager_think()
+{
+       self.nextthink = time + 1;
+
+       entity e;
+       if (autocvar_g_turrets_reloadcvars == 1)
+       {
+               e = nextent(world);
+               while (e)
+               {
+                       if (e.turret_flags & TUR_FLAG_ISTURRET)
+                       {
+                               load_unit_settings(e,e.cvar_basename,1);
+                               TUR_ACTION(self.turretid, TR_THINK);
+                       }
+
+                       e = nextent(e);
+               }
+               cvar_set("g_turrets_reloadcvars","0");
+       }
+}
+
+float turret_initialize(float tur_id)
+{
+       if(!autocvar_g_turrets)
+               return false;
+
+       entity e;
+       entity tur = get_turretinfo(tur_id);
+       if(tur.turretid == 0)
+               return false; // invalid turret
+
+       if(!self.tur_head) { TUR_ACTION(tur_id, TR_PRECACHE); } // if tur_head exists, we can assume this turret re-spawned
+
+       e = find(world, classname, "turret_manager");
+       if(!e)
+       {
+               e = spawn();
+               e.classname = "turret_manager";
+               e.think = turrets_manager_think;
+               e.nextthink = time + 2;
+       }
+
+       if(!(self.spawnflags & TSF_SUSPENDED))
+               builtin_droptofloor();
+
+       self.cvar_basename = tur.cvar_basename;
+       load_unit_settings(self, self.cvar_basename, 0);
+       
+       if(!self.team || !teamplay)             { self.team = MAX_SHOT_DISTANCE; }
+       if(!self.ticrate)                               { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
+       if(!self.health)                                { self.health = 1000; }
+       if(!self.shot_refire)                   { self.shot_refire = 1; }
+       if(!self.tur_shotorg)                   { self.tur_shotorg = '50 0 50'; }
+       if(!self.turret_flags)                  { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; }
+       if(!self.damage_flags)                  { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; }
+       if(!self.aim_flags)                             { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; }
+       if(!self.track_type)                    { self.track_type = TFL_TRACKTYPE_STEPMOTOR; }
+       if(!self.track_flags)                   { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; }
+       if(!self.ammo_flags)                    { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; }
+       if(!self.target_select_flags)   { self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; }
+       if(!self.firecheck_flags)               { self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_LOS
+                                                                                                                  | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_REFIRE; }
+                                                                                                                  
+       if(self.track_type != TFL_TRACKTYPE_STEPMOTOR)
+       {
+               // Fluid / Ineria mode. Looks mutch nicer.
+               // Can reduce aim preformance alot, needs a bit diffrent aimspeed
+               
+               self.aim_speed = bound(0.1, ((!self.aim_speed) ? 180 : self.aim_speed), 1000);
+               
+               if(!self.track_accel_pitch)             { self.track_accel_pitch = 0.5; }
+               if(!self.track_accel_rotate)    { self.track_accel_rotate = 0.5; }
+               if(!self.track_blendrate)               { self.track_blendrate = 0.35; }
+       }
+       
+       self.respawntime                                = max(-1, ((!self.respawntime) ? 60 : self.respawntime));
+       self.shot_refire                                = bound(0.01, ((!self.shot_refire) ? 1 : self.shot_refire), 9999);
+       self.shot_dmg                                   = max(1, ((!self.shot_dmg) ? self.shot_refire * 50 : self.shot_dmg));
+       self.shot_radius                                = max(1, ((!self.shot_radius) ? self.shot_dmg * 0.5 : self.shot_radius));
+       self.shot_speed                                 = max(1, ((!self.shot_speed) ? 2500 : self.shot_speed));
+       self.shot_spread                                = bound(0.0001, ((!self.shot_spread) ? 0.0125 : self.shot_spread), 500);
+       self.shot_force                                 = bound(0.001, ((!self.shot_force) ? self.shot_dmg * 0.5 + self.shot_radius * 0.5 : self.shot_force), 5000);
+       self.shot_volly                                 = bound(1, ((!self.shot_volly) ? 1 : self.shot_volly), floor(self.ammo_max / self.shot_dmg));
+       self.shot_volly_refire                  = bound(self.shot_refire, ((!self.shot_volly_refire) ? self.shot_refire * self.shot_volly : self.shot_volly_refire), 60);
+       self.target_range                               = bound(0, ((!self.target_range) ? self.shot_speed * 0.5 : self.target_range), MAX_SHOT_DISTANCE);
+       self.target_range_min                   = bound(0, ((!self.target_range_min) ? self.shot_radius * 2 : self.target_range_min), MAX_SHOT_DISTANCE);
+       self.target_range_optimal               = bound(0, ((!self.target_range_optimal) ? self.target_range * 0.5 : self.target_range_optimal), MAX_SHOT_DISTANCE);
+       self.aim_maxrotate                              = bound(0, ((!self.aim_maxrotate) ? 90 : self.aim_maxrotate), 360);
+       self.aim_maxpitch                               = bound(0, ((!self.aim_maxpitch) ? 20 : self.aim_maxpitch), 90);
+       self.aim_speed                                  = bound(0.1, ((!self.aim_speed) ? 36 : self.aim_speed), 1000);
+       self.aim_firetolerance_dist     = bound(0.1, ((!self.aim_firetolerance_dist) ? 5 + (self.shot_radius * 2) : self.aim_firetolerance_dist), MAX_SHOT_DISTANCE);
+       self.target_select_rangebias    = bound(-10, ((!self.target_select_rangebias) ? 1 : self.target_select_rangebias), 10);
+       self.target_select_samebias     = bound(-10, ((!self.target_select_samebias) ? 1 : self.target_select_samebias), 10);
+       self.target_select_anglebias    = bound(-10, ((!self.target_select_anglebias) ? 1 : self.target_select_anglebias), 10);
+       self.target_select_missilebias  = bound(-10, ((!self.target_select_missilebias) ? 1 : self.target_select_missilebias), 10);
+       self.target_select_playerbias   = bound(-10, ((!self.target_select_playerbias) ? 1 : self.target_select_playerbias), 10);
+       self.ammo_max                                   = max(self.shot_dmg, ((!self.ammo_max) ? self.shot_dmg * 10 : self.ammo_max));
+       self.ammo_recharge                              = max(0, ((!self.ammo_recharge) ? self.shot_dmg * 0.5 : self.ammo_recharge));
+       
+       self.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags);
+       
+       if(self.turret_flags & TUR_FLAG_SPLASH)
+               self.aim_flags |= TFL_AIM_SPLASH;
+               
+       if(self.turret_flags & TUR_FLAG_MISSILE)
+               self.target_select_flags |= TFL_TARGETSELECT_MISSILES;
+
+       if(self.turret_flags & TUR_FLAG_PLAYER)
+               self.target_select_flags |= TFL_TARGETSELECT_PLAYERS;
+               
+       if(self.spawnflags & TSL_NO_RESPAWN)
+               self.damage_flags |= TFL_DMG_DEATH_NORESPAWN;
+               
+       if (self.turret_flags & TUR_FLAG_SUPPORT)
+               self.turret_score_target = turret_targetscore_support;
+       else
+               self.turret_score_target = turret_targetscore_generic;
+               
+       ++turret_count;
+               
+       setmodel(self, tur.model);
+       setsize(self, tur.mins, tur.maxs);
+       
+       self.turretid                           = tur_id;
+       self.classname                          = "turret_main";
+       self.active                                     = ACTIVE_ACTIVE;
+       self.effects                            = EF_NODRAW;
+       self.netname                            = TUR_NAME(tur_id);
+       self.ticrate                            = bound(sys_frametime, self.ticrate, 60);
+       self.max_health                         = self.health;
+       self.target_validate_flags      = self.target_select_flags;
+       self.ammo                                       = self.ammo_max;
+       self.ammo_recharge                 *= self.ticrate;
+       self.solid                                      = SOLID_BBOX;
+       self.takedamage                         = DAMAGE_AIM;
+       self.movetype                           = MOVETYPE_NOCLIP;
+       self.view_ofs                           = '0 0 0';
+       self.turret_firecheckfunc       = turret_firecheck;
+       self.event_damage                       = turret_damage;
+       self.use                                        = turret_use;
+       self.bot_attack                         = true;
+       self.nextthink                          = time + 1;
+       self.nextthink                     += turret_count * sys_frametime;
+       
+       self.tur_head = spawn();
+       setmodel(self.tur_head, tur.head_model);
+       setsize(self.tur_head, '0 0 0', '0 0 0');
+       setorigin(self.tur_head, '0 0 0');
+       setattachment(self.tur_head, self, "tag_head");
+       
+       self.tur_head.netname           = self.tur_head.classname = "turret_head";
+       self.tur_head.team                      = self.team;
+       self.tur_head.owner                     = self;
+       self.tur_head.takedamage        = DAMAGE_NO;
+       self.tur_head.solid                     = SOLID_NOT;
+       self.tur_head.movetype          = self.movetype;
+       
+       if(!self.tur_defend)
+       if(self.target != "")
+       {
+               self.tur_defend = find(world, targetname, self.target);
+               if (self.tur_defend == world)
+               {
+                       self.target = "";
+                       dprint("Turret has invalid defendpoint!\n");
+               }
+       }
+       
+       if (self.tur_defend)
+               self.idle_aim = self.tur_head.angles + angleofs(self.tur_head, self.tur_defend);
+       else
+               self.idle_aim = '0 0 0';
+               
+#ifdef TURRET_DEBUG
+       self.tur_debug_start = self.nextthink;
+       while (vlen(self.tur_debug_rvec) < 2)
+               self.tur_debug_rvec = randomvec() * 4;
+
+       self.tur_debug_rvec_x = fabs(self.tur_debug_rvec_x);
+       self.tur_debug_rvec_y = fabs(self.tur_debug_rvec_y);
+       self.tur_debug_rvec_z = fabs(self.tur_debug_rvec_z);
+#endif
+
+       turret_link();
+       turret_respawn();
+       turret_tag_fire_update();
+       
+       TUR_ACTION(tur_id, TR_SETUP);
+       
+       if(MUTATOR_CALLHOOK(TurretSpawn))
+               return false;
+
+       return true;
+}
+#endif
diff --git a/qcsrc/common/turrets/sv_turrets.qh b/qcsrc/common/turrets/sv_turrets.qh
new file mode 100644 (file)
index 0000000..c4ff13e
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef SV_TURRETS_H
+#define SV_TURRETS_H
+
+// turret fields
+.float ticrate; // interal ai think rate
+.vector aim_idle; // where to aim while idle
+.entity tur_head; // top part of the turret
+.entity tur_defend; // defend this entity
+.vector tur_shotorg; // shot origin
+.vector tur_aimpos; // aiming location
+.float tur_impacttime; // predicted projectile impact time
+.entity tur_impactent; // entity the projectile hit
+.float tur_dist_enemy; // distance to enemy
+.float tur_dist_aimpos; // distance to aim location
+.float tur_dist_impact_to_aimpos; // distance impact<->aim
+.float volly_counter; // decrement counter from .shot_volly to 0
+
+.float shot_refire; // attack refire
+.float shot_speed; // projectile speed
+.float shot_spread; // inaccuracy
+.float shot_dmg; // core damage of projectile
+.float shot_radius; // projectile damage radius
+.float shot_force; // projectile damage force
+.float shot_volly; // smaller than 1 = shoot # times at target
+.float shot_volly_refire; // refire after completed volly
+
+.float target_range;
+.float target_range_min;
+.float target_range_optimal;
+
+.float target_select_rangebias;
+.float target_select_samebias;
+.float target_select_anglebias;
+.float target_select_missilebias;
+.float target_select_playerbias;
+.float target_select_time; // last time turret had a valid target
+.float target_validate_time; // throttle re-validation of current target
+
+.float aim_firetolerance_dist;
+.float aim_speed;
+.float aim_maxpitch;
+.float aim_maxrotate;
+
+.float ammo; // current ammo
+.float ammo_recharge; // recharge rate
+.float ammo_max; // maximum ammo
+
+.vector idle_aim;
+
+/// Map time control over pain inflicted
+.float turret_scale_damage;
+/// Map time control targetting range
+.float turret_scale_range;
+/// Map time control refire
+.float turret_scale_refire;
+/// Map time control ammo held and recharged
+.float turret_scale_ammo;
+/// Map time control aim speed
+.float turret_scale_aim;
+/// Map time control health
+.float turret_scale_health;
+/// Map time control respawn time
+.float turret_scale_respawn;
+
+// tracking type
+.float track_type;
+const float TFL_TRACKTYPE_STEPMOTOR = 1; // hard angle increments, ugly for fast turning with best accuracy
+const float TFL_TRACKTYPE_FLUIDPRECISE = 2; // smooth absolute movement, looks OK with fair accuracy
+const float TFL_TRACKTYPE_FLUIDINERTIA = 3; // simulated inertia ("wobbly" mode), worst accuracy, depends on below flags
+.float track_accel_pitch;
+.float track_accel_rotate;
+.float track_blendrate;
+
+void() turret_respawn;
+
+/// updates aim org, shot org, shot dir and enemy org for selected turret
+void turret_do_updates(entity e_turret);
+.vector tur_shotdir_updated;
+
+.float() turret_firecheckfunc; // TODO: deprecate!
+
+/// Function to use for target evaluation. usualy turret_targetscore_generic
+.float(entity _turret, entity _target) turret_score_target;
+
+.float(entity e_target,entity e_sender) turret_addtarget;
+
+.entity pathcurrent;
+
+float turret_count;
+
+// debugging
+// Uncomment below to enable various debug output.
+//#define TURRET_DEBUG
+//#define TURRET_DEBUG_TARGETVALIDATE
+//#define TURRET_DEBUG_TARGETSELECT
+#ifdef TURRET_DEBUG
+.float tur_debug_dmg_t_h; // total damage that hit something (can be more than tur_debug_dmg_t_f since it should count radius damage)
+.float tur_debug_dmg_t_f; // total damage
+.float tur_debug_start; // turret initialization time
+.float tur_debug_tmr1; // random timer
+.float tur_debug_tmr2; // random timer
+.float tur_debug_tmr3; // random timer
+.vector tur_debug_rvec; // random vector
+#endif
+
+// aiming
+vector tvt_thadv; // turret head angle diff vector, updated by a successful call to turret_validate_target
+vector tvt_tadv; // turret angle diff vector, updated by a successful call to turret_validate_target
+float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target
+float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target
+float tvt_dist; // turret distance, updated by a successful call to turret_validate_target
+
+#endif
diff --git a/qcsrc/common/turrets/targettrigger.qc b/qcsrc/common/turrets/targettrigger.qc
new file mode 100644 (file)
index 0000000..6551064
--- /dev/null
@@ -0,0 +1,38 @@
+void spawnfunc_turret_targettrigger();
+void turret_targettrigger_touch();
+
+void turret_targettrigger_touch()
+{
+    entity e;
+    if (self.cnt > time) return;
+    entity oldself;
+    oldself = self;
+
+    e = find(world, targetname, self.target);
+    while (e)
+    {
+        if (e.turret_flags & TUR_FLAG_RECIEVETARGETS)
+        {
+            self = e;
+            if(e.turret_addtarget)
+                e.turret_addtarget(other,oldself);
+        }
+
+        e = find(e, targetname, oldself.target);
+    }
+
+    oldself.cnt = time + 0.5;
+
+    self = oldself;
+}
+
+/*QUAKED turret_targettrigger (.5 .5 .5) ?
+*/
+void spawnfunc_turret_targettrigger()
+{
+    if(!autocvar_g_turrets) { remove(self); return; }
+
+    InitTrigger ();
+
+    self.touch = turret_targettrigger_touch;
+}
diff --git a/qcsrc/common/turrets/turrets.qc b/qcsrc/common/turrets/turrets.qc
new file mode 100644 (file)
index 0000000..4c588e9
--- /dev/null
@@ -0,0 +1,78 @@
+#include "all.qh"
+
+// TURRET PLUGIN SYSTEM
+entity turret_info[TUR_MAXCOUNT];
+entity dummy_turret_info;
+
+void turrets_common_precache()
+{
+       precache_sound ("weapons/rocket_impact.wav");
+       precache_model ("models/turrets/base-gib1.md3");
+       precache_model ("models/turrets/base-gib2.md3");
+       precache_model ("models/turrets/base-gib3.md3");
+       precache_model ("models/turrets/base-gib4.md3");
+       precache_model ("models/turrets/head-gib1.md3");
+       precache_model ("models/turrets/head-gib2.md3");
+       precache_model ("models/turrets/head-gib3.md3");
+       precache_model ("models/turrets/head-gib4.md3");
+       precache_model ("models/turrets/base.md3");
+       precache_model ("models/turrets/rocket.md3");
+       
+       precache_model ("models/turrets/c512.md3");
+       precache_model ("models/marker.md3");
+       
+#ifdef TURRET_DEBUG
+       precache_model ("models/turrets/terrainbase.md3");
+       precache_model ("models/turrets/c512.md3");
+       precache_model ("models/pathlib/goodsquare.md3");
+       precache_model ("models/pathlib/badsquare.md3");
+       precache_model ("models/pathlib/square.md3");
+       precache_model ("models/pathlib/edge.md3");
+#endif
+}
+
+void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname)
+{
+       entity e;
+       turret_info[id - 1] = e = spawn();
+       e.classname = "turret_info";
+       e.turretid = id;
+       e.netname = shortname;
+       e.turret_name = mname;
+       e.turret_func = func;
+       e.mdl = modelname;
+       e.cvar_basename = shortname;
+       e.spawnflags = turretflags;
+       e.mins = min_s;
+       e.maxs = max_s;
+       e.model = strzone(strcat("models/turrets/", modelname));
+       e.head_model = strzone(strcat("models/turrets/", headmodelname));
+       
+       #ifndef MENUQC
+       turrets_common_precache();
+       #endif
+}
+float t_null(float dummy) { return 0; }
+void register_turrets_done()
+{
+       dummy_turret_info = spawn();
+       dummy_turret_info.classname = "turret_info";
+       dummy_turret_info.turretid = 0; // you can recognize dummies by this
+       dummy_turret_info.netname = "";
+       dummy_turret_info.turret_name = "Turret";
+       dummy_turret_info.turret_func = t_null;
+       dummy_turret_info.mdl = "";
+       dummy_turret_info.mins = '-0 -0 -0';
+       dummy_turret_info.maxs = '0 0 0';
+       dummy_turret_info.model = "";
+}
+entity get_turretinfo(float id)
+{
+       entity m;
+       if(id < TUR_FIRST || id > TUR_LAST)
+               return dummy_turret_info;
+       m = turret_info[id - 1];
+       if(m)
+               return m;
+       return dummy_turret_info;
+}
diff --git a/qcsrc/common/turrets/turrets.qh b/qcsrc/common/turrets/turrets.qh
new file mode 100644 (file)
index 0000000..b3736af
--- /dev/null
@@ -0,0 +1,202 @@
+#ifndef TURRETS_H
+#define TURRETS_H
+
+// turret requests
+#define TR_SETUP          1 // (BOTH) setup turret data
+#define TR_THINK                 2 // (SERVER) logic to run every frame
+#define TR_DEATH          3 // (SERVER) called when turret dies
+#define TR_PRECACHE       4 // (BOTH) precaches models/sounds used by this turret
+#define TR_ATTACK         5 // (SERVER) called when turret attacks
+#define TR_CONFIG         6 // (ALL)
+
+// functions:
+entity get_turretinfo(int id);
+
+// fields:
+.entity tur_head;
+
+// target selection flags
+.int target_select_flags;
+.int target_validate_flags;
+const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets
+const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets
+const int TFL_TARGETSELECT_PLAYERS = 8; // target players
+const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles
+const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events
+const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection
+const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range
+const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates
+const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered
+const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates
+const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets
+const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range
+const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles
+
+// aim flags
+.int aim_flags;
+const int TFL_AIM_NO = 1; // no aiming
+const int TFL_AIM_SPLASH = 2; // aim for ground around the target's feet
+const int TFL_AIM_LEAD = 4; // try to predict target movement
+const int TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading
+const int TFL_AIM_ZPREDICT = 16; // predict target's z position at impact
+const int TFL_AIM_SIMPLE = 32; // aim at player's current location
+
+// tracking flags
+.int track_flags;
+const int TFL_TRACK_NO = 2; // don't move head
+const int TFL_TRACK_PITCH = 4; // pitch head
+const int TFL_TRACK_ROTATE = 8; // rotate head
+
+// prefire checks
+.int firecheck_flags;
+const int TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?)
+const int TFL_FIRECHECK_DISTANCES = 8; // another range check
+const int TFL_FIRECHECK_LOS = 16; // line of sight
+const int TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot
+const int TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint
+const int TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot
+const int TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates
+const int TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire
+const int TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt
+const int TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max
+const int TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays
+const int TFL_FIRECHECK_NO = 16384; // no prefire checks
+
+// attack flags
+.int shoot_flags;
+const int TFL_SHOOT_NO = 64; // no attacking
+const int TFL_SHOOT_VOLLY = 2; // fire in vollies
+const int TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost
+const int TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets
+const int TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode)
+const int TFL_SHOOT_CUSTOM = 32; // custom attacking
+
+// turret capabilities
+.int turret_flags;
+const int TUR_FLAG_NONE = 0; // no abilities
+const int TUR_FLAG_SNIPER = 2; // sniping turret
+const int TUR_FLAG_SPLASH = 4; // can deal splash damage
+const int TUR_FLAG_HITSCAN = 8; // hit scan
+const int TUR_FLAG_MULTIGUN = 16; // multiple guns
+const int TUR_FLAG_GUIDED = 32; // laser guided projectiles
+const int TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles
+const int TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles
+const int TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles
+const int TUR_FLAG_PLAYER = 512; // can damage players
+const int TUR_FLAG_MISSILE = 1024; // can damage missiles
+const int TUR_FLAG_SUPPORT = 2048; // supports other units
+const int TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition
+const int TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources
+const int TUR_FLAG_MOVE = 16384; // can move
+const int TUR_FLAG_ROAM = 32768; // roams around if not attacking
+const int TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret
+
+// ammo types
+#define ammo_flags currentammo
+const int TFL_AMMO_NONE = 64; // doesn't use ammo
+const int TFL_AMMO_ENERGY = 2; // uses power
+const int TFL_AMMO_BULLETS = 4; // uses bullets
+const int TFL_AMMO_ROCKETS = 8; // uses explosives
+const int TFL_AMMO_RECHARGE = 16; // regenerates ammo
+const int TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units
+
+// damage flags
+.int damage_flags;
+const int TFL_DMG_NO = 256; // doesn't take damage
+const int TFL_DMG_YES = 2; // can be damaged
+const int TFL_DMG_TEAM = 4; // can be damaged by teammates
+const int TFL_DMG_RETALIATE = 8; // target attackers
+const int TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team
+const int TFL_DMG_TARGETLOSS = 32; // loses target when damaged
+const int TFL_DMG_AIMSHAKE = 64; // damage throws off aim
+const int TFL_DMG_HEADSHAKE = 128; // damage shakes head
+const int TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning
+
+// spawn flags
+const int TSF_SUSPENDED = 1;
+const int TSF_TERRAINBASE = 2; // currently unused
+const int TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration
+const int TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible
+const int TSL_NO_RESPAWN = 16; // don't re-spawn
+const int TSL_ROAM = 32; // roam while idle
+
+// send flags
+const int TNSF_UPDATE       = 2;
+const int TNSF_STATUS       = 4;
+const int TNSF_SETUP        = 8;
+const int TNSF_ANG          = 16;
+const int TNSF_AVEL         = 32;
+const int TNSF_MOVE         = 64;
+.float anim_start_time;
+const int TNSF_ANIM         = 128;
+
+const int TNSF_FULL_UPDATE  = 16777215;
+
+
+// entity properties of turretinfo:
+.int turretid; // TUR_...
+.string netname; // short name
+.string turret_name; // human readable name
+.float(float) turret_func; // m_...
+.string mdl; // currently a copy of the model
+.string model; // full name of model
+.string head_model; // full name of tur_head model
+.string cvar_basename; // TODO: deprecate!
+.float spawnflags;
+.vector mins, maxs; // turret hitbox size
+
+// other useful macros
+#define TUR_ACTION(turrettype,mrequest) (get_turretinfo(turrettype)).turret_func(mrequest)
+#define TUR_NAME(turrettype) (get_turretinfo(turrettype)).turret_name
+
+// =====================
+//  Turret Registration
+// =====================
+
+float t_null(float dummy);
+void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname);
+void register_turrets_done();
+
+const float TUR_MAXCOUNT = 24;
+#define TUR_FIRST 1
+float TUR_COUNT;
+float TUR_LAST;
+
+#define REGISTER_TURRET_2(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \
+       float id; \
+       float func(float); \
+       void RegisterTurrets_##id() \
+       { \
+               TUR_LAST = (id = TUR_FIRST + TUR_COUNT); \
+               ++TUR_COUNT; \
+               register_turret(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname); \
+       } \
+       ACCUMULATE_FUNCTION(RegisterTurrets, RegisterTurrets_##id)
+#ifdef MENUQC
+#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \
+       REGISTER_TURRET_2(TUR_##id,t_null,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname)
+#else
+#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \
+       REGISTER_TURRET_2(TUR_##id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname)
+#endif
+
+#define TUR_DUPECHECK(dupecheck,cvar) \
+       #ifndef dupecheck \
+               #define dupecheck \
+               float cvar; \
+       #else \
+               #error DUPLICATE TURRET CVAR: cvar \
+       #endif
+
+#define TUR_ADD_CVAR(turret,name) \
+               TUR_DUPECHECK(TUR_CVAR_##turret##_##name, autocvar_g_turrets_unit_##turret##_##name)
+
+#define TUR_CVAR(turret,name) autocvar_g_turrets_unit_##turret##_##name
+
+#include "all.qh"
+
+#undef TUR_ADD_CVAR
+#undef REGISTER_TURRET
+ACCUMULATE_FUNCTION(RegisterTurrets, register_turrets_done);
+
+#endif
diff --git a/qcsrc/common/turrets/unit/ewheel.qc b/qcsrc/common/turrets/unit/ewheel.qc
new file mode 100644 (file)
index 0000000..414ec4e
--- /dev/null
@@ -0,0 +1,311 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ EWHEEL,
+/* function   */ t_ewheel,
+/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM,
+/* mins,maxs  */ '-32 -32 0', '32 32 48',
+/* model      */ "ewheel-base2.md3",
+/* head_model */ "ewheel-gun1.md3",
+/* netname    */ "ewheel",
+/* fullname   */ _("eWheel Turret")
+);
+#else
+#ifdef SVQC
+float autocvar_g_turrets_unit_ewheel_speed_fast;
+float autocvar_g_turrets_unit_ewheel_speed_slow;
+float autocvar_g_turrets_unit_ewheel_speed_slower;
+float autocvar_g_turrets_unit_ewheel_speed_stop;
+float autocvar_g_turrets_unit_ewheel_turnrate;
+
+const float ewheel_anim_stop = 0;
+const float ewheel_anim_fwd_slow = 1;
+const float ewheel_anim_fwd_fast = 2;
+const float ewheel_anim_bck_slow = 3;
+const float ewheel_anim_bck_fast = 4;
+
+//#define EWHEEL_FANCYPATH
+void ewheel_move_path()
+{
+#ifdef EWHEEL_FANCYPATH
+       // Are we close enougth to a path node to switch to the next?
+       if (vlen(self.origin  - self.pathcurrent.origin) < 64)
+               if (self.pathcurrent.path_next == world)
+               {
+                       // Path endpoint reached
+                       pathlib_deletepath(self.pathcurrent.owner);
+                       self.pathcurrent = world;
+
+                       if (self.pathgoal)
+                       {
+                               if (self.pathgoal.use)
+                                       self.pathgoal.use();
+
+                               if (self.pathgoal.enemy)
+                               {
+                                       self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin);
+                                       self.pathgoal = self.pathgoal.enemy;
+                               }
+                       }
+                       else
+                               self.pathgoal = world;
+               }
+               else
+                       self.pathcurrent = self.pathcurrent.path_next;
+
+#else
+       if (vlen(self.origin - self.pathcurrent.origin) < 64)
+               self.pathcurrent = self.pathcurrent.enemy;
+#endif
+
+       if (self.pathcurrent)
+       {
+
+               self.moveto = self.pathcurrent.origin;
+               self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+               movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
+       }
+}
+
+void ewheel_move_enemy()
+{
+       float newframe;
+
+       self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal);
+       
+       self.moveto  = self.origin + self.steerto * 128;
+
+       if (self.tur_dist_enemy > self.target_range_optimal)
+       {
+               if ( self.tur_head.spawnshieldtime < 1 )
+               {
+                       newframe = ewheel_anim_fwd_fast;
+                       movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
+               }
+               else if (self.tur_head.spawnshieldtime < 2)
+               {
+
+                       newframe = ewheel_anim_fwd_slow;
+                       movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
+          }
+               else
+               {
+                       newframe = ewheel_anim_fwd_slow;
+                       movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4);
+               }
+       }
+       else if (self.tur_dist_enemy < self.target_range_optimal * 0.5)
+       {
+               newframe = ewheel_anim_bck_slow;
+               movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
+       }
+       else
+       {
+               newframe = ewheel_anim_stop;
+               movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
+       }
+
+       turrets_setframe(newframe, false);
+}
+
+void ewheel_move_idle()
+{
+       if(self.frame != 0)
+       {
+               self.SendFlags |= TNSF_ANIM;
+               self.anim_start_time = time;
+       }
+
+       self.frame = 0;
+       if (vlen(self.velocity))
+               movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
+}
+
+void spawnfunc_turret_ewheel() { if(!turret_initialize(TUR_EWHEEL)) remove(self); }
+
+float t_ewheel(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       float i;
+                       entity _mis;
+
+                       for (i = 0; i < 1; ++i)
+                       {
+                               turret_do_updates(self);
+
+                               _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE);
+                               _mis.missile_flags = MIF_SPLASH;
+
+                               pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+                               self.tur_head.frame += 2;
+
+                               if (self.tur_head.frame > 3)
+                                       self.tur_head.frame = 0;
+                       }
+                       
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       float vz;
+                       vector wish_angle, real_angle;
+
+                       vz = self.velocity_z;
+
+                       self.angles_x = anglemods(self.angles_x);
+                       self.angles_y = anglemods(self.angles_y);
+
+                       fixedmakevectors(self.angles);
+
+                       wish_angle = normalize(self.steerto);
+                       wish_angle = vectoangles(wish_angle);
+                       real_angle = wish_angle - self.angles;
+                       real_angle = shortangle_vxy(real_angle, self.tur_head.angles);
+
+                       self.tur_head.spawnshieldtime = fabs(real_angle_y);
+                       real_angle_y  = bound(-self.tur_head.aim_speed, real_angle_y, self.tur_head.aim_speed);
+                       self.angles_y = (self.angles_y + real_angle_y);
+
+                       if(self.enemy)
+                               ewheel_move_enemy();
+                       else if(self.pathcurrent)
+                               ewheel_move_path();
+                       else
+                               ewheel_move_idle();
+
+                       self.velocity_z = vz;
+
+                       if(vlen(self.velocity))
+                               self.SendFlags |= TNSF_MOVE;
+               
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       self.velocity = '0 0 0';
+
+#ifdef EWHEEL_FANCYPATH
+                       if (self.pathcurrent)
+                               pathlib_deletepath(self.pathcurrent.owner);
+#endif
+                       self.pathcurrent = world;
+       
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       entity e;
+                       
+                       if(self.movetype == MOVETYPE_WALK)
+                       {
+                               self.velocity = '0 0 0';
+                               self.enemy = world;
+
+                               setorigin(self, self.pos1);
+
+                               if (self.target != "")
+                               {
+                                       e = find(world, targetname, self.target);
+                                       if (!e)
+                                       {
+                                               dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n");
+                                               self.target = "";
+                                       }
+
+                                       if (e.classname != "turret_checkpoint")
+                                               dprint("Warning: not a turrret path\n");
+                                       else
+                                       {
+
+#ifdef EWHEEL_FANCYPATH
+                                               self.pathcurrent = WALKER_PATH(self.origin,e.origin);
+                                               self.pathgoal = e;
+#else
+                                               self.pathcurrent  = e;
+#endif
+                                       }
+                               }
+                       }
+                       
+                       self.iscreature                         = true;
+                       self.teleportable                       = TELEPORT_NORMAL;
+                       self.damagedbycontents          = true;
+                       self.movetype                           = MOVETYPE_WALK;
+                       self.solid                                      = SOLID_SLIDEBOX;
+                       self.takedamage                         = DAMAGE_AIM;
+                       self.idle_aim                           = '0 0 0';
+                       self.pos1                                       = self.origin;
+                       self.target_select_flags        = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+                       self.target_validate_flags      = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+                       self.frame                                      = self.tur_head.frame = 1;
+                       self.ammo_flags                         = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       
+                       // Convert from dgr / sec to dgr / tic
+                       self.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate);
+                       self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate);
+               
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/ewheel-base2.md3");
+                       precache_model ("models/turrets/ewheel-gun1.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+
+void ewheel_draw()
+{
+       float dt;
+
+       dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0)
+               return;
+
+       fixedmakevectors(self.angles);
+       setorigin(self, self.origin + self.velocity * dt);
+       self.tur_head.angles += dt * self.tur_head.move_avelocity;
+       self.angles_y = self.move_angles_y;
+
+       if (self.health < 127)
+       if(random() < 0.05)
+               te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
+}
+
+float t_ewheel(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       self.gravity            = 1;
+                       self.movetype           = MOVETYPE_BOUNCE;
+                       self.move_movetype      = MOVETYPE_BOUNCE;
+                       self.move_origin        = self.origin;
+                       self.move_time          = time;
+                       self.draw                       = ewheel_draw;
+                       
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/flac.qc b/qcsrc/common/turrets/unit/flac.qc
new file mode 100644 (file)
index 0000000..8068ab1
--- /dev/null
@@ -0,0 +1,103 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ FLAC,
+/* function   */ t_flac,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "flac.md3",
+/* netname       */ "flac",
+/* fullname   */ _("FLAC Cannon")
+);
+#else
+#ifdef SVQC
+void turret_flac_projectile_think_explode()
+{
+       if(self.enemy != world)
+       if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3)
+               setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius);
+
+#ifdef TURRET_DEBUG
+       float d;
+       d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+       self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d;
+       self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#else
+       RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+#endif
+       remove(self);
+}
+
+void spawnfunc_turret_flac() { if(!turret_initialize(TUR_FLAC)) remove(self); }
+
+float t_flac(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity proj;
+
+                       turret_tag_fire_update();
+
+                       proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE);
+                       pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+                       proj.think        = turret_flac_projectile_think_explode;
+                       proj.nextthink  = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01);
+                       proj.missile_flags = MIF_SPLASH | MIF_PROXY;
+
+                       self.tur_head.frame = self.tur_head.frame + 1;
+                       if (self.tur_head.frame >= 4)
+                               self.tur_head.frame = 0;
+                       
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+                       self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+                       self.damage_flags |= TFL_DMG_HEADSHAKE;
+                       self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY;
+               
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/flac.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_flac(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/fusionreactor.qc b/qcsrc/common/turrets/unit/fusionreactor.qc
new file mode 100644 (file)
index 0000000..db49745
--- /dev/null
@@ -0,0 +1,116 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ FUSIONREACTOR,
+/* function   */ t_fusionreactor,
+/* spawnflags */ TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE,
+/* mins,maxs  */ '-34 -34 0', '34 34 90',
+/* model         */ "base.md3",
+/* head_model */ "reactor.md3",
+/* netname       */ "fusionreactor",
+/* fullname   */ _("Fusion Reactor")
+);
+#else
+#ifdef SVQC
+float turret_fusionreactor_firecheck()
+{
+       if (self.attack_finished_single > time)
+               return 0;
+
+       if (self.enemy.deadflag != DEAD_NO)
+               return 0;
+
+       if (self.enemy == world)
+               return 0;
+
+       if (self.ammo < self.shot_dmg)
+               return 0;
+
+       if (self.enemy.ammo >= self.enemy.ammo_max)
+               return 0;
+
+       if (vlen(self.enemy.origin - self.origin) > self.target_range)
+               return 0;
+
+       if(self.team != self.enemy.team)
+               return 0;
+
+       if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY))
+               return 0;
+
+       return 1;
+}
+
+void spawnfunc_turret_fusionreactor() { if(!turret_initialize(TUR_FUSIONREACTOR)) remove(self); }
+
+float t_fusionreactor(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       vector fl_org;
+
+                       self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max);
+                       fl_org = 0.5 * (self.enemy.absmin + self.enemy.absmax);
+                       te_smallflash(fl_org);
+                       
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max);
+               
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags                         = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
+                       self.target_select_flags        = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMITS;
+                       self.firecheck_flags            = TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_AMMO_OTHER | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD;
+                       self.shoot_flags                        = TFL_SHOOT_HITALLVALID;
+                       self.aim_flags                          = TFL_AIM_NO;
+                       self.track_flags                        = TFL_TRACK_NO;
+       
+                       self.tur_head.scale = 0.75;
+                       self.tur_head.avelocity = '0 50 0';
+                       
+                       self.turret_firecheckfunc = turret_fusionreactor_firecheck;
+               
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/reactor.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_fusionreactor(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/hellion.qc b/qcsrc/common/turrets/unit/hellion.qc
new file mode 100644 (file)
index 0000000..d090ef7
--- /dev/null
@@ -0,0 +1,160 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ HELLION,
+/* function   */ t_hellion,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "hellion.md3",
+/* netname       */ "hellion",
+/* fullname   */ _("Hellion Missile Turret")
+);
+#else
+#ifdef SVQC
+float autocvar_g_turrets_unit_hellion_shot_speed_gain;
+float autocvar_g_turrets_unit_hellion_shot_speed_max;
+
+void turret_hellion_missile_think()
+{
+       vector olddir,newdir;
+       vector pre_pos;
+       float itime;
+
+       self.nextthink = time + 0.05;
+
+       olddir = normalize(self.velocity);
+
+       if(self.max_health < time)
+               turret_projectile_explode();
+
+       // Enemy dead? just keep on the current heading then.
+       if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO))
+       {
+
+               // Make sure we dont return to tracking a respawned player
+               self.enemy = world;
+
+               // Turn model
+               self.angles = vectoangles(self.velocity);
+
+               if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) )
+                       turret_projectile_explode();
+
+               // Accelerate
+               self.velocity = olddir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
+
+               UpdateCSQCProjectile(self);
+
+               return;
+       }
+
+       // Enemy in range?
+       if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2)
+               turret_projectile_explode();
+
+       // Predict enemy position
+       itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity);
+       pre_pos = self.enemy.origin + self.enemy.velocity * itime;
+
+       pre_pos = (pre_pos + self.enemy.origin) * 0.5;
+
+       // Find out the direction to that place
+       newdir = normalize(pre_pos - self.origin);
+
+       // Turn
+       newdir = normalize(olddir + newdir * 0.35);
+
+       // Turn model
+       self.angles = vectoangles(self.velocity);
+
+       // Accelerate
+       self.velocity = newdir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
+
+       if (itime < 0.05)
+               self.think = turret_projectile_explode;
+
+       UpdateCSQCProjectile(self);
+}
+
+void spawnfunc_turret_hellion() { if(!turret_initialize(TUR_HELLION)) remove(self); }
+
+float t_hellion(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity missile;
+
+                       if(self.tur_head.frame != 0)
+                               self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
+                       else
+                               self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire2"));
+
+                       missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_HELLION, PROJECTILE_ROCKET, FALSE, FALSE);
+                       te_explosion (missile.origin);
+                       missile.think           = turret_hellion_missile_think;
+                       missile.nextthink       = time;
+                       missile.flags           = FL_PROJECTILE;
+                       missile.max_health   = time + 9;
+                       missile.tur_aimpos   = randomvec() * 128;
+                       missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
+                       self.tur_head.frame += 1;
+                       
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if (self.tur_head.frame != 0)
+                               self.tur_head.frame += 1;
+
+                       if (self.tur_head.frame >= 7)
+                               self.tur_head.frame = 0;
+               
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.aim_flags = TFL_AIM_SIMPLE;
+                       self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK ;
+                       self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_AMMO_OWN;
+                       self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+               
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/hellion.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hellion(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/hk.qc b/qcsrc/common/turrets/unit/hk.qc
new file mode 100644 (file)
index 0000000..414c71e
--- /dev/null
@@ -0,0 +1,361 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ HK,
+/* function   */ t_hk,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "hk.md3",
+/* netname       */ "hk",
+/* fullname   */ _("Hunter-Killer Turret")
+);
+#else
+#ifdef SVQC
+float autocvar_g_turrets_unit_hk_shot_speed;
+float autocvar_g_turrets_unit_hk_shot_speed_accel;
+float autocvar_g_turrets_unit_hk_shot_speed_accel2;
+float autocvar_g_turrets_unit_hk_shot_speed_decel;
+float autocvar_g_turrets_unit_hk_shot_speed_max;
+float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
+
+//#define TURRET_DEBUG_HK
+
+#ifdef TURRET_DEBUG_HK
+.float atime;
+#endif
+
+float hk_is_valid_target(entity e_target)
+{
+       if (e_target == world)
+               return 0;
+
+       // If only this was used more..
+       if (e_target.flags & FL_NOTARGET)
+               return 0;
+
+       // Cant touch this
+       if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
+               return 0;
+
+       // player
+       if (IS_CLIENT(e_target))
+       {
+               if (self.owner.target_select_playerbias < 0)
+                       return 0;
+
+               if (e_target.deadflag != DEAD_NO)
+                       return 0;
+       }
+
+       // Missile
+       if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0))
+               return 0;
+
+       // Team check
+       if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team))
+               return 0;
+
+       return 1;
+}
+
+void turret_hk_missile_think()
+{
+       vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
+       float  fu, fd, ff, fl, fr, fe;  // Fraction to solid
+       vector olddir,wishdir,newdir;   // Final direction
+       float lt_for;   // Length of Trace FORwrad
+       float lt_seek;  // Length of Trace SEEK (left, right, up down)
+       float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
+       vector pre_pos;
+       float myspeed;
+       entity e;
+       float ad,edist;
+
+       self.nextthink = time + self.ticrate;
+
+       //if (self.cnt < time)
+       //      turret_hk_missile_explode();
+
+       if (self.enemy.deadflag != DEAD_NO)
+               self.enemy = world;
+
+       // Pick the closest valid target.
+       if (!self.enemy)
+       {
+               e = findradius(self.origin, 5000);
+               while (e)
+               {
+                       if (hk_is_valid_target(e))
+                       {
+                               if (!self.enemy)
+                                       self.enemy = e;
+                               else
+                                       if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
+                                               self.enemy = e;
+                       }
+                       e = e.chain;
+               }
+       }
+
+       self.angles = vectoangles(self.velocity);
+       self.angles_x = self.angles_x * -1;
+       makevectors(self.angles);
+       self.angles_x = self.angles_x * -1;
+
+       if (self.enemy)
+       {
+               edist = vlen(self.origin - self.enemy.origin);
+               // Close enougth to do decent damage?
+               if ( edist <= (self.owner.shot_radius * 0.25) )
+               {
+                       turret_projectile_explode();
+                       return;
+               }
+
+               // Get data on enemy position
+               pre_pos = self.enemy.origin +
+                                 self.enemy.velocity *
+                                 min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
+
+               traceline(self.origin, pre_pos,true,self.enemy);
+               ve = normalize(pre_pos - self.origin);
+               fe = trace_fraction;
+
+       }
+       else
+       {
+       edist = 0;
+       ve = '0 0 0';
+               fe = 0;
+       }
+
+       if ((fe != 1) || (self.enemy == world) || (edist > 1000))
+       {
+               myspeed = vlen(self.velocity);
+
+               lt_for  = myspeed * 3;
+               lt_seek = myspeed * 2.95;
+
+               // Trace forward
+               traceline(self.origin, self.origin + v_forward * lt_for,false,self);
+               vf = trace_endpos;
+               ff = trace_fraction;
+
+               // Find angular offset
+               ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
+
+               // To close to something, Slow down!
+               if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
+                       myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
+
+               // Failry clear, accelerate.
+               if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
+                       myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
+
+               // Setup trace pitch
+               pt_seek = 1 - ff;
+               pt_seek = bound(0.15,pt_seek,0.8);
+               if (ff < 0.5) pt_seek = 1;
+
+               // Trace left
+               traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
+               vl = trace_endpos;
+               fl = trace_fraction;
+
+               // Trace right
+               traceline(self.origin,  self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+               vr = trace_endpos;
+               fr = trace_fraction;
+
+               // Trace up
+               traceline(self.origin,  self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+               vu = trace_endpos;
+               fu = trace_fraction;
+
+               // Trace down
+               traceline(self.origin,  self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+               vd = trace_endpos;
+               fd = trace_fraction;
+
+               vl = normalize(vl - self.origin);
+               vr = normalize(vr - self.origin);
+               vu = normalize(vu - self.origin);
+               vd = normalize(vd - self.origin);
+
+               // Panic tresh passed, find a single direction and turn as hard as we can
+               if (pt_seek == 1)
+               {
+                       wishdir = v_right;
+                       if (fl > fr) wishdir = -1 * v_right;
+                       if (fu > fl) wishdir = v_up;
+                       if (fd > fu) wishdir = -1 * v_up;
+               }
+               else
+               {
+                       // Normalize our trace vectors to make a smooth path
+                       wishdir = normalize( (vl * fl) + (vr * fr) +  (vu * fu) +  (vd * fd) );
+               }
+
+               if (self.enemy)
+               {
+                       if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
+                       wishdir = (wishdir * (1 - fe)) + (ve * fe);
+               }
+       }
+       else
+       {
+               // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
+               myspeed = vlen(self.velocity);
+               if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
+                       myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
+
+               wishdir = ve;
+       }
+
+       if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
+               myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
+
+       // Ranoutagazfish?
+       if (self.cnt < time)
+       {
+               self.cnt = time + 0.25;
+               self.nextthink = 0;
+               self.movetype            = MOVETYPE_BOUNCE;
+               return;
+       }
+
+       // Calculate new heading
+       olddir = normalize(self.velocity);
+       newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
+
+       // Set heading & speed
+       self.velocity = newdir * myspeed;
+
+       // Align model with new heading
+       self.angles = vectoangles(self.velocity);
+
+
+#ifdef TURRET_DEBUG_HK
+       //if(self.atime < time) {
+       if ((fe <= 0.99)||(edist > 1000))
+       {
+               te_lightning2(world,self.origin, self.origin + vr * lt_seek);
+               te_lightning2(world,self.origin, self.origin + vl * lt_seek);
+               te_lightning2(world,self.origin, self.origin + vu * lt_seek);
+               te_lightning2(world,self.origin, self.origin + vd * lt_seek);
+               te_lightning2(world,self.origin, vf);
+       }
+       else
+       {
+               te_lightning2(world,self.origin, self.enemy.origin);
+       }
+       bprint("Speed: ", ftos(rint(myspeed)), "\n");
+       bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
+       bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
+       self.atime = time + 0.2;
+       //}
+#endif
+
+       UpdateCSQCProjectile(self);
+}
+
+float turret_hk_addtarget(entity e_target,entity e_sender)
+{
+       if (e_target)
+       {
+               if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
+               {
+                       self.enemy = e_target;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+void spawnfunc_turret_hk() { if(!turret_initialize(TUR_HK)) remove(self); }
+
+float t_hk(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity missile;
+
+                       missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, FALSE, FALSE);
+                       te_explosion (missile.origin);
+
+                       missile.think                   = turret_hk_missile_think;
+                       missile.nextthink               = time + 0.25;
+                       missile.movetype                 = MOVETYPE_BOUNCEMISSILE;
+                       missile.velocity                 = self.tur_shotdir_updated * (self.shot_speed * 0.75);
+                       missile.angles             = vectoangles(missile.velocity);
+                       missile.cnt                       = time + 30;
+                       missile.ticrate           = max(autocvar_sys_ticrate, 0.05);
+                       missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI;
+
+                       if (self.tur_head.frame == 0)
+                               self.tur_head.frame = self.tur_head.frame + 1;
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if (self.tur_head.frame != 0)
+                               self.tur_head.frame = self.tur_head.frame + 1;
+
+                       if (self.tur_head.frame > 5)
+                               self.tur_head.frame = 0;
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+                       self.aim_flags = TFL_AIM_SIMPLE;
+                       self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+                       self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK  | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
+                       self.shoot_flags = TFL_SHOOT_CLEARTARGET;
+                       self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
+
+                       self.turret_addtarget = turret_hk_addtarget;
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/hk.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hk(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/machinegun.qc b/qcsrc/common/turrets/unit/machinegun.qc
new file mode 100644 (file)
index 0000000..f4a4621
--- /dev/null
@@ -0,0 +1,79 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ MACHINEGUN,
+/* function   */ t_machinegun,
+/* spawnflags */ TUR_FLAG_PLAYER,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "machinegun.md3",
+/* netname       */ "machinegun",
+/* fullname   */ _("Machinegun Turret")
+);
+#else
+#ifdef SVQC
+void spawnfunc_turret_machinegun() { if(!turret_initialize(TUR_MACHINEGUN)) remove(self); }
+
+float t_machinegun(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0);
+
+                       W_MachineGun_MuzzleFlash();
+                       setattachment(self.muzzle_flash, self.tur_head, "tag_fire");
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.damage_flags |= TFL_DMG_HEADSHAKE;
+                       self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+                       self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+                       self.turret_flags |= TUR_FLAG_HITSCAN;
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/machinegun.md3");
+                       precache_sound ("weapons/uzi_fire.wav");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_machinegun(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/mlrs.qc b/qcsrc/common/turrets/unit/mlrs.qc
new file mode 100644 (file)
index 0000000..254a589
--- /dev/null
@@ -0,0 +1,90 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ MLRS,
+/* function   */ t_mlrs,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "mlrs.md3",
+/* netname       */ "mlrs",
+/* fullname   */ _("MLRS Turret")
+);
+#else
+#ifdef SVQC
+void spawnfunc_turret_mlrs() { if(!turret_initialize(TUR_MLRS)) remove(self); }
+
+float t_mlrs(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity missile;
+
+                       turret_tag_fire_update();
+                       missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE);
+                       missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed);
+                       missile.missile_flags = MIF_SPLASH;
+                       te_explosion (missile.origin);
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       // 0 = full, 6 = empty
+                       self.tur_head.frame = bound(0, 6 - floor(0.1 + self.ammo / self.shot_dmg), 6);
+                       if(self.tur_head.frame < 0)
+                       {
+                               dprint("ammo:",ftos(self.ammo),"\n");
+                               dprint("shot_dmg:",ftos(self.shot_dmg),"\n");
+                       }
+               
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+                       self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+               
+                       self.damage_flags |= TFL_DMG_HEADSHAKE;
+                       self.shoot_flags  |= TFL_SHOOT_VOLLYALWAYS;
+                       self.volly_counter = self.shot_volly;
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/mlrs.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_mlrs(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/phaser.qc b/qcsrc/common/turrets/unit/phaser.qc
new file mode 100644 (file)
index 0000000..32c392b
--- /dev/null
@@ -0,0 +1,173 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ PHASER,
+/* function   */ t_phaser,
+/* spawnflags */ TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "phaser.md3",
+/* netname       */ "phaser",
+/* fullname   */ _("Phaser Cannon")
+);
+#else
+#ifdef SVQC
+.float fireflag;
+
+float turret_phaser_firecheck()
+{
+       if (self.fireflag != 0) return 0;
+       return turret_firecheck();
+}
+
+void beam_think()
+{
+       if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO))
+       {
+               self.owner.attack_finished_single = time + self.owner.shot_refire;
+               self.owner.fireflag = 2;
+               self.owner.tur_head.frame = 10;
+               sound (self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
+               remove(self);
+               return;
+       }
+
+       turret_do_updates(self.owner);
+
+       if (time - self.shot_spread > 0)
+       {
+               self.shot_spread = time + 2;
+               sound (self, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM);
+       }
+
+
+       self.nextthink = time + self.ticrate;
+
+       self.owner.attack_finished_single = time + frametime;
+       entity oldself;
+       oldself = self;
+       self = self.owner;
+       FireImoBeam (   self.tur_shotorg,
+                                       self.tur_shotorg + self.tur_shotdir_updated * self.target_range,
+                                       '-1 -1 -1' * self.shot_radius,
+                                       '1 1 1' * self.shot_radius,
+                                       self.shot_force,
+                                       oldself.shot_dmg,
+                                       0.75,
+                                       DEATH_TURRET_PHASER);
+       self = oldself;
+       self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256;
+
+}
+
+void spawnfunc_turret_phaser() { if(!turret_initialize(TUR_PHASER)) remove(self); }
+
+float t_phaser(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity beam;
+
+                       beam = spawn();
+                       beam.ticrate = 0.1; //autocvar_sys_ticrate;
+                       setmodel(beam,"models/turrets/phaser_beam.md3");
+                       beam.effects = EF_LOWPRECISION;
+                       beam.solid = SOLID_NOT;
+                       beam.think = beam_think;
+                       beam.cnt = time + self.shot_speed;
+                       beam.shot_spread = time + 2;
+                       beam.nextthink = time;
+                       beam.owner = self;
+                       beam.shot_dmg = self.shot_dmg / (self.shot_speed / beam.ticrate);
+                       beam.scale = self.target_range / 256;
+                       beam.movetype = MOVETYPE_NONE;
+                       beam.enemy = self.enemy;
+                       beam.bot_dodge = true;
+                       beam.bot_dodgerating = beam.shot_dmg;
+                       sound (beam, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM);
+                       self.fireflag = 1;
+
+                       beam.attack_finished_single = self.attack_finished_single;
+                       self.attack_finished_single = time; // + autocvar_sys_ticrate;
+
+                       setattachment(beam,self.tur_head,"tag_fire");
+
+                       soundat (self, trace_endpos, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM);
+
+                       if (self.tur_head.frame == 0)
+                               self.tur_head.frame = 1;
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if (self.tur_head.frame != 0)
+                       {
+                               if (self.fireflag == 1)
+                               {
+                                       if (self.tur_head.frame == 10)
+                                               self.tur_head.frame = 1;
+                                       else
+                                               self.tur_head.frame = self.tur_head.frame +1;
+                               }
+                               else if (self.fireflag == 2 )
+                               {
+                                       self.tur_head.frame = self.tur_head.frame +1;
+                                       if (self.tur_head.frame == 15)
+                                       {
+                                               self.tur_head.frame = 0;
+                                               self.fireflag = 0;
+                                       }
+                               }
+                       }
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.aim_flags = TFL_AIM_LEAD;
+
+                       self.turret_firecheckfunc = turret_phaser_firecheck;
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/phaser.md3");
+                       precache_model ("models/turrets/phaser_beam.md3");
+                       precache_sound ("turrets/phaser.wav");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_phaser(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/plasma.qc b/qcsrc/common/turrets/unit/plasma.qc
new file mode 100644 (file)
index 0000000..095aa9d
--- /dev/null
@@ -0,0 +1,111 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ PLASMA,
+/* function   */ t_plasma,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "plasma.md3",
+/* netname       */ "plasma",
+/* fullname   */ _("Plasma Cannon")
+);
+#else
+#ifdef SVQC
+void spawnfunc_turret_plasma() { if(!turret_initialize(TUR_PLASMA)) remove(self); }
+
+float t_plasma(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       if(g_instagib)
+                       {
+                               float flying;
+                               flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+                               FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
+                                                                  800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
+
+                               pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+                               // teamcolor / hit beam effect
+                               vector v;
+                               string s;
+                               v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+                               s = strcat("TE_TEI_G3", ((self.team) ? Static_Team_ColorName_Upper(self.team) : ""));
+                               
+                               WarpZone_TrailParticles(world, particleeffectnum(s), self.tur_shotorg, v);
+                               
+                               if (self.tur_head.frame == 0)
+                                       self.tur_head.frame = 1;
+                       }
+                       else
+                       {
+                               entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
+                               missile.missile_flags = MIF_SPLASH;
+
+                               pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+                               if (self.tur_head.frame == 0)
+                                       self.tur_head.frame = 1;
+                       }
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if (self.tur_head.frame != 0)
+                               self.tur_head.frame = self.tur_head.frame + 1;
+
+                       if (self.tur_head.frame > 5)
+                               self.tur_head.frame = 0;
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.damage_flags |= TFL_DMG_HEADSHAKE;
+                       self.firecheck_flags |= TFL_FIRECHECK_AFF;
+                       self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH;
+                       
+                       turret_do_updates(self);
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/plasma.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/plasma_dual.qc b/qcsrc/common/turrets/unit/plasma_dual.qc
new file mode 100644 (file)
index 0000000..110ae1d
--- /dev/null
@@ -0,0 +1,109 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ PLASMA_DUAL,
+/* function   */ t_plasma_dual,
+/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
+/* mins,maxs  */ '-32 -32 0', '32 32 64',
+/* model         */ "base.md3",
+/* head_model */ "plasmad.md3",
+/* netname       */ "plasma_dual",
+/* fullname   */ _("Dual Plasma Cannon")
+);
+#else
+#ifdef SVQC
+void spawnfunc_turret_plasma_dual() { if(!turret_initialize(TUR_PLASMA_DUAL)) remove(self); }
+
+float t_plasma_dual(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       if(g_instagib)
+                       {
+                               float flying;
+                               flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+                               FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
+                                                                  800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
+
+
+                               pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+                               // teamcolor / hit beam effect
+                               vector v;
+                               string s;
+                               v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+                               s = strcat("TE_TEI_G3", ((self.team) ? Static_Team_ColorName_Upper(self.team) : ""));
+                               
+                               WarpZone_TrailParticles(world, particleeffectnum(s), self.tur_shotorg, v);
+                                       
+                               self.tur_head.frame += 1;
+                       }
+                       else
+                       {
+                               entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
+                               missile.missile_flags = MIF_SPLASH;
+                               pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+                               self.tur_head.frame += 1;
+                       }
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3))
+                               self.tur_head.frame = self.tur_head.frame + 1;
+
+                       if (self.tur_head.frame > 6)
+                               self.tur_head.frame = 0;
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.damage_flags |= TFL_DMG_HEADSHAKE;
+                       self.firecheck_flags |= TFL_FIRECHECK_AFF;
+                       self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH;
+                       
+                       turret_do_updates(self);
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/plasmad.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma_dual(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/tesla.qc b/qcsrc/common/turrets/unit/tesla.qc
new file mode 100644 (file)
index 0000000..61a9f5e
--- /dev/null
@@ -0,0 +1,217 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ TESLA,
+/* function   */ t_tesla,
+/* spawnflags */ TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE,
+/* mins,maxs  */ '-60 -60 0', '60 60 128',
+/* model         */ "tesla_base.md3",
+/* head_model */ "tesla_head.md3",
+/* netname       */ "tesla",
+/* fullname   */ _("Tesla Coil")
+);
+#else
+#ifdef SVQC
+entity toast(entity from, float range, float damage)
+{
+       entity e;
+       entity etarget = world;
+       float d,dd;
+       float r;
+
+       dd = range + 1;
+
+       e = findradius(from.origin,range);
+       while (e)
+       {
+               if ((e.railgunhit != 1) && (e != from))
+               {
+                       r = turret_validate_target(self,e,self.target_validate_flags);
+                       if (r > 0)
+                       {
+                               traceline(from.origin,0.5 * (e.absmin + e.absmax),MOVE_WORLDONLY,from);
+                               if (trace_fraction == 1.0)
+                               {
+                                       d = vlen(e.origin - from.origin);
+                                       if (d < dd)
+                                       {
+                                               dd = d;
+                                               etarget = e;
+                                       }
+                               }
+                       }
+               }
+               e = e.chain;
+       }
+
+       if (etarget)
+       {
+               te_csqc_lightningarc(from.origin,etarget.origin);
+               Damage(etarget, self, self, damage, DEATH_TURRET_TESLA, etarget.origin, '0 0 0');
+               etarget.railgunhit = 1;
+       }
+
+       return etarget;
+}
+
+float turret_tesla_firecheck()
+{
+       // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+       float do_target_scan = 0;
+
+       if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+               do_target_scan = 1;
+
+       // Old target (if any) invalid?
+       if(self.target_validate_time < time)
+       if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+       {
+               self.enemy = world;
+               self.target_validate_time = time + 0.5;
+               do_target_scan = 1;
+       }
+
+       // But never more often then g_turrets_targetscan_mindelay!
+       if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+               do_target_scan = 0;
+
+       if(do_target_scan)
+       {
+               self.enemy = turret_select_target();
+               self.target_select_time = time;
+       }
+
+       if(!turret_firecheck())
+               return 0;
+
+       if(self.enemy)
+               return 1;
+
+       return 0;
+}
+
+void spawnfunc_turret_tesla() { if(!turret_initialize(TUR_TESLA)) remove(self); }
+
+float t_tesla(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       entity e, t;
+                       float d, r, i;
+
+                       d = self.shot_dmg;
+                       r = self.target_range;
+                       e = spawn();
+                       setorigin(e,self.tur_shotorg);
+
+                       self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+
+                       t = toast(e,r,d);
+                       remove(e);
+
+                       if (t == world) return true;
+
+                       self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK;
+
+                       self.attack_finished_single = time + self.shot_refire;
+                       for (i = 0; i < 10; ++i)
+                       {
+                               d *= 0.75;
+                               r *= 0.85;
+                               t = toast(t, r, d);
+                               if (t == world) break;
+
+                       }
+
+                       e = findchainfloat(railgunhit, 1);
+                       while (e)
+                       {
+                               e.railgunhit = 0;
+                               e = e.chain;
+                       }
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       if(!self.active)
+                       {
+                               self.tur_head.avelocity = '0 0 0';
+                               return true;
+                       }
+
+                       if(self.ammo < self.shot_dmg)
+                       {
+                               self.tur_head.avelocity = '0 45 0' * (self.ammo / self.shot_dmg);
+                       }
+                       else
+                       {
+                               self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg);
+
+                               if(self.attack_finished_single > time)
+                                       return true;
+
+                               float f;
+                               f = (self.ammo / self.ammo_max);
+                               f = f * f;
+                               if(f > random())
+                                       if(random() < 0.1)
+                                               te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350));
+                       }
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
+                                                                TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+                                                                
+                       self.turret_firecheckfunc = turret_tesla_firecheck;
+                       self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
+                                                          TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+
+                       self.firecheck_flags    = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN;
+                       self.shoot_flags                = TFL_SHOOT_CUSTOM;
+                       self.ammo_flags                 = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.aim_flags                  = TFL_AIM_NO;
+                       self.track_flags                = TFL_TRACK_NO;
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/tesla_base.md3");
+                       precache_model ("models/turrets/tesla_head.md3");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_tesla(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/walker.qc b/qcsrc/common/turrets/unit/walker.qc
new file mode 100644 (file)
index 0000000..0d5d317
--- /dev/null
@@ -0,0 +1,698 @@
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id   */ WALKER,
+/* function   */ t_walker,
+/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE,
+/* mins,maxs  */ '-70 -70 0', '70 70 95',
+/* model         */ "walker_body.md3",
+/* head_model */ "walker_head_minigun.md3",
+/* netname       */ "walker",
+/* fullname   */ _("Walker Turret")
+);
+#else
+#ifdef SVQC
+float autocvar_g_turrets_unit_walker_melee_damage;
+float autocvar_g_turrets_unit_walker_melee_force;
+float autocvar_g_turrets_unit_walker_melee_range;
+float autocvar_g_turrets_unit_walker_rocket_damage;
+float autocvar_g_turrets_unit_walker_rocket_radius;
+float autocvar_g_turrets_unit_walker_rocket_force;
+float autocvar_g_turrets_unit_walker_rocket_speed;
+float autocvar_g_turrets_unit_walker_rocket_range;
+float autocvar_g_turrets_unit_walker_rocket_range_min;
+float autocvar_g_turrets_unit_walker_rocket_refire;
+float autocvar_g_turrets_unit_walker_rocket_turnrate;
+float autocvar_g_turrets_unit_walker_speed_stop;
+float autocvar_g_turrets_unit_walker_speed_walk;
+float autocvar_g_turrets_unit_walker_speed_run;
+float autocvar_g_turrets_unit_walker_speed_jump;
+float autocvar_g_turrets_unit_walker_speed_swim;
+float autocvar_g_turrets_unit_walker_speed_roam;
+float autocvar_g_turrets_unit_walker_turn;
+float autocvar_g_turrets_unit_walker_turn_walk;
+float autocvar_g_turrets_unit_walker_turn_strafe;
+float autocvar_g_turrets_unit_walker_turn_swim;
+float autocvar_g_turrets_unit_walker_turn_run;
+
+#define ANIM_NO         0
+#define ANIM_TURN       1
+#define ANIM_WALK       2
+#define ANIM_RUN        3
+#define ANIM_STRAFE_L   4
+#define ANIM_STRAFE_R   5
+#define ANIM_JUMP       6
+#define ANIM_LAND       7
+#define ANIM_PAIN       8
+#define ANIM_MELEE      9
+#define ANIM_SWIM       10
+#define ANIM_ROAM       11
+
+.float animflag;
+.float idletime;
+
+#define WALKER_PATH(s,e) pathlib_astar(s,e)
+
+float walker_firecheck()
+{
+       if (self.animflag == ANIM_MELEE)
+               return 0;
+
+       return turret_firecheck();
+}
+
+void walker_melee_do_dmg()
+{
+       vector where;
+       entity e;
+
+       makevectors(self.angles);
+       where = self.origin + v_forward * 128;
+
+       e = findradius(where,32);
+       while (e)
+       {
+               if (turret_validate_target(self, e, self.target_validate_flags))
+                       if (e != self && e.owner != self)
+                               Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force));
+
+               e = e.chain;
+       }
+}
+
+void walker_setnoanim()
+{
+       turrets_setframe(ANIM_NO, false);
+       self.animflag = self.frame;
+}
+void walker_rocket_explode()
+{
+       RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world);
+       remove (self);
+}
+
+void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+       self.health = self.health - damage;
+       self.velocity = self.velocity + vforce;
+
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(self.owner, walker_rocket_explode);
+}
+
+#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self)
+void walker_rocket_loop();
+void walker_rocket_think()
+{
+       vector newdir;
+       float edist;
+       float itime;
+       float m_speed;
+
+       self.nextthink = time;
+
+       edist = vlen(self.enemy.origin - self.origin);
+
+       // Simulate crude guidance
+       if (self.cnt < time)
+       {
+               if (edist < 1000)
+                       self.tur_shotorg = randomvec() * min(edist, 64);
+               else
+                       self.tur_shotorg = randomvec() * min(edist, 256);
+
+               self.cnt = time + 0.5;
+       }
+
+       if (edist < 128)
+               self.tur_shotorg = '0 0 0';
+
+       if (self.max_health < time)
+       {
+               self.think        = walker_rocket_explode;
+               self.nextthink  = time;
+               return;
+       }
+
+       if (self.shot_dmg != 1337 && random() < 0.01)
+       {
+               walker_rocket_loop();
+               return;
+       }
+
+       m_speed = vlen(self.velocity);
+
+       // Enemy dead? just keep on the current heading then.
+       if (self.enemy == world || self.enemy.deadflag != DEAD_NO)
+               self.enemy = world;
+
+       if (self.enemy)
+       {
+               itime = max(edist / m_speed, 1);
+               newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg);
+       }
+       else
+               newdir  = normalize(self.velocity);
+
+       WALKER_ROCKET_MOVE;
+}
+
+void walker_rocket_loop3()
+{
+       vector newdir;
+       self.nextthink = time;
+
+       if (self.max_health < time)
+       {
+               self.think = walker_rocket_explode;
+               return;
+       }
+
+       if (vlen(self.origin - self.tur_shotorg) < 100 )
+       {
+               self.think = walker_rocket_think;
+               return;
+       }
+
+       newdir = steerlib_pull(self.tur_shotorg);
+       WALKER_ROCKET_MOVE;
+
+       self.angles = vectoangles(self.velocity);
+}
+
+void walker_rocket_loop2()
+{
+       vector newdir;
+
+       self.nextthink = time;
+
+       if (self.max_health < time)
+       {
+               self.think = walker_rocket_explode;
+               return;
+       }
+
+       if (vlen(self.origin - self.tur_shotorg) < 100 )
+       {
+               self.tur_shotorg = self.origin - '0 0 200';
+               self.think = walker_rocket_loop3;
+               return;
+       }
+
+       newdir = steerlib_pull(self.tur_shotorg);
+       WALKER_ROCKET_MOVE;
+}
+
+void walker_rocket_loop()
+{
+       self.nextthink = time;
+       self.tur_shotorg = self.origin + '0 0 300';
+       self.think = walker_rocket_loop2;
+       self.shot_dmg = 1337;
+}
+
+void walker_fire_rocket(vector org)
+{
+       entity rocket;
+
+       fixedmakevectors(self.angles);
+
+       te_explosion (org);
+
+       rocket = spawn ();
+       setorigin(rocket, org);
+
+       sound (self, CH_WEAPON_A, "weapons/hagar_fire.wav", VOL_BASE, ATTEN_NORM);
+       setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+       rocket.classname                  = "walker_rocket";
+       rocket.owner                      = self;
+       rocket.bot_dodge                  = true;
+       rocket.bot_dodgerating  = 50;
+       rocket.takedamage                = DAMAGE_YES;
+       rocket.damageforcescale   = 2;
+       rocket.health                    = 25;
+       rocket.tur_shotorg              = randomvec() * 512;
+       rocket.cnt                              = time + 1;
+       rocket.enemy                      = self.enemy;
+
+       if (random() < 0.01)
+               rocket.think              = walker_rocket_loop;
+       else
+               rocket.think              = walker_rocket_think;
+
+       rocket.event_damage        = walker_rocket_damage;
+
+       rocket.nextthink                  = time;
+       rocket.movetype            = MOVETYPE_FLY;
+       rocket.velocity            = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
+       rocket.angles                    = vectoangles(rocket.velocity);
+       rocket.touch                      = walker_rocket_explode;
+       rocket.flags                      = FL_PROJECTILE;
+       rocket.solid                      = SOLID_BBOX;
+       rocket.max_health                = time + 9;
+       rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
+
+       CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound
+}
+
+.vector enemy_last_loc;
+.float enemy_last_time;
+void walker_move_to(vector _target, float _dist)
+{
+       switch (self.waterlevel)
+       {
+               case WATERLEVEL_NONE:
+                       if (_dist > 500)
+                               self.animflag = ANIM_RUN;
+                       else
+                               self.animflag = ANIM_WALK;
+               case WATERLEVEL_WETFEET:
+               case WATERLEVEL_SWIMMING:
+                       if (self.animflag != ANIM_SWIM)
+                               self.animflag = ANIM_WALK;
+                       else
+                               self.animflag = ANIM_SWIM;
+                       break;
+               case WATERLEVEL_SUBMERGED:
+                       self.animflag = ANIM_SWIM;
+       }
+
+       self.moveto = _target;
+       self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+       if(self.enemy)
+       {
+               self.enemy_last_loc = _target;
+               self.enemy_last_time = time;
+       }
+}
+
+//#define WALKER_FANCYPATHING
+
+void walker_move_path()
+{
+#ifdef WALKER_FANCYPATHING
+       // Are we close enougth to a path node to switch to the next?
+       if (vlen(self.origin  - self.pathcurrent.origin) < 64)
+               if (self.pathcurrent.path_next == world)
+               {
+                       // Path endpoint reached
+                       pathlib_deletepath(self.pathcurrent.owner);
+                       self.pathcurrent = world;
+
+                       if (self.pathgoal)
+                       {
+                               if (self.pathgoal.use)
+                                       self.pathgoal.use();
+
+                               if (self.pathgoal.enemy)
+                               {
+                                       self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin);
+                                       self.pathgoal = self.pathgoal.enemy;
+                               }
+                       }
+                       else
+                               self.pathgoal = world;
+               }
+               else
+                       self.pathcurrent = self.pathcurrent.path_next;
+
+       self.moveto = self.pathcurrent.origin;
+       self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95);
+       walker_move_to(self.moveto, 0);
+
+#else
+       if (vlen(self.origin - self.pathcurrent.origin) < 64)
+               self.pathcurrent = self.pathcurrent.enemy;
+
+       if(!self.pathcurrent)
+               return;
+
+       self.moveto = self.pathcurrent.origin;
+       self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+       walker_move_to(self.moveto, 0);
+#endif
+}
+
+void spawnfunc_turret_walker() { if(!turret_initialize(TUR_WALKER)) remove(self); }
+
+float t_walker(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM);
+                       fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0);
+                       pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+                       return true;
+               }
+               case TR_THINK:
+               {
+                       fixedmakevectors(self.angles);
+
+                       if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent)
+                               walker_move_path();
+                       else if (self.enemy == world)
+                       {
+                               if(self.pathcurrent)
+                                       walker_move_path();
+                               else
+                               {
+                                       if(self.enemy_last_time != 0)
+                                       {
+                                               if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10)
+                                                       self.enemy_last_time = 0;
+                                               else
+                                                       walker_move_to(self.enemy_last_loc, 0);
+                                       }
+                                       else
+                                       {
+                                               if(self.animflag != ANIM_NO)
+                                               {
+                                                       traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self);
+
+                                                       if(trace_fraction != 1.0)
+                                                               self.tur_head.idletime = -1337;
+                                                       else
+                                                       {
+                                                               traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self);
+                                                               if(trace_fraction == 1.0)
+                                                                       self.tur_head.idletime = -1337;
+                                                       }
+
+                                                       if(self.tur_head.idletime == -1337)
+                                                       {
+                                                               self.moveto = self.origin + randomvec() * 256;
+                                                               self.tur_head.idletime = 0;
+                                                       }
+
+                                                       self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
+                                                       self.moveto_z = self.origin_z + 64;
+                                                       walker_move_to(self.moveto, 0);
+                                               }
+
+                                               if(self.idletime < time)
+                                               {
+                                                       if(random() < 0.5 || !(self.spawnflags & TSL_ROAM))
+                                                       {
+                                                               self.idletime = time + 1 + random() * 5;
+                                                               self.moveto = self.origin;
+                                                               self.animflag = ANIM_NO;
+                                                       }
+                                                       else
+                                                       {
+                                                               self.animflag = ANIM_WALK;
+                                                               self.idletime = time + 4 + random() * 2;
+                                                               self.moveto = self.origin + randomvec() * 256;
+                                                               self.tur_head.moveto = self.moveto;
+                                                               self.tur_head.idletime = 0;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE)
+                               {
+                                       vector wish_angle;
+
+                                       wish_angle = angleofs(self, self.enemy);
+                                       if (self.animflag != ANIM_SWIM)
+                                       if (fabs(wish_angle_y) < 15)
+                                       {
+                                               self.moveto   = self.enemy.origin;
+                                               self.steerto  = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+                                               self.animflag = ANIM_MELEE;
+                                       }
+                               }
+                               else if (self.tur_head.attack_finished_single < time)
+                               {
+                                       if(self.tur_head.shot_volly)
+                                       {
+                                               self.animflag = ANIM_NO;
+
+                                               self.tur_head.shot_volly = self.tur_head.shot_volly -1;
+                                               if(self.tur_head.shot_volly == 0)
+                                                       self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
+                                               else
+                                                       self.tur_head.attack_finished_single = time + 0.2;
+
+                                               if(self.tur_head.shot_volly > 1)
+                                                       walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
+                                               else
+                                                       walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02")));
+                                       }
+                                       else
+                                       {
+                                               if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min))
+                                               if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range))
+                                                       self.tur_head.shot_volly = 4;
+                                       }
+                               }
+                               else
+                               {
+                                       if (self.animflag != ANIM_MELEE)
+                                               walker_move_to(self.enemy.origin, self.tur_dist_enemy);
+                               }
+                       }
+                       
+                       {
+                               vector real_angle;
+                               float turny = 0, turnx = 0;
+                               float vz;
+
+                               real_angle = vectoangles(self.steerto) - self.angles;
+                               vz = self.velocity_z;
+
+                               switch (self.animflag)
+                               {
+                                       case ANIM_NO:
+                                               movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                                               break;
+
+                                       case ANIM_TURN:
+                                               turny = (autocvar_g_turrets_unit_walker_turn);
+                                               movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                                               break;
+
+                                       case ANIM_WALK:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_walk);
+                                               movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6);
+                                               break;
+
+                                       case ANIM_RUN:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_run);
+                                               movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6);
+                                               break;
+
+                                       case ANIM_STRAFE_L:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+                                               movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+                                               break;
+
+                                       case ANIM_STRAFE_R:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+                                               movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+                                               break;
+
+                                       case ANIM_JUMP:
+                                               self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump);
+                                               break;
+
+                                       case ANIM_LAND:
+                                               break;
+
+                                       case ANIM_PAIN:
+                                               if(self.frame != ANIM_PAIN)
+                                                       defer(0.25, walker_setnoanim);
+
+                                               break;
+
+                                       case ANIM_MELEE:
+                                               if(self.frame != ANIM_MELEE)
+                                               {
+                                                       defer(0.41, walker_setnoanim);
+                                                       defer(0.21, walker_melee_do_dmg);
+                                               }
+
+                                               movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                                               break;
+
+                                       case ANIM_SWIM:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_swim);
+                                               turnx = (autocvar_g_turrets_unit_walker_turn_swim);
+
+                                               self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
+                                               movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3);
+                                               vz = self.velocity_z + sin(time * 4) * 8;
+                                               break;
+
+                                       case ANIM_ROAM:
+                                               turny = (autocvar_g_turrets_unit_walker_turn_walk);
+                                               movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5);
+                                               break;
+                               }
+
+                               if(turny)
+                               {
+                                       turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny );
+                                       self.angles_y += turny;
+                               }
+
+                               if(turnx)
+                               {
+                                       turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx );
+                                       self.angles_x += turnx;
+                               }
+
+                               self.velocity_z = vz;
+                       }
+
+
+                       if(self.origin != self.oldorigin)
+                               self.SendFlags |= TNSF_MOVE;
+
+                       self.oldorigin = self.origin;
+                       turrets_setframe(self.animflag, false);
+
+                       return true;
+               }
+               case TR_DEATH:
+               {
+#ifdef WALKER_FANCYPATHING
+                       if (self.pathcurrent)
+                               pathlib_deletepath(self.pathcurrent.owner);
+#endif
+                       self.pathcurrent = world;
+               
+                       return true;
+               }
+               case TR_SETUP:
+               {
+                       self.ticrate = 0.05;
+                       
+                       entity e;
+
+                       // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn.
+                       if(self.movetype == MOVETYPE_WALK)
+                       {
+                               if(self.pos1)
+                                       setorigin(self, self.pos1);
+                               if(self.pos2)
+                                       self.angles = self.pos2;
+                       }
+                       
+                       self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+                       self.aim_flags = TFL_AIM_LEAD;
+                       self.turret_flags |= TUR_FLAG_HITSCAN;
+                               
+                       self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+                       self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+                       self.iscreature = true;
+                       self.teleportable = TELEPORT_NORMAL;
+                       self.damagedbycontents = true;
+                       self.solid = SOLID_SLIDEBOX;
+                       self.takedamage = DAMAGE_AIM;
+                       if(self.movetype != MOVETYPE_WALK)
+                       {
+                               setorigin(self, self.origin);
+                               tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self);
+                               setorigin(self, trace_endpos + '0 0 4');
+                               self.pos1 = self.origin;
+                               self.pos2 = self.angles;
+                       }
+                       self.movetype = MOVETYPE_WALK;
+                       self.idle_aim = '0 0 0';
+                       self.turret_firecheckfunc = walker_firecheck;
+                       
+                       if (self.target != "")
+                       {
+                               e = find(world, targetname, self.target);
+                               if (!e)
+                               {
+                                       dprint("Initital waypoint for walker does NOT exsist, fix your map!\n");
+                                       self.target = "";
+                               }
+
+                               if (e.classname != "turret_checkpoint")
+                                       dprint("Warning: not a turrret path\n");
+                               else
+                               {
+#ifdef WALKER_FANCYPATHING
+                                       self.pathcurrent = WALKER_PATH(self.origin, e.origin);
+                                       self.pathgoal = e;
+#else
+                                       self.pathcurrent = e;
+#endif
+                               }
+                       }
+
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/walker_body.md3");
+                       precache_model ("models/turrets/walker_head_minigun.md3");
+                       precache_model ("models/turrets/rocket.md3");
+                       precache_sound ("weapons/rocket_impact.wav");
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+
+#include "../../../server/movelib.qh"
+
+void walker_draw()
+{
+       float dt;
+
+       dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0)
+               return;
+
+       fixedmakevectors(self.angles);
+       movelib_groundalign4point(300, 100, 0.25, 45);
+       setorigin(self, self.origin + self.velocity * dt);
+       self.tur_head.angles += dt * self.tur_head.move_avelocity;
+       self.angles_y = self.move_angles_y;
+
+       if (self.health < 127)
+       if(random() < 0.15)
+               te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
+}
+
+float t_walker(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       self.gravity            = 1;
+                       self.movetype           = MOVETYPE_BOUNCE;
+                       self.move_movetype      = MOVETYPE_BOUNCE;
+                       self.move_origin        = self.origin;
+                       self.move_time          = time;
+                       self.draw                       = walker_draw;
+                       
+                       return true;
+               }
+               case TR_PRECACHE:
+               {
+                       return true;
+               }
+       }
+
+       return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/util.qc b/qcsrc/common/turrets/util.qc
new file mode 100644 (file)
index 0000000..3d6d95a
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+* Return a angle within +/- 360.
+*/
+float anglemods(float v)
+{
+       v = v - 360 * floor(v / 360);
+
+       if(v >= 180)
+               return v - 360;
+       else if(v <= -180)
+               return v + 360;
+       else
+               return v;
+}
+
+/*
+* Return the short angle
+*/
+float shortangle_f(float ang1, float ang2)
+{
+       if(ang1 > ang2)
+       {
+               if(ang1 > 180)
+                       return ang1 - 360;
+       }
+       else
+       {
+               if(ang1 < -180)
+                       return ang1 + 360;
+       }
+
+       return ang1;
+}
+
+vector shortangle_v(vector ang1, vector ang2)
+{
+       vector vtmp;
+
+       vtmp_x = shortangle_f(ang1_x,ang2_x);
+       vtmp_y = shortangle_f(ang1_y,ang2_y);
+       vtmp_z = shortangle_f(ang1_z,ang2_z);
+
+       return vtmp;
+}
+
+vector shortangle_vxy(vector ang1, vector ang2)
+{
+       vector vtmp = '0 0 0';
+
+       vtmp_x = shortangle_f(ang1_x,ang2_x);
+       vtmp_y = shortangle_f(ang1_y,ang2_y);
+
+       return vtmp;
+}
+
+
+/*
+* Get "real" origin, in worldspace, even if ent is attached to something else.
+*/
+vector real_origin(entity ent)
+{
+       entity e;
+       vector v = ((ent.absmin + ent.absmax) * 0.5);
+
+       e = ent.tag_entity;
+       while(e)
+       {
+               v = v + ((e.absmin + e.absmax) * 0.5);
+               e = e.tag_entity;
+       }
+
+       return v;
+}
+
+/*
+* Return the angle between two enteties
+*/
+vector angleofs(entity from, entity to)
+{
+       vector v_res;
+
+       v_res = normalize(to.origin - from.origin);
+       v_res = vectoangles(v_res);
+       v_res = v_res - from.angles;
+
+       if (v_res_x < 0)        v_res_x += 360;
+       if (v_res_x > 180)      v_res_x -= 360;
+
+       if (v_res_y < 0)        v_res_y += 360;
+       if (v_res_y > 180)      v_res_y -= 360;
+
+       return v_res;
+}
+
+vector angleofs3(vector from, vector from_a, entity to)
+{
+       vector v_res;
+
+       v_res = normalize(to.origin - from);
+       v_res = vectoangles(v_res);
+       v_res = v_res - from_a;
+
+       if (v_res_x < 0)        v_res_x += 360;
+       if (v_res_x > 180)      v_res_x -= 360;
+
+       if (v_res_y < 0)        v_res_y += 360;
+       if (v_res_y > 180)      v_res_y -= 360;
+
+       return v_res;
+}
+
+/*
+* Update self.tur_shotorg by getting up2date bone info
+* NOTICE this func overwrites the global v_forward, v_right and v_up vectors.
+*/
+float turret_tag_fire_update()
+{
+       if(!self.tur_head)
+       {
+               error("Call to turret_tag_fire_update with self.tur_head missing!\n");
+               self.tur_shotorg = '0 0 0';
+               return false;
+       }
+
+       self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
+       v_forward = normalize(v_forward);
+
+       return true;
+}
+
+/*
+* Railgun-like beam, but has thickness and suppots slowing of target
+*/
+void FireImoBeam (vector start, vector end, vector smin, vector smax,
+                                 float bforce, float f_dmg, float f_velfactor, float deathtype)
+
+{
+       vector hitloc, force, endpoint, dir;
+       entity ent;
+
+       dir = normalize(end - start);
+       force = dir * bforce;
+
+       // go a little bit into the wall because we need to hit this wall later
+       end = end + dir;
+
+       // trace multiple times until we hit a wall, each obstacle will be made unsolid.
+       // note down which entities were hit so we can damage them later
+       while (1)
+       {
+               tracebox(start, smin, smax, end, false, self);
+
+               // if it is world we can't hurt it so stop now
+               if (trace_ent == world || trace_fraction == 1)
+                       break;
+
+               if (trace_ent.solid == SOLID_BSP)
+                       break;
+
+               // make the entity non-solid so we can hit the next one
+               trace_ent.railgunhit = true;
+               trace_ent.railgunhitloc = end;
+               trace_ent.railgunhitsolidbackup = trace_ent.solid;
+
+               // stop if this is a wall
+
+               // make the entity non-solid
+               trace_ent.solid = SOLID_NOT;
+       }
+
+       endpoint = trace_endpos;
+
+       // find all the entities the railgun hit and restore their solid state
+       ent = findfloat(world, railgunhit, true);
+       while (ent)
+       {
+               // restore their solid type
+               ent.solid = ent.railgunhitsolidbackup;
+               ent = findfloat(ent, railgunhit, true);
+       }
+
+       // find all the entities the railgun hit and hurt them
+       ent = findfloat(world, railgunhit, true);
+       while (ent)
+       {
+               // get the details we need to call the damage function
+               hitloc = ent.railgunhitloc;
+               ent.railgunhitloc = '0 0 0';
+               ent.railgunhitsolidbackup = SOLID_NOT;
+               ent.railgunhit = false;
+
+               // apply the damage
+               if (ent.takedamage)
+               {
+                       Damage (ent, self, self, f_dmg, deathtype, hitloc, force);
+                       ent.velocity = ent.velocity * f_velfactor;
+                       //ent.alpha = 0.25 + random() * 0.75;
+               }
+
+               // advance to the next entity
+               ent = findfloat(ent, railgunhit, true);
+       }
+       trace_endpos = endpoint;
+}
+
+#ifdef TURRET_DEBUG
+void SUB_Remove();
+void marker_think()
+{
+       if(self.cnt)
+       if(self.cnt < time)
+       {
+               self.think = SUB_Remove;
+               self.nextthink = time;
+               return;
+       }
+
+       self.frame += 1;
+       if(self.frame > 29)
+               self.frame = 0;
+
+       self.nextthink = time;
+}
+
+void mark_error(vector where,float lifetime)
+{
+       entity err;
+
+       err = spawn();
+       err.classname = "error_marker";
+       setmodel(err,"models/marker.md3");
+       setorigin(err,where);
+       err.movetype = MOVETYPE_NONE;
+       err.think = marker_think;
+       err.nextthink = time;
+       err.skin = 0;
+       if(lifetime)
+               err.cnt = lifetime + time;
+}
+
+void mark_info(vector where,float lifetime)
+{
+       entity err;
+
+       err = spawn();
+       err.classname = "info_marker";
+       setmodel(err,"models/marker.md3");
+       setorigin(err,where);
+       err.movetype = MOVETYPE_NONE;
+       err.think = marker_think;
+       err.nextthink = time;
+       err.skin = 1;
+       if(lifetime)
+               err.cnt = lifetime + time;
+}
+
+entity mark_misc(vector where,float lifetime)
+{
+       entity err;
+
+       err = spawn();
+       err.classname = "mark_misc";
+       setmodel(err,"models/marker.md3");
+       setorigin(err,where);
+       err.movetype = MOVETYPE_NONE;
+       err.think = marker_think;
+       err.nextthink = time;
+       err.skin = 3;
+       if(lifetime)
+               err.cnt = lifetime + time;
+       return err;
+}
+
+/*
+* Paint a v_color colord circle on target onwho
+* that fades away over f_time
+*/
+void paint_target(entity onwho, float f_size, vector v_color, float f_time)
+{
+       entity e;
+
+       e = spawn();
+       setmodel(e, "models/turrets/c512.md3"); // precision set above
+       e.scale = (f_size/512);
+       //setsize(e, '0 0 0', '0 0 0');
+       //setattachment(e,onwho,"");
+       setorigin(e,onwho.origin + '0 0 1');
+       e.alpha = 0.15;
+       e.movetype = MOVETYPE_FLY;
+
+       e.velocity = (v_color * 32); // + '0 0 1' * 64;
+
+       e.colormod = v_color;
+       SUB_SetFade(e,time,f_time);
+}
+
+void paint_target2(entity onwho, float f_size, vector v_color, float f_time)
+{
+       entity e;
+
+       e = spawn();
+       setmodel(e, "models/turrets/c512.md3"); // precision set above
+       e.scale = (f_size/512);
+       setsize(e, '0 0 0', '0 0 0');
+
+       setorigin(e,onwho.origin + '0 0 1');
+       e.alpha = 0.15;
+       e.movetype = MOVETYPE_FLY;
+
+       e.velocity = (v_color * 32); // + '0 0 1' * 64;
+       e.avelocity_x = -128;
+
+       e.colormod = v_color;
+       SUB_SetFade(e,time,f_time);
+}
+
+void paint_target3(vector where, float f_size, vector v_color, float f_time)
+{
+       entity e;
+       e = spawn();
+       setmodel(e, "models/turrets/c512.md3"); // precision set above
+       e.scale = (f_size/512);
+       setsize(e, '0 0 0', '0 0 0');
+       setorigin(e,where+ '0 0 1');
+       e.movetype = MOVETYPE_NONE;
+       e.velocity = '0 0 0';
+       e.colormod = v_color;
+       SUB_SetFade(e,time,f_time);
+}
+#endif
diff --git a/qcsrc/common/turrets/util.qh b/qcsrc/common/turrets/util.qh
new file mode 100644 (file)
index 0000000..71ba066
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef TURRETS_UTIL_H
+#define TURRETS_UTIL_H
+
+vector real_origin(entity ent);
+float shortangle_f(float ang1, float ang2);
+float anglemods(float v);
+float turret_tag_fire_update();
+vector shortangle_vxy(vector ang1, vector ang2);
+vector angleofs(entity from, entity to);
+vector angleofs3(vector from, vector from_a, entity to);
+void FireImoBeam (vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype);
+
+#endif
index 015c13f..69e523c 100644 (file)
@@ -510,41 +510,6 @@ float autocvar_g_turrets_nofire;
 float autocvar_g_turrets_reloadcvars;
 float autocvar_g_turrets_targetscan_maxdelay;
 float autocvar_g_turrets_targetscan_mindelay;
-float autocvar_g_turrets_unit_ewheel_speed_fast;
-float autocvar_g_turrets_unit_ewheel_speed_slow;
-float autocvar_g_turrets_unit_ewheel_speed_slower;
-float autocvar_g_turrets_unit_ewheel_speed_stop;
-float autocvar_g_turrets_unit_ewheel_turnrate;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_gain;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel2;
-float autocvar_g_turrets_unit_hk_std_shot_speed_decel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed_turnrate;
-float autocvar_g_turrets_unit_walker_speed_jump;
-float autocvar_g_turrets_unit_walker_speed_roam;
-float autocvar_g_turrets_unit_walker_speed_run;
-float autocvar_g_turrets_unit_walker_speed_stop;
-float autocvar_g_turrets_unit_walker_speed_swim;
-float autocvar_g_turrets_unit_walker_speed_walk;
-float autocvar_g_turrets_unit_walker_std_meele_dmg;
-float autocvar_g_turrets_unit_walker_std_meele_force;
-float autocvar_g_turrets_unit_walker_std_meele_range;
-float autocvar_g_turrets_unit_walker_std_rocket_dmg;
-float autocvar_g_turrets_unit_walker_std_rocket_force;
-float autocvar_g_turrets_unit_walker_std_rocket_radius;
-float autocvar_g_turrets_unit_walker_std_rocket_refire;
-float autocvar_g_turrets_unit_walker_std_rocket_speed;
-float autocvar_g_turrets_unit_walker_std_rocket_turnrate;
-float autocvar_g_turrets_unit_walker_std_rockets_range;
-float autocvar_g_turrets_unit_walker_std_rockets_range_min;
-float autocvar_g_turrets_unit_walker_turn;
-float autocvar_g_turrets_unit_walker_turn_walk;
-float autocvar_g_turrets_unit_walker_turn_run;
-float autocvar_g_turrets_unit_walker_turn_strafe;
-float autocvar_g_turrets_unit_walker_turn_swim;
 float autocvar_g_use_ammunition;
 float autocvar_g_waypointeditor;
 float autocvar_g_waypointeditor_auto;
index d170cf4..aab2f9c 100644 (file)
@@ -18,7 +18,8 @@
     #include "../common/notifications.qh"
     #include "../common/deathtypes.qh"
     #include "mutators/mutators_include.qh"
-    #include "tturrets/include/turrets_early.qh"
+    #include "../common/turrets/turrets.qh"
+    #include "../common/turrets/sv_turrets.qh"
     #include "vehicles/vehicles_def.qh"
     #include "../csqcmodellib/sv_model.qh"
     #include "../common/playerstats.qh"
@@ -865,7 +866,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                        else
                                victim = targ;
 
-                       if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER))
+                       if(IS_PLAYER(victim) || ((victim.turret_flags & TUR_FLAG_ISTURRET) && victim.active == ACTIVE_ACTIVE) || (victim.flags & FL_MONSTER))
                        {
                                if(DIFF_TEAM(victim, attacker) && !victim.frozen)
                                {
index 4cc3f6f..68f9a0a 100644 (file)
@@ -21,7 +21,6 @@
     #include "../common/notifications.qh"
     #include "../common/deathtypes.qh"
     #include "mutators/mutators_include.qh"
-    #include "tturrets/include/turrets_early.qh"
     #include "vehicles/vehicles_def.qh"
     #include "../csqcmodellib/sv_model.qh"
     #include "../common/playerstats.qh"
index 04f8ff7..24a3d21 100644 (file)
@@ -25,6 +25,7 @@
     #include "command/sv_cmd.qh"
     #include "anticheat.qh"
     #include "cheats.qh"
+       #include "../common/turrets/turrets.qh"
     #include "../common/playerstats.qh"
     #include "g_hook.qh"
     #include "scores.qh"
@@ -558,6 +559,7 @@ void spawnfunc___init_dedicated_server(void)
 
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterMonsters);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
@@ -606,6 +608,7 @@ void spawnfunc_worldspawn (void)
 
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterMonsters);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
index d998ea7..eb45744 100644 (file)
@@ -23,7 +23,6 @@
     #include "../common/notifications.qh"
     #include "../common/deathtypes.qh"
     #include "mutators/mutators_include.qh"
-    #include "tturrets/include/turrets_early.qh"
     #include "../common/mapinfo.qh"
     #include "command/common.qh"
     #include "../csqcmodellib/sv_model.qh"
@@ -1017,11 +1016,6 @@ void precache()
     precache_model ("models/misc/chatbubble.spr");
        precache_model("models/ice/ice.md3");
 
-#ifdef TTURRETS_ENABLED
-    if (autocvar_g_turrets)
-        turrets_precash();
-#endif
-
     // Precache all player models if desired
     if (autocvar_sv_precacheplayermodels)
     {
index 49deac0..841b5a9 100644 (file)
@@ -163,7 +163,6 @@ void assault_roundstart_use()
        activator = self;
        SUB_UseTargets();
 
-#ifdef TTURRETS_ENABLED
        entity ent, oldself;
 
        //(Re)spawn all turrets
@@ -179,12 +178,11 @@ void assault_roundstart_use()
                self = ent;
 
                // Dubbles as teamchange
-               turret_stdproc_respawn();
+               turret_respawn();
 
                ent = find(ent, classname, "turret_main");
        }
        self = oldself;
-#endif
 }
 
 void assault_wall_think()
index d1f20f7..9ad1191 100644 (file)
@@ -1657,6 +1657,29 @@ MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
     return 0;
 }
 
+MUTATOR_HOOKFUNCTION(ons_TurretSpawn)
+{
+       entity e, ee = world;
+       if(self.targetname)
+       {
+               e = find(world, target, self.targetname);
+
+               if(e != world)
+               {
+                       self.team = e.team;
+                       ee = e;
+               }
+       }
+       
+       if(ee)
+       {
+               activator = ee;
+               self.use();
+       }
+
+       return FALSE;
+}
+
 MUTATOR_HOOKFUNCTION(ons_MonsterThink)
 {
        entity e = find(world, targetname, self.target);
@@ -1673,15 +1696,6 @@ MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
        if(self.targetname)
        {
                e = find(world,target,self.targetname);
-               if(e != world)
-               {
-                       self.team = e.team;
-                       ee = e;
-               }
-       }
-
-       if(ee)
-       {
         activator = ee;
         self.use();
     }
@@ -1694,6 +1708,7 @@ MUTATOR_DEFINITION(gamemode_onslaught)
        MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterMove, ons_MonsterThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterSpawn, ons_MonsterSpawn, CBC_ORDER_ANY);
        //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
index a0170e4..95f9604 100644 (file)
@@ -45,7 +45,6 @@
     #include "../../common/notifications.qh"
     #include "../../common/deathtypes.qh"
     #include "mutators_include.qh"
-    #include "../tturrets/include/turrets_early.qh"
     #include "../vehicles/vehicles_def.qh"
     #include "../campaign.qh"
     #include "../../common/campaign_common.qh"
@@ -76,7 +75,6 @@
     #include "../item_key.qh"
     #include "../secret.qh"
     #include "../pathlib/pathlib.qh"
-    #include "../tturrets/include/turrets.qh"
     #include "../vehicles/vehicles.qh"
 #endif
 
index 85e75cd..527c427 100644 (file)
@@ -102,6 +102,12 @@ weapons/weaponsystem.qc
 ../common/test.qc
 ../common/urllib.qc
 ../common/util.qc
+../common/turrets/sv_turrets.qc
+../common/turrets/config.qc
+../common/turrets/util.qc
+../common/turrets/turrets.qc
+../common/turrets/checkpoint.qc
+../common/turrets/targettrigger.qc
 ../common/weapons/config.qc
 ../common/weapons/weapons.qc // TODO
 
index 46df0eb..53704a0 100644 (file)
@@ -13,7 +13,6 @@
     #include "constants.qh"
     #include "defs.qh"
     #include "../common/deathtypes.qh"
-    #include "tturrets/include/turrets_early.qh"
     #include "vehicles/vehicles_def.qh"
     #include "../common/mapinfo.qh"
     #include "anticheat.qh"
@@ -195,7 +194,7 @@ void Teleport_Touch (void)
        if(!other.vehicle.teleportable)
                return;
 
-       if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+       if(other.turret_flags & TUR_FLAG_ISTURRET)
                return;
 
        if(other.deadflag != DEAD_NO)
diff --git a/qcsrc/server/tturrets/include/turrets.qh b/qcsrc/server/tturrets/include/turrets.qh
deleted file mode 100644 (file)
index 50bfa32..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef TURRETS_H
-#define TURRETS_H
-
-#ifdef TTURRETS_ENABLED
-
-// Include section.
-#include "../system/system_misc.qc"       /// Assorted junk & jewls
-#include "../system/system_main.qc"       /// And routines
-#include "../system/system_aimprocs.qc"   /// Aiming realted stuff
-#include "../system/system_scoreprocs.qc" /// Target calssification
-#include "../system/system_damage.qc"     /// Outch, they are hurting me! what should i do?
-
-// Non combat units
-#include "../units/unit_fusionreactor.qc"  /// Supply unites that need it with power
-#include "../units/unit_targettrigger.qc"  /// Hit me!
-#include "../units/unit_checkpoint.qc"     /// Halfsmart pathing.
-
-// Combat units
-#include "../units/unit_plasma.qc"  /// Basic energy cannon
-#include "../units/unit_mlrs.qc"    /// Basic multibay RL
-#include "../units/unit_hellion.qc" /// Seeking missiles MLRS
-#include "../units/unit_flac.qc"    /// anti missile turret
-#include "../units/unit_phaser.qc"     /// ZzzapT
-#include "../units/unit_hk.qc"         /// Hunter killers
-#include "../units/unit_machinegun.qc" /// whacka
-#include "../units/unit_tessla.qc"     /// Chain lightning capabale turret
-#include "../units/unit_walker.qc"     /// Moving minigun-rocket-meele err thing
-#include "../units/unit_ewheel.qc"     /// A evil wheel. with guns on.
-//#include "../units/unit_repulsor.qc" /// Fires a wave that knocks foes back
-//#include "../units/unit_hive.qc"     /// Swarm AI
-
-#endif // TTURRETS_ENABLED
-#endif
diff --git a/qcsrc/server/tturrets/include/turrets_early.qh b/qcsrc/server/tturrets/include/turrets_early.qh
deleted file mode 100644 (file)
index 4c5d95f..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-#ifndef TURRETS_EARLY_H
-#define TURRETS_EARLY_H
-
-// Comment out below to skip turrets
-#define TTURRETS_ENABLED
-
-#ifdef TTURRETS_ENABLED
-#ifdef SVQC
-//#message "with tZork turrets"
-
-float turret_count;
-
-vector real_origin(entity ent);
-
-/// Map time control over pain inflicted
-.float turret_scale_damage;
-/// Map time control targetting range
-.float turret_scale_range;
-/// Map time control refire
-.float turret_scale_refire;
-/// Map time control ammo held and recharged
-.float turret_scale_ammo;
-/// Map time control aim speed
-.float turret_scale_aim;
-/// Map time control health
-.float turret_scale_health;
-/// Map time control respawn time
-.float turret_scale_respawn;
-
-/// Used for cvar reloading
-.string cvar_basename;
-
-//.float spawnflags
-const float TSF_SUSPENDED     = 1;
-/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces
-const float TSF_TERRAINBASE   = 2;
-/// Disable builtin ammo regeneration
-const float TSF_NO_AMMO_REGEN = 4;
-/// Dont break path to chase enemys. will still fire at them if possible.
-const float TSF_NO_PATHBREAK  = 8;
-/// Dont respawn
-const float TSL_NO_RESPAWN    = 16;
-/// Let this turret roam when idle.
-const float TSL_ROAM          = 32;
-
-/// target selection flags
-.float target_select_flags;
-/// target validatoin flags
-.float target_validate_flags;
-/// Dont select a target on its own.
-const float TFL_TARGETSELECT_NO            = 2;
-/// Need line of sight
-const float TFL_TARGETSELECT_LOS           = 4;
-/// Players are valid targets
-const float TFL_TARGETSELECT_PLAYERS       = 8;
-/// Missiles are valid targets
-const float TFL_TARGETSELECT_MISSILES      = 16;
-/// Responds to turret_trigger_target events
-const float TFL_TARGETSELECT_TRIGGERTARGET = 32;
-/// Angular limitations of turret head limits target selection
-const float TFL_TARGETSELECT_ANGLELIMITS   = 64;
-/// Range limits apply in targetselection
-const float TFL_TARGETSELECT_RANGELIMTS    = 128;
-/// DOnt select targets with a .team matching its own
-const float TFL_TARGETSELECT_TEAMCHECK     = 256;
-/// Cant select targets on its own. needs to be triggerd or slaved.
-const float TFL_TARGETSELECT_NOBUILTIN     = 512;
-/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team)
-const float TFL_TARGETSELECT_OWNTEAM       = 1024;
-/// Turrets aren't valid targets
-const float TFL_TARGETSELECT_NOTURRETS     = 2048;
-/// Use feild of view
-const float TFL_TARGETSELECT_FOV           = 4096;
-
-const float TFL_TARGETSELECT_MISSILESONLY  = 8192;
-
-/// aim flags
-.float aim_flags;
-/// Dont aim.
-const float TFL_AIM_NO                  = 1;
-/// Go for ground, not direct hit, but only if target is on ground.
-const float TFL_AIM_GROUNDGROUND        = 2;
-/// Try to predict target movement (does not account for gravity)
-const float TFL_AIM_LEAD                = 4;
-/// Compensate for shot traveltime when lead
-const float TFL_AIM_SHOTTIMECOMPENSATE  = 8;
-/// Try to do real prediction of targets z pos at impact.
-const float TFL_AIM_ZPREDICT            = 16;
-/// Simply aim at target's current location
-const float TFL_AIM_SIMPLE              = 32;
-
-/// track (turn and pitch head) flags
-.float track_flags;
-/// Dont move head
-const float TFL_TRACK_NO    = 2;
-/// Pitch the head
-const float TFL_TRACK_PITCH = 4;
-/// Rotate the head
-const float TFL_TRACK_ROT   = 8;
-
-/// How tracking is preformed
-.float track_type;
-/// Hard angle increments. Ugly for fast turning, best accuracy.
-const float TFL_TRACKTYPE_STEPMOTOR    = 1;
-/// Smoth absolute movement. Looks ok, fair accuracy.
-const float TFL_TRACKTYPE_FLUIDPRECISE = 2;
-/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the fields below are set
-const float TFL_TRACKTYPE_FLUIDINERTIA = 3;
-/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier
-.float track_accel_pitch;
-/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier
-.float  track_accel_rot;
-/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1  = only old, 0 = only new
-.float  track_blendrate;
-
-/// How prefire check is preformed
-.float firecheck_flags;
-/// Dont kill the dead
-const float TFL_FIRECHECK_DEAD        = 4;
-/// Range limits apply
-const float TFL_FIRECHECK_DISTANCES   = 8;
-/// Line Of Sight needs to be clear
-const float TFL_FIRECHECK_LOS         = 16;
-/// Consider distance inpactpoint<->aimspot
-const float TFL_FIRECHECK_AIMDIST     = 32;
-/// Consider enemy origin<->impactpoint
-const float TFL_FIRECHECK_REALDIST    = 64;
-/// Consider angular diff head<->aimspot
-const float TFL_FIRECHECK_ANGLEDIST  = 128;
-/// (re)consider target.team<->self.team
-const float TFL_FIRECHECK_TEAMCECK   = 256;
-/// Try to avoid friendly fire
-const float TFL_FIRECHECK_AFF        = 512;
-/// Own .ammo needs to be >= then own .shot_dmg
-const float TFL_FIRECHECK_OWM_AMMO   = 1024;
-/// Others ammo need to be < others .ammo_max
-const float TFL_FIRECHECK_OTHER_AMMO = 2048;
-/// Check own .attack_finished_single vs time
-const float TFL_FIRECHECK_REFIRE     = 4096;
-/// Move the acctual target to aimspot before tracing impact (and back after)
-//#define TFL_FIRECHECK_VERIFIED   8192
-/// Dont do any chekcs
-const float TFL_FIRECHECK_NO         = 16384;
-
-/// How shooting is done
-.float shoot_flags;
-/// Dont shoot
-const float  TFL_SHOOT_NO          = 64;
-/// Fire in vollys (partial implementation through .shot_volly)
-const float  TFL_SHOOT_VOLLY       = 2;
-/// Always do a full volly, even if target is lost or dead. (not implemented)
-const float  TFL_SHOOT_VOLLYALWAYS = 4;
-/// Loop though all valid tarters, and hit them.
-const float  TFL_SHOOT_HITALLVALID = 8;
-/// Fiering makes unit loose target (after volly is done, if in volly mode)
-const float  TFL_SHOOT_CLEARTARGET = 16;
-///Custom shooting;
-const float  TFL_SHOOT_CUSTOM = 32;
-
-/// Information aboute the units capabilities
-.float turrcaps_flags;
-/// No kown capabilities
-const float  TFL_TURRCAPS_NONE        = 0;
-/// Capable of sniping
-const float  TFL_TURRCAPS_SNIPER      = 2;
-/// Capable of splasdamage
-const float  TFL_TURRCAPS_RADIUSDMG   = 4;
-/// Has one or more cannons with zero shot traveltime
-const float  TFL_TURRCAPS_HITSCAN     = 8;
-/// More then one (type of) gun
-const float  TFL_TURRCAPS_MULTIGUN    = 16;
-/// Carries at least one guided weapon
-const float  TFL_TURRCAPS_GUIDED      = 32;
-/// At least one gun fiers slow projectiles
-const float  TFL_TURRCAPS_SLOWPROJ    = 64;
-/// At least one gun fiers medium speed projectiles
-const float  TFL_TURRCAPS_MEDPROJ     = 128;
-/// At least one gun fiers fast projectiles
-const float  TFL_TURRCAPS_FASTPROJ    = 256;
-/// At least one gun capable of damaging players
-const float  TFL_TURRCAPS_PLAYERKILL  = 512;
-/// At least one gun that can shoot town missiles
-const float  TFL_TURRCAPS_MISSILEKILL = 1024;
-/// Has support capabilities. powerplants and sutch.
-const float  TFL_TURRCAPS_SUPPORT     = 2048;
-/// Proveides at least one type of ammmo
-const float  TFL_TURRCAPS_AMMOSOURCE  = 4096;
-/// Can recive targets from external sources
-const float TFL_TURRCAPS_RECIVETARGETS = 8192;
-/// Capable of self-transport
-const float TFL_TURRCAPS_MOVE = 16384;
-/// Will roam arround even if not chasing anyting
-const float TFL_TURRCAPS_ROAM = 32768;
-const float TFL_TURRCAPS_ISTURRET = 65536;
-
-/// Ammo types needed and/or provided
-//.float ammo_flags;
-#define ammo_flags currentammo
-/// Has and needs no ammo
-const float  TFL_AMMO_NONE     = 64;
-/// Uses power
-const float  TFL_AMMO_ENERGY   = 2;
-/// Uses bullets
-const float  TFL_AMMO_BULLETS  = 4;
-/// Uses explosives
-const float  TFL_AMMO_ROCKETS  = 8;
-/// Regenerates ammo on its own
-const float  TFL_AMMO_RECHARGE = 16;
-/// Can recive ammo from others
-const float  TFL_AMMO_RECIVE   = 32;
-
-/// How incomming damage is handeld
-.float damage_flags;
-/// Cant be hurt
-const float  TFL_DMG_NO              = 256;
-/// Can be damaged
-const float  TFL_DMG_YES             = 2;
-/// Can be damaged  by teammates
-const float  TFL_DMG_TAKEFROMTEAM    = 4;
-/// Traget attackers
-const float  TFL_DMG_RETALIATE       = 8;
-/// Target attackers, even is on own team
-const float  TFL_DMG_RETALIATEONTEAM = 16;
-/// Loses target when damaged
-const float  TFL_DMG_TARGETLOSS      = 32;
-/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented.
-const float  TFL_DMG_AIMSHAKE        = 64;
-/// Reciving damage slaps the head arround
-const float  TFL_DMG_HEADSHAKE       = 128;
-/// Die and stay dead.
-const float  TFL_DMG_DEATH_NORESPAWN = 256;
-
-// Spawnflags
-/// Spawn in teambased modes
-const float TFL_SPAWN_TEAM      = 2;
-/// Spawn in FFA modes
-const float TFL_SPAWN_FFA       = 4;
-
-
-/*
-* Fields used by turrets
-*/
-/// Turrets internal ai speed
-.float      ticrate;
-
-/// Where to point the when no target
-.vector     idle_aim;
-
-/// Top part of turret
-.entity     tur_head;
-
-/// Start/respawn health
-.float      tur_health;
-
-/// Defend this entity (or ratehr this entitys position)
-.entity     tur_defend;
-
-/// and shoot from here. (can be non constant, think MLRS)
-.vector     tur_shotorg;
-
-/// Aim at this spot
-.vector     tur_aimpos;
-
-/// Predicted time the round will impact
-.float      tur_impacttime;
-
-// Predicted place the round will impact
-//.vector     tur_impactpoint; // unused
-
-/// What entity the aimtrace hit, if any.
-.entity     tur_impactent;
-
-/// Distance to enemy
-.float      tur_dist_enemy;
-
-/// Distance to aimspot
-.float      tur_dist_aimpos;
-
-/// Distance impact<->aim
-.float      tur_dist_impact_to_aimpos;
-
-/// Decresment counter form .shot_volly to 0.
-.float      volly_counter;
-
-/*
-* Projectile/missile. its up to the individual turret implementation to
-** deal the damage, blow upp the missile or whatever.
-*/
-/// Track then refireing is possible
-//.float attack_finished; = attack_finished_single
-/// Shoot this often
-.float shot_refire;
-/// Shots travel this fast, when appliable
-.float shot_speed;
-/// Inaccuracy
-.float shot_spread;
-/// Estimated (core) damage of projectiles. also reduce on ammo with this amount when fiering
-.float shot_dmg;
-/// If radius dmg, this is how big that radius is.
-.float shot_radius;
-/// Max force exserted by round impact
-.float shot_force;
-/// < 1 = shoot # times at target (if possible)
-.float shot_volly;
-/// Refire after a compleated volly.
-.float shot_volly_refire;
-
-/// Consider targets within this range
-.float target_range;
-/// Dont consider targets closer then
-.float target_range_min;
-/// Targets closer to this are prefered
-.float target_range_optimal;
-
-/*
-* The standard targetselection tries to select a target based on
-* range, angle offset, target type, "is old target"
-* Thise biases will allow score scaling to (dis)favor diffrent targets
-*/
-/// (dis)Favor best range this mutch
-.float target_select_rangebias;
-/// (dis)Favor targeting my old enemy this mutch
-.float target_select_samebias;
-/// (dis)Favor targeting the enemy closest to my guns current angle this mutch
-.float target_select_anglebias;
-/// (dis)Favor Missiles? (-1 to diable targeting compleatly)
-.float target_select_missilebias;
-/// (dis)Favot living players (-1 to diable targeting compleatly)
-.float target_select_playerbias;
-/// Field of view
-//.float target_select_fov;
-/// Last timestamp this turret aquierd a valid target
-.float target_select_time;
-/// Throttle re-validation of current target
-.float target_validate_time;
-/*
-* Aim refers to real aiming, not gun pos (thats done by track)
-*/
-/// Maximum offset between impact and aim spot to fire
-.float aim_firetolerance_dist;
-/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes)
-.float aim_speed;
-/// cant aim higher/lower then this
-.float aim_maxpitch;
-/// I cant rotate more then this
-.float aim_maxrot;
-
-// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating)
-/// Staring & current ammo
-.float ammo;
-/// Regenerate this mutch ammo (per second)
-.float ammo_recharge;
-/// Max amount of ammo i can hold
-.float ammo_max;
-
-
-// Uncomment below to enable various debug output.
-//#define TURRET_DEBUG
-//#define TURRET_DEBUG_TARGETVALIDATE
-//#define TURRET_DEBUG_TARGETSELECT
-
-#ifdef TURRET_DEBUG
-.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg.
-.float tur_dbg_dmg_t_f; // Total damage spent
-.float tur_dbg_start;   // When did i go online?
-.float tur_dbg_tmr1;    // timer for random use
-.float tur_dbg_tmr2;    // timer for random use
-.float tur_dbg_tmr3;    // timer for random use
-.vector tur_dbg_rvec;   // Random vector, mainly for coloruing stuff'
-#endif
-
-// System main's
-/// Main AI loop
-void turret_think();
-/// Prefire checks and sutch
-void turret_fire();
-
-// Callbacks
-/// implements the actual fiering
-.void()  turret_firefunc;
-/// prefire checks go here. return 1 to go bang, 0 not to.
-.float() turret_firecheckfunc;
-/// Execure AFTER main AI loop
-.void()  turret_postthink;
-
-/// Add a target
-.float(entity e_target,entity e_sender) turret_addtarget;
-
-.void() turret_diehook;
-.void() turret_respawnhook;
-
-/*
-* Target selection, preferably but not nessesarely
-* return a normalized result.
-*/
-/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic
-.float(entity _turret, entity _target) turret_score_target;
-
-/*
-* Target selection
-*/
-/// Generic, fairly smart, bias-aware target selection.
-float   turret_stdproc_targetscore_generic(entity _turret, entity _target);
-/// Experimental supportunits targetselector
-float   turret_stdproc_targetscore_support(entity _turret,entity _target);
-
-/*
-* Aim functions
-*/
-/// Generic aimer guided by self.aim_flags
-vector turret_stdproc_aim_generic();
-
-/*
-* Turret turning & pitch
-*/
-/// Tries to line up the turret head with the aimpos
-void turret_stdproc_track();
-
-/// Generic damage handeling. blows up the turret when health <= 0
-void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce);
-/// Spawns a explotion, does some damage & trows bits arround.
-void turret_stdproc_die();
-/// reassembles the turret.
-void turret_stdproc_respawn();
-
-/// Evaluate target validity
-float turret_validate_target(entity e_turret,entity e_target,float validate_flags);
-/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
-vector tvt_thadv;
-/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
-vector tvt_tadv;
-/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target
-float  tvt_thadf;
-/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target
-float  tvt_tadf;
-/// Distance. updated by a sucsessfull call to turret_validate_target
-float  tvt_dist;
-
-/// updates aim org, shot org, shot dir and enemy org for selected turret
-void turret_do_updates(entity e_turret);
-.vector tur_shotdir_updated;
-
-void turrets_precash();
-#endif // SVQC
-
-// common
-.int turret_type;
-const int TID_COMMON        = 1;
-const int TID_EWHEEL        = 2;
-const int TID_FLAC          = 3;
-const int TID_FUSION        = 4;
-const int TID_HELLION       = 5;
-const int TID_HK            = 6;
-const int TID_MACHINEGUN    = 7;
-const int TID_MLRS          = 8;
-const int TID_PHASER        = 9;
-const int TID_PLASMA        = 10;
-const int TID_PLASMA_DUAL   = 11;
-const int TID_TESLA         = 12;
-const int TID_WALKER        = 13;
-const int TID_LAST          = 13;
-
-const int TNSF_UPDATE       = 2;
-const int TNSF_STATUS       = 4;
-const int TNSF_SETUP        = 8;
-const int TNSF_ANG          = 16;
-const int TNSF_AVEL         = 32;
-const int TNSF_MOVE         = 64;
-.float anim_start_time;
-const int TNSF_ANIM         = 128;
-
-const int TNSF_FULL_UPDATE  = 16777215;
-
-#endif // TTURRETS_ENABLED
-#endif
diff --git a/qcsrc/server/tturrets/system/system_aimprocs.qc b/qcsrc/server/tturrets/system/system_aimprocs.qc
deleted file mode 100644 (file)
index fa21f1a..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-* Generic aim
-
-supports:
-TFL_AIM_NO
-TFL_AIM_GROUNDGROUND
-TFL_AIM_LEAD
-TFL_AIM_SHOTTIMECOMPENSATE
-*/
-vector turret_stdproc_aim_generic()
-{
-
-    vector pre_pos, prep;
-    float distance, i, mintime;
-
-    turret_tag_fire_update();
-
-    if(self.aim_flags & TFL_AIM_SIMPLE)
-        return real_origin(self.enemy);
-
-       mintime = max(self.attack_finished_single - time,0) + sys_frametime;
-
-    // Baseline
-    pre_pos = real_origin(self.enemy);
-
-    // Lead?
-    if (self.aim_flags & TFL_AIM_LEAD)
-    {
-               if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE)       // Need to conpensate for shot traveltime
-               {
-                       // FIXME: this cant be the best way to do this..
-                       prep = pre_pos;
-                       float impact_time = 0;
-                       for(i = 0; i < 4; ++i)
-                       {
-                               distance = vlen(prep - self.tur_shotorg);
-                               impact_time = distance / self.shot_speed;
-                               prep = pre_pos + self.enemy.velocity * impact_time;
-                       }
-
-                       prep = pre_pos + (self.enemy.velocity * (impact_time + mintime));
-
-                       if(self.aim_flags & TFL_AIM_ZPREDICT)
-                       if (!(self.enemy.flags & FL_ONGROUND))
-                       if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE)
-                       {
-                               float vz;
-                               prep.z = pre_pos.z;
-                               vz = self.enemy.velocity.z;
-                               for(i = 0; i < impact_time; i += sys_frametime)
-                               {
-                                       vz = vz - (autocvar_sv_gravity * sys_frametime);
-                                       prep.z = prep.z + vz * sys_frametime;
-                               }
-                       }
-                       pre_pos = prep;
-               }
-               else
-                       pre_pos = pre_pos + self.enemy.velocity * mintime;
-    }
-
-    if(self.aim_flags & TFL_AIM_GROUNDGROUND)
-    {
-        //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
-        traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
-        if(trace_fraction != 1.0)
-            pre_pos = trace_endpos;
-    }
-
-    return pre_pos;
-}
diff --git a/qcsrc/server/tturrets/system/system_damage.qc b/qcsrc/server/tturrets/system/system_damage.qc
deleted file mode 100644 (file)
index 45b2e9b..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-* Spawn a boom, trow fake bits arround
-* and hide the real ones.
-*/
-void turret_hide()
-{
-    self.effects   |= EF_NODRAW;
-    self.nextthink = time + self.respawntime - 0.2;
-    self.think     = turret_stdproc_respawn;
-}
-
-void turret_stdproc_die()
-{
-    self.deadflag           = DEAD_DEAD;
-    self.tur_head.deadflag  = self.deadflag;
-
-// Unsolidify and hide real parts
-    self.solid              = SOLID_NOT;
-    self.tur_head.solid     = self.solid;
-
-    self.event_damage           = func_null;
-    self.takedamage             = DAMAGE_NO;
-
-    self.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);
-
-    if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN)
-    {
-        if (self.turret_diehook)
-            self.turret_diehook();
-
-        remove(self.tur_head);
-        remove(self);
-    }
-    else
-    {
-               // Setup respawn
-        self.SendFlags      |= TNSF_STATUS;
-        self.nextthink      = time + 0.2;
-        self.think          = turret_hide;
-
-        if (self.turret_diehook)
-            self.turret_diehook();
-    }
-}
-
-void turret_stdproc_respawn()
-{
-    // 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_stdproc_damage;
-
-    self.avelocity              = '0 0 0';
-    self.tur_head.avelocity     = self.avelocity;
-    self.tur_head.angles        = self.idle_aim;
-    self.health                 = self.tur_health;
-
-    self.enemy                  = world;
-    self.volly_counter          = self.shot_volly;
-    self.ammo                   = self.ammo_max;
-
-    self.nextthink  = time + self.ticrate;
-    self.think      = turret_think;
-
-    self.SendFlags  = TNSF_FULL_UPDATE;
-
-    if (self.turret_respawnhook)
-        self.turret_respawnhook();
-}
-
-/*
-* Standard damage proc.
-*/
-void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
-{
-    // Enougth allready!
-    if(self.deadflag == DEAD_DEAD)
-        return;
-
-    // Inactive turrets take no damage. (hm..)
-    if (!self.active)
-        return;
-
-    if (teamplay)
-    if (self.team == attacker.team)
-    {
-        // This does not happen anymore. Re-enable if you fix that.
-        if(IS_REAL_CLIENT(attacker))
-            sprint(attacker, "\{1}Turret tells you: I'm on your team!\n");
-
-        if(autocvar_g_friendlyfire)
-            damage = damage * autocvar_g_friendlyfire;
-        else
-            return;
-    }
-
-    self.health = self.health - damage;
-
-    // thorw head slightly off aim when hit?
-    if (self.damage_flags & TFL_DMG_HEADSHAKE)
-    {
-        self.tur_head.angles_x = self.tur_head.angles.x + (-0.5 + random()) * damage;
-        self.tur_head.angles_y = self.tur_head.angles.y + (-0.5 + random()) * damage;
-
-        self.SendFlags  |= TNSF_ANG;
-