]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/weapons/all.qc
Merge branch 'master' into TimePath/csqc_viewmodels
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / all.qc
index 72c92471a9c537435aaeb29ed20072303c7700a3..849403f8713740773c0c7d780b6213b073064f96 100644 (file)
@@ -4,36 +4,29 @@
 #include "all.qh"
 
 #if defined(CSQC)
-       #include "../../dpdefs/csprogsdefs.qh"
        #include "../../client/defs.qh"
        #include "../constants.qh"
        #include "../stats.qh"
-       #include "../../warpzonelib/anglestransform.qh"
-       #include "../../warpzonelib/mathlib.qh"
-       #include "../../warpzonelib/common.qh"
-       #include "../../warpzonelib/client.qh"
+       #include "../../lib/warpzone/anglestransform.qh"
+       #include "../../lib/warpzone/common.qh"
+       #include "../../lib/warpzone/client.qh"
        #include "../util.qh"
-       #include "../buffs.qh"
        #include "../../client/autocvars.qh"
-       #include "../deathtypes.qh"
-       #include "../../csqcmodellib/interpolate.qh"
+       #include "../deathtypes/all.qh"
+       #include "../../lib/csqcmodel/interpolate.qh"
        #include "../movetypes/movetypes.qh"
        #include "../../client/main.qh"
-       #include "../../csqcmodellib/cl_model.qh"
+       #include "../../lib/csqcmodel/cl_model.qh"
 #elif defined(MENUQC)
 #elif defined(SVQC)
-       #include "../../dpdefs/progsdefs.qh"
-    #include "../../dpdefs/dpextensions.qh"
-    #include "../../warpzonelib/anglestransform.qh"
-    #include "../../warpzonelib/mathlib.qh"
-    #include "../../warpzonelib/common.qh"
-    #include "../../warpzonelib/util_server.qh"
-    #include "../../warpzonelib/server.qh"
+    #include "../../lib/warpzone/anglestransform.qh"
+    #include "../../lib/warpzone/common.qh"
+    #include "../../lib/warpzone/util_server.qh"
+    #include "../../lib/warpzone/server.qh"
     #include "../constants.qh"
     #include "../stats.qh"
     #include "../teams.qh"
     #include "../util.qh"
-    #include "../buffs.qh"
     #include "../monsters/all.qh"
     #include "config.qh"
     #include "../../server/weapons/csqcprojectile.qh"
     #include "../../server/constants.qh"
     #include "../../server/defs.qh"
     #include "../notifications.qh"
-    #include "../deathtypes.qh"
-    #include "../../server/mutators/mutators_include.qh"
+    #include "../deathtypes/all.qh"
+    #include "../../server/mutators/all.qh"
     #include "../mapinfo.qh"
     #include "../../server/command/common.qh"
-    #include "../../csqcmodellib/sv_model.qh"
+    #include "../../lib/csqcmodel/sv_model.qh"
     #include "../../server/portals.qh"
     #include "../../server/g_hook.qh"
 #endif
 #ifndef MENUQC
-#include "calculations.qc"
+       #include "calculations.qc"
 #endif
+#define IMPLEMENTATION
 #include "all.inc"
+#undef IMPLEMENTATION
 
 // WEAPON PLUGIN SYSTEM
-entity weapon_info[WEP_MAXCOUNT];
-entity dummy_weapon_info;
 
-#if WEP_MAXCOUNT > 72
-# error Kein Weltraum links auf dem Gerät
-#endif
-
-WepSet WepSet_FromWeapon(int a) {
+WepSet _WepSet_FromWeapon(int a)
+{
        a -= WEP_FIRST;
-#if WEP_MAXCOUNT > 24
-       if(a >= 24) {
-               a -= 24;
-#if WEP_MAXCOUNT > 48
-               if(a >= 24) {
+       if (Weapons_MAX > 24)
+               if (a >= 24)
+               {
                        a -= 24;
-                       return '0 0 1' * power2of(a);
+                       if (Weapons_MAX > 48)
+                               if (a >= 24)
+                               {
+                                       a -= 24;
+                                       return '0 0 1' * power2of(a);
+                               }
+                       return '0 1 0' * power2of(a);
                }
-#endif
-               return '0 1 0' * power2of(a);
-       }
-#endif
        return '1 0 0' * power2of(a);
 }
 #ifdef SVQC
-void WepSet_AddStat()
-{
-       addstat(STAT_WEAPONS, AS_INT, weapons_x);
-#if WEP_MAXCOUNT > 24
-       addstat(STAT_WEAPONS2, AS_INT, weapons_y);
-#if WEP_MAXCOUNT > 48
-       addstat(STAT_WEAPONS3, AS_INT, weapons_z);
-#endif
-#endif
-}
-void WepSet_AddStat_InMap()
-{
-       addstat(STAT_WEAPONSINMAP, AS_INT, weaponsinmap_x);
-#if WEP_MAXCOUNT > 24
-       addstat(STAT_WEAPONSINMAP2, AS_INT, weaponsinmap_y);
-#if WEP_MAXCOUNT > 48
-       addstat(STAT_WEAPONSINMAP3, AS_INT, weaponsinmap_z);
-#endif
-#endif
-}
-void WriteWepSet(float dst, WepSet w)
-{
-#if WEP_MAXCOUNT > 48
-       WriteInt72_t(dst, w);
-#elif WEP_MAXCOUNT > 24
-       WriteInt48_t(dst, w);
-#else
-       WriteInt24_t(dst, w.x);
-#endif
-}
+       void WriteWepSet(float dst, WepSet w)
+       {
+               if (Weapons_MAX > 48) WriteInt72_t(dst, w);
+               else if (Weapons_MAX > 24) WriteInt48_t(dst, w);
+               else WriteInt24_t(dst, w.x);
+       }
 #endif
 #ifdef CSQC
-WepSet WepSet_GetFromStat()
-{
-       WepSet w = '0 0 0';
-       w.x = getstati(STAT_WEAPONS);
-#if WEP_MAXCOUNT > 24
-       w.y = getstati(STAT_WEAPONS2);
-#if WEP_MAXCOUNT > 48
-       w.z = getstati(STAT_WEAPONS3);
-#endif
-#endif
-       return w;
-}
-WepSet WepSet_GetFromStat_InMap()
-{
-       WepSet w = '0 0 0';
-       w_x = getstati(STAT_WEAPONSINMAP);
-#if WEP_MAXCOUNT > 24
-       w_y = getstati(STAT_WEAPONSINMAP2);
-#if WEP_MAXCOUNT > 48
-       w_z = getstati(STAT_WEAPONSINMAP3);
-#endif
-#endif
-       return w;
-}
-WepSet ReadWepSet()
-{
-#if WEP_MAXCOUNT > 48
-       return ReadInt72_t();
-#elif WEP_MAXCOUNT > 24
-       return ReadInt48_t();
-#else
-       return ReadInt24_t() * '1 0 0';
-#endif
-}
+       WepSet WepSet_GetFromStat()
+       {
+               return STAT(WEAPONS);
+       }
+       WepSet WepSet_GetFromStat_InMap()
+       {
+               return STAT(WEAPONSINMAP);
+       }
+       WepSet ReadWepSet()
+       {
+               if (Weapons_MAX > 48) return ReadInt72_t();
+               if (Weapons_MAX > 24) return ReadInt48_t();
+               return ReadInt24_t() * '1 0 0';
+       }
 #endif
 
-void register_weapon(
-       int id,
-       WepSet bit,
-       bool(int) func,
-       .int ammotype,
-       int i,
-       int weapontype,
-       float pickupbasevalue,
-       vector clr,
-       string modelname,
-       string simplemdl,
-       string crosshair,
-       string wepimg,
-       string refname,
-       string wepname)
-{
-       entity e;
-       weapon_info[id - 1] = e = spawn();
-       e.classname = "weapon_info";
-       e.weapon = id;
-       e.weapons = bit;
-       e.weapon_func = func;
-       e.ammo_field = ammotype;
-       e.impulse = i;
-       e.spawnflags = weapontype;
-       e.bot_pickupbasevalue = pickupbasevalue;
-       e.wpcolor = clr;
-       e.wpmodel = strzone(strcat("wpn-", ftos(id)));
-       e.mdl = modelname;
-       e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
-       e.w_simplemdl = strzone(simplemdl); // simpleitems weapon model/image
-       e.w_crosshair = strzone(car(crosshair));
-       string s = cdr(crosshair);
-       e.w_crosshair_size = ((s != "") ? stof(s) : 1); // so that we can scale the crosshair from code (for compat)
-       e.model2 = strzone(wepimg);
-       e.netname = refname;
-       e.message = wepname;
-
-       #ifdef CSQC
-       func(WR_INIT);
-       #endif
-}
-bool w_null(int dummy)
-{
-       return 0;
-}
-void register_weapons_done()
-{
-       dummy_weapon_info = spawn();
-       dummy_weapon_info.classname = "weapon_info";
-       dummy_weapon_info.weapon = 0; // you can recognize dummies by this
-       dummy_weapon_info.weapons = '0 0 0';
-       dummy_weapon_info.netname = "";
-       dummy_weapon_info.message = "AOL CD Thrower";
-       dummy_weapon_info.weapon_func = w_null;
-       dummy_weapon_info.wpmodel = "";
-       dummy_weapon_info.mdl = "";
-       dummy_weapon_info.model = "";
-       dummy_weapon_info.spawnflags = 0;
-       dummy_weapon_info.impulse = -1;
-       dummy_weapon_info.bot_pickupbasevalue = 0;
-       dummy_weapon_info.ammo_field = ammo_none;
-
-       dummy_weapon_info.w_crosshair = "gfx/crosshair1";
-       dummy_weapon_info.w_crosshair_size = 1;
-       dummy_weapon_info.model2 = "";
-
-       int i;
-       weaponorder_byid = "";
-       for(i = WEP_MAXCOUNT; i >= 1; --i)
-               if(weapon_info[i-1])
-                       weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
-       weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
-}
-entity get_weaponinfo(int id)
-{
-       entity w;
-       if(id < WEP_FIRST || id > WEP_LAST)
-               return dummy_weapon_info;
-       w = weapon_info[id - 1];
-       if(w)
-               return w;
-       return dummy_weapon_info;
-}
 string W_FixWeaponOrder(string order, float complete)
 {
-       return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete);
+       return fixPriorityList(order, WEP_FIRST, WEP_LAST, WEP_IMPULSE_BEGIN - WEP_FIRST, complete);
 }
 string W_NameWeaponOrder_MapFunc(string s)
 {
        entity wi;
-       if(s == "0" || stof(s))
+       if (s == "0" || stof(s))
        {
                wi = get_weaponinfo(stof(s));
-               if(wi != dummy_weapon_info)
-                       return wi.netname;
+               if (wi != WEP_Null) return wi.netname;
        }
        return s;
 }
 
 string W_UndeprecateName(string s)
 {
-       switch ( s )
+       switch (s)
        {
-               case "nex"            : return "vortex";
-               case "rocketlauncher" : return "devastator";
-               case "laser"          : return "blaster";
-               case "minstanex"      : return "vaporizer";
+               case "nex": return "vortex";
+               case "rocketlauncher": return "devastator";
+               case "laser": return "blaster";
+               case "minstanex": return "vaporizer";
                case "grenadelauncher": return "mortar";
-               case "uzi"            : return "machinegun";
-               default               : return s;
+               case "uzi": return "machinegun";
+               default: return s;
        }
 }
 string W_NameWeaponOrder(string order)
@@ -269,12 +130,10 @@ string W_NameWeaponOrder(string order)
 string W_NumberWeaponOrder_MapFunc(string s)
 {
        int i;
-       if(s == "0" || stof(s))
-               return s;
+       if (s == "0" || stof(s)) return s;
        s = W_UndeprecateName(s);
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               if(s == get_weaponinfo(i).netname)
-                       return ftos(i);
+       for (i = WEP_FIRST; i <= WEP_LAST; ++i)
+               if (s == get_weaponinfo(i).netname) return ftos(i);
        return s;
 }
 string W_NumberWeaponOrder(string order)
@@ -282,7 +141,7 @@ string W_NumberWeaponOrder(string order)
        return mapPriorityList(order, W_NumberWeaponOrder_MapFunc);
 }
 
-float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT];
+float W_FixWeaponOrder_BuildImpulseList_buf[Weapons_MAX];
 string W_FixWeaponOrder_BuildImpulseList_order;
 void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass)
 {
@@ -298,23 +157,24 @@ float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass)
        e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]);
        e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]);
        d = (e1.impulse + 9) % 10 - (e2.impulse + 9) % 10;
-       if(d != 0)
-               return -d; // high impulse first!
-       return
-               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0)
-               -
-               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0)
-               ; // low char index first!
+       if (d != 0) return -d;  // high impulse first!
+       return strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "),
+               sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0)
+              -
+              strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "),
+               sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0)
+        // low char index first!
 }
 string W_FixWeaponOrder_BuildImpulseList(string o)
 {
        int i;
        W_FixWeaponOrder_BuildImpulseList_order = o;
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+       for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
-       heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world);
+       heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp,
+               world);
        o = "";
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+       for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST]));
        W_FixWeaponOrder_BuildImpulseList_order = string_null;
        return substring(o, 1, -1);
@@ -327,8 +187,7 @@ string W_FixWeaponOrder_AllowIncomplete(string order)
 
 string W_FixWeaponOrder_ForceComplete(string order)
 {
-       if(order == "")
-               order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
+       if (order == "") order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
        return W_FixWeaponOrder(order, 1);
 }
 
@@ -339,12 +198,11 @@ void W_RandomWeapons(entity e, float n)
        WepSet result;
        remaining = e.weapons;
        result = '0 0 0';
-       for(i = 0; i < n; ++i)
+       for (i = 0; i < n; ++i)
        {
                RandomSelection_Init();
-               for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-                       if(remaining & WepSet_FromWeapon(j))
-                               RandomSelection_Add(world, j, string_null, 1, 1);
+               for (j = WEP_FIRST; j <= WEP_LAST; ++j)
+                       if (remaining & WepSet_FromWeapon(j)) RandomSelection_Add(world, j, string_null, 1, 1);
                result |= WepSet_FromWeapon(RandomSelection_chosen_float);
                remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float);
        }
@@ -353,45 +211,403 @@ void W_RandomWeapons(entity e, float n)
 
 string GetAmmoPicture(.int ammotype)
 {
-       switch(ammotype)
+       switch (ammotype)
        {
-               case ammo_shells:  return "ammo_shells";
-               case ammo_nails:   return "ammo_bullets";
-               case ammo_rockets: return "ammo_rockets";
-               case ammo_cells:   return "ammo_cells";
-               case ammo_plasma:  return "ammo_cells";
-               case ammo_fuel:    return "ammo_fuel";
-               default: return ""; // wtf, no ammo type?
+               case ammo_shells:  return ITEM_Shells.m_icon;
+               case ammo_nails:   return ITEM_Bullets.m_icon;
+               case ammo_rockets: return ITEM_Rockets.m_icon;
+               case ammo_cells:   return ITEM_Cells.m_icon;
+               case ammo_plasma:  return ITEM_Plasma.m_icon;
+               case ammo_fuel:    return ITEM_JetpackFuel.m_icon;
+               default: return "";  // wtf, no ammo type?
        }
 }
 
 #ifdef CSQC
-.int GetAmmoFieldFromNum(int i)
+       .int GetAmmoFieldFromNum(int i)
+       {
+               switch (i)
+               {
+                       case 0: return ammo_shells;
+                       case 1: return ammo_nails;
+                       case 2: return ammo_rockets;
+                       case 3: return ammo_cells;
+                       case 4: return ammo_plasma;
+                       case 5: return ammo_fuel;
+                       default: return ammo_none;
+               }
+       }
+
+       int GetAmmoStat(.int ammotype)
+       {
+               switch (ammotype)
+               {
+                       case ammo_shells: return STAT_SHELLS;
+                       case ammo_nails: return STAT_NAILS;
+                       case ammo_rockets: return STAT_ROCKETS;
+                       case ammo_cells: return STAT_CELLS;
+                       case ammo_plasma: return STAT_PLASMA.m_id;
+                       case ammo_fuel: return STAT_FUEL.m_id;
+                       default: return -1;
+               }
+       }
+#endif
+
+string W_Sound(string w_snd)
+{
+       string output = strcat("weapons/", w_snd);
+#ifdef SVQC
+               MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
+               return weapon_sound_output;
+#else
+               return output;
+#endif
+}
+
+string W_Model(string w_mdl)
+{
+       string output = strcat("models/weapons/", w_mdl);
+#ifdef SVQC
+               MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
+               return weapon_model_output;
+#else
+               return output;
+#endif
+}
+
+#ifndef MENUQC
+vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
 {
-       switch(i)
+       switch (algn)
        {
-               case 0: return ammo_shells;
-               case 1: return ammo_nails;
-               case 2: return ammo_rockets;
-               case 3: return ammo_cells;
-               case 4: return ammo_plasma;
-               case 5: return ammo_fuel;
-               default: return ammo_none;
+               default:
+               case 3:
+                       // right alignment
+                       break;
+               case 4:
+                       // left
+                       vecs.y = -vecs.y;
+                       break;
+               case 1:
+               case 2:
+                       // center
+                       vecs.y = 0;
+                       vecs.z -= 2;
+                       break;
        }
+       return vecs;
 }
 
-int GetAmmoStat(.int ammotype)
+vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn)
 {
-       switch(ammotype)
+#ifdef SVQC
+       string s;
+#endif
+       if (visual)
        {
-               case ammo_shells: return STAT_SHELLS;
-               case ammo_nails: return STAT_NAILS;
-               case ammo_rockets: return STAT_ROCKETS;
-               case ammo_cells: return STAT_CELLS;
-               case ammo_plasma: return STAT_PLASMA;
-               case ammo_fuel: return STAT_FUEL;
-               default: return -1;
+               vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
        }
+#ifdef SVQC
+       else if (autocvar_g_shootfromeye)
+       {
+               vecs.y = vecs.z = 0;
+       }
+       else if (autocvar_g_shootfromcenter)
+       {
+               vecs.y = 0;
+               vecs.z -= 2;
+       }
+       else if ((s = autocvar_g_shootfromfixedorigin) != "")
+       {
+               vector v = stov(s);
+               if (y_is_right) v.y = -v.y;
+               if (v.x != 0) vecs.x = v.x;
+               vecs.y = v.y;
+               vecs.z = v.z;
+       }
+#endif
+       else  // just do the same as top
+       {
+               vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
+       }
+
+       return vecs;
 }
+
+#define shotorg_adjust shotorg_adjust_values
+
+/**
+ * supported formats:
+ *
+ * 1. simple animated model, muzzle flash handling on h_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *        weapon = attachment for v_tuba.md3
+ *    v_tuba.md3 - first and third person model
+ *    g_tuba.md3 - pickup model
+ *
+ * 2. simple animated model, muzzle flash handling on v_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ *      tags:
+ *        weapon = attachment for v_tuba.md3
+ *    v_tuba.md3 - first and third person model
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *    g_tuba.md3 - pickup model
+ *
+ * 3. fully animated model, muzzle flash handling on h_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
+ *    v_tuba.md3 - third person model
+ *    g_tuba.md3 - pickup model
+ *
+ * 4. fully animated model, muzzle flash handling on v_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ *      tags:
+ *        shot = muzzle end (shot origin)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *    v_tuba.md3 - third person model
+ *      tags:
+ *        shot = muzzle end (for muzzle flashes)
+ *    g_tuba.md3 - pickup model
+ *
+ * writes:
+ *   this.origin, this.angles
+ *   this.weaponchild
+ *   this.movedir, this.view_ofs
+ *   attachment stuff
+ *   anim stuff
+ * to free:
+ *   call again with ""
+ *   remove the ent
+ */
+void CL_WeaponEntity_SetModel(entity this, string name)
+{
+       if (name == "")
+       {
+               this.model = "";
+               if (this.weaponchild) remove(this.weaponchild);
+               this.weaponchild = NULL;
+               this.movedir = '0 0 0';
+               this.spawnorigin = '0 0 0';
+               this.oldorigin = '0 0 0';
+               this.anim_fire1  = '0 1 0.01';
+               this.anim_fire2  = '0 1 0.01';
+               this.anim_idle   = '0 1 0.01';
+               this.anim_reload = '0 1 0.01';
+       }
+       else
+       {
+               // if there is a child entity, hide it until we're sure we use it
+               if (this.weaponchild) this.weaponchild.model = "";
+               _setmodel(this, W_Model(strcat("v_", name, ".md3")));
+               int v_shot_idx;  // used later
+               (v_shot_idx = gettagindex(this, "shot")) || (v_shot_idx = gettagindex(this, "tag_shot"));
+
+               _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
+               // preset some defaults that work great for renamed zym files (which don't need an animinfo)
+               this.anim_fire1  = animfixfps(this, '0 1 0.01', '0 0 0');
+               this.anim_fire2  = animfixfps(this, '1 1 0.01', '0 0 0');
+               this.anim_idle   = animfixfps(this, '2 1 0.01', '0 0 0');
+               this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
+
+               // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
+               // if we don't, this is a "real" animated model
+               string t;
+               if ((t = "weapon", gettagindex(this, t)) || (t = "tag_weapon", gettagindex(this, t)))
+               {
+                       if (!this.weaponchild)
+                       {
+                               this.weaponchild = new(weaponchild);
+#ifdef CSQC
+                               this.weaponchild.drawmask = MASK_NORMAL;
+                               this.weaponchild.renderflags |= RF_VIEWMODEL;
 #endif
+                       }
+                       _setmodel(this.weaponchild, W_Model(strcat("v_", name, ".md3")));
+                       setattachment(this.weaponchild, this, t);
+               }
+               else
+               {
+                       if (this.weaponchild) remove(this.weaponchild);
+                       this.weaponchild = NULL;
+               }
+
+               setorigin(this, '0 0 0');
+               this.angles = '0 0 0';
+               this.frame = 0;
+#ifdef SVQC
+               this.viewmodelforclient = NULL;
+#else
+               this.renderflags &= ~RF_VIEWMODEL;
+#endif
+               if (v_shot_idx)  // v_ model attached to invisible h_ model
+               {
+                       this.movedir = gettaginfo(this.weaponchild, v_shot_idx);
+               }
+               else
+               {
+                       int idx;
+                       if ((idx = gettagindex(this, "shot")) || (idx = gettagindex(this, "tag_shot")))
+                       {
+                               this.movedir = gettaginfo(this, idx);
+                       }
+                       else
+                       {
+                               LOG_WARNINGF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n",
+                                       this.model);
+                               this.movedir = '0 0 0';
+                       }
+               }
+               {
+                       int idx = 0;
+                       // v_ model attached to invisible h_ model
+                       if (this.weaponchild
+                           && ((idx = gettagindex(this.weaponchild, "shell")) || (idx = gettagindex(this.weaponchild, "tag_shell"))))
+                       {
+                               this.spawnorigin = gettaginfo(this.weaponchild, idx);
+                       }
+                       else if ((idx = gettagindex(this, "shell")) || (idx = gettagindex(this, "tag_shell")))
+                       {
+                               this.spawnorigin = gettaginfo(this, idx);
+                       }
+                       else
+                       {
+                               LOG_WARNINGF("weapon model %s does not support the 'shell' tag, will display casings wrong\n",
+                                       this.model);
+                               this.spawnorigin = this.movedir;
+                       }
+               }
+               if (v_shot_idx)
+               {
+                       this.oldorigin = '0 0 0';  // use regular attachment
+               }
+               else
+               {
+                       int idx;
+                       if (this.weaponchild)
+                               (idx = gettagindex(this, "weapon")) || (idx = gettagindex(this, "tag_weapon"));
+                       else
+                               (idx = gettagindex(this, "handle")) || (idx = gettagindex(this, "tag_handle"));
+                       if (idx)
+                       {
+                               this.oldorigin = this.movedir - gettaginfo(this, idx);
+                       }
+                       else
+                       {
+                               LOG_WARNINGF(
+                                       "weapon model %s does not support the 'handle' tag "
+                                       "and neither does the v_ model support the 'shot' tag, "
+                                       "will display muzzle flashes TOTALLY wrong\n",
+                                       this.model);
+                               this.oldorigin = '0 0 0';  // there is no way to recover from this
+                       }
+               }
+
+#ifdef SVQC
+               this.viewmodelforclient = this.owner;
+#else
+               this.renderflags |= RF_VIEWMODEL;
+#endif
+       }
+
+       this.view_ofs = '0 0 0';
+
+       if (this.movedir.x >= 0)
+       {
+#ifdef SVQC
+               int algn = this.owner.cvar_cl_gunalign;
+#else
+               int algn = autocvar_cl_gunalign;
+#endif
+               vector v = this.movedir;
+               this.movedir = shotorg_adjust(v, false, false, algn);
+               this.view_ofs = shotorg_adjust(v, false, true, algn) - v;
+       }
+       int compressed_shotorg = compressShotOrigin(this.movedir);
+       // make them match perfectly
+#ifdef SVQC
+       this.movedir = decompressShotOrigin(this.owner.stat_shotorg = compressed_shotorg);
+#else
+       this.movedir = decompressShotOrigin(compressed_shotorg);
+#endif
+
+       this.spawnorigin += this.view_ofs;  // offset the casings origin by the same amount
+
+       // check if an instant weapon switch occurred
+       setorigin(this, this.view_ofs);
+       // reset animstate now
+       this.wframe = WFRAME_IDLE;
+       setanim(this, this.anim_idle, true, false, true);
+}
+#endif
+
+#ifndef MENUQC
+
+REGISTER_NET_TEMP(wframe)
+#ifdef CSQC
+NET_HANDLE(wframe, bool isNew)
+{
+       vector a;
+       a.x = ReadCoord();
+    a.y = ReadCoord();
+    a.z = ReadCoord();
+       bool restartanim = ReadByte();
+       anim_set(viewmodel, a, !restartanim, restartanim, restartanim);
+       viewmodel.state = ReadByte();
+       viewmodel.alpha = ReadByte() / 255;
+       return true;
+}
+#endif
+
+#ifdef SVQC
+void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim)
+{
+       if (!IS_REAL_CLIENT(actor)) return;
+       int channel = MSG_ONE;
+       msg_entity = actor;
+       WriteHeader(channel, wframe);
+       WriteCoord(channel, a.x);
+       WriteCoord(channel, a.y);
+       WriteCoord(channel, a.z);
+       WriteByte(channel, restartanim);
+       WriteByte(channel, weaponentity.state);
+       WriteByte(channel, weaponentity.alpha * 255);
+}
+#endif
+
+REGISTER_NET_TEMP(wglow)
+#ifdef CSQC
+NET_HANDLE(wglow, bool isNew)
+{
+       vector g = '0 0 0';
+       g.x = ReadCoord();
+       g.y = ReadCoord();
+       g.z = ReadCoord();
+       viewmodel.glowmod = g;
+       return true;
+}
+#endif
+
+#ifdef SVQC
+void wglow_send(entity actor, vector g)
+{
+       if (!IS_REAL_CLIENT(actor)) return;
+       int channel = MSG_ONE;
+       msg_entity = actor;
+       WriteHeader(channel, wglow);
+       WriteCoord(channel, g.x);
+       WriteCoord(channel, g.y);
+       WriteCoord(channel, g.z);
+}
+#endif
+
+#endif
+
 #endif