Merge branch 'master' into TimePath/unified_weapons
authorTimePath <andrew.hardaker1995@gmail.com>
Tue, 6 Oct 2015 01:41:18 +0000 (12:41 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Tue, 6 Oct 2015 01:41:24 +0000 (12:41 +1100)
# Conflicts:
# qcsrc/common/nades.qc
# qcsrc/common/vehicles/sv_vehicles.qc
# qcsrc/common/vehicles/vehicle/bumblebee.qc
# qcsrc/common/vehicles/vehicle/spiderbot.qc

146 files changed:
defaultXonotic.cfg
qcsrc/client/damage.qc
qcsrc/client/hud.qc
qcsrc/client/main.qc
qcsrc/client/progs.inc
qcsrc/client/scoreboard.qc
qcsrc/client/view.qc
qcsrc/common/animdecide.qc
qcsrc/common/models/models.inc
qcsrc/common/monsters/all.qc
qcsrc/common/monsters/all.qh
qcsrc/common/monsters/monster.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/wyvern.qc
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/itemstime.qc
qcsrc/common/turrets/all.inc
qcsrc/common/turrets/all.qc
qcsrc/common/turrets/all.qh
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/turrets/cl_turrets.qh
qcsrc/common/turrets/config.qc
qcsrc/common/turrets/sv_turrets.qc
qcsrc/common/turrets/sv_turrets.qh
qcsrc/common/turrets/turret.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/ewheel.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/ewheel_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/flac.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/flac_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/fusionreactor.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/hellion.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/hellion_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/hk.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/hk_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/machinegun.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/machinegun_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/mlrs.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/mlrs_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/phaser.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/phaser_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/plasma.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/plasma_dual.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/plasma_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/tesla.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/tesla_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/walker.qc [new file with mode: 0644]
qcsrc/common/turrets/turret/walker_weapon.qc [new file with mode: 0644]
qcsrc/common/turrets/unit/ewheel.qc [deleted file]
qcsrc/common/turrets/unit/flac.qc [deleted file]
qcsrc/common/turrets/unit/fusionreactor.qc [deleted file]
qcsrc/common/turrets/unit/hellion.qc [deleted file]
qcsrc/common/turrets/unit/hk.qc [deleted file]
qcsrc/common/turrets/unit/machinegun.qc [deleted file]
qcsrc/common/turrets/unit/mlrs.qc [deleted file]
qcsrc/common/turrets/unit/phaser.qc [deleted file]
qcsrc/common/turrets/unit/plasma.qc [deleted file]
qcsrc/common/turrets/unit/plasma_dual.qc [deleted file]
qcsrc/common/turrets/unit/tesla.qc [deleted file]
qcsrc/common/turrets/unit/walker.qc [deleted file]
qcsrc/common/vehicles/all.inc
qcsrc/common/vehicles/all.qc
qcsrc/common/vehicles/all.qh
qcsrc/common/vehicles/cl_vehicles.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/unit/bumblebee.qc [deleted file]
qcsrc/common/vehicles/unit/bumblebee.qh [deleted file]
qcsrc/common/vehicles/unit/racer.qc [deleted file]
qcsrc/common/vehicles/unit/raptor.qc [deleted file]
qcsrc/common/vehicles/unit/raptor.qh [deleted file]
qcsrc/common/vehicles/unit/spiderbot.qc [deleted file]
qcsrc/common/vehicles/vehicle.qh
qcsrc/common/vehicles/vehicle/bumblebee.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/bumblebee.qh [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/racer.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/racer_weapon.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/raptor.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/raptor.qh [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/raptor_weapons.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/spiderbot.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc [new file with mode: 0644]
qcsrc/common/weapons/all.inc
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/config.qc
qcsrc/common/weapons/weapon.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/blaster.qc
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/fireball.qc
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/hlac.qc
qcsrc/common/weapons/weapon/hmg.qc
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/rpc.qc
qcsrc/common/weapons/weapon/seeker.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/lib/cvar.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_hook.qh
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/gamemode_invasion.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/gamemode_nexball_weapon.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_hook.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_instagib.qc
qcsrc/server/mutators/mutator_melee_only.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/mutators/mutator_nix.qc
qcsrc/server/mutators/mutator_overkill.qc
qcsrc/server/mutators/mutators_include.qc
qcsrc/server/mutators/mutators_include.qh
qcsrc/server/progs.inc
qcsrc/server/t_items.qc
qcsrc/server/teamplay.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/spawning.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/weapons/weaponsystem.qh
qcsrc/warpzonelib/common.qc
weapons.cfg [deleted file]

index 338808f..c51dfc1 100644 (file)
@@ -640,7 +640,6 @@ alias weapon_group_7 "impulse 7"
 alias weapon_group_8 "impulse 8"
 alias weapon_group_9 "impulse 9"
 alias weapon_group_0 "impulse 14" // cycles the superweapons
-exec weapons.cfg
 
 // score log
 set sv_logscores_console 0     "print scores to server console"
index 4f25aef..7ef7f99 100644 (file)
@@ -355,6 +355,8 @@ void Ent_DamageInfo(float isNew)
                        w_backoff = -1 * normalize(force);
                setorigin(self, w_org + w_backoff * 2); // for sound() calls
 
-               if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) { WEP_ACTION(hitwep, WR_IMPACTEFFECT); }
+               if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) {
+                       Weapon w = get_weaponinfo(hitwep); w.wr_impacteffect(w);
+               }
        }
 }
index 9a0f87b..a47ccd7 100644 (file)
@@ -441,7 +441,7 @@ void HUD_Weapons(void)
                if(cvar("wep_add"))
                {
                        weapons_stat = '0 0 0';
-                       float countw = 1 + floor((floor(time * cvar("wep_add"))) % WEP_COUNT);
+                       float countw = 1 + floor((floor(time * cvar("wep_add"))) % (WEP_COUNT - 1));
                        for(i = WEP_FIRST; i <= countw; ++i)
                                weapons_stat |= WepSet_FromWeapon(i);
                }
@@ -472,7 +472,15 @@ void HUD_Weapons(void)
                vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
 
                // get the all-weapons layout
-               vector table_size = HUD_GetTableSize_BestItemAR(WEP_COUNT, padded_panel_size, aspect);
+               int nHidden = 0;
+               WepSet weapons_stat = WepSet_GetFromStat();
+               for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
+                       WepSet weapons_wep = WepSet_FromWeapon(i);
+                       if (weapons_stat & weapons_wep) continue;
+                       Weapon w = get_weaponinfo(i);
+                       if (w.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1;
+               }
+               vector table_size = HUD_GetTableSize_BestItemAR((WEP_COUNT - 1) - nHidden, padded_panel_size, aspect);
                columns = table_size.x;
                rows = table_size.y;
                weapon_size.x = padded_panel_size.x / columns;
@@ -527,7 +535,7 @@ void HUD_Weapons(void)
                        panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
        }
        else
-               weapon_count = WEP_COUNT;
+               weapon_count = (WEP_COUNT - 1);
 
        // animation for fading in/out the panel respectively when not in use
        if(!autocvar__hud_configure)
@@ -632,7 +640,7 @@ void HUD_Weapons(void)
 
        if(!rows) // if rows is > 0 onlyowned code has already updated these vars
        {
-               vector table_size = HUD_GetTableSize_BestItemAR(WEP_COUNT, panel_size, aspect);
+               vector table_size = HUD_GetTableSize_BestItemAR((WEP_COUNT - 1), panel_size, aspect);
                columns = table_size.x;
                rows = table_size.y;
                weapon_size.x = panel_size.x / columns;
index 898b10e..8ba551d 100644 (file)
@@ -146,7 +146,6 @@ void CSQC_Init(void)
 
        // needs to be done so early because of the constants they create
        static_init();
-       CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
        CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
index 40a745a..39faa98 100644 (file)
 #include "../common/items/all.qc"
 #include "../common/monsters/all.qc"
 #include "../common/mutators/all.qc"
+#include "../common/turrets/all.qc"
 #include "../common/vehicles/all.qc"
 #include "../common/weapons/all.qc"
 
 #include "../common/turrets/cl_turrets.qc"
-#include "../common/turrets/all.qc"
 
 #include "../common/triggers/include.qc"
 
index 407c087..3c331fd 100644 (file)
@@ -994,13 +994,13 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
                        ++disownedcnt;
        }
 
-       int weapon_cnt = WEP_COUNT - disownedcnt;
+       int weapon_cnt = (WEP_COUNT - 1) - disownedcnt;
 
        if(weapon_cnt <= 0)
                return pos;
 
        int rows;
-       if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
+       if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((WEP_COUNT - 1) * 0.5))
                rows = 2;
        else
                rows = 1;
index 26089f9..60fb4b0 100644 (file)
@@ -321,6 +321,7 @@ float TrueAimCheck()
        {
                case WEP_TUBA.m_id: // no aim
                case WEP_PORTO.m_id: // shoots from eye
+               case WEP_NEXBALL.m_id: // shoots from eye
                case WEP_HOOK.m_id: // no trueaim
                case WEP_MORTAR.m_id: // toss curve
                        return SHOTTYPE_HITWORLD;
@@ -440,7 +441,7 @@ bool WantEventchase()
                        return true;
                if(MUTATOR_CALLHOOK(WantEventchase, self))
                        return true;
-               if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_PORTO.m_id)))
+               if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_NEXBALL.m_id)))
                        return true;
                if(autocvar_cl_eventchase_death && (getstati(STAT_HEALTH) <= 0))
                {
@@ -1531,6 +1532,7 @@ void CSQC_UpdateView(float w, float h)
 
        if(autocvar_cl_reticle)
        {
+               Weapon wep = get_weaponinfo(activeweapon);
                // Draw the aiming reticle for weapons that use it
                // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use
                // It must be a persisted float for fading out to work properly (you let go of the zoom button for
@@ -1540,7 +1542,7 @@ void CSQC_UpdateView(float w, float h)
                        // no zoom reticle while dead
                        reticle_type = 0;
                }
-               else if(WEP_ACTION(activeweapon, WR_ZOOMRETICLE) && autocvar_cl_reticle_weapon)
+               else if(wep.wr_zoomreticle(wep) && autocvar_cl_reticle_weapon)
                {
                        if(reticle_image != "") { reticle_type = 2; }
                        else { reticle_type = 0; }
@@ -1859,8 +1861,10 @@ void CSQC_UpdateView(float w, float h)
     if(hud && !intermission)
     if(hud == HUD_BUMBLEBEE_GUN)
        CSQC_BUMBLE_GUN_HUD();
-    else
-               VEH_ACTION(hud, VR_HUD);
+    else {
+       Vehicle info = get_vehicleinfo(hud);
+               info.vr_hud(info);
+       }
 
        cl_notice_run();
 
index 5c2caa6..0db1a6a 100644 (file)
@@ -12,7 +12,7 @@
 
 bool monsters_animoverride(entity e)
 {
-       int monster_id = 0;
+       Monster monster_id = NULL;
        for(int i = MON_FIRST; i <= MON_LAST; ++i)
        {
                entity mon = get_monsterinfo(i);
@@ -20,14 +20,14 @@ bool monsters_animoverride(entity e)
                //if(substring(e.model, 0, strlen(mon.model) - 4) == substring(mon.model, 0, strlen(mon.model) - 4))
                if(e.model == mon.model)
                {
-                       monster_id = i;
+                       monster_id = mon;
                        break;
                }
        }
 
        if(!monster_id) { return false; }
 
-       MON_ACTION(monster_id, MR_ANIM);
+       monster_id.mr_anim(monster_id);
 
        vector none = '0 0 0';
        e.anim_duckwalk = e.anim_walk;
index 467352e..d398cc9 100644 (file)
@@ -331,12 +331,6 @@ MODEL(VEH_SPIDERBOT_MUZZLEFLASH,        "models/uziflash.md3");
 MODEL(VEH_SPIDERBOT_TOP,                "models/vehicles/spiderbot_top.dpm");
 MODEL(VEH_SPIDERBOT_VIEW,               "models/vehicles/spiderbot_cockpit.dpm");
 
-MODEL(MON_MAGE,                         "models/monsters/mage.dpm");
-MODEL(MON_SHAMBLER,                     "models/monsters/shambler.mdl");
-MODEL(MON_SPIDER,                       "models/monsters/spider.dpm");
-MODEL(MON_WYVERN,                       "models/monsters/wizard.mdl");
-MODEL(MON_ZOMBIE,                       "models/monsters/zombie.dpm");
-
 MODEL(CHAT,                             "models/misc/chatbubble.spr");
 
 MODEL(0,                                "models/sprites/0.spr32");
index e17a90a..232441e 100644 (file)
@@ -1,27 +1,19 @@
 #ifndef MONSTERS_ALL_C
 #define MONSTERS_ALL_C
-#include "all.qh"
-
-REGISTER_MONSTER(Null, Monster) {
-       this.netname = "";
-       this.monster_name = "Monster";
-       this.monster_func = m_null;
-       this.mdl = "";
-       this.mins = '-0 -0 -0';
-       this.maxs = '0 0 0';
-       this.model = "";
-}
 
-#include "all.inc"
+#include "all.qh"
 
 entity get_monsterinfo(int id)
 {
-       if (id >= MON_FIRST && id <= MON_LAST)
-       {
+       if (id >= MON_FIRST && id <= MON_LAST) {
                entity m = monster_info[id];
                if (m) return m;
        }
        return MON_Null;
 }
 
+#define IMPLEMENTATION
+#include "all.inc"
+#undef IMPLEMENTATION
+
 #endif
index 20a2103..7c2f13d 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef MONSTERS_ALL_H
 #define MONSTERS_ALL_H
 
+#include "monster.qh"
+
 void RegisterMonsters();
 const int MON_MAXCOUNT = 24;
 entity monster_info[MON_MAXCOUNT], monster_info_first, monster_info_last;
@@ -9,63 +11,13 @@ int MON_COUNT;
 const int MON_FIRST = 1;
 #define MON_LAST (MON_FIRST + MON_COUNT - 1)
 /** If you register a new monster, make sure to add it to all.inc */
-#define REGISTER_MONSTER(id, class) REGISTER(RegisterMonsters, MON, monster_info, MON_COUNT, id, monsterid, NEW(class))
-#include "monster.qh"
-#define REGISTER_MONSTER_SIMPLE(id, monsterflags, min_s, max_s, modelname, shortname, mname) \
-    REGISTER_MONSTER(id, Monster) {                                     \
-        this.netname = shortname;                                       \
-        this.monster_name = mname;                                      \
-        this.mdl = modelname;                                           \
-        this.spawnflags = monsterflags;                                 \
-        this.mins = min_s;                                              \
-        this.maxs = max_s;                                              \
-        this.model = strzone(strcat("models/monsters/", modelname));    \
-    }                                                                   \
-    REGISTER_INIT(MON, id)
+#define REGISTER_MONSTER(id, inst) REGISTER(RegisterMonsters, MON, monster_info, MON_COUNT, id, monsterid, inst)
 REGISTER_REGISTRY(RegisterMonsters)
 
-#include "../util.qh"
-
-// monster requests
-const int MR_SETUP = 1; // (SERVER) setup monster data
-const int MR_THINK = 2; // (SERVER) logic to run every frame
-const int MR_DEATH = 3; // (SERVER) called when monster dies
-const int MR_PRECACHE = 4; // (BOTH) precaches models/sounds used by this monster
-const int MR_PAIN = 5; // (SERVER) called when monster is damaged
-const int MR_ANIM = 6; // (BOTH?) sets animations for monster
-
-// special spawn flags
-const int MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died
-const int MONSTER_TYPE_FLY = 32;
-const int MONSTER_TYPE_SWIM = 64;
-const int MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced
-const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster
-const int MON_FLAG_RANGED = 512; // monster shoots projectiles
-const int MON_FLAG_MELEE = 1024;
-const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes
-const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes
-
-// entity properties of monsterinfo:
-.string netname; // short name
-.string mdl; // currently a copy of the model
-.string model; // full name of model
-.int spawnflags;
-.vector mins, maxs; // monster hitbox size
-.bool(int) monster_attackfunc;
+entity get_monsterinfo(int id);
 
-// animations
-.vector anim_blockend;
-.vector anim_blockstart;
-.vector anim_melee1;
-.vector anim_melee2;
-.vector anim_melee3;
-.vector anim_pain3;
-.vector anim_pain4;
-.vector anim_pain5;
-.vector anim_walk;
-.vector anim_spawn;
+REGISTER_MONSTER(Null, NEW(Monster));
 
-// other useful macros
-#define MON_ACTION(monstertype,mrequest) (get_monsterinfo(monstertype)).monster_func(mrequest)
+#include "all.inc"
 
 #endif
index 79077ac..0a78b77 100644 (file)
@@ -1,15 +1,61 @@
 #ifndef MONSTER_H
 #define MONSTER_H
 
-bool m_null(int) { return false; }
+// special spawn flags
+const int MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died
+const int MONSTER_TYPE_FLY = 32;
+const int MONSTER_TYPE_SWIM = 64;
+const int MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced
+const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster
+const int MON_FLAG_RANGED = 512; // monster shoots projectiles
+const int MON_FLAG_MELEE = 1024;
+const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes
+const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes
+
+// entity properties of monsterinfo:
+.bool(int, entity targ) monster_attackfunc;
+
+// animations
+.vector anim_blockend;
+.vector anim_blockstart;
+.vector anim_melee1;
+.vector anim_melee2;
+.vector anim_melee3;
+.vector anim_pain3;
+.vector anim_pain4;
+.vector anim_pain5;
+.vector anim_walk;
+.vector anim_spawn;
 
 /** If you register a new monster, make sure to add it to all.inc */
 CLASS(Monster, Object)
     ATTRIB(Monster, monsterid, int, 0)
-    ATTRIB(Monster, classname, string, "monster_info")
+    /** attributes */
+    ATTRIB(Monster, spawnflags, int, 0)
     /** human readable name */
-    ATTRIB(Monster, monster_name, string, string_null)
-    ATTRIB(Monster, monster_func, bool(int), m_null)
+    ATTRIB(Monster, monster_name, string, "Monster")
+    /** short name */
+    ATTRIB(Monster, netname, string, "")
+    /** model */
+    ATTRIB(Monster, m_model, entity, NULL)
+    /** hitbox size */
+    ATTRIB(Monster, mins, vector, '-0 -0 -0')
+    /** hitbox size */
+    ATTRIB(Monster, maxs, vector, '0 0 0')
+    
+    /** (SERVER) setup monster data */
+    METHOD(Monster, mr_setup, bool(Monster this)) { return false; }
+    /** (SERVER) logic to run every frame */
+    METHOD(Monster, mr_think, bool(Monster this)) { return false; }
+    /** (SERVER) called when monster dies */
+    METHOD(Monster, mr_death, bool(Monster this)) { return false; }
+    /** (BOTH) precaches models/sounds used by this monster */
+    METHOD(Monster, mr_precache, bool(Monster this)) { return false; }
+    /** (SERVER) called when monster is damaged */
+    METHOD(Monster, mr_pain, bool(Monster this)) { return false; }
+    /** (BOTH?) sets animations for monster */
+    METHOD(Monster, mr_anim, bool(Monster this)) { return false; }
+
 ENDCLASS(Monster)
 
 #endif
index 433fed6..9952063 100644 (file)
@@ -1,21 +1,75 @@
+#ifndef MAGE_H
+#define MAGE_H
+
 #ifndef MENUQC
-bool M_Mage(int);
+MODEL(MON_MAGE, "models/monsters/mage.dpm");
 #endif
-REGISTER_MONSTER_SIMPLE(
-/* MON_##id   */ MAGE,
-/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
-/* mins,maxs  */ '-36 -36 -24', '36 36 50',
-/* model      */ "mage.dpm",
-/* netname    */ "mage",
-/* fullname   */ _("Mage")
-) {
+
+CLASS(Mage, Monster)
+    ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
+    ATTRIB(Mage, mins, vector, '-36 -36 -24');
+    ATTRIB(Mage, maxs, vector, '36 36 50');
 #ifndef MENUQC
-    this.monster_func = M_Mage;
-    this.monster_func(MR_PRECACHE);
+    ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
+#endif
+    ATTRIB(Mage, netname, string, "mage");
+    ATTRIB(Mage, monster_name, string, _("Mage"));
+ENDCLASS(Mage)
+
+REGISTER_MONSTER(MAGE, NEW(Mage)) {
+#ifndef MENUQC
+    this.mr_precache(this);
 #endif
 }
 
+#include "../../weapons/all.qh"
+
+CLASS(MageSpike, PortoLaunch)
+/* flags     */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(MageSpike, impulse, int, 9);
+/* refname   */ ATTRIB(MageSpike, netname, string, "magespike");
+/* wepname   */ ATTRIB(MageSpike, message, string, _("Mage spike"));
+ENDCLASS(MageSpike)
+REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
 #ifdef SVQC
+
+void M_Mage_Attack_Spike(vector dir);
+void M_Mage_Attack_Push();
+METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, bool fire1, bool fire2)) {
+    if (fire1)
+    if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, false, 0.2)) {
+        if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range;
+        actor.enemy = Monster_FindTarget(actor);
+        W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+       if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
+        M_Mage_Attack_Spike(w_shotdir);
+        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+    }
+    if (fire2)
+    if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, true, 0.5)) {
+        M_Mage_Attack_Push();
+        weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
+    }
+}
+
+void M_Mage_Attack_Teleport();
+
+CLASS(OffhandMageTeleport, OffhandWeapon)
+    .bool OffhandMageTeleport_key_pressed;
+    METHOD(OffhandMageTeleport, offhand_think, void(OffhandMageTeleport this, entity player, bool key_pressed))
+    {
+        if (key_pressed && !player.OffhandMageTeleport_key_pressed)
+            WITH(entity, self, player, M_Mage_Attack_Teleport());
+        player.OffhandMageTeleport_key_pressed = key_pressed;
+    }
+ENDCLASS(OffhandMageTeleport)
+OffhandMageTeleport OFFHAND_MAGE_TELEPORT; STATIC_INIT(OFFHAND_MAGE_TELEPORT) { OFFHAND_MAGE_TELEPORT = NEW(OffhandMageTeleport); }
+
 float autocvar_g_monster_mage_health;
 float autocvar_g_monster_mage_damageforcescale = 0.5;
 float autocvar_g_monster_mage_attack_spike_damage;
@@ -111,19 +165,12 @@ void M_Mage_Attack_Spike_Touch()
 // copied from W_Seeker_Think
 void M_Mage_Attack_Spike_Think()
 {SELFPARAM();
-       entity e;
-       vector desireddir, olddir, newdir, eorg;
-       float turnrate;
-       float dist;
-       float spd;
-
-       if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0)
-       {
+       if (time > self.ltime || (self.enemy && self.enemy.health <= 0) || self.owner.health <= 0) {
                self.projectiledeathtype |= HITTYPE_SPLASH;
                M_Mage_Attack_Spike_Explode();
        }
 
-       spd = vlen(self.velocity);
+       float spd = vlen(self.velocity);
        spd = bound(
                spd - (autocvar_g_monster_mage_attack_spike_decel) * frametime,
                (autocvar_g_monster_mage_attack_spike_speed_max),
@@ -136,12 +183,12 @@ void M_Mage_Attack_Spike_Think()
 
        if (self.enemy != world)
        {
-               e                               = self.enemy;
-               eorg                    = 0.5 * (e.absmin + e.absmax);
-               turnrate                = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
-               desireddir              = normalize(eorg - self.origin);
-               olddir                  = normalize(self.velocity); // get my current direction
-               dist                    = vlen(eorg - self.origin);
+               entity e = self.enemy;
+               vector eorg = 0.5 * (e.absmin + e.absmax);
+               float turnrate = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
+               vector desireddir = normalize(eorg - self.origin);
+               vector olddir = normalize(self.velocity); // get my current direction
+               float dist = vlen(eorg - self.origin);
 
                // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
                if ((autocvar_g_monster_mage_attack_spike_smart) && (dist > (autocvar_g_monster_mage_attack_spike_smart_mindist)))
@@ -159,11 +206,9 @@ void M_Mage_Attack_Spike_Think()
                        desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
                }
 
-               newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
+               vector newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
                self.velocity = newdir * spd; // make me fly in the new direction at my flight speed
        }
-       else
-               dist = 0;
 
        ///////////////
 
@@ -172,14 +217,12 @@ void M_Mage_Attack_Spike_Think()
        UpdateCSQCProjectile(self);
 }
 
-void M_Mage_Attack_Spike()
-{SELFPARAM();
-       entity missile;
-       vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
-
+void M_Mage_Attack_Spike(vector dir)
+{
+       SELFPARAM();
        makevectors(self.angles);
 
-       missile = spawn ();
+       entity missile = spawn();
        missile.owner = missile.realowner = self;
        missile.think = M_Mage_Attack_Spike_Think;
        missile.ltime = time + 7;
@@ -188,7 +231,7 @@ void M_Mage_Attack_Spike()
        missile.movetype = MOVETYPE_FLYMISSILE;
        missile.flags = FL_PROJECTILE;
        setorigin(missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14);
-       setsize (missile, '0 0 0', '0 0 0');
+       setsize(missile, '0 0 0', '0 0 0');
        missile.velocity = dir * 400;
        missile.avelocity = '300 300 300';
        missile.enemy = self.enemy;
@@ -268,17 +311,25 @@ void M_Mage_Attack_Push()
 
 void M_Mage_Attack_Teleport()
 {SELFPARAM();
-       if(vlen(self.enemy.origin - self.origin) >= 500)
-               return;
+       entity targ = self.enemy;
+       if (!targ) return;
+       if (vlen(targ.origin - self.origin) > 1500) return;
 
-       makevectors(self.enemy.angles);
-       tracebox(self.enemy.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self);
+       makevectors(targ.angles);
+       tracebox(targ.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self);
 
        if(trace_fraction < 1)
                return;
 
        Send_Effect(EFFECT_SPAWN_NEUTRAL, self.origin, '0 0 0', 1);
-       setorigin(self, self.enemy.origin + ((v_forward * -1) * 200));
+       setorigin(self, targ.origin + ((v_forward * -1) * 200));
+
+       vector a = vectoangles(targ.origin - self.origin);
+       a.x = -a.x;
+       self.angles_x = a.x;
+       self.angles_y = a.y;
+       self.fixangle = true;
+       self.velocity *= 0.5;
 
        self.attack_finished_single = time + 0.2;
 }
@@ -300,7 +351,7 @@ void M_Mage_Defend_Shield()
        self.anim_finished = time + 1;
 }
 
-float M_Mage_Attack(float attack_type)
+float M_Mage_Attack(float attack_type, entity targ)
 {SELFPARAM();
        switch(attack_type)
        {
@@ -308,7 +359,8 @@ float M_Mage_Attack(float attack_type)
                {
                        if(random() <= 0.7)
                        {
-                               M_Mage_Attack_Push();
+                               Weapon wep = WEP_MAGE_SPIKE;
+                               wep.wr_think(wep, self, false, true);
                                return true;
                        }
 
@@ -320,7 +372,8 @@ float M_Mage_Attack(float attack_type)
                        {
                                if(random() <= 0.4)
                                {
-                                       M_Mage_Attack_Teleport();
+                                       OffhandWeapon off = OFFHAND_MAGE_TELEPORT;
+                                       off.offhand_think(off, self, true);
                                        return true;
                                }
                                else
@@ -328,7 +381,8 @@ float M_Mage_Attack(float attack_type)
                                        setanim(self, self.anim_shoot, true, true, true);
                                        self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay);
                                        self.anim_finished = time + 1;
-                                       Monster_Delay(1, 0, 0.2, M_Mage_Attack_Spike);
+                                       Weapon wep = WEP_MAGE_SPIKE;
+                                       wep.wr_think(wep, self, true, false);
                                        return true;
                                }
                        }
@@ -347,13 +401,10 @@ spawnfunc(monster_mage) { Monster_Spawn(MON_MAGE.monsterid); }
 
 #endif // SVQC
 
-bool M_Mage(int req)
-{SELFPARAM();
-       switch(req)
-       {
                #ifdef SVQC
-               case MR_THINK:
+               METHOD(Mage, mr_think, bool(Monster thismon))
                {
+                       SELFPARAM();
                        entity head;
                        bool need_help = false;
 
@@ -382,19 +433,21 @@ bool M_Mage(int req)
 
                        return true;
                }
-               case MR_PAIN:
+               METHOD(Mage, mr_pain, bool(Monster thismon))
                {
                        return true;
                }
-               case MR_DEATH:
+               METHOD(Mage, mr_death, bool(Monster thismon))
                {
+                       SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
                #endif
                #ifndef MENUQC
-               case MR_ANIM:
+               METHOD(Mage, mr_anim, bool(Monster thismon))
                {
+                       SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_die1 = animfixfps(self, '4 1 0.5', none); // 2 seconds
                        self.anim_walk = animfixfps(self, '1 1 1', none);
@@ -407,8 +460,9 @@ bool M_Mage(int req)
                }
                #endif
                #ifdef SVQC
-               case MR_SETUP:
+               METHOD(Mage, mr_setup, bool(Monster thismon))
                {
+                       SELFPARAM();
                        if(!self.health) self.health = (autocvar_g_monster_mage_health);
                        if(!self.speed) { self.speed = (autocvar_g_monster_mage_speed_walk); }
                        if(!self.speed2) { self.speed2 = (autocvar_g_monster_mage_speed_run); }
@@ -420,12 +474,10 @@ bool M_Mage(int req)
 
                        return true;
                }
-               case MR_PRECACHE:
+               METHOD(Mage, mr_precache, bool(Monster thismon))
                {
                        return true;
                }
                #endif
-       }
 
-       return true;
-}
+#endif
index fe60783..937c83c 100644 (file)
@@ -1,20 +1,31 @@
+#ifndef SHAMBLER_H
+#define SHAMBLER_H
+
 #ifndef MENUQC
-bool M_Shambler(int);
+MODEL(MON_SHAMBLER, "models/monsters/shambler.mdl");
 #endif
-REGISTER_MONSTER_SIMPLE(
-/* MON_##id   */ SHAMBLER,
-/* spawnflags */ MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED,
-/* mins,maxs  */ '-41 -41 -31', '41 41 65',
-/* model      */ "shambler.mdl",
-/* netname    */ "shambler",
-/* fullname   */ _("Shambler")
-) {
+
+CLASS(Shambler, Monster)
+    ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
+    ATTRIB(Shambler, mins, vector, '-41 -41 -31');
+    ATTRIB(Shambler, maxs, vector, '41 41 65');
 #ifndef MENUQC
-       this.monster_func = M_Shambler;
-       this.monster_func(MR_PRECACHE);
+    ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
+#endif
+    ATTRIB(Shambler, netname, string, "shambler");
+    ATTRIB(Shambler, monster_name, string, _("Shambler"));
+ENDCLASS(Shambler)
+
+REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) {
+#ifndef MENUQC
+    this.mr_precache(this);
 #endif
 }
 
+#endif
+
+#ifdef IMPLEMENTATION
+
 #ifdef SVQC
 float autocvar_g_monster_shambler_health;
 float autocvar_g_monster_shambler_damageforcescale = 0.1;
@@ -168,7 +179,7 @@ void M_Shambler_Attack_Lightning()
        CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
 }
 
-float M_Shambler_Attack(float attack_type)
+float M_Shambler_Attack(float attack_type, entity targ)
 {SELFPARAM();
        switch(attack_type)
        {
@@ -214,30 +225,29 @@ float M_Shambler_Attack(float attack_type)
 spawnfunc(monster_shambler) { Monster_Spawn(MON_SHAMBLER.monsterid); }
 #endif // SVQC
 
-bool M_Shambler(int req)
-{SELFPARAM();
-       switch(req)
-       {
                #ifdef SVQC
-               case MR_THINK:
+               METHOD(Shambler, mr_think, bool(Shambler thismon))
                {
                        return true;
                }
-               case MR_PAIN:
+               METHOD(Shambler, mr_pain, bool(Shambler thismon))
                {
+                       SELFPARAM();
                        self.pain_finished = time + 0.5;
                        setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
-               case MR_DEATH:
+               METHOD(Shambler, mr_death, bool(Shambler thismon))
                {
+                       SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
                #endif
                #ifndef MENUQC
-               case MR_ANIM:
+               METHOD(Shambler, mr_anim, bool(Shambler thismon))
                {
+                       SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_die1 = animfixfps(self, '8 1 0.5', none); // 2 seconds
                        self.anim_walk = animfixfps(self, '1 1 1', none);
@@ -253,8 +263,9 @@ bool M_Shambler(int req)
                }
                #endif
                #ifdef SVQC
-               case MR_SETUP:
+               METHOD(Shambler, mr_setup, bool(Shambler thismon))
                {
+                       SELFPARAM();
                        if(!self.health) self.health = (autocvar_g_monster_shambler_health);
                        if(!self.attack_range) self.attack_range = 150;
                        if(!self.speed) { self.speed = (autocvar_g_monster_shambler_speed_walk); }
@@ -272,12 +283,10 @@ bool M_Shambler(int req)
 
                        return true;
                }
-               case MR_PRECACHE:
+               METHOD(Shambler, mr_precache, bool(Shambler thismon))
                {
                        return true;
                }
                #endif
-       }
 
-       return true;
-}
+#endif
index ba2b126..35a320b 100644 (file)
@@ -1,29 +1,85 @@
+#ifndef SPIDER_H
+#define SPIDER_H
+
 #ifndef MENUQC
-bool M_Spider(int);
+MODEL(MON_SPIDER, "models/monsters/spider.dpm");
 #endif
-REGISTER_MONSTER_SIMPLE(
-/* MON_##id   */ SPIDER,
-/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE,
-/* mins,maxs  */ '-18 -18 -25', '18 18 30',
-/* model      */ "spider.dpm",
-/* netname    */ "spider",
-/* fullname   */ _("Spider")
-) {
+
+CLASS(Spider, Monster)
+    ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
+    ATTRIB(Spider, mins, vector, '-18 -18 -25');
+    ATTRIB(Spider, maxs, vector, '18 18 30');
+#ifndef MENUQC
+    ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
+#endif
+    ATTRIB(Spider, netname, string, "spider");
+    ATTRIB(Spider, monster_name, string, _("Spider"));
+ENDCLASS(Spider)
+
+REGISTER_MONSTER(SPIDER, NEW(Spider)) {
 #ifndef MENUQC
-       this.monster_func = M_Spider;
-       this.monster_func(MR_PRECACHE);
+    this.mr_precache(this);
 #endif
 }
 
+#include "../../weapons/all.qh"
+
+CLASS(SpiderAttack, PortoLaunch)
+/* flags     */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(SpiderAttack, impulse, int, 9);
+/* refname   */ ATTRIB(SpiderAttack, netname, string, "spider");
+/* wepname   */ ATTRIB(SpiderAttack, message, string, _("Spider attack"));
+ENDCLASS(SpiderAttack)
+REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
 #ifdef SVQC
-float autocvar_g_monster_spider_health;
-float autocvar_g_monster_spider_damageforcescale = 0.6;
-float autocvar_g_monster_spider_attack_bite_damage;
-float autocvar_g_monster_spider_attack_bite_delay;
+
+.float spider_web_delay;
+
 float autocvar_g_monster_spider_attack_web_damagetime;
 float autocvar_g_monster_spider_attack_web_speed;
 float autocvar_g_monster_spider_attack_web_speed_up;
 float autocvar_g_monster_spider_attack_web_delay;
+
+float autocvar_g_monster_spider_attack_bite_damage;
+float autocvar_g_monster_spider_attack_bite_delay;
+
+void M_Spider_Attack_Web();
+
+METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, false, autocvar_g_monster_spider_attack_web_delay)) {
+               if (!isPlayer) {
+                       actor.spider_web_delay = time + 3;
+                       setanim(actor, actor.anim_shoot, true, true, true);
+                       actor.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
+                       actor.anim_finished = time + 1;
+               }
+        if (isPlayer) actor.enemy = Monster_FindTarget(actor);
+        W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+       if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
+               M_Spider_Attack_Web();
+        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+        return;
+    }
+    if (fire2)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, true, 0.5)) {
+       if (isPlayer) {
+               actor.enemy = Monster_FindTarget(actor);
+               actor.attack_range = 60;
+       }
+       Monster_Attack_Melee(actor.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? self.anim_melee : self.anim_shoot), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true);
+        weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
+    }
+}
+
+float autocvar_g_monster_spider_health;
+float autocvar_g_monster_spider_damageforcescale = 0.6;
 float autocvar_g_monster_spider_speed_stop;
 float autocvar_g_monster_spider_speed_run;
 float autocvar_g_monster_spider_speed_walk;
@@ -35,8 +91,6 @@ const float spider_anim_attack                = 2;
 const float spider_anim_attack2                = 3;
 */
 
-.float spider_web_delay;
-
 void M_Spider_Attack_Web_Explode()
 {SELFPARAM();
        entity e;
@@ -97,27 +151,20 @@ void M_Spider_Attack_Web()
        CSQCProjectile(proj, true, PROJECTILE_ELECTRO, true);
 }
 
-bool M_Spider_Attack(int attack_type)
+bool M_Spider_Attack(int attack_type, entity targ)
 {SELFPARAM();
        switch(attack_type)
        {
+               Weapon wep = WEP_SPIDER_ATTACK;
                case MONSTER_ATTACK_MELEE:
                {
-                       return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? self.anim_melee : self.anim_shoot), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true);
+                       wep.wr_think(wep, self, false, true);
+                       return true;
                }
                case MONSTER_ATTACK_RANGED:
                {
-                       if(time >= self.spider_web_delay)
-                       {
-                               setanim(self, self.anim_shoot, true, true, true);
-                               self.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
-                               self.anim_finished = time + 1;
-                               M_Spider_Attack_Web();
-                               self.spider_web_delay = time + 3;
-                               return true;
-                       }
-
-                       return false;
+                       wep.wr_think(wep, self, true, false);
+                       return true;
                }
        }
 
@@ -127,29 +174,27 @@ bool M_Spider_Attack(int attack_type)
 spawnfunc(monster_spider) { Monster_Spawn(MON_SPIDER.monsterid); }
 #endif // SVQC
 
-bool M_Spider(int req)
-{SELFPARAM();
-       switch(req)
-       {
                #ifdef SVQC
-               case MR_THINK:
+               METHOD(Spider, mr_think, bool(Spider thismon))
                {
                        return true;
                }
-               case MR_PAIN:
+               METHOD(Spider, mr_pain, bool(Spider thismon))
                {
                        return true;
                }
-               case MR_DEATH:
+               METHOD(Spider, mr_death, bool(Spider thismon))
                {
+                       SELFPARAM();
                        setanim(self, self.anim_melee, false, true, true);
                        self.angles_x = 180;
                        return true;
                }
                #endif
                #ifndef MENUQC
-               case MR_ANIM:
+               METHOD(Spider, mr_anim, bool(Spider thismon))
                {
+                       SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_walk = animfixfps(self, '1 1 1', none);
                        self.anim_idle = animfixfps(self, '0 1 1', none);
@@ -161,8 +206,9 @@ bool M_Spider(int req)
                }
                #endif
                #ifdef SVQC
-               case MR_SETUP:
+               METHOD(Spider, mr_setup, bool(Spider thismon))
                {
+                       SELFPARAM();
                        if(!self.health) self.health = (autocvar_g_monster_spider_health);
                        if(!self.speed) { self.speed = (autocvar_g_monster_spider_speed_walk); }
                        if(!self.speed2) { self.speed2 = (autocvar_g_monster_spider_speed_run); }
@@ -174,12 +220,10 @@ bool M_Spider(int req)
 
                        return true;
                }
-               case MR_PRECACHE:
+               METHOD(Spider, mr_precache, bool(Spider thismon))
                {
                        return true;
                }
                #endif
-       }
 
-       return true;
-}
+#endif
index d65bcf6..102fd6a 100644 (file)
@@ -1,29 +1,88 @@
+#ifndef WYVERN_H
+#define WYVERN_H
+
 #ifndef MENUQC
-bool M_Wyvern(int);
+MODEL(MON_WYVERN, "models/monsters/wizard.mdl");
 #endif
-REGISTER_MONSTER_SIMPLE(
-/* MON_##id   */ WYVERN,
-/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE,
-/* mins,maxs  */ '-20 -20 -58', '20 20 20',
-/* model      */ "wizard.mdl",
-/* netname    */ "wyvern",
-/* fullname   */ _("Wyvern")
-) {
+
+CLASS(Wyvern, Monster)
+    ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
+    ATTRIB(Wyvern, mins, vector, '-20 -20 -58');
+    ATTRIB(Wyvern, maxs, vector, '20 20 20');
+#ifndef MENUQC
+    ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
+#endif
+    ATTRIB(Wyvern, netname, string, "wyvern");
+    ATTRIB(Wyvern, monster_name, string, _("Wyvern"));
+ENDCLASS(Wyvern)
+
+REGISTER_MONSTER(WYVERN, NEW(Wyvern)) {
 #ifndef MENUQC
-       this.monster_func = M_Wyvern;
-       this.monster_func(MR_PRECACHE);
+    this.mr_precache(this);
 #endif
 }
 
+#include "../../weapons/all.qh"
+
+CLASS(WyvernAttack, PortoLaunch)
+/* flags     */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(WyvernAttack, impulse, int, 9);
+/* refname   */ ATTRIB(WyvernAttack, netname, string, "wyvern");
+/* wepname   */ ATTRIB(WyvernAttack, message, string, _("Wyvern attack"));
+ENDCLASS(WyvernAttack)
+REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
 #ifdef SVQC
-float autocvar_g_monster_wyvern_health;
-float autocvar_g_monster_wyvern_damageforcescale = 0.6;
+
 float autocvar_g_monster_wyvern_attack_fireball_damage;
 float autocvar_g_monster_wyvern_attack_fireball_edgedamage;
 float autocvar_g_monster_wyvern_attack_fireball_damagetime;
 float autocvar_g_monster_wyvern_attack_fireball_force;
 float autocvar_g_monster_wyvern_attack_fireball_radius;
 float autocvar_g_monster_wyvern_attack_fireball_speed;
+
+void M_Wyvern_Attack_Fireball_Explode();
+void M_Wyvern_Attack_Fireball_Touch();
+
+METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, bool fire1, bool fire2)) {
+    if (fire1)
+    if (time > actor.attack_finished_single || weapon_prepareattack(thiswep, actor, false, 1.2)) {
+        if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+               if (IS_MONSTER(actor)) {
+                       actor.attack_finished_single = time + 1.2;
+                       actor.anim_finished = time + 1.2;
+                       monster_makevectors(actor.enemy);
+               }
+
+               entity missile = spawn();
+               missile.owner = missile.realowner = actor;
+               missile.solid = SOLID_TRIGGER;
+               missile.movetype = MOVETYPE_FLYMISSILE;
+               missile.projectiledeathtype = DEATH_MONSTER_WYVERN;
+               setsize(missile, '-6 -6 -6', '6 6 6');
+               setorigin(missile, actor.origin + actor.view_ofs + v_forward * 14);
+               missile.flags = FL_PROJECTILE;
+               missile.velocity = w_shotdir * (autocvar_g_monster_wyvern_attack_fireball_speed);
+               missile.avelocity = '300 300 300';
+               missile.nextthink = time + 5;
+               missile.think = M_Wyvern_Attack_Fireball_Explode;
+               missile.touch = M_Wyvern_Attack_Fireball_Touch;
+               CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
+
+        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+    }
+}
+
+METHOD(WyvernAttack, wr_checkammo1, bool(WyvernAttack thiswep)) {
+       return true;
+}
+
+float autocvar_g_monster_wyvern_health;
+float autocvar_g_monster_wyvern_damageforcescale = 0.6;
 float autocvar_g_monster_wyvern_speed_stop;
 float autocvar_g_monster_wyvern_speed_run;
 float autocvar_g_monster_wyvern_speed_walk;
@@ -37,19 +96,19 @@ const float wyvern_anim_death       = 4;
 */
 
 void M_Wyvern_Attack_Fireball_Explode()
-{SELFPARAM();
-       entity e;
-       if(self)
-       {
-               Send_Effect(EFFECT_FIREBALL_EXPLODE, self.origin, '0 0 0', 1);
+{
+       SELFPARAM();
+       Send_Effect(EFFECT_FIREBALL_EXPLODE, self.origin, '0 0 0', 1);
 
-               RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world);
+       entity owner = self.realowner;
 
-               for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
-                       Fire_AddDamage(e, self, 5 * MONSTER_SKILLMOD(self), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
+       RadiusDamage(self, owner, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force, world, world, autocvar_g_monster_wyvern_attack_fireball_radius, self.projectiledeathtype, world);
 
-               remove(self);
-       }
+       for (entity e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); )
+       if (vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
+               Fire_AddDamage(e, owner, 5 * MONSTER_SKILLMOD(owner), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
+
+       remove(self);
 }
 
 void M_Wyvern_Attack_Fireball_Touch()
@@ -59,41 +118,17 @@ void M_Wyvern_Attack_Fireball_Touch()
        M_Wyvern_Attack_Fireball_Explode();
 }
 
-void M_Wyvern_Attack_Fireball()
-{SELFPARAM();
-       entity missile = spawn();
-       vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
-
-       monster_makevectors(self.enemy);
-
-       missile.owner = missile.realowner = self;
-       missile.solid = SOLID_TRIGGER;
-       missile.movetype = MOVETYPE_FLYMISSILE;
-       missile.projectiledeathtype = DEATH_MONSTER_WYVERN;
-       setsize(missile, '-6 -6 -6', '6 6 6');
-       setorigin(missile, self.origin + self.view_ofs + v_forward * 14);
-       missile.flags = FL_PROJECTILE;
-       missile.velocity = dir * (autocvar_g_monster_wyvern_attack_fireball_speed);
-       missile.avelocity = '300 300 300';
-       missile.nextthink = time + 5;
-       missile.think = M_Wyvern_Attack_Fireball_Explode;
-       missile.enemy = self.enemy;
-       missile.touch = M_Wyvern_Attack_Fireball_Touch;
-       CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
-}
-
-float M_Wyvern_Attack(float attack_type)
-{SELFPARAM();
+float M_Wyvern_Attack(float attack_type, entity targ)
+{
+       SELFPARAM();
        switch(attack_type)
        {
                case MONSTER_ATTACK_MELEE:
                case MONSTER_ATTACK_RANGED:
                {
-                       self.attack_finished_single = time + 1.2;
-                       self.anim_finished = time + 1.2;
-
-                       M_Wyvern_Attack_Fireball();
-
+                       w_shotdir = normalize((self.enemy.origin + '0 0 10') - self.origin);
+                       Weapon wep = WEP_WYVERN_ATTACK;
+                       wep.wr_think(wep, self, true, false);
                        return true;
                }
        }
@@ -104,23 +139,21 @@ float M_Wyvern_Attack(float attack_type)
 spawnfunc(monster_wyvern) { Monster_Spawn(MON_WYVERN.monsterid); }
 #endif // SVQC
 
-bool M_Wyvern(int req)
-{SELFPARAM();
-       switch(req)
-       {
                #ifdef SVQC
-               case MR_THINK:
+               METHOD(Wyvern, mr_think, bool(Wyvern thismon))
                {
                        return true;
                }
-               case MR_PAIN:
+               METHOD(Wyvern, mr_pain, bool(Wyvern thismon))
                {
+                       SELFPARAM();
                        self.pain_finished = time + 0.5;
                        setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
-               case MR_DEATH:
+               METHOD(Wyvern, mr_death, bool(Wyvern thismon))
                {
+                       SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        self.velocity_x = -200 + 400 * random();
                        self.velocity_y = -200 + 400 * random();
@@ -129,8 +162,9 @@ bool M_Wyvern(int req)
                }
                #endif
                #ifndef MENUQC
-               case MR_ANIM:
+               METHOD(Wyvern, mr_anim, bool(Wyvern thismon))
                {
+                       SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_die1 = animfixfps(self, '4 1 0.5', none); // 2 seconds
                        self.anim_walk = animfixfps(self, '1 1 1', none);
@@ -143,8 +177,9 @@ bool M_Wyvern(int req)
                }
                #endif
                #ifdef SVQC
-               case MR_SETUP:
+               METHOD(Wyvern, mr_setup, bool(Wyvern thismon))
                {
+                       SELFPARAM();
                        if(!self.health) self.health = (autocvar_g_monster_wyvern_health);
                        if(!self.speed) { self.speed = (autocvar_g_monster_wyvern_speed_walk); }
                        if(!self.speed2) { self.speed2 = (autocvar_g_monster_wyvern_speed_run); }
@@ -156,12 +191,10 @@ bool M_Wyvern(int req)
 
                        return true;
                }
-               case MR_PRECACHE:
+               METHOD(Wyvern, mr_precache, bool(Wyvern thismon))
                {
                        return true;
                }
                #endif
-       }
 
-       return true;
-}
+#endif
index 6242a75..8425a12 100644 (file)
@@ -1,20 +1,31 @@
+#ifndef ZOMBIE_H
+#define ZOMBIE_H
+
 #ifndef MENUQC
-bool M_Zombie(int);
+MODEL(MON_ZOMBIE, "models/monsters/zombie.dpm");
 #endif
-REGISTER_MONSTER_SIMPLE(
-/* MON_##id   */ ZOMBIE,
-/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RIDE,
-/* mins,maxs  */ '-18 -18 -25', '18 18 47',
-/* model      */ "zombie.dpm",
-/* netname    */ "zombie",
-/* fullname   */ _("Zombie")
-) {
+
+CLASS(Zombie, Monster)
+    ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE);
+    ATTRIB(Zombie, mins, vector, '-18 -18 -25');
+    ATTRIB(Zombie, maxs, vector, '18 18 47');
 #ifndef MENUQC
-       this.monster_func = M_Zombie;
-       this.monster_func(MR_PRECACHE);
+    ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE);
+#endif
+    ATTRIB(Zombie, netname, string, "zombie");
+    ATTRIB(Zombie, monster_name, string, _("Zombie"));
+ENDCLASS(Zombie)
+
+REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) {
+#ifndef MENUQC
+    this.mr_precache(this);
 #endif
 }
 
+#endif
+
+#ifdef IMPLEMENTATION
+
 #ifdef SVQC
 float autocvar_g_monster_zombie_health;
 float autocvar_g_monster_zombie_damageforcescale = 0.55;
@@ -107,7 +118,7 @@ float M_Zombie_Defend_Block()
        return true;
 }
 
-float M_Zombie_Attack(float attack_type)
+float M_Zombie_Attack(float attack_type, entity targ)
 {SELFPARAM();
        switch(attack_type)
        {
@@ -141,25 +152,24 @@ float M_Zombie_Attack(float attack_type)
 spawnfunc(monster_zombie) { Monster_Spawn(MON_ZOMBIE.monsterid); }
 #endif // SVQC
 
-bool M_Zombie(int req)
-{SELFPARAM();
-       switch(req)
-       {
                #ifdef SVQC
-               case MR_THINK:
+               METHOD(Zombie, mr_think, bool(Zombie thismon))
                {
+                       SELFPARAM();
                        if(time >= self.spawn_time)
                                self.damageforcescale = autocvar_g_monster_zombie_damageforcescale;
                        return true;
                }
-               case MR_PAIN:
+               METHOD(Zombie, mr_pain, bool(Zombie thismon))
                {
+                       SELFPARAM();
                        self.pain_finished = time + 0.34;
                        setanim(self, ((random() > 0.5) ? self.anim_pain1 : self.anim_pain2), true, true, false);
                        return true;
                }
-               case MR_DEATH:
+               METHOD(Zombie, mr_death, bool(Zombie thismon))
                {
+                       SELFPARAM();
                        self.armorvalue = autocvar_g_monsters_armor_blockpercent;
 
                        setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
@@ -167,8 +177,9 @@ bool M_Zombie(int req)
                }
                #endif
                #ifndef MENUQC
-               case MR_ANIM:
+               METHOD(Zombie, mr_anim, bool(Zombie thismon))
                {
+                       SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_die1 = animfixfps(self, '9 1 0.5', none); // 2 seconds
                        self.anim_die2 = animfixfps(self, '12 1 0.5', none); // 2 seconds
@@ -189,7 +200,7 @@ bool M_Zombie(int req)
                }
                #endif
                #ifdef SVQC
-               case MR_SETUP:
+               METHOD(Zombie, mr_setup, bool(Zombie thismon))
                {
                        if(!self.health) self.health = (autocvar_g_monster_zombie_health);
                        if(!self.speed) { self.speed = (autocvar_g_monster_zombie_speed_walk); }
@@ -212,12 +223,10 @@ bool M_Zombie(int req)
 
                        return true;
                }
-               case MR_PRECACHE:
+               METHOD(Zombie, mr_precache, bool(Zombie thismon))
                {
                        return true;
                }
                #endif
-       }
 
-       return true;
-}
+#endif
index 1474ad5..8b05f78 100644 (file)
@@ -268,7 +268,7 @@ void Monster_Sound_Precache(string f)
 
 void Monster_Sounds_Precache()
 {SELFPARAM();
-       string m = (get_monsterinfo(self.monsterid)).model;
+       string m = (get_monsterinfo(self.monsterid)).m_model.model_str();
        float globhandle, n, i;
        string f;
 
@@ -435,7 +435,7 @@ void Monster_Attack_Check(entity e, entity targ)
 
        if(targ_vlen <= e.attack_range)
        {
-               float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE);
+               float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE, targ);
                if(attack_success == 1)
                        Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -444,7 +444,7 @@ void Monster_Attack_Check(entity e, entity targ)
 
        if(targ_vlen > e.attack_range)
        {
-               float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED);
+               float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED, targ);
                if(attack_success == 1)
                        Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -472,7 +472,8 @@ void Monster_UpdateModel()
        self.anim_die2   = animfixfps(self, '9 1 0.01', '0 0 0');*/
 
        // then get the real values
-       MON_ACTION(self.monsterid, MR_ANIM);
+       Monster mon = get_monsterinfo(self.monsterid);
+       mon.mr_anim(mon);
 }
 
 void Monster_Touch()
@@ -1038,7 +1039,8 @@ void Monster_Dead(entity attacker, float gibbed)
 
        CSQCModel_UnlinkEntity(self);
 
-       MON_ACTION(self.monsterid, MR_DEATH);
+       Monster mon = get_monsterinfo(self.monsterid);
+       mon.mr_death(mon);
 
        if(self.candrop && self.weapon)
                W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
@@ -1071,7 +1073,8 @@ void Monster_Damage(entity inflictor, entity attacker, float damage, int deathty
        damage_take = take;
        frag_attacker = attacker;
        frag_deathtype = deathtype;
-       MON_ACTION(self.monsterid, MR_PAIN);
+       Monster mon = get_monsterinfo(self.monsterid);
+       mon.mr_pain(mon);
        take = damage_take;
 
        if(take)
@@ -1232,7 +1235,8 @@ void Monster_Think()
                return;
        }
 
-       if(MON_ACTION(self.monsterid, MR_THINK))
+       Monster mon = get_monsterinfo(self.monsterid);
+       if(mon.mr_think(mon))
                Monster_Move(self.speed2, self.speed, self.stopspeed);
 
        Monster_Anim();
@@ -1242,7 +1246,8 @@ void Monster_Think()
 
 float Monster_Spawn_Setup()
 {SELFPARAM();
-       MON_ACTION(self.monsterid, MR_SETUP);
+       Monster mon = get_monsterinfo(self.monsterid);
+       mon.mr_setup(mon);
 
        // ensure some basic needs are met
        if(!self.health) { self.health = 100; }
@@ -1308,7 +1313,6 @@ bool Monster_Spawn(int mon_id)
 
        if(!autocvar_g_monsters) { Monster_Remove(self); return false; }
 
-       self.mdl = mon.model;
        if(Monster_Appear_Check(self, mon_id)) { return true; } // return true so the monster isn't removed
 
        if(!self.monster_skill)
@@ -1326,7 +1330,7 @@ bool Monster_Spawn(int mon_id)
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
                monsters_total += 1;
 
-       _setmodel(self, self.mdl);
+       setmodel(self, mon.m_model);
        self.flags                              = FL_MONSTER;
        self.classname                  = "monster";
        self.takedamage                 = DAMAGE_AIM;
index 4d859b1..3cdb7b6 100644 (file)
@@ -118,7 +118,7 @@ float Item_ItemsTime_UpdateTime(entity e, float t)
     {
         for (entity head = world; (head = nextent(head)); )
         {
-            if (clienttype(head) != CLIENTTYPE_NOTACLIENT || !(head.weapons & WEPSET_SUPERWEAPONS) || head.classname == "weapon_info")
+            if (clienttype(head) != CLIENTTYPE_NOTACLIENT || !(head.weapons & WEPSET_SUPERWEAPONS) || head.instanceOfWeapon)
                 continue;
             if (e == head)
                 continue;
index 04bb10f..5c9f1e1 100644 (file)
@@ -1,12 +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"
+#include "turret/ewheel.qc"
+#include "turret/flac.qc"
+#include "turret/fusionreactor.qc"
+#include "turret/hellion.qc"
+#include "turret/hk.qc"
+#include "turret/machinegun.qc"
+#include "turret/mlrs.qc"
+#include "turret/phaser.qc"
+#include "turret/plasma.qc"
+#include "turret/plasma_dual.qc"
+#include "turret/tesla.qc"
+#include "turret/walker.qc"
index f0fc719..c23a138 100644 (file)
@@ -1,5 +1,22 @@
 #include "all.qh"
 
+Turret get_turretinfo(int id)
+{
+    if (id < 1 || id > TUR_COUNT - 1)
+        return TUR_Null;
+    Turret m = turret_info[id];
+    if (m) return m;
+    return TUR_Null;
+}
+
+#ifdef SVQC
+#include "sv_turrets.qh"
+#endif
+
+#ifdef CSQC
+#include "cl_turrets.qh"
+#endif
+
 #define IMPLEMENTATION
 #include "all.inc"
 #undef IMPLEMENTATION
index 4f8a728..87a30c9 100644 (file)
-#ifndef TURRETS_H
-#define TURRETS_H
+#ifndef TURRETS_ALL_H
+#define TURRETS_ALL_H
 
-// turret requests
-#define TR_SETUP          1 // (BOTH) setup turret data
-#define TR_THINK                 2 // (SERVER) logic to run every frame
-#define TR_DEATH          3 // (SERVER) called when turret dies
-#define TR_PRECACHE       4 // (BOTH) precaches models/sounds used by this turret
-#define TR_ATTACK         5 // (SERVER) called when turret attacks
-#define TR_CONFIG         6 // (ALL)
-
-// functions:
-entity get_turretinfo(int id);
-
-// fields:
-.entity tur_head;
-
-// target selection flags
-.int target_select_flags;
-.int target_validate_flags;
-const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets
-const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets
-const int TFL_TARGETSELECT_PLAYERS = 8; // target players
-const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles
-const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events
-const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection
-const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range
-const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates
-const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered
-const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates
-const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets
-const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range
-const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles
-
-// aim flags
-.int aim_flags;
-const int TFL_AIM_NO = 1; // no aiming
-const int TFL_AIM_SPLASH = 2; // aim for ground around the target's feet
-const int TFL_AIM_LEAD = 4; // try to predict target movement
-const int TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading
-const int TFL_AIM_ZPREDICT = 16; // predict target's z position at impact
-const int TFL_AIM_SIMPLE = 32; // aim at player's current location
-
-// tracking flags
-.int track_flags;
-const int TFL_TRACK_NO = 2; // don't move head
-const int TFL_TRACK_PITCH = 4; // pitch head
-const int TFL_TRACK_ROTATE = 8; // rotate head
-
-// prefire checks
-.int firecheck_flags;
-const int TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?)
-const int TFL_FIRECHECK_DISTANCES = 8; // another range check
-const int TFL_FIRECHECK_LOS = 16; // line of sight
-const int TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot
-const int TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint
-const int TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot
-const int TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates
-const int TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire
-const int TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt
-const int TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max
-const int TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays
-const int TFL_FIRECHECK_NO = 16384; // no prefire checks
-
-// attack flags
-.int shoot_flags;
-const int TFL_SHOOT_NO = 64; // no attacking
-const int TFL_SHOOT_VOLLY = 2; // fire in vollies
-const int TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost
-const int TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets
-const int TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode)
-const int TFL_SHOOT_CUSTOM = 32; // custom attacking
-
-// turret capabilities
-.int turret_flags;
-const int TUR_FLAG_NONE = 0; // no abilities
-const int TUR_FLAG_SNIPER = 2; // sniping turret
-const int TUR_FLAG_SPLASH = 4; // can deal splash damage
-const int TUR_FLAG_HITSCAN = 8; // hit scan
-const int TUR_FLAG_MULTIGUN = 16; // multiple guns
-const int TUR_FLAG_GUIDED = 32; // laser guided projectiles
-const int TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles
-const int TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles
-const int TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles
-const int TUR_FLAG_PLAYER = 512; // can damage players
-const int TUR_FLAG_MISSILE = 1024; // can damage missiles
-const int TUR_FLAG_SUPPORT = 2048; // supports other units
-const int TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition
-const int TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources
-const int TUR_FLAG_MOVE = 16384; // can move
-const int TUR_FLAG_ROAM = 32768; // roams around if not attacking
-const int TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret
-
-// ammo types
-#define ammo_flags currentammo
-const int TFL_AMMO_NONE = 64; // doesn't use ammo
-const int TFL_AMMO_ENERGY = 2; // uses power
-const int TFL_AMMO_BULLETS = 4; // uses bullets
-const int TFL_AMMO_ROCKETS = 8; // uses explosives
-const int TFL_AMMO_RECHARGE = 16; // regenerates ammo
-const int TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units
-
-// damage flags
-.int damage_flags;
-const int TFL_DMG_NO = 256; // doesn't take damage
-const int TFL_DMG_YES = 2; // can be damaged
-const int TFL_DMG_TEAM = 4; // can be damaged by teammates
-const int TFL_DMG_RETALIATE = 8; // target attackers
-const int TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team
-const int TFL_DMG_TARGETLOSS = 32; // loses target when damaged
-const int TFL_DMG_AIMSHAKE = 64; // damage throws off aim
-const int TFL_DMG_HEADSHAKE = 128; // damage shakes head
-const int TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning
-
-// spawn flags
-const int TSF_SUSPENDED = 1;
-const int TSF_TERRAINBASE = 2; // currently unused
-const int TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration
-const int TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible
-const int TSL_NO_RESPAWN = 16; // don't re-spawn
-const int TSL_ROAM = 32; // roam while idle
-
-// send flags
-const int TNSF_UPDATE       = 2;
-const int TNSF_STATUS       = 4;
-const int TNSF_SETUP        = 8;
-const int TNSF_ANG          = 16;
-const int TNSF_AVEL         = 32;
-const int TNSF_MOVE         = 64;
-.float anim_start_time;
-const int TNSF_ANIM         = 128;
-
-const int TNSF_FULL_UPDATE  = 16777215;
-
-
-// other useful macros
-#define TUR_ACTION(turrettype,mrequest) (get_turretinfo(turrettype)).turret_func(mrequest)
-#define TUR_NAME(turrettype) (get_turretinfo(turrettype)).turret_name
-
-// =====================
-//  Turret Registration
-// =====================
-
-float t_null(float dummy);
-void register_turret(entity e, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname);
+#include "turret.qh"
 
 const int TUR_MAXCOUNT = 24;
 entity turret_info[TUR_MAXCOUNT], turret_info_first, turret_info_last;
 float TUR_COUNT;
 
-#define _REGISTER_TURRET(id, func, turretflags, min_s, max_s, modelname, headmodelname, shortname, mname) \
-       float func(float); \
-       REGISTER(RegisterTurrets, TUR, turret_info, TUR_COUNT, id, m_id, NEW(Turret)) { \
-               register_turret(this, func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname); \
-       }
 void RegisterTurrets();
 REGISTER_REGISTRY(RegisterTurrets)
 
-#ifdef MENUQC
-#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \
-       _REGISTER_TURRET(id,t_null,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname)
-#else
-#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \
-       _REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname)
-#endif
-
-float t_null(float dummy) { return 0; }
-
-CLASS(Turret, Object)
-    ATTRIB(Turret, m_id, int, 0)
+#define REGISTER_TURRET(id, inst) REGISTER(RegisterTurrets, TUR, turret_info, TUR_COUNT, id, m_id, inst)
 
-    ATTRIB(Turret, turretid, int, 0)
-    /** short name */
-    ATTRIB(Turret, netname, string, string_null)
-    /** human readable name */
-    ATTRIB(Turret, turret_name, string, string_null)
-    /** t_... */
-    ATTRIB(Turret, turret_func, float(float), t_null)
-    /** currently a copy of the model */
-    ATTRIB(Turret, mdl, string, string_null)
-    /** full name of model */
-    ATTRIB(Turret, model, string, string_null)
-    /** full name of tur_head model */
-    ATTRIB(Turret, head_model, string, string_null)
-    /** TODO: deprecate! */
-    ATTRIB(Turret, cvar_basename, string, string_null)
-
-    ATTRIB(Turret, spawnflags, int, 0)
-    /** turret hitbox size */
-    ATTRIB(Turret, mins, vector, '0 0 0')
-    /** turret hitbox size */
-    ATTRIB(Turret, maxs, vector, '0 0 0')
-
-    METHOD(Turret, display, void(entity this, void(string name, string icon) returns)) {
-        returns(this.turret_name, string_null);
-    }
-
-void register_turret(Turret e, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname)
-{
-       e.classname = "turret_info";
-       e.turretid = e.m_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));
-}
-ENDCLASS(Turret)
-
-
-REGISTER_TURRET(Null,
-       t_null,
-       0,
-       '-0 -0 -0',
-       '0 0 0',
-       "",
-       "",
-       "",
-       "Turret"
-);
+entity get_turretinfo(int id);
 
-entity get_turretinfo(float id)
-{
-       entity m;
-       if(id < 1 || id > TUR_COUNT - 1)
-               return TUR_Null;
-       m = turret_info[id];
-       if(m)
-               return m;
-       return TUR_Null;
-}
+REGISTER_TURRET(Null, NEW(Turret));
 
 #include "all.inc"
 
index b2a0fe1..03cb16b 100644 (file)
@@ -1,5 +1,3 @@
-#include "all.qh"
-
 void turret_remove()
 {SELFPARAM();
        remove(self.tur_head);
@@ -86,7 +84,7 @@ void turret_draw2d()
                o_z = 0;
                if(hud != HUD_NORMAL)
                {
-                       if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE)
+                       if((get_turretinfo(self.m_id)).spawnflags & TUR_FLAG_MOVE)
                                txt = "gfx/vehicles/turret_moving.tga";
                        else
                                txt = "gfx/vehicles/turret_stationary.tga";
@@ -205,12 +203,12 @@ void turret_draw2d()
 
 void turret_construct()
 {SELFPARAM();
-       entity tur = get_turretinfo(self.turretid);
+       entity tur = get_turretinfo(self.m_id);
 
        if(self.tur_head == world)
                self.tur_head = spawn();
 
-       self.netname = TUR_NAME(self.turretid);
+       self.netname = tur.turret_name;
 
        setorigin(self, self.origin);
        _setmodel(self, tur.model);
@@ -218,7 +216,7 @@ void turret_construct()
        setsize(self, tur.mins, tur.maxs);
        setsize(self.tur_head, '0 0 0', '0 0 0');
 
-       if(self.turretid == TUR_EWHEEL.m_id)
+       if(self.m_id == TUR_EWHEEL.m_id)
                setattachment(self.tur_head, self, "");
        else
                setattachment(self.tur_head, self, "tag_head");
@@ -243,7 +241,7 @@ void turret_construct()
        self.teamradar_color = '1 0 0';
        self.alpha = 1;
 
-       TUR_ACTION(self.turretid, TR_SETUP);
+       tur.tr_setup(tur, self);
 }
 
 entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode);
@@ -325,12 +323,12 @@ void turret_die()
        if (!autocvar_cl_nogibs)
        {
                // Base
-               if(self.turretid == TUR_EWHEEL.m_id)
-                       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.m_id)
-                       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.m_id)
-                       turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false);
+               if(self.m_id == TUR_EWHEEL.m_id)
+                       turret_gibtoss((get_turretinfo(self.m_id)).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.m_id == TUR_WALKER.m_id)
+                       turret_gibtoss((get_turretinfo(self.m_id)).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.m_id == TUR_TESLA.m_id)
+                       turret_gibtoss((get_turretinfo(self.m_id)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false);
                else
                {
                        if (random() > 0.5)
@@ -342,7 +340,7 @@ void turret_die()
                        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);
+                       entity headgib = turret_gibtoss((get_turretinfo(self.m_id)).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;
@@ -364,7 +362,7 @@ void ent_turret()
 
        if(sf & TNSF_SETUP)
        {
-               self.turretid = ReadByte();
+               self.m_id = ReadByte();
 
                self.origin_x = ReadCoord();
                self.origin_y = ReadCoord();
index f8ea64a..0f8ff94 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef CL_TURRETS_H
 #define CL_TURRETS_H
 
-#include "all.qh"
-
 void ent_turret();
 
 #endif
index 1a8c2cb..e052d6c 100644 (file)
@@ -35,19 +35,19 @@ void Dump_Turret_Settings(void)
                        { tur_config_queue[x] = string_null; }
 
                // step 2: build new queue
-               TUR_ACTION(i, TR_CONFIG);
+               it.tr_config(it);
 
                // 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)))
+               TUR_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, it.turret_name))
                for(x = 0; x <= TUR_CONFIG_COUNT; ++x)
                        { TUR_CONFIG_WRITETOFILE(tur_config_queue[x]) }
                TUR_CONFIG_WRITETOFILE("// }}}\n")
 
                // step 5: debug info
-               LOG_INFO(sprintf("#%d: %s: %d settings...\n", i, TUR_NAME(i), TUR_CONFIG_COUNT));
+               LOG_INFO(sprintf("#%d: %s: %d settings...\n", i, it.turret_name, TUR_CONFIG_COUNT));
                totalsettings += TUR_CONFIG_COUNT;
        }));
 
index c17061b..a53a81e 100644 (file)
@@ -1,5 +1,4 @@
 #ifdef SVQC
-#include "all.qh"
 #include "../../server/autocvars.qh"
 
 // Generic aiming
@@ -189,9 +188,10 @@ void turret_die()
 // 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);
 
+       Turret tur = get_turretinfo(self.m_id);
        if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN)
        {
-               TUR_ACTION(self.turretid, TR_DEATH);
+               tur.tr_death(tur, self);
 
                remove(self.tur_head);
                remove(self);
@@ -203,7 +203,7 @@ void turret_die()
                self.nextthink   = time + 0.2;
                self.think               = turret_hide;
 
-               TUR_ACTION(self.turretid, TR_DEATH);
+               tur.tr_death(tur, self);
        }
 }
 
@@ -276,7 +276,8 @@ void turret_respawn()
 
        self.SendFlags = TNSF_FULL_UPDATE;
 
-       TUR_ACTION(self.turretid, TR_SETUP);
+       Turret tur = get_turretinfo(self.m_id);
+       tur.tr_setup(tur, self);
 }
 
 
@@ -305,7 +306,7 @@ float turret_send(entity to, float sf)
        WriteByte(MSG_ENTITY, sf);
        if(sf & TNSF_SETUP)
        {
-               WriteByte(MSG_ENTITY, self.turretid);
+               WriteByte(MSG_ENTITY, self.m_id);
 
                WriteCoord(MSG_ENTITY, self.origin_x);
                WriteCoord(MSG_ENTITY, self.origin_y);
@@ -359,8 +360,9 @@ float turret_send(entity to, float sf)
        return true;
 }
 
-void load_unit_settings(entity ent, string unitname, float is_reload)
+void load_unit_settings(entity ent, float is_reload)
 {SELFPARAM();
+       string unitname = ent.netname;
        string sbase;
 
        if (ent == world)
@@ -419,8 +421,10 @@ void load_unit_settings(entity ent, string unitname, float is_reload)
        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);
+       if(is_reload) {
+               Turret tur = get_turretinfo(self.m_id);
+               tur.tr_setup(tur, self);
+       }
 }
 
 void turret_projectile_explode()
@@ -990,7 +994,8 @@ void turret_fire()
        if(MUTATOR_CALLHOOK(TurretFire, self))
                return;
 
-       TUR_ACTION(self.turretid, TR_ATTACK);
+       Turret info = get_turretinfo(self.m_id);
+       info.tr_attack(info);
 
        self.attack_finished_single = time + self.shot_refire;
        self.ammo -= self.shot_dmg;
@@ -1118,7 +1123,8 @@ void turret_think()
                                if (turret_checkfire())
                                        turret_fire();
 
-                               TUR_ACTION(self.turretid, TR_THINK);
+                               Turret tur = get_turretinfo(self.m_id);
+                               tur.tr_think(tur);
 
                                return;
                        }
@@ -1156,7 +1162,8 @@ void turret_think()
                        if(!(self.track_flags & TFL_TRACK_NO))
                                turret_track();
 
-                       TUR_ACTION(self.turretid, TR_THINK);
+                       Turret tur = get_turretinfo(self.m_id);
+                       tur.tr_think(tur);
 
                        // And bail.
                        return;
@@ -1179,7 +1186,8 @@ void turret_think()
                        turret_fire();
        }
 
-       TUR_ACTION(self.turretid, TR_THINK);
+       Turret tur = get_turretinfo(self.m_id);
+       tur.tr_think(tur);
 }
 
 /*
@@ -1219,8 +1227,9 @@ void turrets_manager_think()
                {
                        if (IS_TURRET(e))
                        {
-                               load_unit_settings(e,e.cvar_basename,1);
-                               TUR_ACTION(self.turretid, TR_THINK);
+                               load_unit_settings(e,1);
+                               Turret tur = get_turretinfo(self.m_id);
+                               tur.tr_think(tur);
                        }
 
                        e = nextent(e);
@@ -1229,19 +1238,49 @@ void turrets_manager_think()
        }
 }
 
-float turret_initialize(float tur_id)
+void turret_initparams(entity tur)
+{
+       #define TRY(x) (x) ? (x)
+       tur.respawntime                 = max  (-1,              (TRY(tur.respawntime)               :  60                                         ));
+       tur.shot_refire                 = bound(0.01,            (TRY(tur.shot_refire)               :  1                                          ), 9999);
+       tur.shot_dmg                    = max  (1,               (TRY(tur.shot_dmg)                  :  tur.shot_refire * 50                       ));
+       tur.shot_radius                 = max  (1,               (TRY(tur.shot_radius)               :  tur.shot_dmg * 0.5                         ));
+       tur.shot_speed                  = max  (1,               (TRY(tur.shot_speed)                :  2500                                       ));
+       tur.shot_spread                 = bound(0.0001,          (TRY(tur.shot_spread)               :  0.0125                                     ), 500);
+       tur.shot_force                  = bound(0.001,           (TRY(tur.shot_force)                :  tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000);
+       tur.shot_volly                  = bound(1,               (TRY(tur.shot_volly)                :  1                                          ), floor(tur.ammo_max / tur.shot_dmg));
+       tur.shot_volly_refire           = bound(tur.shot_refire, (TRY(tur.shot_volly_refire)         :  tur.shot_refire * tur.shot_volly           ), 60);
+       tur.target_range                = bound(0,               (TRY(tur.target_range)              :  tur.shot_speed * 0.5                       ), MAX_SHOT_DISTANCE);
+       tur.target_range_min            = bound(0,               (TRY(tur.target_range_min)          :  tur.shot_radius * 2                        ), MAX_SHOT_DISTANCE);
+       tur.target_range_optimal        = bound(0,               (TRY(tur.target_range_optimal)      :  tur.target_range * 0.5                     ), MAX_SHOT_DISTANCE);
+       tur.aim_maxrotate               = bound(0,               (TRY(tur.aim_maxrotate)             :  90                                         ), 360);
+       tur.aim_maxpitch                = bound(0,               (TRY(tur.aim_maxpitch)              :  20                                         ), 90);
+       tur.aim_speed                   = bound(0.1,             (TRY(tur.aim_speed)                 :  36                                         ), 1000);
+       tur.aim_firetolerance_dist      = bound(0.1,             (TRY(tur.aim_firetolerance_dist)    :  5 + (tur.shot_radius * 2)                  ), MAX_SHOT_DISTANCE);
+       tur.target_select_rangebias     = bound(-10,             (TRY(tur.target_select_rangebias)   :  1                                          ), 10);
+       tur.target_select_samebias      = bound(-10,             (TRY(tur.target_select_samebias)    :  1                                          ), 10);
+       tur.target_select_anglebias     = bound(-10,             (TRY(tur.target_select_anglebias)   :  1                                          ), 10);
+       tur.target_select_missilebias   = bound(-10,             (TRY(tur.target_select_missilebias) :  1                                          ), 10);
+       tur.target_select_playerbias    = bound(-10,             (TRY(tur.target_select_playerbias)  :  1                                          ), 10);
+       tur.ammo_max                    = max  (tur.shot_dmg,    (TRY(tur.ammo_max)                  :  tur.shot_dmg * 10                          ));
+       tur.ammo_recharge               = max  (0,               (TRY(tur.ammo_recharge)             :  tur.shot_dmg * 0.5                         ));
+       #undef TRY
+}
+
+float turret_initialize(Turret tur)
 {SELFPARAM();
        if(!autocvar_g_turrets)
                return false;
 
-       entity e;
-       entity tur = get_turretinfo(tur_id);
-       if(tur.turretid == 0)
+       if(tur.m_id == 0)
                return false; // invalid turret
 
-       if(!self.tur_head) { TUR_ACTION(tur_id, TR_PRECACHE); } // if tur_head exists, we can assume this turret re-spawned
+       // if tur_head exists, we can assume this turret re-spawned
+       if(!self.tur_head) {
+               tur.tr_precache(tur);
+       }
 
-       e = find(world, classname, "turret_manager");
+       entity e = find(world, classname, "turret_manager");
        if(!e)
        {
                e = spawn();
@@ -1253,8 +1292,8 @@ float turret_initialize(float tur_id)
        if(!(self.spawnflags & TSF_SUSPENDED))
                builtin_droptofloor();
 
-       self.cvar_basename = tur.cvar_basename;
-       load_unit_settings(self, self.cvar_basename, 0);
+       self.netname = tur.netname;
+       load_unit_settings(self, 0);
 
        if(!self.team || !teamplay)             { self.team = MAX_SHOT_DISTANCE; }
        if(!self.ticrate)                               { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
@@ -1283,29 +1322,7 @@ float turret_initialize(float tur_id)
                if(!self.track_blendrate)               { self.track_blendrate = 0.35; }
        }
 
-       self.respawntime                                = max(-1, ((!self.respawntime) ? 60 : self.respawntime));
-       self.shot_refire                                = bound(0.01, ((!self.shot_refire) ? 1 : self.shot_refire), 9999);
-       self.shot_dmg                                   = max(1, ((!self.shot_dmg) ? self.shot_refire * 50 : self.shot_dmg));
-       self.shot_radius                                = max(1, ((!self.shot_radius) ? self.shot_dmg * 0.5 : self.shot_radius));
-       self.shot_speed                                 = max(1, ((!self.shot_speed) ? 2500 : self.shot_speed));
-       self.shot_spread                                = bound(0.0001, ((!self.shot_spread) ? 0.0125 : self.shot_spread), 500);
-       self.shot_force                                 = bound(0.001, ((!self.shot_force) ? self.shot_dmg * 0.5 + self.shot_radius * 0.5 : self.shot_force), 5000);
-       self.shot_volly                                 = bound(1, ((!self.shot_volly) ? 1 : self.shot_volly), floor(self.ammo_max / self.shot_dmg));
-       self.shot_volly_refire                  = bound(self.shot_refire, ((!self.shot_volly_refire) ? self.shot_refire * self.shot_volly : self.shot_volly_refire), 60);
-       self.target_range                               = bound(0, ((!self.target_range) ? self.shot_speed * 0.5 : self.target_range), MAX_SHOT_DISTANCE);
-       self.target_range_min                   = bound(0, ((!self.target_range_min) ? self.shot_radius * 2 : self.target_range_min), MAX_SHOT_DISTANCE);
-       self.target_range_optimal               = bound(0, ((!self.target_range_optimal) ? self.target_range * 0.5 : self.target_range_optimal), MAX_SHOT_DISTANCE);
-       self.aim_maxrotate                              = bound(0, ((!self.aim_maxrotate) ? 90 : self.aim_maxrotate), 360);
-       self.aim_maxpitch                               = bound(0, ((!self.aim_maxpitch) ? 20 : self.aim_maxpitch), 90);
-       self.aim_speed                                  = bound(0.1, ((!self.aim_speed) ? 36 : self.aim_speed), 1000);
-       self.aim_firetolerance_dist     = bound(0.1, ((!self.aim_firetolerance_dist) ? 5 + (self.shot_radius * 2) : self.aim_firetolerance_dist), MAX_SHOT_DISTANCE);
-       self.target_select_rangebias    = bound(-10, ((!self.target_select_rangebias) ? 1 : self.target_select_rangebias), 10);
-       self.target_select_samebias     = bound(-10, ((!self.target_select_samebias) ? 1 : self.target_select_samebias), 10);
-       self.target_select_anglebias    = bound(-10, ((!self.target_select_anglebias) ? 1 : self.target_select_anglebias), 10);
-       self.target_select_missilebias  = bound(-10, ((!self.target_select_missilebias) ? 1 : self.target_select_missilebias), 10);
-       self.target_select_playerbias   = bound(-10, ((!self.target_select_playerbias) ? 1 : self.target_select_playerbias), 10);
-       self.ammo_max                                   = max(self.shot_dmg, ((!self.ammo_max) ? self.shot_dmg * 10 : self.ammo_max));
-       self.ammo_recharge                              = max(0, ((!self.ammo_recharge) ? self.shot_dmg * 0.5 : self.ammo_recharge));
+       turret_initparams(self);
 
        self.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags);
 
@@ -1331,11 +1348,11 @@ float turret_initialize(float tur_id)
        _setmodel(self, tur.model);
        setsize(self, tur.mins, tur.maxs);
 
-       self.turretid                           = tur_id;
+       self.m_id                                       = tur.m_id;
        self.classname                          = "turret_main";
        self.active                                     = ACTIVE_ACTIVE;
        self.effects                            = EF_NODRAW;
-       self.netname                            = TUR_NAME(tur_id);
+       self.netname                            = tur.turret_name;
        self.ticrate                            = bound(sys_frametime, self.ticrate, 60);
        self.max_health                         = self.health;
        self.target_validate_flags      = self.target_select_flags;
@@ -1395,7 +1412,7 @@ float turret_initialize(float tur_id)
        turret_respawn();
        turret_tag_fire_update();
 
-       TUR_ACTION(tur_id, TR_SETUP);
+       tur.tr_setup(tur, self);
 
        if(MUTATOR_CALLHOOK(TurretSpawn, self))
                return false;
index 1a4ade6..2d5a5f5 100644 (file)
@@ -1,7 +1,11 @@
 #ifndef SV_TURRETS_H
 #define SV_TURRETS_H
 
-#include "all.qh"
+entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim);
+void turret_projectile_explode();
+float turret_validate_target(entity e_turret, entity e_target, float validate_flags);
+float turret_firecheck();
+entity turret_select_target();
 
 // turret fields
 .float ticrate; // interal ai think rate
@@ -83,7 +87,7 @@ void turret_do_updates(entity e_turret);
 
 void turrets_setframe(float _frame, float client_only);
 
-float turret_initialize(float tur_id);
+float turret_initialize(Turret tur);
 
 /// Function to use for target evaluation. usualy turret_targetscore_generic
 .float(entity _turret, entity _target) turret_score_target;
diff --git a/qcsrc/common/turrets/turret.qh b/qcsrc/common/turrets/turret.qh
new file mode 100644 (file)
index 0000000..8862f47
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef TURRET_H
+#define TURRET_H
+
+#include "../weapons/all.qh"
+
+CLASS(Turret, Object)
+    ATTRIB(Turret, m_id, int, 0)
+
+    /** short name */
+    ATTRIB(Turret, netname, string, string_null)
+    /** human readable name */
+    ATTRIB(Turret, turret_name, string, _("Turret"))
+    /** currently a copy of the model */
+    ATTRIB(Turret, mdl, string, string_null)
+    /** full name of model */
+    ATTRIB(Turret, model, string, string_null)
+    /** full name of tur_head model */
+    ATTRIB(Turret, head_model, string, string_null)
+
+    ATTRIB(Turret, spawnflags, int, 0)
+    /** turret hitbox size */
+    ATTRIB(Turret, mins, vector, '-0 -0 -0')
+    /** turret hitbox size */
+    ATTRIB(Turret, maxs, vector, '0 0 0')
+
+    METHOD(Turret, display, void(entity this, void(string name, string icon) returns)) {
+        returns(this.turret_name, string_null);
+    }
+    /** (BOTH) setup turret data */
+    METHOD(Turret, tr_setup, void(Turret this, entity it)) {
+
+    }
+    /** (SERVER) logic to run every frame */
+    METHOD(Turret, tr_think, void(Turret this)) {
+
+    }
+    /** (SERVER) called when turret dies */
+    METHOD(Turret, tr_death, void(Turret this, entity it)) {
+
+    }
+    /** (BOTH) precaches models/sounds used by this turret */
+    METHOD(Turret, tr_precache, void(Turret this)) {
+
+    }
+    ATTRIB(Turret, m_weapon, Weapon, WEP_Null)
+    /** (SERVER) called when turret attacks */
+    METHOD(Turret, tr_attack, void(Turret this)) {
+        Weapon w = this.m_weapon;
+        w.wr_think(w, self, true, false);
+    }
+    /** (ALL) */
+    METHOD(Turret, tr_config, void(Turret this)) {
+        // TODO
+    }
+ENDCLASS(Turret)
+
+// fields:
+.entity tur_head;
+
+// target selection flags
+.int target_select_flags;
+.int target_validate_flags;
+const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets
+const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets
+const int TFL_TARGETSELECT_PLAYERS = 8; // target players
+const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles
+const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events
+const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection
+const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range
+const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates
+const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered
+const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates
+const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets
+const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range
+const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles
+
+// aim flags
+.int aim_flags;
+const int TFL_AIM_NO = 1; // no aiming
+const int TFL_AIM_SPLASH = 2; // aim for ground around the target's feet
+const int TFL_AIM_LEAD = 4; // try to predict target movement
+const int TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading
+const int TFL_AIM_ZPREDICT = 16; // predict target's z position at impact
+const int TFL_AIM_SIMPLE = 32; // aim at player's current location
+
+// tracking flags
+.int track_flags;
+const int TFL_TRACK_NO = 2; // don't move head
+const int TFL_TRACK_PITCH = 4; // pitch head
+const int TFL_TRACK_ROTATE = 8; // rotate head
+
+// prefire checks
+.int firecheck_flags;
+const int TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?)
+const int TFL_FIRECHECK_DISTANCES = 8; // another range check
+const int TFL_FIRECHECK_LOS = 16; // line of sight
+const int TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot
+const int TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint
+const int TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot
+const int TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates
+const int TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire
+const int TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt
+const int TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max
+const int TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays
+const int TFL_FIRECHECK_NO = 16384; // no prefire checks
+
+// attack flags
+.int shoot_flags;
+const int TFL_SHOOT_NO = 64; // no attacking
+const int TFL_SHOOT_VOLLY = 2; // fire in vollies
+const int TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost
+const int TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets
+const int TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode)
+const int TFL_SHOOT_CUSTOM = 32; // custom attacking
+
+// turret capabilities
+.int turret_flags;
+const int TUR_FLAG_NONE = 0; // no abilities
+const int TUR_FLAG_SNIPER = 2; // sniping turret
+const int TUR_FLAG_SPLASH = 4; // can deal splash damage
+const int TUR_FLAG_HITSCAN = 8; // hit scan
+const int TUR_FLAG_MULTIGUN = 16; // multiple guns
+const int TUR_FLAG_GUIDED = 32; // laser guided projectiles
+const int TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles
+const int TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles
+const int TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles
+const int TUR_FLAG_PLAYER = 512; // can damage players
+const int TUR_FLAG_MISSILE = 1024; // can damage missiles
+const int TUR_FLAG_SUPPORT = 2048; // supports other units
+const int TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition
+const int TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources
+const int TUR_FLAG_MOVE = 16384; // can move
+const int TUR_FLAG_ROAM = 32768; // roams around if not attacking
+const int TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret
+
+// ammo types
+#define ammo_flags currentammo
+const int TFL_AMMO_NONE = 64; // doesn't use ammo
+const int TFL_AMMO_ENERGY = 2; // uses power
+const int TFL_AMMO_BULLETS = 4; // uses bullets
+const int TFL_AMMO_ROCKETS = 8; // uses explosives
+const int TFL_AMMO_RECHARGE = 16; // regenerates ammo
+const int TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units
+
+// damage flags
+.int damage_flags;
+const int TFL_DMG_NO = 256; // doesn't take damage
+const int TFL_DMG_YES = 2; // can be damaged
+const int TFL_DMG_TEAM = 4; // can be damaged by teammates
+const int TFL_DMG_RETALIATE = 8; // target attackers
+const int TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team
+const int TFL_DMG_TARGETLOSS = 32; // loses target when damaged
+const int TFL_DMG_AIMSHAKE = 64; // damage throws off aim
+const int TFL_DMG_HEADSHAKE = 128; // damage shakes head
+const int TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning
+
+// spawn flags
+const int TSF_SUSPENDED = 1;
+const int TSF_TERRAINBASE = 2; // currently unused
+const int TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration
+const int TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible
+const int TSL_NO_RESPAWN = 16; // don't re-spawn
+const int TSL_ROAM = 32; // roam while idle
+
+// send flags
+const int TNSF_UPDATE       = 2;
+const int TNSF_STATUS       = 4;
+const int TNSF_SETUP        = 8;
+const int TNSF_ANG          = 16;
+const int TNSF_AVEL         = 32;
+const int TNSF_MOVE         = 64;
+.float anim_start_time;
+const int TNSF_ANIM         = 128;
+
+const int TNSF_FULL_UPDATE  = 16777215;
+
+#endif
diff --git a/qcsrc/common/turrets/turret/ewheel.qc b/qcsrc/common/turrets/turret/ewheel.qc
new file mode 100644 (file)
index 0000000..cdb9a60
--- /dev/null
@@ -0,0 +1,270 @@
+#ifndef TURRET_EWHEEL_H
+#define TURRET_EWHEEL_H
+
+//#define EWHEEL_FANCYPATH
+
+#include "ewheel_weapon.qc"
+
+CLASS(EWheel, Turret)
+/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM);
+/* mins       */ ATTRIB(EWheel, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(EWheel, maxs, vector, '32 32 48');
+/* modelname  */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3");
+/* model      */ ATTRIB(EWheel, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(EWheel, head_model, string, strzone(strcat("models/turrets/", "ewheel-gun1.md3")));
+/* netname    */ ATTRIB(EWheel, netname, string, "ewheel");
+/* fullname   */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret"));
+    ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL);
+ENDCLASS(EWheel)
+REGISTER_TURRET(EWHEEL, NEW(EWheel));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "ewheel_weapon.qc"
+
+#ifdef SVQC
+
+float autocvar_g_turrets_unit_ewheel_speed_fast;
+float autocvar_g_turrets_unit_ewheel_speed_slow;
+float autocvar_g_turrets_unit_ewheel_speed_slower;
+float autocvar_g_turrets_unit_ewheel_speed_stop;
+float autocvar_g_turrets_unit_ewheel_turnrate;
+
+const float ewheel_anim_stop = 0;
+const float ewheel_anim_fwd_slow = 1;
+const float ewheel_anim_fwd_fast = 2;
+const float ewheel_anim_bck_slow = 3;
+const float ewheel_anim_bck_fast = 4;
+
+void ewheel_move_path()
+{SELFPARAM();
+#ifdef EWHEEL_FANCYPATH
+    // Are we close enougth to a path node to switch to the next?
+    if (vlen(self.origin  - self.pathcurrent.origin) < 64)
+        if (self.pathcurrent.path_next == world)
+        {
+            // Path endpoint reached
+            pathlib_deletepath(self.pathcurrent.owner);
+            self.pathcurrent = world;
+
+            if (self.pathgoal)
+            {
+                if (self.pathgoal.use)
+                    self.pathgoal.use();
+
+                if (self.pathgoal.enemy)
+                {
+                    self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin);
+                    self.pathgoal = self.pathgoal.enemy;
+                }
+            }
+            else
+                self.pathgoal = world;
+        }
+        else
+            self.pathcurrent = self.pathcurrent.path_next;
+
+#else
+    if (vlen(self.origin - self.pathcurrent.origin) < 64)
+        self.pathcurrent = self.pathcurrent.enemy;
+#endif
+
+    if (self.pathcurrent)
+    {
+
+        self.moveto = self.pathcurrent.origin;
+        self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
+    }
+}
+
+void ewheel_move_enemy()
+{SELFPARAM();
+    float newframe;
+
+    self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal);
+
+    self.moveto  = self.origin + self.steerto * 128;
+
+    if (self.tur_dist_enemy > self.target_range_optimal)
+    {
+        if ( self.tur_head.spawnshieldtime < 1 )
+        {
+            newframe = ewheel_anim_fwd_fast;
+            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
+        }
+        else if (self.tur_head.spawnshieldtime < 2)
+        {
+
+            newframe = ewheel_anim_fwd_slow;
+            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
+       }
+        else
+        {
+            newframe = ewheel_anim_fwd_slow;
+            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4);
+        }
+    }
+    else if (self.tur_dist_enemy < self.target_range_optimal * 0.5)
+    {
+        newframe = ewheel_anim_bck_slow;
+        movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
+    }
+    else
+    {
+        newframe = ewheel_anim_stop;
+        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
+    }
+
+    turrets_setframe(newframe, false);
+}
+
+void ewheel_move_idle()
+{SELFPARAM();
+    if(self.frame != 0)
+    {
+        self.SendFlags |= TNSF_ANIM;
+        self.anim_start_time = time;
+    }
+
+    self.frame = 0;
+    if (vlen(self.velocity))
+        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
+}
+
+spawnfunc(turret_ewheel) { if(!turret_initialize(TUR_EWHEEL)) remove(self); }
+
+        METHOD(EWheel, tr_think, void(EWheel thistur))
+        {
+            SELFPARAM();
+            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;
+        }
+        METHOD(EWheel, tr_death, void(EWheel this, entity it))
+        {
+            it.velocity = '0 0 0';
+
+#ifdef EWHEEL_FANCYPATH
+            if (self.pathcurrent)
+                pathlib_deletepath(it.pathcurrent.owner);
+#endif
+            it.pathcurrent = NULL;
+        }
+        METHOD(EWheel, tr_setup, void(EWheel this, entity it))
+        {
+            entity e;
+
+            if(it.movetype == MOVETYPE_WALK)
+            {
+                it.velocity = '0 0 0';
+                it.enemy = world;
+
+                setorigin(it, it.pos1);
+
+                if (it.target != "")
+                {
+                    e = find(world, targetname, it.target);
+                    if (!e)
+                    {
+                        LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!\n");
+                        it.target = "";
+                    }
+
+                    if (e.classname != "turret_checkpoint")
+                        LOG_TRACE("Warning: not a turrret path\n");
+                    else
+                    {
+
+#ifdef EWHEEL_FANCYPATH
+                        it.pathcurrent = WALKER_PATH(it.origin,e.origin);
+                        it.pathgoal = e;
+#else
+                        it.pathcurrent  = e;
+#endif
+                    }
+                }
+            }
+
+            it.iscreature                              = true;
+            it.teleportable                    = TELEPORT_NORMAL;
+            it.damagedbycontents               = true;
+            it.movetype                                = MOVETYPE_WALK;
+            it.solid                                   = SOLID_SLIDEBOX;
+            it.takedamage                              = DAMAGE_AIM;
+            it.idle_aim                                = '0 0 0';
+            it.pos1                                    = it.origin;
+            it.target_select_flags     = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+            it.target_validate_flags   = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+            it.frame                                   = it.tur_head.frame = 1;
+            it.ammo_flags                              = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+
+            // Convert from dgr / sec to dgr / tic
+            it.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate);
+            it.tur_head.aim_speed = it.tur_head.aim_speed / (1 / it.ticrate);
+        }
+
+#endif // SVQC
+#ifdef CSQC
+
+void ewheel_draw()
+{SELFPARAM();
+    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);
+}
+
+        METHOD(EWheel, tr_setup, void(EWheel this, entity it))
+        {
+            it.gravity         = 1;
+            it.movetype                = MOVETYPE_BOUNCE;
+            it.move_movetype   = MOVETYPE_BOUNCE;
+            it.move_origin     = it.origin;
+            it.move_time               = time;
+            it.draw                    = ewheel_draw;
+        }
+
+#endif // CSQC
+#endif
diff --git a/qcsrc/common/turrets/turret/ewheel_weapon.qc b/qcsrc/common/turrets/turret/ewheel_weapon.qc
new file mode 100644 (file)
index 0000000..86444bb
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef TURRET_EWHEEL_WEAPON_H
+#define TURRET_EWHEEL_WEAPON_H
+
+CLASS(EWheelAttack, PortoLaunch)
+/* flags     */ ATTRIB(EWheelAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(EWheelAttack, impulse, int, 5);
+/* refname   */ ATTRIB(EWheelAttack, netname, string, "turret_ewheel");
+/* wepname   */ ATTRIB(EWheelAttack, message, string, _("eWheel"));
+ENDCLASS(EWheelAttack)
+REGISTER_WEAPON(EWHEEL, NEW(EWheelAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+void turret_initparams(entity);
+METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+
+        turret_do_updates(actor);
+
+        entity missile = turret_projectile(SND(LASERGUN_FIRE), 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, true, true);
+        missile.missile_flags = MIF_SPLASH;
+
+        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, actor.tur_shotorg, actor.tur_shotdir_updated * 1000, 1);
+
+        if (!isPlayer) {
+            actor.tur_head.frame += 2;
+
+            if (actor.tur_head.frame > 3)
+                actor.tur_head.frame = 0;
+        }
+    }
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/flac.qc b/qcsrc/common/turrets/turret/flac.qc
new file mode 100644 (file)
index 0000000..1c9122f
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef TURRET_FLAC_H
+#define TURRET_FLAC_H
+
+#include "flac_weapon.qc"
+
+CLASS(Flac, Turret)
+/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(Flac, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(Flac, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(Flac, mdl, string, "base.md3");
+/* model      */ ATTRIB(Flac, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(Flac, head_model, string, strzone(strcat("models/turrets/", "flac.md3")));
+/* netname    */ ATTRIB(Flac, netname, string, "flac");
+/* fullname   */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon"));
+    ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC);
+ENDCLASS(Flac)
+REGISTER_TURRET(FLAC, NEW(Flac));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "flac_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_flac) { if (!turret_initialize(TUR_FLAC)) remove(self); }
+
+METHOD(Flac, tr_setup, void(Flac this, entity it))
+{
+    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+    it.damage_flags |= TFL_DMG_HEADSHAKE;
+    it.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY;
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/flac_weapon.qc b/qcsrc/common/turrets/turret/flac_weapon.qc
new file mode 100644 (file)
index 0000000..4c60805
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef TURRET_FLAC_WEAPON_H
+#define TURRET_FLAC_WEAPON_H
+
+CLASS(FlacAttack, PortoLaunch)
+/* flags     */ ATTRIB(FlacAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(FlacAttack, impulse, int, 5);
+/* refname   */ ATTRIB(FlacAttack, netname, string, "turret_flac");
+/* wepname   */ ATTRIB(FlacAttack, message, string, _("FLAC"));
+ENDCLASS(FlacAttack)
+REGISTER_WEAPON(FLAC, NEW(FlacAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+void turret_flac_projectile_think_explode();
+METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            actor.tur_impacttime = 10;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+
+        turret_tag_fire_update();
+
+        entity proj = turret_projectile(SND(HAGAR_FIRE), 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, true, true);
+        proj.missile_flags = MIF_SPLASH | MIF_PROXY;
+        proj.think       = turret_flac_projectile_think_explode;
+        proj.nextthink  = time + actor.tur_impacttime + (random() * 0.01 - random() * 0.01);
+        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, actor.tur_shotorg, actor.tur_shotdir_updated * 1000, 1);
+
+        if (!isPlayer) {
+            actor.tur_head.frame = actor.tur_head.frame + 1;
+            if (actor.tur_head.frame >= 4)
+                actor.tur_head.frame = 0;
+        }
+    }
+}
+
+void turret_flac_projectile_think_explode()
+{
+    SELFPARAM();
+    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 = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+    self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d;
+    self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#else
+    RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+#endif
+    remove(self);
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/fusionreactor.qc b/qcsrc/common/turrets/turret/fusionreactor.qc
new file mode 100644 (file)
index 0000000..1077d5c
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef TURRET_FUSIONREACTOR_H
+#define TURRET_FUSIONREACTOR_H
+
+CLASS(FusionReactor, Turret)
+/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE);
+/* mins       */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0');
+/* maxs       */ ATTRIB(FusionReactor, maxs, vector, '34 34 90');
+/* modelname  */ ATTRIB(FusionReactor, mdl, string, "base.md3");
+/* model      */ ATTRIB(FusionReactor, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(FusionReactor, head_model, string, strzone(strcat("models/turrets/", "reactor.md3")));
+/* netname    */ ATTRIB(FusionReactor, netname, string, "fusionreactor");
+/* fullname   */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor"));
+ENDCLASS(FusionReactor)
+REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor));
+
+#endif
+
+#ifdef IMPLEMENTATION
+#ifdef SVQC
+bool turret_fusionreactor_firecheck()
+{SELFPARAM();
+    if (self.attack_finished_single > time)
+        return false;
+
+    if (self.enemy.deadflag != DEAD_NO)
+        return false;
+
+    if (self.enemy == world)
+        return false;
+
+    if (self.ammo < self.shot_dmg)
+        return false;
+
+    if (self.enemy.ammo >= self.enemy.ammo_max)
+        return false;
+
+    if (vlen(self.enemy.origin - self.origin) > self.target_range)
+        return false;
+
+    if(self.team != self.enemy.team)
+        return false;
+
+    if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY))
+        return false;
+
+    return true;
+}
+
+spawnfunc(turret_fusionreactor) { if (!turret_initialize(TUR_FUSIONREACTOR)) remove(self); }
+
+METHOD(FusionReactor, tr_attack, void(FusionReactor this))
+{
+    self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max);
+    vector fl_org = 0.5 * (self.enemy.absmin + self.enemy.absmax);
+    te_smallflash(fl_org);
+}
+METHOD(FusionReactor, tr_think, void(FusionReactor thistur))
+{
+    self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max);
+}
+METHOD(FusionReactor, tr_setup, void(FusionReactor this, entity it))
+{
+    it.ammo_flags                              = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
+    it.target_select_flags     = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMITS;
+    it.firecheck_flags         = TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_AMMO_OTHER | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD;
+    it.shoot_flags                     = TFL_SHOOT_HITALLVALID;
+    it.aim_flags                               = TFL_AIM_NO;
+    it.track_flags                     = TFL_TRACK_NO;
+
+    it.tur_head.scale = 0.75;
+    it.tur_head.avelocity = '0 50 0';
+
+    it.turret_firecheckfunc = turret_fusionreactor_firecheck;
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/hellion.qc b/qcsrc/common/turrets/turret/hellion.qc
new file mode 100644 (file)
index 0000000..203be93
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef TURRET_HELLION_H
+#define TURRET_HELLION_H
+
+#include "hellion_weapon.qc"
+
+CLASS(Hellion, Turret)
+/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(Hellion, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(Hellion, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(Hellion, mdl, string, "base.md3");
+/* model      */ ATTRIB(Hellion, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(Hellion, head_model, string, strzone(strcat("models/turrets/", "hellion.md3")));
+/* netname    */ ATTRIB(Hellion, netname, string, "hellion");
+/* fullname   */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret"));
+    ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION);
+ENDCLASS(Hellion)
+REGISTER_TURRET(HELLION, NEW(Hellion));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "hellion_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_hellion) { if (!turret_initialize(TUR_HELLION)) remove(self); }
+
+METHOD(Hellion, tr_think, void(Hellion thistur))
+{
+    if (self.tur_head.frame != 0)
+        self.tur_head.frame += 1;
+
+    if (self.tur_head.frame >= 7)
+        self.tur_head.frame = 0;
+}
+METHOD(Hellion, tr_setup, void(Hellion this, entity it))
+{
+    it.aim_flags = TFL_AIM_SIMPLE;
+    it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK ;
+    it.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_AMMO_OWN;
+    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/hellion_weapon.qc b/qcsrc/common/turrets/turret/hellion_weapon.qc
new file mode 100644 (file)
index 0000000..491fa73
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef TURRET_HELLION_WEAPON_H
+#define TURRET_HELLION_WEAPON_H
+
+CLASS(HellionAttack, PortoLaunch)
+/* flags     */ ATTRIB(HellionAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(HellionAttack, impulse, int, 9);
+/* refname   */ ATTRIB(HellionAttack, netname, string, "turret_hellion");
+/* wepname   */ ATTRIB(HellionAttack, message, string, _("Hellion"));
+ENDCLASS(HellionAttack)
+REGISTER_WEAPON(HELLION, NEW(HellionAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+float autocvar_g_turrets_unit_hellion_shot_speed_gain;
+float autocvar_g_turrets_unit_hellion_shot_speed_max;
+
+void turret_hellion_missile_think();
+METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            actor.shot_radius = 500;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+        if (!isPlayer) {
+            if (actor.tur_head.frame != 0)
+                actor.tur_shotorg = gettaginfo(actor.tur_head, gettagindex(actor.tur_head, "tag_fire"));
+            else
+                actor.tur_shotorg = gettaginfo(actor.tur_head, gettagindex(actor.tur_head, "tag_fire2"));
+        }
+
+        entity missile = turret_projectile(SND(ROCKET_FIRE), 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;
+        if (!isPlayer) actor.tur_head.frame += 1;
+    }
+}
+
+void turret_hellion_missile_think()
+{SELFPARAM();
+    vector olddir,newdir;
+    vector pre_pos;
+    float itime;
+
+    self.nextthink = time + 0.05;
+
+    olddir = normalize(self.velocity);
+
+    if(self.max_health < time)
+        turret_projectile_explode();
+
+    // Enemy dead? just keep on the current heading then.
+    if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO))
+    {
+
+        // Make sure we dont return to tracking a respawned player
+        self.enemy = world;
+
+        // Turn model
+        self.angles = vectoangles(self.velocity);
+
+        if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) )
+            turret_projectile_explode();
+
+        // Accelerate
+        self.velocity = olddir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
+
+        UpdateCSQCProjectile(self);
+
+        return;
+    }
+
+    // Enemy in range?
+    if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2)
+        turret_projectile_explode();
+
+    // Predict enemy position
+    itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity);
+    pre_pos = self.enemy.origin + self.enemy.velocity * itime;
+
+    pre_pos = (pre_pos + self.enemy.origin) * 0.5;
+
+    // Find out the direction to that place
+    newdir = normalize(pre_pos - self.origin);
+
+    // Turn
+    newdir = normalize(olddir + newdir * 0.35);
+
+    // Turn model
+    self.angles = vectoangles(self.velocity);
+
+    // Accelerate
+    self.velocity = newdir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
+
+    if (itime < 0.05)
+        self.think = turret_projectile_explode;
+
+    UpdateCSQCProjectile(self);
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/hk.qc b/qcsrc/common/turrets/turret/hk.qc
new file mode 100644 (file)
index 0000000..61d8318
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef TURRET_HK_H
+#define TURRET_HK_H
+
+//#define TURRET_DEBUG_HK
+
+#include "hk_weapon.qc"
+
+CLASS(HunterKiller, Turret)
+/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS);
+/* mins       */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(HunterKiller, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(HunterKiller, mdl, string, "base.md3");
+/* model      */ ATTRIB(HunterKiller, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(HunterKiller, head_model, string, strzone(strcat("models/turrets/", "hk.md3")));
+/* netname    */ ATTRIB(HunterKiller, netname, string, "hk");
+/* fullname   */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret"));
+    ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK);
+ENDCLASS(HunterKiller)
+REGISTER_TURRET(HK, NEW(HunterKiller));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "hk_weapon.qc"
+
+#ifdef SVQC
+
+#ifdef TURRET_DEBUG_HK
+.float atime;
+#endif
+
+spawnfunc(turret_hk) { if(!turret_initialize(TUR_HK)) remove(self); }
+
+METHOD(HunterKiller, tr_think, void(HunterKiller thistur))
+{
+    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;
+}
+
+float turret_hk_addtarget(entity e_target,entity e_sender);
+METHOD(HunterKiller, tr_setup, void(HunterKiller this, entity it))
+{
+    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+    it.aim_flags = TFL_AIM_SIMPLE;
+    it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+    it.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK  | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
+    it.shoot_flags = TFL_SHOOT_CLEARTARGET;
+    it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
+
+    it.turret_addtarget = turret_hk_addtarget;
+}
+
+float turret_hk_addtarget(entity e_target,entity e_sender)
+{SELFPARAM();
+    if (e_target)
+    {
+        if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
+        {
+            self.enemy = e_target;
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+#endif // SVQC
+#endif
diff --git a/qcsrc/common/turrets/turret/hk_weapon.qc b/qcsrc/common/turrets/turret/hk_weapon.qc
new file mode 100644 (file)
index 0000000..9fe73f8
--- /dev/null
@@ -0,0 +1,295 @@
+#ifndef TURRET_HK_WEAPON_H
+#define TURRET_HK_WEAPON_H
+
+CLASS(HunterKillerAttack, PortoLaunch)
+/* flags     */ ATTRIB(HunterKillerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(HunterKillerAttack, impulse, int, 9);
+/* refname   */ ATTRIB(HunterKillerAttack, netname, string, "turret_hk");
+/* wepname   */ ATTRIB(HunterKillerAttack, message, string, _("Hunter-Killer"));
+ENDCLASS(HunterKillerAttack)
+REGISTER_WEAPON(HK, NEW(HunterKillerAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+float autocvar_g_turrets_unit_hk_shot_speed;
+float autocvar_g_turrets_unit_hk_shot_speed_accel;
+float autocvar_g_turrets_unit_hk_shot_speed_accel2;
+float autocvar_g_turrets_unit_hk_shot_speed_decel;
+float autocvar_g_turrets_unit_hk_shot_speed_max;
+float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
+
+void turret_hk_missile_think();
+METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+{
+       bool isPlayer = IS_PLAYER(actor);
+       if (fire1)
+       if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+               if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+        entity missile = turret_projectile(SND(ROCKET_FIRE), 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 = actor.tur_shotdir_updated * (actor.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 (!isPlayer)
+        if (actor.tur_head.frame == 0)
+            actor.tur_head.frame = actor.tur_head.frame + 1;
+       }
+}
+
+bool hk_is_valid_target(entity e_target);
+void turret_hk_missile_think()
+{SELFPARAM();
+    vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
+    float  fu, fd, ff, fl, fr, fe;  // Fraction to solid
+    vector olddir,wishdir,newdir;   // Final direction
+    float lt_for;   // Length of Trace FORwrad
+    float lt_seek;  // Length of Trace SEEK (left, right, up down)
+    float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
+    vector pre_pos;
+    float myspeed;
+    entity e;
+    float ad,edist;
+
+    self.nextthink = time + self.ticrate;
+
+    //if (self.cnt < time)
+    // turret_hk_missile_explode();
+
+    if (self.enemy.deadflag != DEAD_NO)
+        self.enemy = world;
+
+    // Pick the closest valid target.
+    if (!self.enemy)
+    {
+        e = findradius(self.origin, 5000);
+        while (e)
+        {
+            if (hk_is_valid_target(e))
+            {
+                if (!self.enemy)
+                    self.enemy = e;
+                else
+                    if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
+                        self.enemy = e;
+            }
+            e = e.chain;
+        }
+    }
+
+    self.angles = vectoangles(self.velocity);
+    self.angles_x = self.angles_x * -1;
+    makevectors(self.angles);
+    self.angles_x = self.angles_x * -1;
+
+    if (self.enemy)
+    {
+        edist = vlen(self.origin - self.enemy.origin);
+        // Close enougth to do decent damage?
+        if ( edist <= (self.owner.shot_radius * 0.25) )
+        {
+            turret_projectile_explode();
+            return;
+        }
+
+        // Get data on enemy position
+        pre_pos = self.enemy.origin +
+                  self.enemy.velocity *
+                  min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
+
+        traceline(self.origin, pre_pos,true,self.enemy);
+        ve = normalize(pre_pos - self.origin);
+        fe = trace_fraction;
+
+    }
+    else
+    {
+    edist = 0;
+    ve = '0 0 0';
+        fe = 0;
+    }
+
+    if ((fe != 1) || (self.enemy == world) || (edist > 1000))
+    {
+        myspeed = vlen(self.velocity);
+
+        lt_for  = myspeed * 3;
+        lt_seek = myspeed * 2.95;
+
+        // Trace forward
+        traceline(self.origin, self.origin + v_forward * lt_for,false,self);
+        vf = trace_endpos;
+        ff = trace_fraction;
+
+        // Find angular offset
+        ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
+
+        // To close to something, Slow down!
+        if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
+            myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
+
+        // Failry clear, accelerate.
+        if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
+            myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
+
+        // Setup trace pitch
+        pt_seek = 1 - ff;
+        pt_seek = bound(0.15,pt_seek,0.8);
+        if (ff < 0.5) pt_seek = 1;
+
+        // Trace left
+        traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
+        vl = trace_endpos;
+        fl = trace_fraction;
+
+        // Trace right
+        traceline(self.origin,  self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+        vr = trace_endpos;
+        fr = trace_fraction;
+
+        // Trace up
+        traceline(self.origin,  self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+        vu = trace_endpos;
+        fu = trace_fraction;
+
+        // Trace down
+        traceline(self.origin,  self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
+        vd = trace_endpos;
+        fd = trace_fraction;
+
+        vl = normalize(vl - self.origin);
+        vr = normalize(vr - self.origin);
+        vu = normalize(vu - self.origin);
+        vd = normalize(vd - self.origin);
+
+        // Panic tresh passed, find a single direction and turn as hard as we can
+        if (pt_seek == 1)
+        {
+            wishdir = v_right;
+            if (fl > fr) wishdir = -1 * v_right;
+            if (fu > fl) wishdir = v_up;
+            if (fd > fu) wishdir = -1 * v_up;
+        }
+        else
+        {
+            // Normalize our trace vectors to make a smooth path
+            wishdir = normalize( (vl * fl) + (vr * fr) +  (vu * fu) +  (vd * fd) );
+        }
+
+        if (self.enemy)
+        {
+            if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
+            wishdir = (wishdir * (1 - fe)) + (ve * fe);
+        }
+    }
+    else
+    {
+        // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
+        myspeed = vlen(self.velocity);
+        if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
+            myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
+
+        wishdir = ve;
+    }
+
+    if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
+        myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
+
+    // Ranoutagazfish?
+    if (self.cnt < time)
+    {
+        self.cnt = time + 0.25;
+        self.nextthink = 0;
+        self.movetype           = MOVETYPE_BOUNCE;
+        return;
+    }
+
+    // Calculate new heading
+    olddir = normalize(self.velocity);
+    newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
+
+    // Set heading & speed
+    self.velocity = newdir * myspeed;
+
+    // Align model with new heading
+    self.angles = vectoangles(self.velocity);
+
+
+#ifdef TURRET_DEBUG_HK
+    //if(self.atime < time) {
+    if ((fe <= 0.99)||(edist > 1000))
+    {
+        te_lightning2(world,self.origin, self.origin + vr * lt_seek);
+        te_lightning2(world,self.origin, self.origin + vl * lt_seek);
+        te_lightning2(world,self.origin, self.origin + vu * lt_seek);
+        te_lightning2(world,self.origin, self.origin + vd * lt_seek);
+        te_lightning2(world,self.origin, vf);
+    }
+    else
+    {
+        te_lightning2(world,self.origin, self.enemy.origin);
+    }
+    bprint("Speed: ", ftos(rint(myspeed)), "\n");
+    bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
+    bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
+    self.atime = time + 0.2;
+    //}
+#endif
+
+    UpdateCSQCProjectile(self);
+}
+
+bool hk_is_valid_target(entity e_target)
+{SELFPARAM();
+    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;
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/machinegun.qc b/qcsrc/common/turrets/turret/machinegun.qc
new file mode 100644 (file)
index 0000000..9911b97
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef TURRET_MACHINEGUN_H
+#define TURRET_MACHINEGUN_H
+
+#include "machinegun_weapon.qc"
+
+CLASS(MachineGunTurret, Turret)
+/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(MachineGunTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB(MachineGunTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(MachineGunTurret, head_model, string, strzone(strcat("models/turrets/", "machinegun.md3")));
+/* netname    */ ATTRIB(MachineGunTurret, netname, string, "machinegun");
+/* fullname   */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret"));
+    ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN);
+ENDCLASS(MachineGunTurret)
+REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "machinegun_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_machinegun) { if (!turret_initialize(TUR_MACHINEGUN)) remove(self); }
+
+METHOD(MachineGunTurret, tr_setup, void(MachineGunTurret this, entity it))
+{
+    it.damage_flags |= TFL_DMG_HEADSHAKE;
+    it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+    it.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+    it.turret_flags |= TUR_FLAG_HITSCAN;
+}
+
+#endif // SVQC
+#endif
diff --git a/qcsrc/common/turrets/turret/machinegun_weapon.qc b/qcsrc/common/turrets/turret/machinegun_weapon.qc
new file mode 100644 (file)
index 0000000..9b14898
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef TURRET_MACHINEGUN_WEAPON_H
+#define TURRET_MACHINEGUN_WEAPON_H
+
+CLASS(MachineGunTurretAttack, PortoLaunch)
+/* flags     */ ATTRIB(MachineGunTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(MachineGunTurretAttack, impulse, int, 9);
+/* refname   */ ATTRIB(MachineGunTurretAttack, netname, string, "turret_machinegun");
+/* wepname   */ ATTRIB(MachineGunTurretAttack, message, string, _("Machinegun"));
+ENDCLASS(MachineGunTurretAttack)
+REGISTER_WEAPON(TUR_MACHINEGUN, NEW(MachineGunTurretAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+void W_MachineGun_MuzzleFlash();
+
+METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+{
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+        }
+        fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN, 0);
+        W_MachineGun_MuzzleFlash();
+        setattachment(actor.muzzle_flash, actor.tur_head, "tag_fire");
+    }
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/mlrs.qc b/qcsrc/common/turrets/turret/mlrs.qc
new file mode 100644 (file)
index 0000000..ae2d1d4
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef TURRET_MLRS_H
+#define TURRET_MLRS_H
+
+#include "mlrs_weapon.qc"
+
+CLASS(MLRSTurret, Turret)
+/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(MLRSTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB(MLRSTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(MLRSTurret, head_model, string, strzone(strcat("models/turrets/", "mlrs.md3")));
+/* netname    */ ATTRIB(MLRSTurret, netname, string, "mlrs");
+/* fullname   */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret"));
+    ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS);
+ENDCLASS(MLRSTurret)
+REGISTER_TURRET(MLRS, NEW(MLRSTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "mlrs_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_mlrs) { if (!turret_initialize(TUR_MLRS)) remove(self); }
+
+METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur))
+{
+    // 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)
+    {
+        LOG_TRACE("ammo:",ftos(self.ammo),"\n");
+        LOG_TRACE("shot_dmg:",ftos(self.shot_dmg),"\n");
+    }
+}
+METHOD(MLRSTurret, tr_setup, void(MLRSTurret this, entity it))
+{
+    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+
+    it.damage_flags |= TFL_DMG_HEADSHAKE;
+    it.shoot_flags  |= TFL_SHOOT_VOLLYALWAYS;
+    it.volly_counter = it.shot_volly;
+}
+
+#endif // SVQC
+#endif
diff --git a/qcsrc/common/turrets/turret/mlrs_weapon.qc b/qcsrc/common/turrets/turret/mlrs_weapon.qc
new file mode 100644 (file)
index 0000000..3b420f3
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef TURRET_MLRS_WEAPON_H
+#define TURRET_MLRS_WEAPON_H
+
+CLASS(MLRSTurretAttack, PortoLaunch)
+/* flags     */ ATTRIB(MLRSTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(MLRSTurretAttack, impulse, int, 9);
+/* refname   */ ATTRIB(MLRSTurretAttack, netname, string, "turret_mlrs");
+/* wepname   */ ATTRIB(MLRSTurretAttack, message, string, _("MLRS"));
+ENDCLASS(MLRSTurretAttack)
+REGISTER_WEAPON(TUR_MLRS, NEW(MLRSTurretAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+{
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            actor.shot_radius = 500;
+            weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+        }
+        turret_tag_fire_update();
+        entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE);
+        missile.nextthink = time + max(actor.tur_impacttime,(actor.shot_radius * 2) / actor.shot_speed);
+        missile.missile_flags = MIF_SPLASH;
+        te_explosion (missile.origin);
+    }
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/phaser.qc b/qcsrc/common/turrets/turret/phaser.qc
new file mode 100644 (file)
index 0000000..24d4d30
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef TURRET_PHASER_H
+#define TURRET_PHASER_H
+
+#include "phaser_weapon.qc"
+
+CLASS(PhaserTurret, Turret)
+/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(PhaserTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB(PhaserTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(PhaserTurret, head_model, string, strzone(strcat("models/turrets/", "phaser.md3")));
+/* netname    */ ATTRIB(PhaserTurret, netname, string, "phaser");
+/* fullname   */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon"));
+    ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER);
+ENDCLASS(PhaserTurret)
+REGISTER_TURRET(PHASER, NEW(PhaserTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "phaser_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_phaser) { if (!turret_initialize(TUR_PHASER)) remove(self); }
+
+METHOD(PhaserTurret, tr_think, void(PhaserTurret thistur))
+{
+    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;
+            }
+        }
+    }
+}
+float turret_phaser_firecheck();
+METHOD(PhaserTurret, tr_setup, void(PhaserTurret this, entity it))
+{
+    it.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+    it.aim_flags = TFL_AIM_LEAD;
+
+    it.turret_firecheckfunc = turret_phaser_firecheck;
+}
+float turret_phaser_firecheck()
+{
+    SELFPARAM();
+    if (self.fireflag != 0) return 0;
+    return turret_firecheck();
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/phaser_weapon.qc b/qcsrc/common/turrets/turret/phaser_weapon.qc
new file mode 100644 (file)
index 0000000..2cb036b
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef TURRET_PHASER_WEAPON_H
+#define TURRET_PHASER_WEAPON_H
+
+CLASS(PhaserTurretAttack, PortoLaunch)
+/* flags     */ ATTRIB(PhaserTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(PhaserTurretAttack, impulse, int, 9);
+/* refname   */ ATTRIB(PhaserTurretAttack, netname, string, "turret_phaser");
+/* wepname   */ ATTRIB(PhaserTurretAttack, message, string, _("Phaser"));
+ENDCLASS(PhaserTurretAttack)
+REGISTER_WEAPON(PHASER, NEW(PhaserTurretAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+void beam_think();
+
+.int fireflag;
+
+METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+{
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            actor.shot_speed = 1;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+        entity beam = spawn();
+        beam.ticrate = 0.1; //autocvar_sys_ticrate;
+        setmodel(beam, MDL_TUR_PHASER_BEAM);
+        beam.effects = EF_LOWPRECISION;
+        beam.solid = SOLID_NOT;
+        beam.think = beam_think;
+        beam.cnt = time + actor.shot_speed;
+        beam.shot_spread = time + 2;
+        beam.nextthink = time;
+        beam.owner = actor;
+        beam.shot_dmg = actor.shot_dmg / (actor.shot_speed / beam.ticrate);
+        beam.scale = actor.target_range / 256;
+        beam.movetype = MOVETYPE_NONE;
+        beam.enemy = actor.enemy;
+        beam.bot_dodge = true;
+        beam.bot_dodgerating = beam.shot_dmg;
+        sound (beam, CH_SHOTS_SINGLE, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM);
+        actor.fireflag = 1;
+
+        beam.attack_finished_single = actor.attack_finished_single;
+        actor.attack_finished_single = time; // + autocvar_sys_ticrate;
+
+        setattachment(beam,actor.tur_head, "tag_fire");
+
+        soundat (actor, trace_endpos, CH_SHOTS, SND(NEXIMPACT), VOL_BASE, ATTEN_NORM);
+        if (!isPlayer)
+        if (actor.tur_head.frame == 0)
+            actor.tur_head.frame = 1;
+    }
+}
+
+void beam_think()
+{SELFPARAM();
+    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, SND_Null, 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, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM);
+    }
+
+
+    self.nextthink = time + self.ticrate;
+
+    self.owner.attack_finished_single = time + frametime;
+    setself(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,
+                    this.shot_dmg,
+                    0.75,
+                    DEATH_TURRET_PHASER);
+    setself(this);
+    self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256;
+
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc
new file mode 100644 (file)
index 0000000..7c40299
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef TURRET_PLASMA_H
+#define TURRET_PLASMA_H
+
+#include "plasma_weapon.qc"
+
+CLASS(PlasmaTurret, Turret)
+/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(PlasmaTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB(PlasmaTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(PlasmaTurret, head_model, string, strzone(strcat("models/turrets/", "plasma.md3")));
+/* netname    */ ATTRIB(PlasmaTurret, netname, string, "plasma");
+/* fullname   */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon"));
+    ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA);
+ENDCLASS(PlasmaTurret)
+REGISTER_TURRET(PLASMA, NEW(PlasmaTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "plasma_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_plasma) { if (!turret_initialize(TUR_PLASMA)) remove(self); }
+
+METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this))
+{
+    if(g_instagib)
+    {
+        FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
+                           800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
+
+        Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+        // teamcolor / hit beam effect
+        vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+        WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
+    }
+    else
+    {
+        super.tr_attack(this);
+    }
+    if (self.tur_head.frame == 0)
+        self.tur_head.frame = 1;
+}
+METHOD(PlasmaTurret, tr_think, void(PlasmaTurret thistur))
+{
+    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;
+}
+METHOD(PlasmaTurret, tr_setup, void(PlasmaTurret this, entity it))
+{
+    it.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+    it.damage_flags |= TFL_DMG_HEADSHAKE;
+    it.firecheck_flags |= TFL_FIRECHECK_AFF;
+    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH;
+
+    turret_do_updates(it);
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc
new file mode 100644 (file)
index 0000000..e1143bc
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef TURRET_PLASMA_DUAL_H
+#define TURRET_PLASMA_DUAL_H
+
+CLASS(PlasmaDualAttack, PlasmaAttack)
+/* refname   */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual");
+/* wepname   */ ATTRIB(PlasmaDualAttack, message, string, _("Dual plasma"));
+ENDCLASS(PlasmaDualAttack)
+REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack));
+
+CLASS(DualPlasmaTurret, PlasmaTurret)
+/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB(DualPlasmaTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(DualPlasmaTurret, head_model, string, strzone(strcat("models/turrets/", "plasmad.md3")));
+/* netname    */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual");
+/* fullname   */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon"));
+    ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL);
+ENDCLASS(DualPlasmaTurret)
+REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+spawnfunc(turret_plasma_dual) { if (!turret_initialize(TUR_PLASMA_DUAL)) remove(self); }
+
+METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret this))
+{
+    if (g_instagib) {
+        FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
+                           800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
+
+
+        Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+        // teamcolor / hit beam effect
+        vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+        WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
+    } else {
+        super.vtblbase.tr_attack(this);
+    }
+    self.tur_head.frame += 1;
+}
+METHOD(DualPlasmaTurret, tr_think, void(DualPlasmaTurret thistur))
+{
+    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;
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/plasma_weapon.qc b/qcsrc/common/turrets/turret/plasma_weapon.qc
new file mode 100644 (file)
index 0000000..65a6630
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef TURRET_PLASMA_WEAPON_H
+#define TURRET_PLASMA_WEAPON_H
+
+CLASS(PlasmaAttack, PortoLaunch)
+/* flags     */ ATTRIB(PlasmaAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(PlasmaAttack, impulse, int, 5);
+/* refname   */ ATTRIB(PlasmaAttack, netname, string, "turret_plasma");
+/* wepname   */ ATTRIB(PlasmaAttack, message, string, _("Plasma"));
+ENDCLASS(PlasmaAttack)
+REGISTER_WEAPON(PLASMA, NEW(PlasmaAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+       bool isPlayer = IS_PLAYER(actor);
+       if (fire1)
+       if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+               if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+        entity missile = turret_projectile(SND(HAGAR_FIRE), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, true, true);
+        missile.missile_flags = MIF_SPLASH;
+        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, actor.tur_shotorg, actor.tur_shotdir_updated * 1000, 1);
+       }
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/tesla.qc b/qcsrc/common/turrets/turret/tesla.qc
new file mode 100644 (file)
index 0000000..3ae039c
--- /dev/null
@@ -0,0 +1,111 @@
+#ifndef TURRET_TESLA_H
+#define TURRET_TESLA_H
+
+#include "tesla_weapon.qc"
+
+CLASS(TeslaCoil, Turret)
+/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0');
+/* maxs       */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128');
+/* modelname  */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3");
+/* model      */ ATTRIB(TeslaCoil, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(TeslaCoil, head_model, string, strzone(strcat("models/turrets/", "tesla_head.md3")));
+/* netname    */ ATTRIB(TeslaCoil, netname, string, "tesla");
+/* fullname   */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil"));
+    ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA);
+ENDCLASS(TeslaCoil)
+REGISTER_TURRET(TESLA, NEW(TeslaCoil));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "tesla_weapon.qc"
+
+#ifdef SVQC
+
+spawnfunc(turret_tesla) { if (!turret_initialize(TUR_TESLA)) remove(self); }
+
+METHOD(TeslaCoil, tr_think, void(TeslaCoil thistur))
+{
+    if(!self.active)
+    {
+        self.tur_head.avelocity = '0 0 0';
+        return;
+    }
+
+    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;
+
+        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));
+    }
+}
+
+float turret_tesla_firecheck();
+METHOD(TeslaCoil, tr_setup, void(TeslaCoil this, entity it))
+{
+    it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
+                         TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+
+    it.turret_firecheckfunc = turret_tesla_firecheck;
+    it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
+                       TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+
+    it.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN;
+    it.shoot_flags             = TFL_SHOOT_CUSTOM;
+    it.ammo_flags                      = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+    it.aim_flags                       = TFL_AIM_NO;
+    it.track_flags             = TFL_TRACK_NO;
+}
+
+float turret_tesla_firecheck()
+{SELFPARAM();
+    // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+    float do_target_scan = 0;
+
+    if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+        do_target_scan = 1;
+
+    // Old target (if any) invalid?
+    if(self.target_validate_time < time)
+    if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+    {
+        self.enemy = world;
+        self.target_validate_time = time + 0.5;
+        do_target_scan = 1;
+    }
+
+    // But never more often then g_turrets_targetscan_mindelay!
+    if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+        do_target_scan = 0;
+
+    if(do_target_scan)
+    {
+        self.enemy = turret_select_target();
+        self.target_select_time = time;
+    }
+
+    if(!turret_firecheck())
+        return 0;
+
+    if(self.enemy)
+        return 1;
+
+    return 0;
+}
+
+#endif
+#endif
diff --git a/qcsrc/common/turrets/turret/tesla_weapon.qc b/qcsrc/common/turrets/turret/tesla_weapon.qc
new file mode 100644 (file)
index 0000000..3a8eb1d
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef TURRET_TESLA_WEAPON_H
+#define TURRET_TESLA_WEAPON_H
+
+CLASS(TeslaCoilTurretAttack, PortoLaunch)
+/* flags     */ ATTRIB(TeslaCoilTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(TeslaCoilTurretAttack, impulse, int, 9);
+/* refname   */ ATTRIB(TeslaCoilTurretAttack, netname, string, "turret_tesla");
+/* wepname   */ ATTRIB(TeslaCoilTurretAttack, message, string, _("Tesla Coil"));
+ENDCLASS(TeslaCoilTurretAttack)
+REGISTER_WEAPON(TESLA, NEW(TeslaCoilTurretAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+entity toast(entity from, float range, float damage);
+METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+
+        float d = actor.shot_dmg;
+        float r = actor.target_range;
+        entity e = spawn();
+        setorigin(e,actor.tur_shotorg);
+
+        actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
+
+        entity t = toast(e,r,d);
+        remove(e);
+
+        if (t == NULL) return;
+
+        actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK;
+
+        actor.attack_finished_single = time + actor.shot_refire;
+        for (int 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;
+        }
+
+    }
+}
+
+entity toast(entity from, float range, float damage)
+{SELFPARAM();
+    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;
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc
new file mode 100644 (file)
index 0000000..1feace4
--- /dev/null
@@ -0,0 +1,667 @@
+#ifndef TURRET_WALKER_H
+#define TURRET_WALKER_H
+
+//#define WALKER_FANCYPATHING
+
+#include "walker_weapon.qc"
+
+CLASS(WalkerTurret, Turret)
+/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE);
+/* mins       */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0');
+/* maxs       */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95');
+/* modelname  */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3");
+/* model      */ ATTRIB(WalkerTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
+/* head_model */ ATTRIB(WalkerTurret, head_model, string, strzone(strcat("models/turrets/", "walker_head_minigun.md3")));
+/* netname    */ ATTRIB(WalkerTurret, netname, string, "walker");
+/* fullname   */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret"));
+    ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER);
+ENDCLASS(WalkerTurret)
+REGISTER_TURRET(WALKER, NEW(WalkerTurret));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "walker_weapon.qc"
+
+#ifdef SVQC
+
+float autocvar_g_turrets_unit_walker_melee_damage;
+float autocvar_g_turrets_unit_walker_melee_force;
+float autocvar_g_turrets_unit_walker_melee_range;
+float autocvar_g_turrets_unit_walker_rocket_damage;
+float autocvar_g_turrets_unit_walker_rocket_radius;
+float autocvar_g_turrets_unit_walker_rocket_force;
+float autocvar_g_turrets_unit_walker_rocket_speed;
+float autocvar_g_turrets_unit_walker_rocket_range;
+float autocvar_g_turrets_unit_walker_rocket_range_min;
+float autocvar_g_turrets_unit_walker_rocket_refire;
+float autocvar_g_turrets_unit_walker_rocket_turnrate;
+float autocvar_g_turrets_unit_walker_speed_stop;
+float autocvar_g_turrets_unit_walker_speed_walk;
+float autocvar_g_turrets_unit_walker_speed_run;
+float autocvar_g_turrets_unit_walker_speed_jump;
+float autocvar_g_turrets_unit_walker_speed_swim;
+float autocvar_g_turrets_unit_walker_speed_roam;
+float autocvar_g_turrets_unit_walker_turn;
+float autocvar_g_turrets_unit_walker_turn_walk;
+float autocvar_g_turrets_unit_walker_turn_strafe;
+float autocvar_g_turrets_unit_walker_turn_swim;
+float autocvar_g_turrets_unit_walker_turn_run;
+
+const int ANIM_NO         = 0;
+const int ANIM_TURN       = 1;
+const int ANIM_WALK       = 2;
+const int ANIM_RUN        = 3;
+const int ANIM_STRAFE_L   = 4;
+const int ANIM_STRAFE_R   = 5;
+const int ANIM_JUMP       = 6;
+const int ANIM_LAND       = 7;
+const int ANIM_PAIN       = 8;
+const int ANIM_MELEE      = 9;
+const int ANIM_SWIM       = 10;
+const int ANIM_ROAM       = 11;
+
+.float animflag;
+.float idletime;
+
+#define WALKER_PATH(s,e) pathlib_astar(s,e)
+
+float walker_firecheck()
+{SELFPARAM();
+    if (self.animflag == ANIM_MELEE)
+        return 0;
+
+    return turret_firecheck();
+}
+
+void walker_melee_do_dmg()
+{SELFPARAM();
+    vector where;
+    entity e;
+
+    makevectors(self.angles);
+    where = self.origin + v_forward * 128;
+
+    e = findradius(where,32);
+    while (e)
+    {
+        if (turret_validate_target(self, e, self.target_validate_flags))
+            if (e != self && e.owner != self)
+                Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force));
+
+        e = e.chain;
+    }
+}
+
+void walker_setnoanim()
+{SELFPARAM();
+    turrets_setframe(ANIM_NO, false);
+    self.animflag = self.frame;
+}
+void walker_rocket_explode()
+{SELFPARAM();
+    RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world);
+    remove (self);
+}
+
+void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{SELFPARAM();
+    self.health = self.health - damage;
+    self.velocity = self.velocity + vforce;
+
+    if (self.health <= 0)
+        W_PrepareExplosionByDamage(self.owner, walker_rocket_explode);
+}
+
+#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self)
+void walker_rocket_loop();
+void walker_rocket_think()
+{SELFPARAM();
+    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()
+{SELFPARAM();
+    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()
+{SELFPARAM();
+    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()
+{SELFPARAM();
+    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)
+{SELFPARAM();
+    entity rocket;
+
+    fixedmakevectors(self.angles);
+
+    te_explosion (org);
+
+    rocket = spawn ();
+    setorigin(rocket, org);
+
+    sound (self, CH_WEAPON_A, SND_HAGAR_FIRE, VOL_BASE, ATTEN_NORM);
+    setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+    rocket.classname             = "walker_rocket";
+    rocket.owner                         = self;
+    rocket.bot_dodge             = true;
+    rocket.bot_dodgerating     = 50;
+    rocket.takedamage           = DAMAGE_YES;
+    rocket.damageforcescale   = 2;
+    rocket.health                       = 25;
+    rocket.tur_shotorg         = randomvec() * 512;
+    rocket.cnt                         = time + 1;
+    rocket.enemy                         = self.enemy;
+
+    if (random() < 0.01)
+        rocket.think             = walker_rocket_loop;
+    else
+        rocket.think             = walker_rocket_think;
+
+    rocket.event_damage           = walker_rocket_damage;
+
+    rocket.nextthink             = time;
+    rocket.movetype               = MOVETYPE_FLY;
+    rocket.velocity               = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
+    rocket.angles                       = vectoangles(rocket.velocity);
+    rocket.touch                         = walker_rocket_explode;
+    rocket.flags                         = FL_PROJECTILE;
+    rocket.solid                         = SOLID_BBOX;
+    rocket.max_health           = time + 9;
+    rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
+
+    CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound
+}
+
+.vector enemy_last_loc;
+.float enemy_last_time;
+void walker_move_to(vector _target, float _dist)
+{SELFPARAM();
+    switch (self.waterlevel)
+    {
+        case WATERLEVEL_NONE:
+            if (_dist > 500)
+                self.animflag = ANIM_RUN;
+            else
+                self.animflag = ANIM_WALK;
+        case WATERLEVEL_WETFEET:
+        case WATERLEVEL_SWIMMING:
+            if (self.animflag != ANIM_SWIM)
+                self.animflag = ANIM_WALK;
+            else
+                self.animflag = ANIM_SWIM;
+            break;
+        case WATERLEVEL_SUBMERGED:
+            self.animflag = ANIM_SWIM;
+    }
+
+    self.moveto = _target;
+    self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+    if(self.enemy)
+    {
+        self.enemy_last_loc = _target;
+        self.enemy_last_time = time;
+    }
+}
+
+void walker_move_path()
+{SELFPARAM();
+#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
+}
+
+spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER)) remove(self); }
+
+        METHOD(WalkerTurret, tr_think, void(WalkerTurret thistur))
+        {
+            fixedmakevectors(self.angles);
+
+            if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent)
+                walker_move_path();
+            else if (self.enemy == world)
+            {
+                if(self.pathcurrent)
+                    walker_move_path();
+                else
+                {
+                    if(self.enemy_last_time != 0)
+                    {
+                        if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10)
+                            self.enemy_last_time = 0;
+                        else
+                            walker_move_to(self.enemy_last_loc, 0);
+                    }
+                    else
+                    {
+                        if(self.animflag != ANIM_NO)
+                        {
+                            traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self);
+
+                            if(trace_fraction != 1.0)
+                                self.tur_head.idletime = -1337;
+                            else
+                            {
+                                traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self);
+                                if(trace_fraction == 1.0)
+                                    self.tur_head.idletime = -1337;
+                            }
+
+                            if(self.tur_head.idletime == -1337)
+                            {
+                                self.moveto = self.origin + randomvec() * 256;
+                                self.tur_head.idletime = 0;
+                            }
+
+                            self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
+                            self.moveto_z = self.origin_z + 64;
+                            walker_move_to(self.moveto, 0);
+                        }
+
+                        if(self.idletime < time)
+                        {
+                            if(random() < 0.5 || !(self.spawnflags & TSL_ROAM))
+                            {
+                                self.idletime = time + 1 + random() * 5;
+                                self.moveto = self.origin;
+                                self.animflag = ANIM_NO;
+                            }
+                            else
+                            {
+                                self.animflag = ANIM_WALK;
+                                self.idletime = time + 4 + random() * 2;
+                                self.moveto = self.origin + randomvec() * 256;
+                                self.tur_head.moveto = self.moveto;
+                                self.tur_head.idletime = 0;
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE)
+                {
+                    vector wish_angle;
+
+                    wish_angle = angleofs(self, self.enemy);
+                    if (self.animflag != ANIM_SWIM)
+                    if (fabs(wish_angle_y) < 15)
+                    {
+                        self.moveto   = self.enemy.origin;
+                        self.steerto  = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+                        self.animflag = ANIM_MELEE;
+                    }
+                }
+                else if (self.tur_head.attack_finished_single < time)
+                {
+                    if(self.tur_head.shot_volly)
+                    {
+                        self.animflag = ANIM_NO;
+
+                        self.tur_head.shot_volly = self.tur_head.shot_volly -1;
+                        if(self.tur_head.shot_volly == 0)
+                            self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
+                        else
+                            self.tur_head.attack_finished_single = time + 0.2;
+
+                        if(self.tur_head.shot_volly > 1)
+                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
+                        else
+                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02")));
+                    }
+                    else
+                    {
+                        if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min))
+                        if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range))
+                            self.tur_head.shot_volly = 4;
+                    }
+                }
+                else
+                {
+                    if (self.animflag != ANIM_MELEE)
+                        walker_move_to(self.enemy.origin, self.tur_dist_enemy);
+                }
+            }
+
+            {
+                vector real_angle;
+                float turny = 0, turnx = 0;
+                float vz;
+
+                real_angle = vectoangles(self.steerto) - self.angles;
+                vz = self.velocity_z;
+
+                switch (self.animflag)
+                {
+                    case ANIM_NO:
+                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                        break;
+
+                    case ANIM_TURN:
+                        turny = (autocvar_g_turrets_unit_walker_turn);
+                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                        break;
+
+                    case ANIM_WALK:
+                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
+                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6);
+                        break;
+
+                    case ANIM_RUN:
+                        turny = (autocvar_g_turrets_unit_walker_turn_run);
+                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6);
+                        break;
+
+                    case ANIM_STRAFE_L:
+                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+                        movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+                        break;
+
+                    case ANIM_STRAFE_R:
+                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+                        movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+                        break;
+
+                    case ANIM_JUMP:
+                        self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump);
+                        break;
+
+                    case ANIM_LAND:
+                        break;
+
+                    case ANIM_PAIN:
+                        if(self.frame != ANIM_PAIN)
+                            defer(0.25, walker_setnoanim);
+
+                        break;
+
+                    case ANIM_MELEE:
+                        if(self.frame != ANIM_MELEE)
+                        {
+                            defer(0.41, walker_setnoanim);
+                            defer(0.21, walker_melee_do_dmg);
+                        }
+
+                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+                        break;
+
+                    case ANIM_SWIM:
+                        turny = (autocvar_g_turrets_unit_walker_turn_swim);
+                        turnx = (autocvar_g_turrets_unit_walker_turn_swim);
+
+                        self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
+                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3);
+                        vz = self.velocity_z + sin(time * 4) * 8;
+                        break;
+
+                    case ANIM_ROAM:
+                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
+                        movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5);
+                        break;
+                }
+
+                if(turny)
+                {
+                    turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny );
+                    self.angles_y += turny;
+                }
+
+                if(turnx)
+                {
+                    turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx );
+                    self.angles_x += turnx;
+                }
+
+                self.velocity_z = vz;
+            }
+
+
+            if(self.origin != self.oldorigin)
+                self.SendFlags |= TNSF_MOVE;
+
+            self.oldorigin = self.origin;
+            turrets_setframe(self.animflag, false);
+        }
+        METHOD(WalkerTurret, tr_death, void(WalkerTurret this, entity it))
+        {
+#ifdef WALKER_FANCYPATHING
+            if (it.pathcurrent)
+                pathlib_deletepath(it.pathcurrent.owner);
+#endif
+            it.pathcurrent = NULL;
+        }
+        METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
+        {
+            it.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(it.movetype == MOVETYPE_WALK)
+            {
+                if(it.pos1)
+                    setorigin(it, it.pos1);
+                if(it.pos2)
+                    it.angles = it.pos2;
+            }
+
+            it.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+            it.aim_flags = TFL_AIM_LEAD;
+            it.turret_flags |= TUR_FLAG_HITSCAN;
+
+            it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+            it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+            it.iscreature = true;
+            it.teleportable = TELEPORT_NORMAL;
+            it.damagedbycontents = true;
+            it.solid = SOLID_SLIDEBOX;
+            it.takedamage = DAMAGE_AIM;
+            if(it.movetype != MOVETYPE_WALK)
+            {
+                setorigin(it, it.origin);
+                tracebox(it.origin + '0 0 128', it.mins, it.maxs, it.origin - '0 0 10000', MOVE_NORMAL, it);
+                setorigin(it, trace_endpos + '0 0 4');
+                it.pos1 = it.origin;
+                it.pos2 = it.angles;
+            }
+            it.movetype = MOVETYPE_WALK;
+            it.idle_aim = '0 0 0';
+            it.turret_firecheckfunc = walker_firecheck;
+
+            if (it.target != "")
+            {
+                e = find(world, targetname, it.target);
+                if (!e)
+                {
+                    LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!\n");
+                    it.target = "";
+                }
+
+                if (e.classname != "turret_checkpoint")
+                    LOG_TRACE("Warning: not a turrret path\n");
+                else
+                {
+#ifdef WALKER_FANCYPATHING
+                    it.pathcurrent = WALKER_PATH(it.origin, e.origin);
+                    it.pathgoal = e;
+#else
+                    it.pathcurrent = e;
+#endif
+                }
+            }
+        }
+
+#endif // SVQC
+#ifdef CSQC
+
+#include "../../../client/movelib.qh"
+
+void walker_draw()
+{SELFPARAM();
+    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);
+}
+
+        METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
+        {
+            it.gravity         = 1;
+            it.movetype                = MOVETYPE_BOUNCE;
+            it.move_movetype   = MOVETYPE_BOUNCE;
+            it.move_origin     = it.origin;
+            it.move_time               = time;
+            it.draw                    = walker_draw;
+        }
+
+#endif // CSQC
+#endif
diff --git a/qcsrc/common/turrets/turret/walker_weapon.qc b/qcsrc/common/turrets/turret/walker_weapon.qc
new file mode 100644 (file)
index 0000000..9a54f81
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef TURRET_WALKER_WEAPON_H
+#define TURRET_WALKER_WEAPON_H
+
+CLASS(WalkerTurretAttack, PortoLaunch)
+/* flags     */ ATTRIB(WalkerTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(WalkerTurretAttack, impulse, int, 5);
+/* refname   */ ATTRIB(WalkerTurretAttack, netname, string, "turret_walker");
+/* wepname   */ ATTRIB(WalkerTurretAttack, message, string, _("Walker"));
+ENDCLASS(WalkerTurretAttack)
+REGISTER_WEAPON(WALKER, NEW(WalkerTurretAttack));
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#ifdef SVQC
+
+METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+    bool isPlayer = IS_PLAYER(actor);
+    if (fire1)
+    if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+        if (isPlayer) {
+            turret_initparams(actor);
+            W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+            actor.tur_shotdir_updated = w_shotdir;
+            actor.tur_shotorg = w_shotorg;
+            actor.tur_head = actor;
+            weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+        }
+        sound (actor, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM);
+        fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_WALK_GUN, 0);
+        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, actor.tur_shotorg, actor.tur_shotdir_updated * 1000, 1);
+    }
+}
+
+#endif
+
+#endif
diff --git a/qcsrc/common/turrets/unit/ewheel.qc b/qcsrc/common/turrets/unit/ewheel.qc
deleted file mode 100644 (file)
index 117e347..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ EWHEEL,
-/* function   */ t_ewheel,
-/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM,
-/* mins,maxs  */ '-32 -32 0', '32 32 48',
-/* model      */ "ewheel-base2.md3",
-/* head_model */ "ewheel-gun1.md3",
-/* netname    */ "ewheel",
-/* fullname   */ _("eWheel Turret")
-);
-#else
-#ifdef SVQC
-float autocvar_g_turrets_unit_ewheel_speed_fast;
-float autocvar_g_turrets_unit_ewheel_speed_slow;
-float autocvar_g_turrets_unit_ewheel_speed_slower;
-float autocvar_g_turrets_unit_ewheel_speed_stop;
-float autocvar_g_turrets_unit_ewheel_turnrate;
-
-const float ewheel_anim_stop = 0;
-const float ewheel_anim_fwd_slow = 1;
-const float ewheel_anim_fwd_fast = 2;
-const float ewheel_anim_bck_slow = 3;
-const float ewheel_anim_bck_fast = 4;
-
-//#define EWHEEL_FANCYPATH
-void ewheel_move_path()
-{SELFPARAM();
-#ifdef EWHEEL_FANCYPATH
-    // Are we close enougth to a path node to switch to the next?
-    if (vlen(self.origin  - self.pathcurrent.origin) < 64)
-        if (self.pathcurrent.path_next == world)
-        {
-            // Path endpoint reached
-            pathlib_deletepath(self.pathcurrent.owner);
-            self.pathcurrent = world;
-
-            if (self.pathgoal)
-            {
-                if (self.pathgoal.use)
-                    self.pathgoal.use();
-
-                if (self.pathgoal.enemy)
-                {
-                    self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin);
-                    self.pathgoal = self.pathgoal.enemy;
-                }
-            }
-            else
-                self.pathgoal = world;
-        }
-        else
-            self.pathcurrent = self.pathcurrent.path_next;
-
-#else
-    if (vlen(self.origin - self.pathcurrent.origin) < 64)
-        self.pathcurrent = self.pathcurrent.enemy;
-#endif
-
-    if (self.pathcurrent)
-    {
-
-        self.moveto = self.pathcurrent.origin;
-        self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
-        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
-    }
-}
-
-void ewheel_move_enemy()
-{SELFPARAM();
-    float newframe;
-
-    self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal);
-
-    self.moveto  = self.origin + self.steerto * 128;
-
-    if (self.tur_dist_enemy > self.target_range_optimal)
-    {
-        if ( self.tur_head.spawnshieldtime < 1 )
-        {
-            newframe = ewheel_anim_fwd_fast;
-            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
-        }
-        else if (self.tur_head.spawnshieldtime < 2)
-        {
-
-            newframe = ewheel_anim_fwd_slow;
-            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
-       }
-        else
-        {
-            newframe = ewheel_anim_fwd_slow;
-            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4);
-        }
-    }
-    else if (self.tur_dist_enemy < self.target_range_optimal * 0.5)
-    {
-        newframe = ewheel_anim_bck_slow;
-        movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
-    }
-    else
-    {
-        newframe = ewheel_anim_stop;
-        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
-    }
-
-    turrets_setframe(newframe, false);
-}
-
-void ewheel_move_idle()
-{SELFPARAM();
-    if(self.frame != 0)
-    {
-        self.SendFlags |= TNSF_ANIM;
-        self.anim_start_time = time;
-    }
-
-    self.frame = 0;
-    if (vlen(self.velocity))
-        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
-}
-
-spawnfunc(turret_ewheel) { if(!turret_initialize(TUR_EWHEEL.m_id)) remove(self); }
-
-float t_ewheel(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            float i;
-            entity _mis;
-
-            for (i = 0; i < 1; ++i)
-            {
-                turret_do_updates(self);
-
-                _mis = turret_projectile(SND(LASERGUN_FIRE), 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE);
-                _mis.missile_flags = MIF_SPLASH;
-
-                Send_Effect(EFFECT_BLASTER_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)
-                    {
-                        LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!\n");
-                        self.target = "";
-                    }
-
-                    if (e.classname != "turret_checkpoint")
-                        LOG_TRACE("Warning: not a turrret path\n");
-                    else
-                    {
-
-#ifdef EWHEEL_FANCYPATH
-                        self.pathcurrent = WALKER_PATH(self.origin,e.origin);
-                        self.pathgoal = e;
-#else
-                        self.pathcurrent  = e;
-#endif
-                    }
-                }
-            }
-
-            self.iscreature                            = true;
-            self.teleportable                  = TELEPORT_NORMAL;
-            self.damagedbycontents             = true;
-            self.movetype                              = MOVETYPE_WALK;
-            self.solid                                 = SOLID_SLIDEBOX;
-            self.takedamage                            = DAMAGE_AIM;
-            self.idle_aim                              = '0 0 0';
-            self.pos1                                  = self.origin;
-            self.target_select_flags   = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
-            self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
-            self.frame                                 = self.tur_head.frame = 1;
-            self.ammo_flags                            = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
-
-            // Convert from dgr / sec to dgr / tic
-            self.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate);
-            self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate);
-
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-
-void ewheel_draw()
-{SELFPARAM();
-    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)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            self.gravity               = 1;
-            self.movetype              = MOVETYPE_BOUNCE;
-            self.move_movetype = MOVETYPE_BOUNCE;
-            self.move_origin   = self.origin;
-            self.move_time             = time;
-            self.draw                  = ewheel_draw;
-
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/flac.qc b/qcsrc/common/turrets/unit/flac.qc
deleted file mode 100644 (file)
index 3b9330f..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ FLAC,
-/* function   */ t_flac,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "flac.md3",
-/* netname       */ "flac",
-/* fullname   */ _("FLAC Cannon")
-);
-#else
-#ifdef SVQC
-void turret_flac_projectile_think_explode()
-{SELFPARAM();
-    if(self.enemy != world)
-    if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3)
-        setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius);
-
-#ifdef TURRET_DEBUG
-    float d;
-    d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
-    self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d;
-    self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
-#else
-    RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
-#endif
-    remove(self);
-}
-
-spawnfunc(turret_flac) { if(!turret_initialize(TUR_FLAC.m_id)) remove(self); }
-
-float t_flac(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            entity proj;
-
-            turret_tag_fire_update();
-
-            proj = turret_projectile(SND(HAGAR_FIRE), 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE);
-            Send_Effect(EFFECT_BLASTER_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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_flac(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/fusionreactor.qc b/qcsrc/common/turrets/unit/fusionreactor.qc
deleted file mode 100644 (file)
index 3b6389a..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ FUSIONREACTOR,
-/* function   */ t_fusionreactor,
-/* spawnflags */ TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE,
-/* mins,maxs  */ '-34 -34 0', '34 34 90',
-/* model         */ "base.md3",
-/* head_model */ "reactor.md3",
-/* netname       */ "fusionreactor",
-/* fullname   */ _("Fusion Reactor")
-);
-#else
-#ifdef SVQC
-bool turret_fusionreactor_firecheck()
-{SELFPARAM();
-    if (self.attack_finished_single > time)
-        return false;
-
-    if (self.enemy.deadflag != DEAD_NO)
-        return false;
-
-    if (self.enemy == world)
-        return false;
-
-    if (self.ammo < self.shot_dmg)
-        return false;
-
-    if (self.enemy.ammo >= self.enemy.ammo_max)
-        return false;
-
-    if (vlen(self.enemy.origin - self.origin) > self.target_range)
-        return false;
-
-    if(self.team != self.enemy.team)
-        return false;
-
-    if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY))
-        return false;
-
-    return true;
-}
-
-spawnfunc(turret_fusionreactor) { if(!turret_initialize(TUR_FUSIONREACTOR.m_id)) remove(self); }
-
-float t_fusionreactor(float req)
-{SELFPARAM();
-    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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_fusionreactor(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/hellion.qc b/qcsrc/common/turrets/unit/hellion.qc
deleted file mode 100644 (file)
index a253d20..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ HELLION,
-/* function   */ t_hellion,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "hellion.md3",
-/* netname       */ "hellion",
-/* fullname   */ _("Hellion Missile Turret")
-);
-#else
-#ifdef SVQC
-float autocvar_g_turrets_unit_hellion_shot_speed_gain;
-float autocvar_g_turrets_unit_hellion_shot_speed_max;
-
-void turret_hellion_missile_think()
-{SELFPARAM();
-    vector olddir,newdir;
-    vector pre_pos;
-    float itime;
-
-    self.nextthink = time + 0.05;
-
-    olddir = normalize(self.velocity);
-
-    if(self.max_health < time)
-        turret_projectile_explode();
-
-    // Enemy dead? just keep on the current heading then.
-    if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO))
-    {
-
-        // Make sure we dont return to tracking a respawned player
-        self.enemy = world;
-
-        // Turn model
-        self.angles = vectoangles(self.velocity);
-
-        if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) )
-            turret_projectile_explode();
-
-        // Accelerate
-        self.velocity = olddir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
-
-        UpdateCSQCProjectile(self);
-
-        return;
-    }
-
-    // Enemy in range?
-    if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2)
-        turret_projectile_explode();
-
-    // Predict enemy position
-    itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity);
-    pre_pos = self.enemy.origin + self.enemy.velocity * itime;
-
-    pre_pos = (pre_pos + self.enemy.origin) * 0.5;
-
-    // Find out the direction to that place
-    newdir = normalize(pre_pos - self.origin);
-
-    // Turn
-    newdir = normalize(olddir + newdir * 0.35);
-
-    // Turn model
-    self.angles = vectoangles(self.velocity);
-
-    // Accelerate
-    self.velocity = newdir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max));
-
-    if (itime < 0.05)
-        self.think = turret_projectile_explode;
-
-    UpdateCSQCProjectile(self);
-}
-
-spawnfunc(turret_hellion) { if(!turret_initialize(TUR_HELLION.m_id)) remove(self); }
-
-float t_hellion(float req)
-{SELFPARAM();
-    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(SND(ROCKET_FIRE), 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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_hellion(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/hk.qc b/qcsrc/common/turrets/unit/hk.qc
deleted file mode 100644 (file)
index 2523573..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ HK,
-/* function   */ t_hk,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "hk.md3",
-/* netname       */ "hk",
-/* fullname   */ _("Hunter-Killer Turret")
-);
-#else
-#ifdef SVQC
-float autocvar_g_turrets_unit_hk_shot_speed;
-float autocvar_g_turrets_unit_hk_shot_speed_accel;
-float autocvar_g_turrets_unit_hk_shot_speed_accel2;
-float autocvar_g_turrets_unit_hk_shot_speed_decel;
-float autocvar_g_turrets_unit_hk_shot_speed_max;
-float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
-
-//#define TURRET_DEBUG_HK
-
-#ifdef TURRET_DEBUG_HK
-.float atime;
-#endif
-
-float hk_is_valid_target(entity e_target)
-{SELFPARAM();
-    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()
-{SELFPARAM();
-    vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
-    float  fu, fd, ff, fl, fr, fe;  // Fraction to solid
-    vector olddir,wishdir,newdir;   // Final direction
-    float lt_for;   // Length of Trace FORwrad
-    float lt_seek;  // Length of Trace SEEK (left, right, up down)
-    float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
-    vector pre_pos;
-    float myspeed;
-    entity e;
-    float ad,edist;
-
-    self.nextthink = time + self.ticrate;
-
-    //if (self.cnt < time)
-    // turret_hk_missile_explode();
-
-    if (self.enemy.deadflag != DEAD_NO)
-        self.enemy = world;
-
-    // Pick the closest valid target.
-    if (!self.enemy)
-    {
-        e = findradius(self.origin, 5000);
-        while (e)
-        {
-            if (hk_is_valid_target(e))
-            {
-                if (!self.enemy)
-                    self.enemy = e;
-                else
-                    if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
-                        self.enemy = e;
-            }
-            e = e.chain;
-        }
-    }
-
-    self.angles = vectoangles(self.velocity);
-    self.angles_x = self.angles_x * -1;
-    makevectors(self.angles);
-    self.angles_x = self.angles_x * -1;
-
-    if (self.enemy)
-    {
-        edist = vlen(self.origin - self.enemy.origin);
-        // Close enougth to do decent damage?
-        if ( edist <= (self.owner.shot_radius * 0.25) )
-        {
-            turret_projectile_explode();
-            return;
-        }
-
-        // Get data on enemy position
-        pre_pos = self.enemy.origin +
-                  self.enemy.velocity *
-                  min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
-
-        traceline(self.origin, pre_pos,true,self.enemy);
-        ve = normalize(pre_pos - self.origin);
-        fe = trace_fraction;
-
-    }
-    else
-    {
-    edist = 0;
-    ve = '0 0 0';
-        fe = 0;
-    }
-
-    if ((fe != 1) || (self.enemy == world) || (edist > 1000))
-    {
-        myspeed = vlen(self.velocity);
-
-        lt_for  = myspeed * 3;
-        lt_seek = myspeed * 2.95;
-
-        // Trace forward
-        traceline(self.origin, self.origin + v_forward * lt_for,false,self);
-        vf = trace_endpos;
-        ff = trace_fraction;
-
-        // Find angular offset
-        ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
-
-        // To close to something, Slow down!
-        if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
-            myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
-
-        // Failry clear, accelerate.
-        if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
-            myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
-
-        // Setup trace pitch
-        pt_seek = 1 - ff;
-        pt_seek = bound(0.15,pt_seek,0.8);
-        if (ff < 0.5) pt_seek = 1;
-
-        // Trace left
-        traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
-        vl = trace_endpos;
-        fl = trace_fraction;
-
-        // Trace right
-        traceline(self.origin,  self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
-        vr = trace_endpos;
-        fr = trace_fraction;
-
-        // Trace up
-        traceline(self.origin,  self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
-        vu = trace_endpos;
-        fu = trace_fraction;
-
-        // Trace down
-        traceline(self.origin,  self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
-        vd = trace_endpos;
-        fd = trace_fraction;
-
-        vl = normalize(vl - self.origin);
-        vr = normalize(vr - self.origin);
-        vu = normalize(vu - self.origin);
-        vd = normalize(vd - self.origin);
-
-        // Panic tresh passed, find a single direction and turn as hard as we can
-        if (pt_seek == 1)
-        {
-            wishdir = v_right;
-            if (fl > fr) wishdir = -1 * v_right;
-            if (fu > fl) wishdir = v_up;
-            if (fd > fu) wishdir = -1 * v_up;
-        }
-        else
-        {
-            // Normalize our trace vectors to make a smooth path
-            wishdir = normalize( (vl * fl) + (vr * fr) +  (vu * fu) +  (vd * fd) );
-        }
-
-        if (self.enemy)
-        {
-            if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
-            wishdir = (wishdir * (1 - fe)) + (ve * fe);
-        }
-    }
-    else
-    {
-        // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
-        myspeed = vlen(self.velocity);
-        if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
-            myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
-
-        wishdir = ve;
-    }
-
-    if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
-        myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
-
-    // Ranoutagazfish?
-    if (self.cnt < time)
-    {
-        self.cnt = time + 0.25;
-        self.nextthink = 0;
-        self.movetype           = MOVETYPE_BOUNCE;
-        return;
-    }
-
-    // Calculate new heading
-    olddir = normalize(self.velocity);
-    newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
-
-    // Set heading & speed
-    self.velocity = newdir * myspeed;
-
-    // Align model with new heading
-    self.angles = vectoangles(self.velocity);
-
-
-#ifdef TURRET_DEBUG_HK
-    //if(self.atime < time) {
-    if ((fe <= 0.99)||(edist > 1000))
-    {
-        te_lightning2(world,self.origin, self.origin + vr * lt_seek);
-        te_lightning2(world,self.origin, self.origin + vl * lt_seek);
-        te_lightning2(world,self.origin, self.origin + vu * lt_seek);
-        te_lightning2(world,self.origin, self.origin + vd * lt_seek);
-        te_lightning2(world,self.origin, vf);
-    }
-    else
-    {
-        te_lightning2(world,self.origin, self.enemy.origin);
-    }
-    bprint("Speed: ", ftos(rint(myspeed)), "\n");
-    bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
-    bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
-    self.atime = time + 0.2;
-    //}
-#endif
-
-    UpdateCSQCProjectile(self);
-}
-
-float turret_hk_addtarget(entity e_target,entity e_sender)
-{SELFPARAM();
-    if (e_target)
-    {
-        if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
-        {
-            self.enemy = e_target;
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-spawnfunc(turret_hk) { if(!turret_initialize(TUR_HK.m_id)) remove(self); }
-
-float t_hk(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            entity missile;
-
-            missile = turret_projectile(SND(ROCKET_FIRE), 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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_hk(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/machinegun.qc b/qcsrc/common/turrets/unit/machinegun.qc
deleted file mode 100644 (file)
index eeb8890..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ MACHINEGUN,
-/* function   */ t_machinegun,
-/* spawnflags */ TUR_FLAG_PLAYER,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "machinegun.md3",
-/* netname       */ "machinegun",
-/* fullname   */ _("Machinegun Turret")
-);
-#else
-#ifdef SVQC
-spawnfunc(turret_machinegun) { if(!turret_initialize(TUR_MACHINEGUN.m_id)) remove(self); }
-
-void W_MachineGun_MuzzleFlash(void);
-
-float t_machinegun(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0);
-
-            W_MachineGun_MuzzleFlash();
-            setattachment(self.muzzle_flash, self.tur_head, "tag_fire");
-
-            return true;
-        }
-        case TR_THINK:
-        {
-            return true;
-        }
-        case TR_DEATH:
-        {
-            return true;
-        }
-        case TR_SETUP:
-        {
-            self.damage_flags |= TFL_DMG_HEADSHAKE;
-            self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
-            self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
-            self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
-            self.turret_flags |= TUR_FLAG_HITSCAN;
-
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_machinegun(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/mlrs.qc b/qcsrc/common/turrets/unit/mlrs.qc
deleted file mode 100644 (file)
index 7e130c6..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ MLRS,
-/* function   */ t_mlrs,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "mlrs.md3",
-/* netname       */ "mlrs",
-/* fullname   */ _("MLRS Turret")
-);
-#else
-#ifdef SVQC
-spawnfunc(turret_mlrs) { if(!turret_initialize(TUR_MLRS.m_id)) remove(self); }
-
-float t_mlrs(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            entity missile;
-
-            turret_tag_fire_update();
-            missile = turret_projectile(SND(ROCKET_FIRE), 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)
-            {
-                LOG_TRACE("ammo:",ftos(self.ammo),"\n");
-                LOG_TRACE("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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_mlrs(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/phaser.qc b/qcsrc/common/turrets/unit/phaser.qc
deleted file mode 100644 (file)
index 6eec37a..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ PHASER,
-/* function   */ t_phaser,
-/* spawnflags */ TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "phaser.md3",
-/* netname       */ "phaser",
-/* fullname   */ _("Phaser Cannon")
-);
-#else
-#ifdef SVQC
-.float fireflag;
-
-float turret_phaser_firecheck()
-{SELFPARAM();
-    if (self.fireflag != 0) return 0;
-    return turret_firecheck();
-}
-
-void beam_think()
-{SELFPARAM();
-    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, SND_Null, 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, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM);
-    }
-
-
-    self.nextthink = time + self.ticrate;
-
-    self.owner.attack_finished_single = time + frametime;
-    setself(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,
-                    this.shot_dmg,
-                    0.75,
-                    DEATH_TURRET_PHASER);
-    setself(this);
-    self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256;
-
-}
-
-spawnfunc(turret_phaser) { if(!turret_initialize(TUR_PHASER.m_id)) remove(self); }
-
-float t_phaser(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            entity beam;
-
-            beam = spawn();
-            beam.ticrate = 0.1; //autocvar_sys_ticrate;
-            setmodel(beam, MDL_TUR_PHASER_BEAM);
-            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, SND_TUR_PHASER, 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, SND(NEXIMPACT), 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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_phaser(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/plasma.qc b/qcsrc/common/turrets/unit/plasma.qc
deleted file mode 100644 (file)
index 59c0f85..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ PLASMA,
-/* function   */ t_plasma,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "plasma.md3",
-/* netname       */ "plasma",
-/* fullname   */ _("Plasma Cannon")
-);
-#else
-#ifdef SVQC
-spawnfunc(turret_plasma) { if(!turret_initialize(TUR_PLASMA.m_id)) remove(self); }
-
-float t_plasma(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            if(g_instagib)
-            {
-                float flying;
-                flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
-
-                FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
-                                   800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
-
-                Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
-
-                // teamcolor / hit beam effect
-                vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
-                WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
-                if (self.tur_head.frame == 0)
-                    self.tur_head.frame = 1;
-            }
-            else
-            {
-                entity missile = turret_projectile(SND(HAGAR_FIRE), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
-                missile.missile_flags = MIF_SPLASH;
-
-                Send_Effect(EFFECT_BLASTER_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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_plasma(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/plasma_dual.qc b/qcsrc/common/turrets/unit/plasma_dual.qc
deleted file mode 100644 (file)
index edcf7ed..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ PLASMA_DUAL,
-/* function   */ t_plasma_dual,
-/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER,
-/* mins,maxs  */ '-32 -32 0', '32 32 64',
-/* model         */ "base.md3",
-/* head_model */ "plasmad.md3",
-/* netname       */ "plasma_dual",
-/* fullname   */ _("Dual Plasma Cannon")
-);
-#else
-#ifdef SVQC
-spawnfunc(turret_plasma_dual) { if(!turret_initialize(TUR_PLASMA_DUAL.m_id)) remove(self); }
-
-float t_plasma_dual(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            if(g_instagib)
-            {
-                float flying;
-                flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
-
-                FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
-                                   800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
-
-
-                Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
-
-                // teamcolor / hit beam effect
-                vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
-                WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
-                self.tur_head.frame += 1;
-            }
-            else
-            {
-                entity missile = turret_projectile(SND(HAGAR_FIRE), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
-                missile.missile_flags = MIF_SPLASH;
-                Send_Effect(EFFECT_BLASTER_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:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_plasma_dual(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/tesla.qc b/qcsrc/common/turrets/unit/tesla.qc
deleted file mode 100644 (file)
index ebcc4e9..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ TESLA,
-/* function   */ t_tesla,
-/* spawnflags */ TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE,
-/* mins,maxs  */ '-60 -60 0', '60 60 128',
-/* model         */ "tesla_base.md3",
-/* head_model */ "tesla_head.md3",
-/* netname       */ "tesla",
-/* fullname   */ _("Tesla Coil")
-);
-#else
-#ifdef SVQC
-entity toast(entity from, float range, float damage)
-{SELFPARAM();
-    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()
-{SELFPARAM();
-    // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
-    float do_target_scan = 0;
-
-    if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
-        do_target_scan = 1;
-
-    // Old target (if any) invalid?
-    if(self.target_validate_time < time)
-    if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
-    {
-        self.enemy = world;
-        self.target_validate_time = time + 0.5;
-        do_target_scan = 1;
-    }
-
-    // But never more often then g_turrets_targetscan_mindelay!
-    if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
-        do_target_scan = 0;
-
-    if(do_target_scan)
-    {
-        self.enemy = turret_select_target();
-        self.target_select_time = time;
-    }
-
-    if(!turret_firecheck())
-        return 0;
-
-    if(self.enemy)
-        return 1;
-
-    return 0;
-}
-
-spawnfunc(turret_tesla) { if(!turret_initialize(TUR_TESLA.m_id)) remove(self); }
-
-float t_tesla(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            entity e, t;
-            float d, r, i;
-
-            d = self.shot_dmg;
-            r = self.target_range;
-            e = spawn();
-            setorigin(e,self.tur_shotorg);
-
-            self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
-
-            t = toast(e,r,d);
-            remove(e);
-
-            if (t == world) return true;
-
-            self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK;
-
-            self.attack_finished_single = time + self.shot_refire;
-            for (i = 0; i < 10; ++i)
-            {
-                d *= 0.75;
-                r *= 0.85;
-                t = toast(t, r, d);
-                if (t == world) break;
-
-            }
-
-            e = findchainfloat(railgunhit, 1);
-            while (e)
-            {
-                e.railgunhit = 0;
-                e = e.chain;
-            }
-
-            return true;
-        }
-        case TR_THINK:
-        {
-            if(!self.active)
-            {
-                self.tur_head.avelocity = '0 0 0';
-                return true;
-            }
-
-            if(self.ammo < self.shot_dmg)
-            {
-                self.tur_head.avelocity = '0 45 0' * (self.ammo / self.shot_dmg);
-            }
-            else
-            {
-                self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg);
-
-                if(self.attack_finished_single > time)
-                    return true;
-
-                float f;
-                f = (self.ammo / self.ammo_max);
-                f = f * f;
-                if(f > random())
-                    if(random() < 0.1)
-                        te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350));
-            }
-
-            return true;
-        }
-        case TR_DEATH:
-        {
-            return true;
-        }
-        case TR_SETUP:
-        {
-            self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
-                                 TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
-
-            self.turret_firecheckfunc = turret_tesla_firecheck;
-            self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
-                               TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
-
-            self.firecheck_flags       = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN;
-            self.shoot_flags           = TFL_SHOOT_CUSTOM;
-            self.ammo_flags                    = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
-            self.aim_flags                     = TFL_AIM_NO;
-            self.track_flags           = TFL_TRACK_NO;
-
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // SVQC
-#ifdef CSQC
-float t_tesla(float req)
-{
-    switch(req)
-    {
-        case TR_SETUP:
-        {
-            return true;
-        }
-        case TR_PRECACHE:
-        {
-            return true;
-        }
-    }
-
-    return true;
-}
-
-#endif // CSQC
-#endif // REGISTER_TURRET
diff --git a/qcsrc/common/turrets/unit/walker.qc b/qcsrc/common/turrets/unit/walker.qc
deleted file mode 100644 (file)
index a2927cf..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-#ifndef IMPLEMENTATION
-REGISTER_TURRET(
-/* TUR_##id   */ WALKER,
-/* function   */ t_walker,
-/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE,
-/* mins,maxs  */ '-70 -70 0', '70 70 95',
-/* model         */ "walker_body.md3",
-/* head_model */ "walker_head_minigun.md3",
-/* netname       */ "walker",
-/* fullname   */ _("Walker Turret")
-);
-#else
-#ifdef SVQC
-float autocvar_g_turrets_unit_walker_melee_damage;
-float autocvar_g_turrets_unit_walker_melee_force;
-float autocvar_g_turrets_unit_walker_melee_range;
-float autocvar_g_turrets_unit_walker_rocket_damage;
-float autocvar_g_turrets_unit_walker_rocket_radius;
-float autocvar_g_turrets_unit_walker_rocket_force;
-float autocvar_g_turrets_unit_walker_rocket_speed;
-float autocvar_g_turrets_unit_walker_rocket_range;
-float autocvar_g_turrets_unit_walker_rocket_range_min;
-float autocvar_g_turrets_unit_walker_rocket_refire;
-float autocvar_g_turrets_unit_walker_rocket_turnrate;
-float autocvar_g_turrets_unit_walker_speed_stop;
-float autocvar_g_turrets_unit_walker_speed_walk;
-float autocvar_g_turrets_unit_walker_speed_run;
-float autocvar_g_turrets_unit_walker_speed_jump;
-float autocvar_g_turrets_unit_walker_speed_swim;
-float autocvar_g_turrets_unit_walker_speed_roam;
-float autocvar_g_turrets_unit_walker_turn;
-float autocvar_g_turrets_unit_walker_turn_walk;
-float autocvar_g_turrets_unit_walker_turn_strafe;
-float autocvar_g_turrets_unit_walker_turn_swim;
-float autocvar_g_turrets_unit_walker_turn_run;
-
-#define ANIM_NO         0
-#define ANIM_TURN       1
-#define ANIM_WALK       2
-#define ANIM_RUN        3
-#define ANIM_STRAFE_L   4
-#define ANIM_STRAFE_R   5
-#define ANIM_JUMP       6
-#define ANIM_LAND       7
-#define ANIM_PAIN       8
-#define ANIM_MELEE      9
-#define ANIM_SWIM       10
-#define ANIM_ROAM       11
-
-.float animflag;
-.float idletime;
-
-#define WALKER_PATH(s,e) pathlib_astar(s,e)
-
-float walker_firecheck()
-{SELFPARAM();
-    if (self.animflag == ANIM_MELEE)
-        return 0;
-
-    return turret_firecheck();
-}
-
-void walker_melee_do_dmg()
-{SELFPARAM();
-    vector where;
-    entity e;
-
-    makevectors(self.angles);
-    where = self.origin + v_forward * 128;
-
-    e = findradius(where,32);
-    while (e)
-    {
-        if (turret_validate_target(self, e, self.target_validate_flags))
-            if (e != self && e.owner != self)
-                Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force));
-
-        e = e.chain;
-    }
-}
-
-void walker_setnoanim()
-{SELFPARAM();
-    turrets_setframe(ANIM_NO, false);
-    self.animflag = self.frame;
-}
-void walker_rocket_explode()
-{SELFPARAM();
-    RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world);
-    remove (self);
-}
-
-void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
-{SELFPARAM();
-    self.health = self.health - damage;
-    self.velocity = self.velocity + vforce;
-
-    if (self.health <= 0)
-        W_PrepareExplosionByDamage(self.owner, walker_rocket_explode);
-}
-
-#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self)
-void walker_rocket_loop();
-void walker_rocket_think()
-{SELFPARAM();
-    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()
-{SELFPARAM();
-    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()
-{SELFPARAM();
-    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()
-{SELFPARAM();
-    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)
-{SELFPARAM();
-    entity rocket;
-
-    fixedmakevectors(self.angles);
-
-    te_explosion (org);
-
-    rocket = spawn ();
-    setorigin(rocket, org);
-
-    sound (self, CH_WEAPON_A, SND_HAGAR_FIRE, VOL_BASE, ATTEN_NORM);
-    setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
-
-    rocket.classname             = "walker_rocket";
-    rocket.owner                         = self;
-    rocket.bot_dodge             = true;
-    rocket.bot_dodgerating     = 50;
-    rocket.takedamage           = DAMAGE_YES;
-    rocket.damageforcescale   = 2;
-    rocket.health                       = 25;
-    rocket.tur_shotorg         = randomvec() * 512;
-    rocket.cnt                         = time + 1;
-    rocket.enemy                         = self.enemy;
-
-    if (random() < 0.01)
-        rocket.think             = walker_rocket_loop;
-    else
-        rocket.think             = walker_rocket_think;
-
-    rocket.event_damage           = walker_rocket_damage;
-
-    rocket.nextthink             = time;
-    rocket.movetype               = MOVETYPE_FLY;
-    rocket.velocity               = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
-    rocket.angles                       = vectoangles(rocket.velocity);
-    rocket.touch                         = walker_rocket_explode;
-    rocket.flags                         = FL_PROJECTILE;
-    rocket.solid                         = SOLID_BBOX;
-    rocket.max_health           = time + 9;
-    rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
-
-    CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound
-}
-
-.vector enemy_last_loc;
-.float enemy_last_time;
-void walker_move_to(vector _target, float _dist)
-{SELFPARAM();
-    switch (self.waterlevel)
-    {
-        case WATERLEVEL_NONE:
-            if (_dist > 500)
-                self.animflag = ANIM_RUN;
-            else
-                self.animflag = ANIM_WALK;
-        case WATERLEVEL_WETFEET:
-        case WATERLEVEL_SWIMMING:
-            if (self.animflag != ANIM_SWIM)
-                self.animflag = ANIM_WALK;
-            else
-                self.animflag = ANIM_SWIM;
-            break;
-        case WATERLEVEL_SUBMERGED:
-            self.animflag = ANIM_SWIM;
-    }
-
-    self.moveto = _target;
-    self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
-    if(self.enemy)
-    {
-        self.enemy_last_loc = _target;
-        self.enemy_last_time = time;
-    }
-}
-
-//#define WALKER_FANCYPATHING
-
-void walker_move_path()
-{SELFPARAM();
-#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
-}
-
-spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER.m_id)) remove(self); }
-
-float t_walker(float req)
-{SELFPARAM();
-    switch(req)
-    {
-        case TR_ATTACK:
-        {
-            sound (self, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM);
-            fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0);
-            Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
-
-            return true;
-        }
-        case TR_THINK:
-        {
-            fixedmakevectors(self.angles);
-
-            if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent)
-                walker_move_path();
-            else if (self.enemy == world)
-            {
-                if(self.pathcurrent)
-                    walker_move_path();
-                else
-                {
-                    if(self.enemy_last_time != 0)
-                    {
-                        if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10)
-                            self.enemy_last_time = 0;
-                        else
-                            walker_move_to(self.enemy_last_loc, 0);
-                    }
-                    else
-                    {
-                        if(self.animflag != ANIM_NO)
-                        {
-                            traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self);
-
-                            if(trace_fraction != 1.0)
-                                self.tur_head.idletime = -1337;
-                            else
-                            {
-                                traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self);
-                                if(trace_fraction == 1.0)
-                                    self.tur_head.idletime = -1337;
-                            }
-
-                            if(self.tur_head.idletime == -1337)
-                            {
-                                self.moveto = self.origin + randomvec() * 256;
-                                self.tur_head.idletime = 0;
-                            }
-
-                            self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
-                            self.moveto_z = self.origin_z + 64;
-                            walker_move_to(self.moveto, 0);
-                        }
-
-                        if(self.idletime < time)
-                        {
-                            if(random() < 0.5 || !(self.spawnflags & TSL_ROAM))
-                            {
-                                self.idletime = time + 1 + random() * 5;
-                                self.moveto = self.origin;
-                                self.animflag = ANIM_NO;
-                            }
-                            else
-                            {
-                                self.animflag = ANIM_WALK;
-                                self.idletime = time + 4 + random() * 2;
-                                self.moveto = self.origin + randomvec() * 256;
-                                self.tur_head.moveto = self.moveto;
-                                self.tur_head.idletime = 0;
-                            }
-                        }
-                    }
-                }
-            }
-            else
-            {
-                if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE)
-                {
-                    vector wish_angle;
-
-                    wish_angle = angleofs(self, self.enemy);
-                    if (self.animflag != ANIM_SWIM)
-                    if (fabs(wish_angle_y) < 15)
-                    {
-                        self.moveto   = self.enemy.origin;
-                        self.steerto  = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-                        self.animflag = ANIM_MELEE;
-                    }
-                }
-                else if (self.tur_head.attack_finished_single < time)
-                {
-                    if(self.tur_head.shot_volly)
-                    {
-                        self.animflag = ANIM_NO;
-
-                        self.tur_head.shot_volly = self.tur_head.shot_volly -1;
-                        if(self.tur_head.shot_volly == 0)
-                            self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
-                        else
-                            self.tur_head.attack_finished_single = time + 0.2;
-
-                        if(self.tur_head.shot_volly > 1)
-                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
-                        else
-                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02")));
-                    }
-                    else
-                    {
-                        if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min))
-                        if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range))
-                            self.tur_head.shot_volly = 4;
-                    }
-                }
-                else
-                {
-                    if (self.animflag != ANIM_MELEE)
-                        walker_move_to(self.enemy.origin, self.tur_dist_enemy);
-                }
-            }
-
-            {
-                vector real_angle;
-                float turny = 0, turnx = 0;
-                float vz;
-
-                real_angle = vectoangles(self.steerto) - self.angles;
-                vz = self.velocity_z;
-
-                switch (self.animflag)
-                {
-                    case ANIM_NO:
-                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
-                        break;
-
-                    case ANIM_TURN:
-                        turny = (autocvar_g_turrets_unit_walker_turn);
-                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
-                        break;
-
-                    case ANIM_WALK:
-                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
-                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6);
-                        break;
-
-                    case ANIM_RUN:
-                        turny = (autocvar_g_turrets_unit_walker_turn_run);
-                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6);
-                        break;
-
-                    case ANIM_STRAFE_L:
-                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
-                        movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
-                        break;
-
-                    case ANIM_STRAFE_R:
-                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
-                        movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
-                        break;
-
-                    case ANIM_JUMP:
-                        self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump);
-                        break;
-
-                    case ANIM_LAND:
-                        break;
-
-                    case ANIM_PAIN:
-                        if(self.frame != ANIM_PAIN)
-                            defer(0.25, walker_setnoanim);
-
-                        break;
-
-                    case ANIM_MELEE:
-                        if(self.frame != ANIM_MELEE)
-                        {
-                            defer(0.41, walker_setnoanim);
-                            defer(0.21, walker_melee_do_dmg);
-                        }
-
-                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
-                        break;
-
-                    case ANIM_SWIM:
-                        turny = (autocvar_g_turrets_unit_walker_turn_swim);
-                        turnx = (autocvar_g_turrets_unit_walker_turn_swim);
-
-                        self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
-                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3);
-                        vz = self.velocity_z + sin(time * 4) * 8;
-                        break;
-
-                    case ANIM_ROAM:
-                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
-                        movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5);
-                        break;
-                }
-
-                if(turny)
-                {
-                    turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny );
-                    self.angles_y += turny;
-                }
-
-                if(turnx)
-                {
-                    turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx );
-                    self.angles_x += turnx;
-                }
-
-                self.velocity_z = vz;
-            }
-
-
-            if(self.origin != self.oldorigin)
-                self.SendFlags |= TNSF_MOVE;
-
-            self.oldorigin = self.origin;
-            turrets_setframe(self.animflag, false);
-
-            return true;
-        }
-        case TR_DEATH:
- &