Clean up turrets system a bit
authorMario <mario.mario@y7mail.com>
Wed, 9 Oct 2013 05:50:09 +0000 (22:50 -0700)
committerMario <mario.mario@y7mail.com>
Wed, 9 Oct 2013 05:50:09 +0000 (22:50 -0700)
76 files changed:
qcsrc/client/Main.qc
qcsrc/client/damage.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/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/server/autocvars.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_assault.qc
qcsrc/server/mutators/gamemode_onslaught.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/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 8e36cc9..6dd9318 100644 (file)
@@ -104,6 +104,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(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
@@ -122,7 +123,6 @@ void CSQC_Init(void)
        Casings_Precache();
        DamageInfo_Precache();
        Vehicles_Precache();
-       turrets_precache();
        Tuba_Precache();
        CSQCPlayer_Precache();
        
index cd7ac7b..3bab63b 100644 (file)
@@ -319,7 +319,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 114f0a5..c69a860 100644 (file)
@@ -34,6 +34,9 @@ autocvars.qh
 ../common/notifications.qh // must be after autocvars
 ../common/deathtypes.qh // must be after notifications
 
+../common/turrets/turrets.qh
+../common/turrets/cl_turrets.qh
+
 damage.qh
 
 ../csqcmodellib/interpolate.qh
@@ -45,8 +48,6 @@ movetypes.qh
 prandom.qh
 bgmscript.qh
 noise.qh
-tturrets.qh
-../server/tturrets/include/turrets_early.qh
 ../server/movelib.qc
 main.qh
 vehicles/vehicles.qh
@@ -117,7 +118,9 @@ command/cl_cmd.qc
 ../warpzonelib/mathlib.qc
 ../warpzonelib/common.qc
 ../warpzonelib/client.qc
-tturrets.qc
+
+../common/turrets/cl_turrets.qc
+../common/turrets/turrets.qc
 
 player_skeleton.qc
 ../common/animdecide.qc
diff --git a/qcsrc/client/tturrets.qc b/qcsrc/client/tturrets.qc
deleted file mode 100644 (file)
index 161bfa3..0000000
+++ /dev/null
@@ -1,685 +0,0 @@
-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(float _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';
-               print(sprintf("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(entity e, entity tagentity, string tagname) setattachment = #443;
-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()
-{
-    float sf;
-    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)
-    {
-        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/client/tturrets.qh b/qcsrc/client/tturrets.qh
deleted file mode 100644 (file)
index cf74f48..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-void ent_turret();
-void turrets_precache();
-.entity tur_head;
index 4ca8b37..d00aa29 100644 (file)
@@ -277,6 +277,59 @@ void GenericCommand_dumpnotifs(float request)
        }
 }
 
+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)
@@ -585,6 +638,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("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") \
        GENERIC_COMMAND("qc_curl", GenericCommand_qc_curl(request, arguments), "Queries a URL") \
index cc7154d..8a35450 100644 (file)
@@ -35,7 +35,7 @@
        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 0214ec3..edcfb81 100644 (file)
@@ -406,7 +406,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"), "") \
@@ -732,7 +732,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..dde5c28
--- /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..224f703
--- /dev/null
@@ -0,0 +1,474 @@
+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((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);
+                                       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';
+               print(sprintf("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(entity e, entity tagentity, string tagname) setattachment = #443;
+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..b616782
--- /dev/null
@@ -0,0 +1 @@
+void ent_turret();
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..ad3a0c7
--- /dev/null
@@ -0,0 +1,29 @@
+// ==========================
+//  Turret Config Generator
+// ==========================
+
+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
diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc
new file mode 100644 (file)
index 0000000..aa15989
--- /dev/null
@@ -0,0 +1,1399 @@
+// =========================
+//  SVQC Turret Properties
+// =========================
+
+
+// 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
+               {
+                       // FIXME: this cant be the best way to do this..
+                       prep = pre_pos;
+
+                       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 not(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_respawn;
+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)
+{
+       // Enougth allready!
+       if(self.deadflag == DEAD_DEAD)
+               return;
+
+       // Inactive turrets take no damage. (hm..)
+       if not (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 -= 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 not (ent.turret_scale_damage)        ent.turret_scale_damage = 1;
+       if not (ent.turret_scale_range)  ent.turret_scale_range  = 1;
+       if not (ent.turret_scale_refire)        ent.turret_scale_refire = 1;
+       if not (ent.turret_scale_ammo)    ent.turret_scale_ammo = 1;
+       if not (ent.turret_scale_aim)      ent.turret_scale_aim  = 1;
+       if not (ent.turret_scale_health)        ent.turret_scale_health = 1;
+       if not (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, 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, 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 not (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.owner == e_turret)
+               return -0.5;
+
+       if not(checkpvs(e_target.origin, e_turret))
+               return -1;
+
+       if not (e_target)
+               return -2;
+
+       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;
+
+       // player
+       if (IS_CLIENT(e_target))
+       {
+               if not (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 not (validate_flags & TFL_TARGETSELECT_MISSILES)
+                       return -10;
+
+       if (validate_flags & TFL_TARGETSELECT_MISSILESONLY)
+               if not (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 not (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 not (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 not(self.aim_flags & TFL_AIM_NO)
+                       self.tur_aimpos = turret_aim_generic();
+
+               // Turn & pitch?
+               if not(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 not(self.aim_flags & TFL_AIM_NO)
+                                       self.tur_aimpos = turret_aim_generic();
+
+                               // Turn & pitch
+                               if not(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 not(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 not(self.aim_flags & TFL_AIM_NO)
+                       self.tur_aimpos = turret_aim_generic();
+
+               // Turn & pitch?
+               if not(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 not(autocvar_g_turrets)
+               return FALSE;
+
+       entity e;
+       entity tur = get_turretinfo(tur_id);
+       if(tur.turretid == 0)
+               return FALSE; // invalid turret
+
+       e = find(world, classname, "turret_manager");
+       if not (e)
+       {
+               e = spawn();
+               e.classname = "turret_manager";
+               e.think = turrets_manager_think;
+               e.nextthink = time + 2;
+       }
+
+       if not(self.spawnflags & TSF_SUSPENDED)
+               builtin_droptofloor();
+
+       self.cvar_basename = tur.cvar_basename;
+       load_unit_settings(self, self.cvar_basename, 0);
+       
+       if not(self.team || teamplay)   { self.team = MAX_SHOT_DISTANCE; }
+       if not(self.ticrate)                    { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
+       if not(self.health)                     { self.health = 1000; }
+       if not(self.shot_refire)                { self.shot_refire = 1; }
+       if not(self.tur_shotorg)                { self.tur_shotorg = '50 0 50'; }
+       if not(self.turret_flags)               { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; }
+       if not(self.damage_flags)               { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; }
+       if not(self.aim_flags)                  { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; }
+       if not(self.track_type)                 { self.track_type = TFL_TRACKTYPE_STEPMOTOR; }
+       if not(self.track_flags)                { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; }
+       if not(self.ammo_flags)                 { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; }
+       if not(self.target_select_flags){ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; }
+       if not(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 not(self.track_accel_pitch)  { self.track_accel_pitch = 0.5; }
+               if not(self.track_accel_rotate)         { self.track_accel_rotate = 0.5; }
+               if not(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 not(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;
+}
\ No newline at end of file
diff --git a/qcsrc/common/turrets/sv_turrets.qh b/qcsrc/common/turrets/sv_turrets.qh
new file mode 100644 (file)
index 0000000..f89ef15
--- /dev/null
@@ -0,0 +1,106 @@
+// 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;
+
+/// 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
diff --git a/qcsrc/common/turrets/targettrigger.qc b/qcsrc/common/turrets/targettrigger.qc
new file mode 100644 (file)
index 0000000..01f2413
--- /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 not(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..01fb660
--- /dev/null
@@ -0,0 +1,79 @@
+#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/terrainbase.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/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();
+       func(TR_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..9f90d3e
--- /dev/null
@@ -0,0 +1,197 @@
+// 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(float id);
+
+// fields:
+.entity tur_head;
+
+// target selection flags
+.float target_select_flags;
+.float target_validate_flags;
+const float TFL_TARGETSELECT_NO = 2; // don't automatically find targets
+const float TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets
+const float TFL_TARGETSELECT_PLAYERS = 8; // target players
+const float TFL_TARGETSELECT_MISSILES = 16; // target projectiles
+const float TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events
+const float TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection
+const float TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range
+const float TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates
+const float TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered
+const float TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates
+const float TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets
+const float TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range
+const float TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles
+
+// aim flags
+.float aim_flags;
+const float TFL_AIM_NO = 1; // no aiming
+const float TFL_AIM_SPLASH = 2; // aim for ground around the target's feet
+const float TFL_AIM_LEAD = 4; // try to predict target movement
+const float TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading
+const float TFL_AIM_ZPREDICT = 16; // predict target's z position at impact
+const float TFL_AIM_SIMPLE = 32; // aim at player's current location
+
+// tracking flags
+.float track_flags;
+const float TFL_TRACK_NO = 2; // don't move head
+const float TFL_TRACK_PITCH = 4; // pitch head
+const float TFL_TRACK_ROTATE = 8; // rotate head
+
+// prefire checks
+.float firecheck_flags;
+const float TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?)
+const float TFL_FIRECHECK_DISTANCES = 8; // another range check
+const float TFL_FIRECHECK_LOS = 16; // line of sight
+const float TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot
+const float TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint
+const float TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot
+const float TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates
+const float TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire
+const float TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt
+const float TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max
+const float TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays
+const float TFL_FIRECHECK_NO = 16384; // no prefire checks
+
+// attack flags
+.float shoot_flags;
+const float TFL_SHOOT_NO = 64; // no attacking
+const float TFL_SHOOT_VOLLY = 2; // fire in vollies
+const float TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost
+const float TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets
+const float TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode)
+const float TFL_SHOOT_CUSTOM = 32; // custom attacking
+
+// turret capabilities
+.float turret_flags;
+const float TUR_FLAG_NONE = 0; // no abilities
+const float TUR_FLAG_SNIPER = 2; // sniping turret
+const float TUR_FLAG_SPLASH = 4; // can deal splash damage
+const float TUR_FLAG_HITSCAN = 8; // hit scan
+const float TUR_FLAG_MULTIGUN = 16; // multiple guns
+const float TUR_FLAG_GUIDED = 32; // laser guided projectiles
+const float TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles
+const float TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles
+const float TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles
+const float TUR_FLAG_PLAYER = 512; // can damage players
+const float TUR_FLAG_MISSILE = 1024; // can damage missiles
+const float TUR_FLAG_SUPPORT = 2048; // supports other units
+const float TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition
+const float TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources
+const float TUR_FLAG_MOVE = 16384; // can move
+const float TUR_FLAG_ROAM = 32768; // roams around if not attacking
+const float TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret
+
+// ammo types
+#define ammo_flags currentammo
+const float TFL_AMMO_NONE = 64; // doesn't use ammo
+const float TFL_AMMO_ENERGY = 2; // uses power
+const float TFL_AMMO_BULLETS = 4; // uses bullets
+const float TFL_AMMO_ROCKETS = 8; // uses explosives
+const float TFL_AMMO_RECHARGE = 16; // regenerates ammo
+const float TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units
+
+// damage flags
+.float damage_flags;
+const float TFL_DMG_NO = 256; // doesn't take damage
+const float TFL_DMG_YES = 2; // can be damaged
+const float TFL_DMG_TEAM = 4; // can be damaged by teammates
+const float TFL_DMG_RETALIATE = 8; // target attackers
+const float TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team
+const float TFL_DMG_TARGETLOSS = 32; // loses target when damaged
+const float TFL_DMG_AIMSHAKE = 64; // damage throws off aim
+const float TFL_DMG_HEADSHAKE = 128; // damage shakes head
+const float TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning
+
+// spawn flags
+const float TSF_SUSPENDED = 1;
+const float TSF_TERRAINBASE = 2; // currently unused
+const float TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration
+const float TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible
+const float TSL_NO_RESPAWN = 16; // don't re-spawn
+const float TSL_ROAM = 32; // roam while idle
+
+// send flags
+const float TNSF_UPDATE       = 2;
+const float TNSF_STATUS       = 4;
+const float TNSF_SETUP        = 8;
+const float TNSF_ANG          = 16;
+const float TNSF_AVEL         = 32;
+const float TNSF_MOVE         = 64;
+.float anim_start_time;
+const float TNSF_ANIM         = 128;
+
+const float TNSF_FULL_UPDATE  = 16777215;
+
+
+// entity properties of turretinfo:
+.float 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 m_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,m_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);
diff --git a/qcsrc/common/turrets/unit/ewheel.qc b/qcsrc/common/turrets/unit/ewheel.qc
new file mode 100644 (file)
index 0000000..f0732e9
--- /dev/null
@@ -0,0 +1,324 @@
+#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")
+);
+
+#define EWHEEL_SETTINGS(turret) \
+       TUR_ADD_CVAR(turret, speed_fast) \
+       TUR_ADD_CVAR(turret, speed_slow) \
+       TUR_ADD_CVAR(turret, speed_slower) \
+       TUR_ADD_CVAR(turret, speed_stop) \
+       TUR_ADD_CVAR(turret, turnrate) 
+       
+
+#ifdef SVQC
+EWHEEL_SETTINGS(ewheel)
+#endif // SVQC
+#else
+#ifdef SVQC
+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, TUR_CVAR(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, TUR_CVAR(ewheel, speed_fast), 0.4);
+               }
+               else if (self.tur_head.spawnshieldtime < 2)
+               {
+
+                       newframe = ewheel_anim_fwd_slow;
+                       movelib_move_simple(v_forward, TUR_CVAR(ewheel, speed_slow), 0.4);
+          }
+               else
+               {
+                       newframe = ewheel_anim_fwd_slow;
+                       movelib_move_simple(v_forward, TUR_CVAR(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, TUR_CVAR(ewheel, speed_slow), 0.4);
+       }
+       else
+       {
+               newframe = ewheel_anim_stop;
+               movelib_beak_simple(TUR_CVAR(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(TUR_CVAR(ewheel, speed_stop));
+}
+
+void spawnfunc_turret_ewheel() { if not(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_LASER, 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 = TUR_CVAR(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(EWHEEL_SETTINGS(ewheel))
+                       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:
+               {
+                       precache_model ("models/turrets/ewheel-base2.md3");
+                       precache_model ("models/turrets/ewheel-gun1.md3");
+                       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..2c81a34
--- /dev/null
@@ -0,0 +1,118 @@
+#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")
+);
+
+#define FLAC_SETTINGS(turret) 
+
+
+#ifdef SVQC
+FLAC_SETTINGS(flac)
+#endif // SVQC
+#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, 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, self.owner.shot_force, self.totalfrags, world);
+#endif
+       remove(self);
+}
+
+void spawnfunc_turret_flac() { if not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(FLAC_SETTINGS(flac))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_flac(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/flac.md3");
+                       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..a4d763f
--- /dev/null
@@ -0,0 +1,131 @@
+#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")
+);
+
+#define FUSIONREACTOR_SETTINGS(turret) 
+
+
+#ifdef SVQC
+FUSIONREACTOR_SETTINGS(fusionreactor)
+#endif // SVQC
+#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 not (self.enemy.ammo_flags & TFL_AMMO_ENERGY)
+               return 0;
+
+       return 1;
+}
+
+void spawnfunc_turret_fusionreactor() { if not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(FUSIONREACTOR_SETTINGS(fusionreactor))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_fusionreactor(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/reactor.md3");
+                       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..1f94192
--- /dev/null
@@ -0,0 +1,174 @@
+#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")
+);
+
+#define HELLION_SETTINGS(turret) \
+       TUR_ADD_CVAR(turret, shot_speed_gain) \
+       TUR_ADD_CVAR(turret, shot_speed_max) 
+
+
+#ifdef SVQC
+HELLION_SETTINGS(hellion)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+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) * TUR_CVAR(hellion, shot_speed_gain), TUR_CVAR(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) * TUR_CVAR(hellion, shot_speed_gain), TUR_CVAR(hellion, shot_speed_max));
+
+       if (itime < 0.05)
+               self.think = turret_projectile_explode;
+
+       UpdateCSQCProjectile(self);
+}
+
+void spawnfunc_turret_hellion() { if not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(HELLION_SETTINGS(hellion))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hellion(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/hellion.md3");
+                       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..6c4e5d3
--- /dev/null
@@ -0,0 +1,375 @@
+#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")
+);
+
+#define HK_SETTINGS(turret) \
+       TUR_ADD_CVAR(turret, shot_speed) \
+       TUR_ADD_CVAR(turret, shot_speed_accel) \
+       TUR_ADD_CVAR(turret, shot_speed_accel2) \
+       TUR_ADD_CVAR(turret, shot_speed_decel) \
+       TUR_ADD_CVAR(turret, shot_speed_max) \
+       TUR_ADD_CVAR(turret, shot_speed_turnrate) 
+
+
+#ifdef SVQC
+HK_SETTINGS(hk)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+//#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 > TUR_CVAR(hk, shot_speed)) )
+                       myspeed = max(myspeed * TUR_CVAR(hk, shot_speed_decel), TUR_CVAR(hk, shot_speed));
+
+               // Failry clear, accelerate.
+               if ( (ff > 0.7) && (myspeed < TUR_CVAR(hk, shot_speed_max)) )
+                       myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel), TUR_CVAR(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 < TUR_CVAR(hk, shot_speed_max))
+                       myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel2),TUR_CVAR(hk, shot_speed_max));
+
+               wishdir = ve;
+       }
+
+       if ((myspeed > TUR_CVAR(hk, shot_speed)) && (self.cnt > time))
+               myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel2),TUR_CVAR(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 * TUR_CVAR(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 not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(HK_SETTINGS(hk))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hk(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/hk.md3");
+                       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..9481483
--- /dev/null
@@ -0,0 +1,97 @@
+#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")
+);
+
+#define MACHINEGUN_SETTINGS(turret) 
+
+
+#ifdef SVQC
+MACHINEGUN_SETTINGS(machinegun)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+void spawnfunc_turret_machinegun() { if not(turret_initialize(TUR_MACHINEGUN)) remove(self); }
+
+float t_machinegun(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0, 1, autocvar_g_balance_uzi_bulletconstant);
+                       endFireBallisticBullet();
+
+                       UziFlash();
+                       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;
+                       
+                       if not(autocvar_g_antilag_bullets)
+                               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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(MACHINEGUN_SETTINGS(machinegun))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_machinegun(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/machinegun.md3");
+                       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..2bc26ab
--- /dev/null
@@ -0,0 +1,105 @@
+#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")
+);
+
+#define MLRS_SETTINGS(turret) 
+
+
+#ifdef SVQC
+MLRS_SETTINGS(mlrs)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+void spawnfunc_turret_mlrs() { if not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(MLRS_SETTINGS(mlrs))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_mlrs(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/mlrs.md3");
+                       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..a36fa54
--- /dev/null
@@ -0,0 +1,188 @@
+#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")
+);
+
+#define PHASER_SETTINGS(turret) 
+
+
+#ifdef SVQC
+PHASER_SETTINGS(phaser)
+#endif // SVQC
+#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 not(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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(PHASER_SETTINGS(phaser))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_phaser(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/phaser.md3");
+                       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..670efd2
--- /dev/null
@@ -0,0 +1,142 @@
+#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")
+);
+
+#define PLASMA_SETTINGS(turret) 
+
+
+#ifdef SVQC
+PLASMA_SETTINGS(plasma)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+void spawnfunc_turret_plasma() { if not(turret_initialize(TUR_PLASMA)) remove(self); }
+
+float t_plasma(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       if(g_minstagib)
+                       {
+                               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;
+                               v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+                               if(teamplay)
+                               {
+                                       switch(self.team)
+                                       {
+                                               case NUM_TEAM_1:   // Red
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_2:   // Blue
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_3:   // Yellow
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_4:   // Pink
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v);
+                                                       break;
+                                       }
+                               }
+                               else
+                                       WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), 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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(PLASMA_SETTINGS(plasma))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/plasma.md3");
+                       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..5842478
--- /dev/null
@@ -0,0 +1,140 @@
+#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")
+);
+
+#define PLASMA_DUAL_SETTINGS(turret) 
+
+
+#ifdef SVQC
+PLASMA_DUAL_SETTINGS(plasma_dual)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+void spawnfunc_turret_plasma_dual() { if not(turret_initialize(TUR_PLASMA_DUAL)) remove(self); }
+
+float t_plasma_dual(float req)
+{
+       switch(req)
+       {
+               case TR_ATTACK:
+               {
+                       if(g_minstagib)
+                       {
+                               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;
+                               v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+                               if(teamplay)
+                               {
+                                       switch(self.team)
+                                       {
+                                               case NUM_TEAM_1:   // Red
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_2:   // Blue
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_3:   // Yellow
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v);
+                                                       break;
+                                               case NUM_TEAM_4:   // Pink
+                                                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v);
+                                                       break;
+                                       }
+                               }
+                               else
+                                       WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), 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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(PLASMA_DUAL_SETTINGS(plasma_dual))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma_dual(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/base.md3");
+                       precache_model ("models/turrets/plasmad.md3");
+                       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..23171c0
--- /dev/null
@@ -0,0 +1,232 @@
+#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")
+);
+
+#define TESLA_SETTINGS(turret) 
+
+
+#ifdef SVQC
+TESLA_SETTINGS(tesla)
+#endif // SVQC
+#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 not (turret_firecheck())
+               return 0;
+
+       if(self.enemy)
+               return 1;
+
+       return 0;
+}
+
+void spawnfunc_turret_tesla() { if not(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 not (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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(TESLA_SETTINGS(tesla))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_tesla(float req)
+{
+       switch(req)
+       {
+               case TR_SETUP:
+               {
+                       return TRUE;
+               }
+               case TR_PRECACHE:
+               {
+                       precache_model ("models/turrets/tesla_base.md3");
+                       precache_model ("models/turrets/tesla_head.md3");
+                       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..267214f
--- /dev/null
@@ -0,0 +1,715 @@
+#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")
+);
+
+#define WALKER_SETTINGS(turret) \
+       TUR_ADD_CVAR(turret, melee_damage) \
+       TUR_ADD_CVAR(turret, melee_force) \
+       TUR_ADD_CVAR(turret, melee_range) \
+       TUR_ADD_CVAR(turret, rocket_damage) \
+       TUR_ADD_CVAR(turret, rocket_radius) \
+       TUR_ADD_CVAR(turret, rocket_force) \
+       TUR_ADD_CVAR(turret, rocket_speed) \
+       TUR_ADD_CVAR(turret, rocket_range) \
+       TUR_ADD_CVAR(turret, rocket_range_min) \
+       TUR_ADD_CVAR(turret, rocket_refire) \
+       TUR_ADD_CVAR(turret, rocket_turnrate) \
+       TUR_ADD_CVAR(turret, speed_stop) \
+       TUR_ADD_CVAR(turret, speed_walk) \
+       TUR_ADD_CVAR(turret, speed_run) \
+       TUR_ADD_CVAR(turret, speed_jump) \
+       TUR_ADD_CVAR(turret, speed_swim) \
+       TUR_ADD_CVAR(turret, speed_roam) \
+       TUR_ADD_CVAR(turret, turn) \
+       TUR_ADD_CVAR(turret, turn_walk) \
+       TUR_ADD_CVAR(turret, turn_strafe) \
+       TUR_ADD_CVAR(turret, turn_swim) \
+       TUR_ADD_CVAR(turret, turn_run) 
+
+
+#ifdef SVQC
+WALKER_SETTINGS(walker)
+#endif // SVQC
+#else
+#ifdef SVQC
+
+const float walker_anim_stop = 0;
+const float walker_anim_turn = 1;
+const float walker_anim_walk = 2;
+const float walker_anim_run = 3;
+const float walker_anim_strafeleft = 4;
+const float walker_anim_straferight = 5;
+const float walker_anim_jump = 6;
+const float walker_anim_land = 7;
+const float walker_anim_pain = 8;
+const float walker_anim_melee = 9;
+const float walker_anim_swim = 10;
+const float walker_anim_roam = 11;
+
+.float animflag;
+.float idletime;
+
+#define WALKER_PATH(s,e) pathlib_astar(s,e)
+
+float walker_firecheck()
+{
+       if (self.animflag == walker_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, TUR_CVAR(walker, melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * TUR_CVAR(walker, melee_force));
+
+               e = e.chain;
+       }
+}
+
+void walker_setnoanim()
+{
+       turrets_setframe(walker_anim_stop, FALSE);
+       self.animflag = self.frame;
+}
+void walker_rocket_explode()
+{
+       RadiusDamage (self, self.owner, TUR_CVAR(walker, rocket_damage), 0, TUR_CVAR(walker, rocket_radius), self, TUR_CVAR(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, TUR_CVAR(walker, rocket_speed), TUR_CVAR(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)) * TUR_CVAR(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 = walker_anim_run;
+               else
+                       self.animflag = walker_anim_walk;
+       case WATERLEVEL_WETFEET:
+       case WATERLEVEL_SWIMMING:
+               if (self.animflag != walker_anim_swim)
+                       self.animflag = walker_anim_walk;
+               else
+                       self.animflag = walker_anim_swim;
+               break;
+       case WATERLEVEL_SUBMERGED:
+               self.animflag = walker_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 not(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);
+                       fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0, 1, autocvar_g_balance_uzi_bulletconstant);
+                       endFireBallisticBullet();
+                       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 != walker_anim_stop)
+                                               {
+                                                       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 = walker_anim_stop;
+                                                       }
+                                                       else
+                                                       {
+                                                               self.animflag = walker_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 < TUR_CVAR(walker, melee_range) && self.animflag != walker_anim_melee)
+                               {
+                                       vector wish_angle;
+
+                                       wish_angle = angleofs(self, self.enemy);
+                                       if (self.animflag != walker_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 = walker_anim_melee;
+                                       }
+                               }
+                               else if (self.tur_head.attack_finished_single < time)
+                               {
+                                       if(self.tur_head.shot_volly)
+                                       {
+                                               self.animflag = walker_anim_stop;
+
+                                               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 + TUR_CVAR(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 > TUR_CVAR(walker, rocket_range_min))
+                                               if (self.tur_dist_enemy < TUR_CVAR(walker, rocket_range))
+                                                       self.tur_head.shot_volly = 4;
+                                       }
+                               }
+                               else
+                               {
+                                       if (self.animflag != walker_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 walker_anim_stop:
+                                               movelib_beak_simple(TUR_CVAR(walker, speed_stop));
+                                               break;
+
+                                       case walker_anim_turn:
+                                               turny = TUR_CVAR(walker, turn);
+                                               movelib_beak_simple(TUR_CVAR(walker, speed_stop));
+                                               break;
+
+                                       case walker_anim_walk:
+                                               turny = TUR_CVAR(walker, turn_walk);
+                                               movelib_move_simple(v_forward, TUR_CVAR(walker, speed_walk), 0.6);
+                                               break;
+
+                                       case walker_anim_run:
+                                               turny = TUR_CVAR(walker, turn_run);
+                                               movelib_move_simple(v_forward, TUR_CVAR(walker, speed_run), 0.6);
+                                               break;
+
+                                       case walker_anim_strafeleft:
+                                               turny = TUR_CVAR(walker, turn_strafe);
+                                               movelib_move_simple(v_right * -1, TUR_CVAR(walker, speed_walk), 0.8);
+                                               break;
+
+                                       case walker_anim_straferight:
+                                               turny = TUR_CVAR(walker, turn_strafe);
+                                               movelib_move_simple(v_right, TUR_CVAR(walker, speed_walk), 0.8);
+                                               break;
+
+                                       case walker_anim_jump:
+                                               self.velocity += '0 0 1' * TUR_CVAR(walker, speed_jump);
+                                               break;
+
+                                       case walker_anim_land:
+                                               break;
+
+                                       case walker_anim_pain:
+                                               if(self.frame != walker_anim_pain)
+                                                       defer(0.25, walker_setnoanim);
+
+                                               break;
+
+                                       case walker_anim_melee:
+                                               if(self.frame != walker_anim_melee)
+                                               {
+                                                       defer(0.41, walker_setnoanim);
+                                                       defer(0.21, walker_melee_do_dmg);
+                                               }
+
+                                               movelib_beak_simple(TUR_CVAR(walker, speed_stop));
+                                               break;
+
+                                       case walker_anim_swim:
+                                               turny = TUR_CVAR(walker, turn_swim);
+                                               turnx = TUR_CVAR(walker, turn_swim);
+
+                                               self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
+                                               movelib_move_simple(v_forward, TUR_CVAR(walker, speed_swim), 0.3);
+                                               vz = self.velocity_z + sin(time * 4) * 8;
+                                               break;
+
+                                       case walker_anim_roam:
+                                               turny = TUR_CVAR(walker, turn_walk);
+                                               movelib_move_simple(v_forward ,TUR_CVAR(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;
+
+                       if (autocvar_g_antilag_bullets)
+                               self.turret_flags |= TUR_FLAG_HITSCAN;
+                       else
+                               self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE;
+                               
+                       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;
+               }
+               case TR_CONFIG:
+               {
+                       TUR_CONFIG_SETTINGS(WALKER_SETTINGS(walker))
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+
+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:
+               {
+                       precache_model ("models/turrets/walker_body.md3");
+                       precache_model ("models/turrets/walker_head_minigun.md3");
+                       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..6de5902
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+* 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.
+*/
+#define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward)
+float turret_tag_fire_update_s()
+{
+       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
index 759aea6..cba83c6 100644 (file)
@@ -1000,41 +1000,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 c01cddd..f34f452 100644 (file)
@@ -732,7 +732,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)
+                       if(IS_PLAYER(victim) || victim.turret_flags & TUR_FLAG_ISTURRET)
                        {
                                if(DIFF_TEAM(victim, attacker))
                                {
index 6853219..baaaa37 100644 (file)
@@ -544,6 +544,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(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
@@ -592,6 +593,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(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
index 0b5b427..7a6f972 100644 (file)
@@ -1298,11 +1298,6 @@ void precache()
     // gamemode related things
     precache_model ("models/misc/chatbubble.spr");
 
-#ifdef TTURRETS_ENABLED
-    if (autocvar_g_turrets)
-        turrets_precash();
-#endif
-
     // Precache all player models if desired
     if (autocvar_sv_precacheplayermodels)
     {
index 94e1047..4a6308a 100644 (file)
@@ -170,7 +170,6 @@ void assault_roundstart_use()
        activator = self;
        SUB_UseTargets();
 
-#ifdef TTURRETS_ENABLED
        entity ent, oldself;
 
        //(Re)spawn all turrets
@@ -186,12 +185,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 b5b6594..31a7fce 100644 (file)
@@ -1644,11 +1644,34 @@ 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_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(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
index e9e9a4b..5b86e07 100644 (file)
@@ -34,6 +34,11 @@ defs.qh              // Should rename this, it has fields and globals
 ../common/notifications.qh // must be after autocvars
 ../common/deathtypes.qh // must be after notifications
 
+../common/turrets/config.qh
+../common/turrets/turrets.qh
+../common/turrets/sv_turrets.qh
+../common/turrets/util.qc
+
 mutators/base.qh
 mutators/mutators.qh
 mutators/gamemode_assault.qh
@@ -48,8 +53,7 @@ mutators/gamemode_lms.qh
 mutators/mutator_dodging.qh
 mutators/mutator_nades.qh
 
-//// tZork Turrets ////
-tturrets/include/turrets_early.qh
+//// tZork Vehicles ////
 vehicles/vehicles_def.qh
 
 campaign.qh
@@ -195,8 +199,7 @@ t_quake.qc
 race.qc
 
 
-//// tZork Turrets ////
-tturrets/include/turrets.qh
+//// tZork Vehicles ////
 vehicles/vehicles.qh
 
 scores.qc
@@ -226,6 +229,12 @@ round_handler.qc
 
 ../common/explosion_equation.qc
 
+../common/turrets/sv_turrets.qc
+../common/turrets/config.qc
+../common/turrets/turrets.qc
+../common/turrets/checkpoint.qc
+../common/turrets/targettrigger.qc
+
 mutators/base.qc
 mutators/gamemode_assault.qc
 mutators/gamemode_arena.qc
index d61911e..65131f8 100644 (file)
@@ -209,7 +209,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 6093641..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#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
diff --git a/qcsrc/server/tturrets/include/turrets_early.qh b/qcsrc/server/tturrets/include/turrets_early.qh
deleted file mode 100644 (file)
index 4ce95fc..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-// 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
-#define TSF_SUSPENDED     1
-/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces
-#define TSF_TERRAINBASE   2
-/// Disable builtin ammo regeneration
-#define TSF_NO_AMMO_REGEN 4
-/// Dont break path to chase enemys. will still fire at them if possible.
-#define TSF_NO_PATHBREAK  8
-/// Dont respawn
-#define TSL_NO_RESPAWN    16
-/// Let this turret roam when idle.
-#define TSL_ROAM          32
-
-/// target selection flags
-.float target_select_flags;
-/// target validatoin flags
-.float target_validate_flags;
-/// Dont select a target on its own.
-#define TFL_TARGETSELECT_NO            2
-/// Need line of sight
-#define TFL_TARGETSELECT_LOS           4
-/// Players are valid targets
-#define TFL_TARGETSELECT_PLAYERS       8
-/// Missiles are valid targets
-#define TFL_TARGETSELECT_MISSILES      16
-/// Responds to turret_trigger_target events
-#define TFL_TARGETSELECT_TRIGGERTARGET 32
-/// Angular limitations of turret head limits target selection
-#define TFL_TARGETSELECT_ANGLELIMITS   64
-/// Range limits apply in targetselection
-#define TFL_TARGETSELECT_RANGELIMTS    128
-/// DOnt select targets with a .team matching its own
-#define TFL_TARGETSELECT_TEAMCHECK     256
-/// Cant select targets on its own. needs to be triggerd or slaved.
-#define TFL_TARGETSELECT_NOBUILTIN     512
-/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team)
-#define TFL_TARGETSELECT_OWNTEAM       1024
-/// Turrets aren't valid targets
-#define TFL_TARGETSELECT_NOTURRETS     2048
-/// Use feild of view
-#define TFL_TARGETSELECT_FOV           4096
-
-#define TFL_TARGETSELECT_MISSILESONLY  8192
-
-/// aim flags
-.float aim_flags;
-/// Dont aim.
-#define TFL_AIM_NO                  1
-/// Go for ground, not direct hit, but only if target is on ground.
-#define TFL_AIM_GROUNDGROUND        2
-/// Try to predict target movement (does not account for gravity)
-#define TFL_AIM_LEAD                4
-/// Compensate for shot traveltime when lead
-#define TFL_AIM_SHOTTIMECOMPENSATE  8
-/// Try to do real prediction of targets z pos at impact.
-#define TFL_AIM_ZPREDICT            16
-/// Simply aim at target's current location
-#define TFL_AIM_SIMPLE              32
-
-/// track (turn and pitch head) flags
-.float track_flags;
-/// Dont move head
-#define TFL_TRACK_NO    2
-/// Pitch the head
-#define TFL_TRACK_PITCH 4
-/// Rotate the head
-#define TFL_TRACK_ROT   8
-
-/// How tracking is preformed
-.float track_type;
-/// Hard angle increments. Ugly for fast turning, best accuracy.
-#define TFL_TRACKTYPE_STEPMOTOR    1
-/// Smoth absolute movement. Looks ok, fair accuracy.
-#define TFL_TRACKTYPE_FLUIDPRECISE 2
-/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the fields below are set
-#define 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
-#define TFL_FIRECHECK_DEAD        4
-/// Range limits apply
-#define TFL_FIRECHECK_DISTANCES   8
-/// Line Of Sight needs to be clear
-#define TFL_FIRECHECK_LOS         16
-/// Consider distance inpactpoint<->aimspot
-#define TFL_FIRECHECK_AIMDIST     32
-/// Consider enemy origin<->impactpoint
-#define TFL_FIRECHECK_REALDIST    64
-/// Consider angular diff head<->aimspot
-#define TFL_FIRECHECK_ANGLEDIST  128
-/// (re)consider target.team<->self.team
-#define TFL_FIRECHECK_TEAMCECK   256
-/// Try to avoid friendly fire
-#define TFL_FIRECHECK_AFF        512
-/// Own .ammo needs to be >= then own .shot_dmg
-#define TFL_FIRECHECK_OWM_AMMO   1024
-/// Others ammo need to be < others .ammo_max
-#define TFL_FIRECHECK_OTHER_AMMO 2048
-/// Check own .attack_finished_single vs time
-#define 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
-#define TFL_FIRECHECK_NO         16384
-
-/// How shooting is done
-.float shoot_flags;
-/// Dont shoot
-#define  TFL_SHOOT_NO          64
-/// Fire in vollys (partial implementation through .shot_volly)
-#define  TFL_SHOOT_VOLLY       2
-/// Always do a full volly, even if target is lost or dead. (not implemented)
-#define  TFL_SHOOT_VOLLYALWAYS 4
-/// Loop though all valid tarters, and hit them.
-#define  TFL_SHOOT_HITALLVALID 8
-/// Fiering makes unit loose target (after volly is done, if in volly mode)
-#define  TFL_SHOOT_CLEARTARGET 16
-///Custom shooting;
-#define  TFL_SHOOT_CUSTOM 32
-
-/// Information aboute the units capabilities
-.float turrcaps_flags;
-/// No kown capabilities
-#define  TFL_TURRCAPS_NONE        0
-/// Capable of sniping
-#define  TFL_TURRCAPS_SNIPER      2
-/// Capable of splasdamage
-#define  TFL_TURRCAPS_RADIUSDMG   4
-/// Has one or more cannons with zero shot traveltime
-#define  TFL_TURRCAPS_HITSCAN     8
-/// More then one (type of) gun
-#define  TFL_TURRCAPS_MULTIGUN    16
-/// Carries at least one guided weapon
-#define  TFL_TURRCAPS_GUIDED      32
-/// At least one gun fiers slow projectiles
-#define  TFL_TURRCAPS_SLOWPROJ    64
-/// At least one gun fiers medium speed projectiles
-#define  TFL_TURRCAPS_MEDPROJ     128
-/// At least one gun fiers fast projectiles
-#define  TFL_TURRCAPS_FASTPROJ    256
-/// At least one gun capable of damaging players
-#define  TFL_TURRCAPS_PLAYERKILL  512
-/// At least one gun that can shoot town missiles
-#define  TFL_TURRCAPS_MISSILEKILL 1024
-/// Has support capabilities. powerplants and sutch.
-#define  TFL_TURRCAPS_SUPPORT     2048
-/// Proveides at least one type of ammmo
-#define  TFL_TURRCAPS_AMMOSOURCE  4096
-/// Can recive targets from external sources
-#define TFL_TURRCAPS_RECIVETARGETS 8192
-/// Capable of self-transport
-#define TFL_TURRCAPS_MOVE 16384
-/// Will roam arround even if not chasing anyting
-#define TFL_TURRCAPS_ROAM 32768
-#define TFL_TURRCAPS_ISTURRET 65536
-
-/// Ammo types needed and/or provided
-//.float ammo_flags;
-#define ammo_flags currentammo
-/// Has and needs no ammo
-#define  TFL_AMMO_NONE     64
-/// Uses power
-#define  TFL_AMMO_ENERGY   2
-/// Uses bullets
-#define  TFL_AMMO_BULLETS  4
-/// Uses explosives
-#define  TFL_AMMO_ROCKETS  8
-/// Regenerates ammo on its own
-#define  TFL_AMMO_RECHARGE 16
-/// Can recive ammo from others
-#define  TFL_AMMO_RECIVE   32
-
-/// How incomming damage is handeld
-.float damage_flags;
-/// Cant be hurt
-#define  TFL_DMG_NO              256
-/// Can be damaged
-#define  TFL_DMG_YES             2
-/// Can be damaged  by teammates
-#define  TFL_DMG_TAKEFROMTEAM    4
-/// Traget attackers
-#define  TFL_DMG_RETALIATE       8
-/// Target attackers, even is on own team
-#define  TFL_DMG_RETALIATEONTEAM 16
-/// Loses target when damaged
-#define  TFL_DMG_TARGETLOSS      32
-/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented.
-#define  TFL_DMG_AIMSHAKE        64
-/// Reciving damage slaps the head arround
-#define  TFL_DMG_HEADSHAKE       128
-/// Die and stay dead.
-#define  TFL_DMG_DEATH_NORESPAWN 256
-
-// Spawnflags
-/// Spawn in teambased modes
-#define TFL_SPAWN_TEAM      2
-/// Spawn in FFA modes
-#define 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
-.float turret_type;
-const float TID_COMMON        = 1;
-const float TID_EWHEEL        = 2;
-const float TID_FLAC          = 3;
-const float TID_FUSION        = 4;
-const float TID_HELLION       = 5;
-const float TID_HK            = 6;
-const float TID_MACHINEGUN    = 7;
-const float TID_MLRS          = 8;
-const float TID_PHASER        = 9;
-const float TID_PLASMA        = 10;
-const float TID_PLASMA_DUAL   = 11;
-const float TID_TESLA         = 12;
-const float TID_WALKER        = 13;
-const float TID_LAST          = 13;
-
-const float TNSF_UPDATE       = 2;
-const float TNSF_STATUS       = 4;
-const float TNSF_SETUP        = 8;
-const float TNSF_ANG          = 16;
-const float TNSF_AVEL         = 32;
-const float TNSF_MOVE         = 64;
-.float anim_start_time;
-const float TNSF_ANIM         = 128;
-
-const float TNSF_FULL_UPDATE  = 16777215;
-
-#endif // TTURRETS_ENABLED
-
-
diff --git a/qcsrc/server/tturrets/system/system_aimprocs.qc b/qcsrc/server/tturrets/system/system_aimprocs.qc
deleted file mode 100644 (file)
index 675b5de..0000000
+++ /dev/null
@@ -1,73 +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, impact_time, 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;
-#ifdef GMQCC
-                       impact_time = 0;
-#endif
-                       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 not(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 4145a6e..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;