]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into TimePath/csqc_viewmodels
authorTimePath <andrew.hardaker1995@gmail.com>
Fri, 6 Nov 2015 08:55:40 +0000 (19:55 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Fri, 6 Nov 2015 08:59:26 +0000 (19:59 +1100)
# Conflicts:
# qcsrc/common/weapons/all.qc
# qcsrc/common/weapons/weapon/rifle.qc
# qcsrc/server/weapons/weaponsystem.qc

23 files changed:
qcsrc/Makefile
qcsrc/client/progs.inc
qcsrc/client/view.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/anim.qc [new file with mode: 0644]
qcsrc/common/anim.qh [new file with mode: 0644]
qcsrc/common/stats.qh
qcsrc/common/triggers/subs.qh
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/lib/_all.inc
qcsrc/lib/enumclass.qh [new file with mode: 0644]
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/g_subs.qc
qcsrc/server/g_subs.qh
qcsrc/server/g_world.qc
qcsrc/server/progs.inc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/weapons/weaponsystem.qh

index 452dfc9a6451ad219bfcfd3b84c9a06b924c504f..b5b77e09ba392461593e1237d834119f90a728ba 100644 (file)
@@ -22,6 +22,7 @@ QCCFLAGS ?= \
        -fftepp -flno -futf8 -fno-bail-on-werror -fftepp-predefs \
        -frelaxed-switch -freturn-assignments \
        $(QCCFLAGS_WATERMARK) \
+       -DNDEBUG=1 \
        $(QCCFLAGS_FEATURES) \
        $(QCCFLAGS_EXTRA)
 
index 4e127917aa48cb136eeb6b77b2f788f81238d670..12d43b5c4c44eca85fd368f046f6119bed35063c 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "weapons/projectile.qc" // TODO
 
+#include "../common/anim.qc"
 #include "../common/animdecide.qc"
 #include "../common/effects/effectinfo.qc"
 #include "../common/mapinfo.qc"
index 6f37f39e346d1a272ca3ada94ac1eecdf80ff19d..f1e107de2f0c8e8faebee4f30acc2443c29a33a9 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "mutators/events.qh"
 
+#include "../common/anim.qh"
 #include "../common/constants.qh"
 #include "../common/debug.qh"
 #include "../common/mapinfo.qh"
 #include "../lib/warpzone/client.qh"
 #include "../lib/warpzone/common.qh"
 
+#define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOSHADOW | EF_SELECTABLE | EF_TELEPORT_BIT)
+
+void viewmodel_draw(entity this)
+{
+       int mask = (intermission || (getstati(STAT_HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
+       float a = this.alpha;
+       int c = stof(getplayerkeyvalue(current_player, "colors"));
+       vector g = this.glowmod; // TODO: completely clientside: colormapPaletteColor(c & 0x0F, true) * 2;
+       entity me = CSQCModel_server2csqc(player_localentnum);
+       int fx = ((me.csqcmodel_effects & EFMASK_CHEAP)
+               | EF_NODEPTHTEST)
+               &~ (EF_FULLBRIGHT); // can mask team color, so get rid of it
+       for (entity e = this; e; e = e.weaponchild)
+       {
+               e.drawmask = mask;
+               e.alpha = a;
+               e.colormap = c;
+               e.glowmod = g;
+               e.csqcmodel_effects = fx;
+               WITH(entity, self, e, CSQCModel_Effects_Apply());
+       }
+       {
+               static string name_last;
+               string name = get_weaponinfo(activeweapon).mdl;
+               if (name != name_last)
+               {
+                       name_last = name;
+                       CL_WeaponEntity_SetModel(this, name);
+                       updateanim(this);
+                       if (!this.animstate_override)
+                               setanim(this, this.anim_idle, true, false, false);
+               }
+       }
+       float eta = (getstatf(STAT_WEAPON_NEXTTHINK) - time); // TODO: / W_WeaponRateFactor();
+       float f = 0; // 0..1; 0: fully active
+       switch (this.state)
+       {
+               case WS_RAISE:
+               {
+                       // entity newwep = Weapons_from(activeweapon);
+                       float delay = 0.2; // TODO: newwep.switchdelay_raise;
+                       f = eta / max(eta, delay);
+                       this.angles_x = -90 * f * f;
+                       break;
+               }
+               case WS_DROP:
+               {
+                       // entity oldwep = Weapons_from(activeweapon);
+                       float delay = 0.2; // TODO: newwep.switchdelay_drop;
+                       f = 1 - eta / max(eta, delay);
+                       this.angles_x = -90 * f * f;
+                       break;
+               }
+               case WS_CLEAR:
+               {
+                       f = 1;
+                       break;
+               }
+       }
+       this.angles_x = -90 * f * f;
+}
+
+entity viewmodel;
+STATIC_INIT(viewmodel) {
+    viewmodel = new(viewmodel);
+       viewmodel.draw = viewmodel_draw;
+}
+
 entity porto;
 vector polyline[16];
 void Porto_Draw(entity this)
index 04a600cb694ffcfeb1be630e459f56a9a2128ef0..c423032fe963870fe39e3a90290e563e2d1c4408 100644 (file)
@@ -279,47 +279,47 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                self.traileffect = 0;
                switch (self.cnt)
                {
-                       #define CASE(id) case PROJECTILE_##id: setmodel(self, MDL_PROJECTILE_##id);
-                       CASE(ELECTRO)            self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
-                       CASE(ROCKET)             self.traileffect = EFFECT_TR_ROCKET.m_id; self.scale = 2; break;
-                       CASE(CRYLINK)            self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
-                       CASE(CRYLINK_BOUNCING)   self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
-                       CASE(ELECTRO_BEAM)       self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
-                       CASE(GRENADE)            self.traileffect = EFFECT_TR_GRENADE.m_id; break;
-                       CASE(GRENADE_BOUNCING)   self.traileffect = EFFECT_TR_GRENADE.m_id; break;
-                       CASE(MINE)               self.traileffect = EFFECT_TR_GRENADE.m_id; break;
-                       CASE(BLASTER)            self.traileffect = EFFECT_Null.m_id; break;
-                       CASE(HLAC)               self.traileffect = EFFECT_Null.m_id; break;
-                       CASE(PORTO_RED)          self.traileffect = EFFECT_TR_WIZSPIKE.m_id; self.scale = 4; break;
-                       CASE(PORTO_BLUE)         self.traileffect = EFFECT_TR_WIZSPIKE.m_id; self.scale = 4; break;
-                       CASE(HOOKBOMB)           self.traileffect = EFFECT_TR_KNIGHTSPIKE.m_id; break;
-                       CASE(HAGAR)              self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break;
-                       CASE(HAGAR_BOUNCING)     self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break;
-                       CASE(NAPALM_FOUNTAIN)                                                                         // fallthrough // sself.modelindex = 0; self.traileffect = _particleeffectnum("torch_small"); break;
-                       CASE(FIREBALL)           self.modelindex = 0; self.traileffect = EFFECT_FIREBALL.m_id; break; // particle effect is good enough
-                       CASE(FIREMINE)           self.modelindex = 0; self.traileffect = EFFECT_FIREMINE.m_id; break; // particle effect is good enough
-                       CASE(TAG)                self.traileffect = EFFECT_TR_ROCKET.m_id; break;
-                       CASE(FLAC)               self.scale = 0.4; self.traileffect = EFFECT_FLAC_TRAIL.m_id; break;
-                       CASE(SEEKER)             self.traileffect = EFFECT_SEEKER_TRAIL.m_id; break;
-
-                       CASE(MAGE_SPIKE)         self.traileffect = EFFECT_TR_VORESPIKE.m_id; break;
-                       CASE(SHAMBLER_LIGHTNING) self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
-
-                       CASE(RAPTORBOMB)         self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = EFFECT_Null.m_id; break;
-                       CASE(RAPTORBOMBLET)      self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = EFFECT_Null.m_id; break;
-                       CASE(RAPTORCANNON)       self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
-
-                       CASE(SPIDERROCKET)       self.traileffect = EFFECT_SPIDERBOT_ROCKET_TRAIL.m_id; break;
-                       CASE(WAKIROCKET)         self.traileffect = EFFECT_RACER_ROCKET_TRAIL.m_id; break;
-                       CASE(WAKICANNON)         self.traileffect = EFFECT_Null.m_id; break;
-
-                       CASE(BUMBLE_GUN)         self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
-                       CASE(BUMBLE_BEAM)        self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
-
-                       CASE(RPC)                self.traileffect = EFFECT_TR_ROCKET.m_id; break;
-
-                       CASE(ROCKETMINSTA_LASER) self.traileffect = EFFECT_ROCKETMINSTA_LASER(self.team).m_id; break;
-#undef CASE
+                       #define HANDLE(id) case PROJECTILE_##id: setmodel(self, MDL_PROJECTILE_##id);
+                       HANDLE(ELECTRO)            self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+                       HANDLE(ROCKET)             self.traileffect = EFFECT_TR_ROCKET.m_id; self.scale = 2; break;
+                       HANDLE(CRYLINK)            self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
+                       HANDLE(CRYLINK_BOUNCING)   self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
+                       HANDLE(ELECTRO_BEAM)       self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+                       HANDLE(GRENADE)            self.traileffect = EFFECT_TR_GRENADE.m_id; break;
+                       HANDLE(GRENADE_BOUNCING)   self.traileffect = EFFECT_TR_GRENADE.m_id; break;
+                       HANDLE(MINE)               self.traileffect = EFFECT_TR_GRENADE.m_id; break;
+                       HANDLE(BLASTER)            self.traileffect = EFFECT_Null.m_id; break;
+                       HANDLE(HLAC)               self.traileffect = EFFECT_Null.m_id; break;
+                       HANDLE(PORTO_RED)          self.traileffect = EFFECT_TR_WIZSPIKE.m_id; self.scale = 4; break;
+                       HANDLE(PORTO_BLUE)         self.traileffect = EFFECT_TR_WIZSPIKE.m_id; self.scale = 4; break;
+                       HANDLE(HOOKBOMB)           self.traileffect = EFFECT_TR_KNIGHTSPIKE.m_id; break;
+                       HANDLE(HAGAR)              self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break;
+                       HANDLE(HAGAR_BOUNCING)     self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break;
+                       HANDLE(NAPALM_FOUNTAIN)                                                                         // fallthrough // sself.modelindex = 0; self.traileffect = _particleeffectnum("torch_small"); break;
+                       HANDLE(FIREBALL)           self.modelindex = 0; self.traileffect = EFFECT_FIREBALL.m_id; break; // particle effect is good enough
+                       HANDLE(FIREMINE)           self.modelindex = 0; self.traileffect = EFFECT_FIREMINE.m_id; break; // particle effect is good enough
+                       HANDLE(TAG)                self.traileffect = EFFECT_TR_ROCKET.m_id; break;
+                       HANDLE(FLAC)               self.scale = 0.4; self.traileffect = EFFECT_FLAC_TRAIL.m_id; break;
+                       HANDLE(SEEKER)             self.traileffect = EFFECT_SEEKER_TRAIL.m_id; break;
+
+                       HANDLE(MAGE_SPIKE)         self.traileffect = EFFECT_TR_VORESPIKE.m_id; break;
+                       HANDLE(SHAMBLER_LIGHTNING) self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+
+                       HANDLE(RAPTORBOMB)         self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = EFFECT_Null.m_id; break;
+                       HANDLE(RAPTORBOMBLET)      self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = EFFECT_Null.m_id; break;
+                       HANDLE(RAPTORCANNON)       self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break;
+
+                       HANDLE(SPIDERROCKET)       self.traileffect = EFFECT_SPIDERBOT_ROCKET_TRAIL.m_id; break;
+                       HANDLE(WAKIROCKET)         self.traileffect = EFFECT_RACER_ROCKET_TRAIL.m_id; break;
+                       HANDLE(WAKICANNON)         self.traileffect = EFFECT_Null.m_id; break;
+
+                       HANDLE(BUMBLE_GUN)         self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+                       HANDLE(BUMBLE_BEAM)        self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+
+                       HANDLE(RPC)                self.traileffect = EFFECT_TR_ROCKET.m_id; break;
+
+                       HANDLE(ROCKETMINSTA_LASER) self.traileffect = EFFECT_ROCKETMINSTA_LASER(self.team).m_id; break;
+#undef HANDLE
                        default:
                                if (MUTATOR_CALLHOOK(Ent_Projectile, self))
                                        break;
diff --git a/qcsrc/common/anim.qc b/qcsrc/common/anim.qc
new file mode 100644 (file)
index 0000000..6a4dbe5
--- /dev/null
@@ -0,0 +1,49 @@
+#include "anim.qh"
+
+/**
+ * @param anim x = startframe, y = numframes, z = framerate
+ */
+void anim_set(entity e, vector anim, bool looping, bool override, bool restart)
+{
+       if (!anim) return;  // no animation was given to us! We can't use this.
+
+       if (anim.x == e.animstate_startframe)
+       {
+               if (anim.y == e.animstate_numframes)
+               {
+                       if (anim.z == e.animstate_framerate)
+                       {
+                               if (!restart) return;
+                               if (anim.y == 1)  // ZYM animation
+                                       BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
+                       }
+               }
+       }
+       e.animstate_startframe = anim.x;
+       e.animstate_numframes = anim.y;
+       e.animstate_framerate = anim.z;
+       e.animstate_starttime = time - 0.1 * frametime;  // shift it a little bit into the past to prevent float inaccuracy hiccups
+       e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
+       e.animstate_looping = looping;
+       e.animstate_override = override;
+       e.frame = e.animstate_startframe;
+       e.frame1time = time;
+}
+
+/**
+ * Update e.frame based on its animstate relative to time
+ */
+void anim_update(entity e)
+{
+       if (time >= e.animstate_endtime)
+       {
+               if (e.animstate_looping)
+               {
+                       e.animstate_starttime = e.animstate_endtime;
+                       e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
+               }
+               e.animstate_override = false;
+       }
+       float frameofs = bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
+       e.frame = e.animstate_startframe + frameofs;
+}
diff --git a/qcsrc/common/anim.qh b/qcsrc/common/anim.qh
new file mode 100644 (file)
index 0000000..acf6735
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef ANIM_H
+#define ANIM_H
+
+// begin engine fields
+
+/** primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4) */
+.float frame;
+/** secondary framegroup animation (strength = lerpfrac) */
+.float frame2;
+/** tertiary framegroup animation (strength = lerpfrac3) */
+.float frame3;
+/** quaternary framegroup animation (strength = lerpfrac4) */
+.float frame4;
+
+/** strength of framegroup blend */
+.float lerpfrac;
+/** strength of framegroup blend */
+.float lerpfrac3;
+/** strength of framegroup blend */
+.float lerpfrac4;
+
+/** start time of framegroup animation */
+.float frame1time;
+/** start time of framegroup animation */
+.float frame2time;
+/** start time of framegroup animation */
+.float frame3time;
+/** start time of framegroup animation */
+.float frame4time;
+
+// end engine fields
+
+// player animation state
+
+.int animstate_startframe;
+.int animstate_numframes;
+.float animstate_framerate;
+.float animstate_starttime;
+.float animstate_endtime;
+/** whether to repeat */
+.bool animstate_looping;
+/** true for one cycle, then changed to false */
+.bool animstate_override;
+
+void anim_set(entity e, vector anim, bool looping, bool override, bool restart);
+#define setanim(...) anim_set(__VA_ARGS__)
+void anim_update(entity e);
+#define updateanim(...) anim_update(__VA_ARGS__)
+
+#endif
index 9bdcb5773a5e4bcff57fe8079b53542babfa2bef..68bfbe8332ee4b5b881b79a7f04da839fe8f6093 100644 (file)
@@ -109,7 +109,7 @@ const int STAT_OK_AMMO_CHARGE         = 85;
 const int STAT_OK_AMMO_CHARGEPOOL     = 86;
 const int STAT_FROZEN                 = 87;
 const int STAT_REVIVE_PROGRESS        = 88;
-// 89 empty?
+const int STAT_WEAPON_NEXTTHINK       = 89;
 // 90 empty?
 // 91 empty?
 // 92 empty?
index 95cc59904a5017710f4ff9104ddd1de7cd833f41..5e3f543fa5441bcc757891fad5083df4f8a930c1 100644 (file)
@@ -48,15 +48,6 @@ void SUB_VanishOrRemove (entity ent);
 .vector destvec;
 .vector destvec2;
 
-// player animation state
-.float animstate_startframe;
-.float animstate_numframes;
-.float animstate_framerate;
-.float animstate_starttime;
-.float animstate_endtime;
-.float animstate_override;
-.float animstate_looping;
-
 .float delay;
 .float wait;
 .float lip;
index b338122139730e150fa773b28e27ded825aca60d..177d12c2d36d54e221be9ef5aec6145c09e2f5ea 100644 (file)
        #include "../../lib/csqcmodel/cl_model.qh"
 #elif defined(MENUQC)
 #elif defined(SVQC)
-    #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/all.qh"
-    #include "../monsters/all.qh"
-    #include "config.qh"
-    #include "../../server/weapons/csqcprojectile.qh"
-    #include "../../server/weapons/tracing.qh"
-    #include "../../server/t_items.qh"
-    #include "../../server/autocvars.qh"
-    #include "../../server/constants.qh"
-    #include "../../server/defs.qh"
-    #include "../notifications.qh"
-    #include "../deathtypes/all.qh"
-    #include "../../server/mutators/all.qh"
-    #include "../mapinfo.qh"
-    #include "../../server/command/common.qh"
-    #include "../../lib/csqcmodel/sv_model.qh"
-    #include "../../server/portals.qh"
-    #include "../../server/g_hook.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/all.qh"
+       #include "../monsters/all.qh"
+       #include "config.qh"
+       #include "../../server/weapons/csqcprojectile.qh"
+       #include "../../server/weapons/tracing.qh"
+       #include "../../server/t_items.qh"
+       #include "../../server/autocvars.qh"
+       #include "../../server/constants.qh"
+       #include "../../server/defs.qh"
+       #include "../notifications.qh"
+       #include "../deathtypes/all.qh"
+       #include "../../server/mutators/all.qh"
+       #include "../mapinfo.qh"
+       #include "../../server/command/common.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"
 
 // WEAPON PLUGIN SYSTEM
 
-WepSet WepSet_FromWeapon(int a) {
+WepSet WepSet_FromWeapon(int a)
+{
        a -= WEP_FIRST;
        if (Weapons_MAX > 24)
-       if (a >= 24) {
-               a -= 24;
-               if (Weapons_MAX > 48)
-               if (a >= 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);
                }
-               return '0 1 0' * power2of(a);
-       }
        return '1 0 0' * power2of(a);
 }
 #ifdef SVQC
-void WepSet_AddStat()
-{
-       addstat(STAT_WEAPONS, AS_INT, weapons_x);
-       if (Weapons_MAX > 24)
-       addstat(STAT_WEAPONS2, AS_INT, weapons_y);
-       if (Weapons_MAX > 48)
-       addstat(STAT_WEAPONS3, AS_INT, weapons_z);
-}
-void WepSet_AddStat_InMap()
-{
-       addstat(STAT_WEAPONSINMAP, AS_INT, weaponsinmap_x);
-       if (Weapons_MAX > 24)
-       addstat(STAT_WEAPONSINMAP2, AS_INT, weaponsinmap_y);
-       if (Weapons_MAX > 48)
-       addstat(STAT_WEAPONSINMAP3, AS_INT, weaponsinmap_z);
-}
-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);
-}
+       void WepSet_AddStat()
+       {
+               addstat(STAT_WEAPONS, AS_INT, weapons_x);
+               if (Weapons_MAX > 24) addstat(STAT_WEAPONS2, AS_INT, weapons_y);
+               if (Weapons_MAX > 48) addstat(STAT_WEAPONS3, AS_INT, weapons_z);
+       }
+       void WepSet_AddStat_InMap()
+       {
+               addstat(STAT_WEAPONSINMAP, AS_INT, weaponsinmap_x);
+               if (Weapons_MAX > 24) addstat(STAT_WEAPONSINMAP2, AS_INT, weaponsinmap_y);
+               if (Weapons_MAX > 48) addstat(STAT_WEAPONSINMAP3, AS_INT, weaponsinmap_z);
+       }
+       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 (Weapons_MAX > 24)
-       w.y = getstati(STAT_WEAPONS2);
-       if (Weapons_MAX > 48)
-       w.z = getstati(STAT_WEAPONS3);
-       return w;
-}
-WepSet WepSet_GetFromStat_InMap()
-{
-       WepSet w = '0 0 0';
-       w_x = getstati(STAT_WEAPONSINMAP);
-       if (Weapons_MAX > 24)
-       w_y = getstati(STAT_WEAPONSINMAP2);
-       if (Weapons_MAX > 48)
-       w_z = getstati(STAT_WEAPONSINMAP3);
-       return w;
-}
-WepSet ReadWepSet()
-{
-       if (Weapons_MAX > 48)
-       return ReadInt72_t();
-       if (Weapons_MAX > 24)
-       return ReadInt48_t();
-       return ReadInt24_t() * '1 0 0';
-}
+       WepSet WepSet_GetFromStat()
+       {
+               WepSet w = '0 0 0';
+               w.x = getstati(STAT_WEAPONS);
+               if (Weapons_MAX > 24) w.y = getstati(STAT_WEAPONS2);
+               if (Weapons_MAX > 48) w.z = getstati(STAT_WEAPONS3);
+               return w;
+       }
+       WepSet WepSet_GetFromStat_InMap()
+       {
+               WepSet w = '0 0 0';
+               w_x = getstati(STAT_WEAPONSINMAP);
+               if (Weapons_MAX > 24) w_y = getstati(STAT_WEAPONSINMAP2);
+               if (Weapons_MAX > 48) w_z = getstati(STAT_WEAPONSINMAP3);
+               return w;
+       }
+       WepSet ReadWepSet()
+       {
+               if (Weapons_MAX > 48) return ReadInt72_t();
+               if (Weapons_MAX > 24) return ReadInt48_t();
+               return ReadInt24_t() * '1 0 0';
+       }
 #endif
 
 string W_FixWeaponOrder(string order, float complete)
@@ -134,26 +124,25 @@ string W_FixWeaponOrder(string order, float 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 != WEP_Null)
-                       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)
@@ -163,12 +152,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)
@@ -192,23 +179,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);
@@ -221,8 +209,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);
 }
 
@@ -233,12 +220,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);
        }
@@ -255,48 +241,48 @@ string GetAmmoPicture(.int ammotype)
                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?
+               default: return "";  // wtf, no ammo type?
        }
 }
 
 #ifdef CSQC
-.int GetAmmoFieldFromNum(int i)
-{
-       switch(i)
+       .int GetAmmoFieldFromNum(int 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;
+               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)
+       int GetAmmoStat(.int 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;
-               case ammo_fuel: return STAT_FUEL;
-               default: return -1;
+               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;
+                       case ammo_fuel: return STAT_FUEL;
+                       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;
+               MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
+               return weapon_sound_output;
 #else
-       return output;
+               return output;
 #endif
 }
 
@@ -304,11 +290,345 @@ 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;
+               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 (algn)
+       {
+               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;
+}
+
+vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn)
+{
+#ifdef SVQC
+       string s;
+#endif
+       if (visual)
+       {
+               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;
+#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
-       return output;
+               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();
+       setanim(viewmodel, a, restartanim == false, 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
index 10e804b479be2c3ab761b7c839258d705a1e1eb3..76d536b4a604719db1bd665bf2be92cc46f4b38d 100644 (file)
@@ -167,4 +167,39 @@ STATIC_INIT(register_weapons_done)
     weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
 }
 
+#ifndef MENUQC
+
+.entity weaponchild;
+.entity exteriorweaponentity;
+.vector weaponentity_glowmod;
+
+//.int weapon; // current weapon
+.int switchweapon; // weapon requested to switch to
+.int switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
+.string weaponname; // name of .weapon
+
+.vector spawnorigin; // for casings
+
+// weapon animation vectors:
+.vector anim_fire1;
+.vector anim_fire2;
+.vector anim_idle;
+.vector anim_reload;
+
+// static frame globals
+
+ENUMCLASS(WFRAME)
+CASE(WFRAME, DONTCHANGE)
+CASE(WFRAME, FIRE1)
+CASE(WFRAME, FIRE2)
+CASE(WFRAME, IDLE)
+CASE(WFRAME, RELOAD)
+ENUMCLASS_END(WFRAME)
+
+.WFRAME wframe;
+
+vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn);
+void CL_WeaponEntity_SetModel(entity this, string name);
+#endif
+
 #endif
index 3a878a461d1f933a93160409ae4f873e5aae5ff7..2a92f955b50fc1cd16beb89dbe64f50de2ec3c2f 100644 (file)
@@ -17,6 +17,18 @@ int weaponslot(.entity weaponentity)
        return 0;
 }
 
+// weapon states (actor.(weaponentity).state)
+/** no weapon selected */
+const int WS_CLEAR  = 0;
+/** raise frame */
+const int WS_RAISE  = 1;
+/** deselecting frame */
+const int WS_DROP   = 2;
+/** fire state */
+const int WS_INUSE  = 3;
+/** idle frame */
+const int WS_READY  = 4;
+
 .int ammo_shells;
 .int ammo_nails;
 .int ammo_rockets;
index e7cd2606c1c4a5abbeb8ab5589a53708dc274dbb..be9788fcc7414649398387b3be54be5685c4a990 100644 (file)
@@ -87,7 +87,7 @@ void W_Rifle_Attack2()
 }
 
 .void() rifle_bullethail_attackfunc;
-.float rifle_bullethail_frame;
+.WFRAME rifle_bullethail_frame;
 .float rifle_bullethail_animtime;
 .float rifle_bullethail_refire;
 void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponentity, int fire)
@@ -113,7 +113,7 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent
        }
 }
 
-void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, float fr, float animtime, float refire)
+void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, WFRAME fr, float animtime, float refire)
 {SELFPARAM();
        // if we get here, we have at least one bullet to fire
        AttackFunc();
index cffa8f7223112bd286813b5a8d98b926b81ddcd6..a752898b43edbf86a7bc7816e06087f1bdcc3f9c 100644 (file)
@@ -35,6 +35,7 @@
 #include "cvar.qh"
 #include "defer.qh"
 #include "draw.qh"
+#include "enumclass.qh"
 #include "file.qh"
 #include "functional.qh"
 #include "i18n.qh"
diff --git a/qcsrc/lib/enumclass.qh b/qcsrc/lib/enumclass.qh
new file mode 100644 (file)
index 0000000..2959565
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef ENUMCLASS_H
+#define ENUMCLASS_H
+
+#include "oo.qh"
+
+// purpose: prevent transposed parameter passing
+
+#if NDEBUG
+
+// zero overhead mode, use this for releases
+
+#define ENUMCLASS(id) typedef int id; enum {
+#define CASE(class, id) class##_##id,
+#define ENUMCLASS_END(id) };
+
+#else
+
+// edict overhead mode, use this for type checking
+
+#define ENUMCLASS(id) CLASS(id, Object)
+#define CASE(class, id) class class##_##id; STATIC_INIT(class##_##id) { class##_##id = NEW(class); }
+#define ENUMCLASS_END(id) ENDCLASS(id)
+
+#endif
+
+#endif
index 4994c193652cd65b8266ae53c6262768bf669773..9cd8da807ee9905600faba3fd31ef394c717bf19 100644 (file)
@@ -1687,6 +1687,7 @@ void SpectateCopy(entity spectatee)
        self.switchweapon = spectatee.switchweapon;
        self.switchingweapon = spectatee.switchingweapon;
        self.weapon = spectatee.weapon;
+       self.weapon_nextthink = spectatee.weapon_nextthink;
        self.vortex_charge = spectatee.vortex_charge;
        self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
        self.hagar_load = spectatee.hagar_load;
@@ -2144,6 +2145,8 @@ void PlayerUseKey()
 }
 
 
+void wglow_send(entity actor, vector g);
+
 /*
 =============
 PlayerPreThink
@@ -2319,22 +2322,28 @@ void PlayerPreThink ()
 
                if(frametime)
                {
-                       if(self.weapon == WEP_VORTEX.m_id && WEP_CVAR(vortex, charge))
+                       vector g;
+                       if (IS_SPEC(self))
+                       {
+                               g = self.enemy.weaponentity_glowmod;
+                       }
+                       else if(self.weapon == WEP_VORTEX.m_id && WEP_CVAR(vortex, charge))
                        {
-                               self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
-                               self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
-                               self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
+                               g.x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
+                               g.y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
+                               g.z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
 
                                if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit))
                                {
-                                       self.weaponentity_glowmod_x = self.weaponentity_glowmod.x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
-                                       self.weaponentity_glowmod_y = self.weaponentity_glowmod.y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
-                                       self.weaponentity_glowmod_z = self.weaponentity_glowmod.z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+                                       g.x += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+                                       g.y += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+                                       g.z += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
                                }
                        }
                        else
-                               self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, true) * 2;
-
+                               g = colormapPaletteColor(self.clientcolors & 0x0F, true) * 2;
+                       if (g != self.weaponentity_glowmod)
+                               wglow_send(self, self.weaponentity_glowmod = g);
                        player_powerups();
                }
 
index 84e905e85fea28e858763e3932a21bc2c09ad2d7..bacb6bcb9e4c5c95cb044333a4056bcbc8e5d22f 100644 (file)
@@ -9,6 +9,7 @@
 #include "teamplay.qh"
 #include "weapons/throwing.qh"
 #include "command/common.qh"
+#include "../common/anim.qh"
 #include "../common/animdecide.qh"
 #include "../common/csqcmodel_settings.qh"
 #include "../common/deathtypes/all.qh"
@@ -165,16 +166,6 @@ void player_anim ()
                animbits |= ANIMSTATE_DUCK;
        animdecide_setstate(self, animbits, false);
        animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
-
-       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-       {
-               if (self.(weaponentity))
-               {
-                       updateanim(self.(weaponentity));
-                       if (!self.(weaponentity).animstate_override)
-                               setanim(self.(weaponentity), self.(weaponentity).anim_idle, true, false, false);
-               }
-       }
 }
 
 void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
index 2027b70f172f5f23cf5607258af86b12cb4d8a02..0fc8381ace8e73b6c054df6fd60f156179101667 100644 (file)
@@ -92,14 +92,7 @@ float server_is_dedicated;
 .float fade_time;
 .float fade_rate;
 
-// weapon animation vectors:
-.vector anim_fire1;
-.vector anim_fire2;
-.vector anim_idle;
-.vector anim_reload;
-
 void() player_setupanimsformodel;
-void setanim(entity e, vector anim, float looping, float override, float restart);
 
 .string mdl;
 
@@ -150,16 +143,6 @@ const float MAX_DAMAGEEXTRARADIUS = 16;
 .entity item_pickupsound_ent;
 .entity item_model_ent;
 
-// definitions for weaponsystem
-// more WEAPONTODO: move these to their proper files
-.entity exteriorweaponentity;
-.vector weaponentity_glowmod;
-
-//.int weapon; // current weapon
-.int switchweapon; // weapon requested to switch to
-.int switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
-.string weaponname; // name of .weapon
-
 // WEAPONTODO
 .float autoswitch;
 float client_hasweapon(entity cl, float wpn, float andammo, float complain);
@@ -170,13 +153,6 @@ void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire);
 .void(Weapon thiswep, entity actor, .entity weaponentity, int fire) weapon_think;
 
 
-// weapon states (self.weaponentity.state)
-const int WS_CLEAR                     = 0; // no weapon selected
-const int WS_RAISE                     = 1; // raise frame
-const int WS_DROP                              = 2; // deselecting frame
-const int WS_INUSE                     = 3; // fire state
-const int WS_READY                     = 4; // idle frame
-
 // there is 2 weapon tics that can run in one server frame
 const int W_TICSPERFRAME = 2;
 
@@ -213,8 +189,6 @@ float nJoinAllowed(entity ignore);
 float playerid_last;
 .float noalign;                // if set to 1, the item or spawnpoint won't be dropped to the floor
 
-.vector spawnorigin;
-
 .vector death_origin;
 .vector killer_origin;
 
index f2e3da898645ddfb755f0bd852e7a077988efa94..f7dcf3073a114b4dbf7078bc9e71cd7f0853c925 100644 (file)
@@ -11,50 +11,6 @@ spawnfunc(info_null)
        // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
 }
 
-void setanim(entity e, vector anim, float looping, float override, float restart)
-{
-       if (!anim)
-               return; // no animation was given to us! We can't use this.
-
-       if (anim.x == e.animstate_startframe)
-       if (anim.y == e.animstate_numframes)
-       if (anim.z == e.animstate_framerate)
-       {
-               if(restart)
-               {
-                       if(restart > 0)
-                       if(anim.y == 1) // ZYM animation
-                               BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
-               }
-               else
-                       return;
-       }
-       e.animstate_startframe = anim.x;
-       e.animstate_numframes = anim.y;
-       e.animstate_framerate = anim.z;
-       e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
-       e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
-       e.animstate_looping = looping;
-       e.animstate_override = override;
-       e.frame = e.animstate_startframe;
-       e.frame1time = servertime;
-}
-
-void updateanim(entity e)
-{
-       if (time >= e.animstate_endtime)
-       {
-               if (e.animstate_looping)
-               {
-                       e.animstate_starttime = e.animstate_endtime;
-                       e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
-               }
-               e.animstate_override = false;
-       }
-       e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
-       //print(ftos(time), " -> ", ftos(e.frame), "\n");
-}
-
 /*
 ==================
 main
index a50885a7fe45f34208b8c7eea2f57d3382cfa10e..24cbe917e4ac428b3cf54bf7e7ef29e2e663578d 100644 (file)
@@ -10,10 +10,6 @@ void() SUB_Remove;
 
 spawnfunc(info_null);
 
-void setanim(entity e, vector anim, float looping, float override, float restart);
-
-void updateanim(entity e);
-
 /*
 ==================
 SUB_Remove
index aea8b4876a94eedef66e8c580a8cdb47525716c9..f8c1e15256c3cada6d91cdd0c42bc3dd92cce79e 100644 (file)
@@ -759,6 +759,7 @@ spawnfunc(worldspawn)
        WepSet_AddStat_InMap();
        addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
        addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
+       addstat(STAT_WEAPON_NEXTTHINK, AS_FLOAT, weapon_nextthink);
        addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
        addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
        addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam);
@@ -1480,8 +1481,8 @@ void FixIntermissionClient(entity e)
                        if(e.(weaponentity))
                        {
                                e.(weaponentity).effects = EF_NODRAW;
-                               if (e.(weaponentity).(weaponentity))
-                                       e.(weaponentity).(weaponentity).effects = EF_NODRAW;
+                               if (e.(weaponentity).weaponchild)
+                                       e.(weaponentity).weaponchild.effects = EF_NODRAW;
                        }
                }
                if(IS_REAL_CLIENT(e))
index 6ab895f21f9cd7936f78869e630d70b8b1a9f3b9..d83234e906252eadb70ab9eb26b4f3afb0203639 100644 (file)
@@ -54,6 +54,7 @@
 #include "weapons/weaponstats.qc"
 #include "weapons/weaponsystem.qc"
 
+#include "../common/anim.qc"
 #include "../common/animdecide.qc"
 #include "../common/campaign_file.qc"
 #include "../common/campaign_setup.qc"
index 3ea45ad46814ca7dfcc306288048b41cded229fb..930bb9065cff943e92ea5749c41f6d86594bcae4 100644 (file)
 #include "../../common/weapons/all.qh"
 #include "../../lib/csqcmodel/sv_model.qh"
 
-vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
-{
-       switch (algn)
-       {
-               default: case 3: break; // right alignment
-               case 4: vecs.y = -vecs.y;
-                       break;              // left
-               case 1: case 2: vecs.y = 0;
-                       vecs.z -= 2;
-                       break;              // center
-       }
-
-       return vecs;
-}
-
-vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn)
-{
-       string s;
-
-       if (visual)
-       {
-               vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
-       }
-       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;
-       }
-       else  // just do the same as top
-       {
-               vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
-       }
-
-       return vecs;
-}
-
-vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
-{
-       return shotorg_adjust_values(vecs, y_is_right, visual, algn);
-}
-
 .int state;
 
 .float weapon_frametime;
@@ -92,9 +39,6 @@ float W_WeaponSpeedFactor()
 }
 
 
-void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor,
-    .entity weaponentity, int fire) func);
-
 bool CL_Weaponentity_CustomizeEntityForClient()
 {
        SELFPARAM();
@@ -103,219 +47,13 @@ bool CL_Weaponentity_CustomizeEntityForClient()
        return true;
 }
 
-/*
- * 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.weaponentity
-//   this.movedir, this.view_ofs
-//   attachment stuff
-//   anim stuff
-// to free:
-//   call again with ""
-//   remove the ent
-void CL_WeaponEntity_SetModel(entity this, .entity weaponentity, string name)
-{
-       if (name != "")
-       {
-               // if there is a child entity, hide it until we're sure we use it
-               if (this.(weaponentity)) this.(weaponentity).model = "";
-               _setmodel(this, W_Model(strcat("v_", name, ".md3")));
-               int v_shot_idx = gettagindex(this, "shot");  // used later
-               if (!v_shot_idx) 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
-               if (gettagindex(this, "weapon"))
-               {
-                       if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity);
-                       _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3")));
-                       setattachment(this.(weaponentity), this, "weapon");
-               }
-               else if (gettagindex(this, "tag_weapon"))
-               {
-                       if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity);
-                       _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3")));
-                       setattachment(this.(weaponentity), this, "tag_weapon");
-               }
-               else
-               {
-                       if (this.(weaponentity)) remove(this.(weaponentity));
-                       this.(weaponentity) = NULL;
-               }
-
-               setorigin(this, '0 0 0');
-               this.angles = '0 0 0';
-               this.frame = 0;
-               this.viewmodelforclient = NULL;
-
-               float idx;
-
-               if (v_shot_idx)  // v_ model attached to invisible h_ model
-               {
-                       this.movedir = gettaginfo(this.(weaponentity), v_shot_idx);
-               }
-               else
-               {
-                       idx = gettagindex(this, "shot");
-                       if (!idx) idx = gettagindex(this, "tag_shot");
-                       if (idx)
-                       {
-                               this.movedir = gettaginfo(this, idx);
-                       }
-                       else
-                       {
-                               LOG_INFO("WARNING: weapon model ", this.model,
-                                       " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
-                               this.movedir = '0 0 0';
-                       }
-               }
-
-               if (this.(weaponentity))  // v_ model attached to invisible h_ model
-               {
-                       idx = gettagindex(this.(weaponentity), "shell");
-                       if (!idx) idx = gettagindex(this.(weaponentity), "tag_shell");
-                       if (idx) this.spawnorigin = gettaginfo(this.(weaponentity), idx);
-               }
-               else
-               {
-                       idx = 0;
-               }
-               if (!idx)
-               {
-                       idx = gettagindex(this, "shell");
-                       if (!idx) idx = gettagindex(this, "tag_shell");
-                       if (idx)
-                       {
-                               this.spawnorigin = gettaginfo(this, idx);
-                       }
-                       else
-                       {
-                               LOG_INFO("WARNING: weapon model ", this.model,
-                                       " does not support the 'shell' tag, will display casings wrong\n");
-                               this.spawnorigin = this.movedir;
-                       }
-               }
-
-               if (v_shot_idx)
-               {
-                       this.oldorigin = '0 0 0';  // use regular attachment
-               }
-               else
-               {
-                       if (this.(weaponentity))
-                       {
-                               idx = gettagindex(this, "weapon");
-                               if (!idx) idx = gettagindex(this, "tag_weapon");
-                       }
-                       else
-                       {
-                               idx = gettagindex(this, "handle");
-                               if (!idx) idx = gettagindex(this, "tag_handle");
-                       }
-                       if (idx)
-                       {
-                               this.oldorigin = this.movedir - gettaginfo(this, idx);
-                       }
-                       else
-                       {
-                               LOG_INFO("WARNING: weapon model ", this.model,
-                                       " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
-                               this.oldorigin = '0 0 0';  // there is no way to recover from this
-                       }
-               }
-
-               this.viewmodelforclient = this.owner;
-       }
-       else
-       {
-               this.model = "";
-               if (this.(weaponentity)) remove(this.(weaponentity));
-               this.(weaponentity) = 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';
-       }
-
-       this.view_ofs = '0 0 0';
-
-       if (this.movedir.x >= 0)
-       {
-               vector v0 = this.movedir;
-               this.movedir = shotorg_adjust(v0, false, false, this.owner.cvar_cl_gunalign);
-               this.view_ofs = shotorg_adjust(v0, false, true, this.owner.cvar_cl_gunalign) - v0;
-       }
-       this.owner.stat_shotorg = compressShotOrigin(this.movedir);
-       this.movedir = decompressShotOrigin(this.owner.stat_shotorg); // make them match perfectly
-
-       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);
-}
-
-vector CL_Weapon_GetShotOrg(float wpn)
+vector CL_Weapon_GetShotOrg(int wpn)
 {
        entity wi = Weapons_from(wpn);
        entity e = spawn();
-       .entity weaponentity = weaponentities[0];
-       CL_WeaponEntity_SetModel(e, weaponentity, wi.mdl);
+       CL_WeaponEntity_SetModel(e, wi.mdl);
        vector ret = e.movedir;
-       CL_WeaponEntity_SetModel(e, weaponentity, "");
+       CL_WeaponEntity_SetModel(e, "");
        remove(e);
        return ret;
 }
@@ -330,68 +68,39 @@ void CL_Weaponentity_Think()
        .entity weaponentity = this.weaponentity_fld;
        if (this.owner.(weaponentity) != this)
        {
-               if (this.(weaponentity)) remove(this.(weaponentity));
+               // owner has new gun; remove self
+               if (this.weaponchild) remove(this.weaponchild);
                remove(this);
                return;
        }
        if (this.owner.deadflag != DEAD_NO)
        {
+               // owner died; disappear
                this.model = "";
-               if (this.(weaponentity)) this.(weaponentity).model = "";
+               if (this.weaponchild) this.weaponchild.model = "";
                return;
        }
-       if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex
+       if (this.weaponname != this.owner.weaponname
+           || this.dmg != this.owner.modelindex
            || this.deadflag != this.owner.deadflag)
        {
+               // owner changed weapons; update appearance
                this.weaponname = this.owner.weaponname;
                this.dmg = this.owner.modelindex;
                this.deadflag = this.owner.deadflag;
 
-               CL_WeaponEntity_SetModel(this, weaponentity, this.owner.weaponname);
+               CL_WeaponEntity_SetModel(this, this.owner.weaponname);
        }
 
-       int tb = (this.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
-       this.effects = this.owner.effects & EFMASK_CHEAP;
-       this.effects &= ~EF_LOWPRECISION;
-       this.effects &= ~EF_FULLBRIGHT;  // can mask team color, so get rid of it
-       this.effects &= ~EF_TELEPORT_BIT;
-       this.effects &= ~EF_RESTARTANIM_BIT;
-       this.effects |= tb;
-       if (weaponentity != weaponentities[0]) this.effects |= EF_NODRAW;
+       this.effects = EF_NODRAW; // TODO: don't render this entity at all
 
        if (this.owner.alpha == default_player_alpha) this.alpha = default_weapon_alpha;
        else if (this.owner.alpha != 0) this.alpha = this.owner.alpha;
        else this.alpha = 1;
 
-       this.glowmod = this.owner.weaponentity_glowmod;
-       this.colormap = this.owner.colormap;
-       if (this.(weaponentity))
+       if (this.weaponchild)
        {
-               this.(weaponentity).effects = this.effects;
-               this.(weaponentity).alpha = this.alpha;
-               this.(weaponentity).colormap = this.colormap;
-               this.(weaponentity).glowmod = this.glowmod;
-       }
-
-       this.angles = '0 0 0';
-
-       float f = this.weapon_nextthink - time;
-       if (this.state == WS_RAISE && !intermission_running)
-       {
-               entity newwep = Weapons_from(this.owner.switchweapon);
-               f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
-               this.angles_x = -90 * f * f;
-       }
-       else if (this.state == WS_DROP && !intermission_running)
-       {
-               entity oldwep = Weapons_from(this.owner.weapon);
-               f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
-               this.angles_x = -90 * f * f;
-       }
-       else if (this.state == WS_CLEAR)
-       {
-               f = 1;
-               this.angles_x = -90 * f * f;
+               this.weaponchild.effects = this.effects;
        }
 }
 
@@ -443,31 +152,27 @@ void CL_ExteriorWeaponentity_Think()
 }
 
 // spawning weaponentity for client
-void CL_SpawnWeaponentity(entity e, .entity weaponentity)
+void CL_SpawnWeaponentity(entity actor, .entity weaponentity)
 {
-       entity view = e.(weaponentity) = new(weaponentity);
+       entity view = actor.(weaponentity) = new(weaponentity);
        make_pure(view);
        view.solid = SOLID_NOT;
-       view.owner = e;
+       view.owner = actor;
        setmodel(view, MDL_Null);  // precision set when changed
        setorigin(view, '0 0 0');
-       view.angles = '0 0 0';
-       view.viewmodelforclient = e;
-       view.flags = 0;
        view.weaponentity_fld = weaponentity;
        view.think = CL_Weaponentity_Think;
-       view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
        view.nextthink = time;
+       view.viewmodelforclient = actor;
+       view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
 
        if (weaponentity == weaponentities[0])
        {
-               entity exterior = e.exteriorweaponentity = new(exteriorweaponentity);
+               entity exterior = actor.exteriorweaponentity = new(exteriorweaponentity);
                make_pure(exterior);
                exterior.solid = SOLID_NOT;
-               exterior.exteriorweaponentity = exterior;
-               exterior.owner = e;
+               exterior.owner = actor;
                setorigin(exterior, '0 0 0');
-               exterior.angles = '0 0 0';
                exterior.think = CL_ExteriorWeaponentity_Think;
                exterior.nextthink = time;
 
@@ -608,7 +313,9 @@ bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bo
        return false;
 }
 
-void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor,
+void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim);
+
+void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor,
        .entity weaponentity, int fire) func)
 {
        entity this = actor.(weaponentity);
@@ -641,7 +348,9 @@ void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(W
                else  // if (fr == WFRAME_RELOAD)
                        a = this.anim_reload;
                a.z *= g_weaponratefactor;
-               setanim(this, a, restartanim == false, restartanim, restartanim);
+               entity e; FOR_EACH_CLIENT(e) if (e == actor || (IS_SPEC(e) && e.enemy == actor)) {
+                       wframe_send(e, this, a, restartanim);
+               }
        }
 
        v_forward = of;
@@ -665,7 +374,8 @@ void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(W
                this.weapon_nextthink = time;
                // dprint("reset weapon animation timer at ", ftos(time), "\n");
        }
-       this.weapon_nextthink = this.weapon_nextthink + t;
+       this.weapon_nextthink += t;
+       if (weaponentity == weaponentities[0]) actor.weapon_nextthink = this.weapon_nextthink;
        this.weapon_think = func;
        // dprint("next ", ftos(this.weapon_nextthink), "\n");
 
@@ -880,9 +590,11 @@ void W_AttachToShotorg(entity actor, entity flash, vector offset)
        flash.owner = actor;
        flash.angles_z = random() * 360;
 
-       entity this = actor.(weaponentity);
-       if (gettagindex(this, "shot")) setattachment(flash, this, "shot");
-       else setattachment(flash, this, "tag_shot");
+       entity view = actor.(weaponentity);
+       entity exterior = actor.exteriorweaponentity;
+
+       if (gettagindex(view, "shot")) setattachment(flash, view, "shot");
+       else setattachment(flash, view, "tag_shot");
        setorigin(flash, offset);
 
        entity xflash = spawn();
@@ -890,15 +602,15 @@ void W_AttachToShotorg(entity actor, entity flash, vector offset)
 
        flash.viewmodelforclient = actor;
 
-       if (this.oldorigin.x > 0)
+       if (view.oldorigin.x > 0)
        {
-               setattachment(xflash, actor.exteriorweaponentity, "");
-               setorigin(xflash, this.oldorigin + offset);
+               setattachment(xflash, exterior, "");
+               setorigin(xflash, view.oldorigin + offset);
        }
        else
        {
-               if (gettagindex(actor.exteriorweaponentity, "shot")) setattachment(xflash, actor.exteriorweaponentity, "shot");
-               else setattachment(xflash, actor.exteriorweaponentity, "tag_shot");
+               if (gettagindex(exterior, "shot")) setattachment(xflash, exterior, "shot");
+               else setattachment(xflash, exterior, "tag_shot");
                setorigin(xflash, offset);
        }
 }
index 97e6c72b4be3698caee7cfca657bf4fd4858c4f2..0000ac2155b762298e15bbb956187955cbec7fb5 100644 (file)
@@ -1,23 +1,11 @@
 #ifndef WEAPONSYSTEM_H
 #define WEAPONSYSTEM_H
 
-.float wframe;
 
 float internalteam;
 float weaponswapping;
 entity weapon_dropevent_item;
 
-// VorteX: static frame globals
-const float WFRAME_DONTCHANGE = -1;
-const float WFRAME_FIRE1 = 0;
-const float WFRAME_FIRE2 = 1;
-const float WFRAME_IDLE = 2;
-const float WFRAME_RELOAD = 3;
-
-vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn);
-
-vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn);
-
 void CL_SpawnWeaponentity(entity e, .entity weaponentity);
 
 vector CL_Weapon_GetShotOrg(float wpn);
@@ -44,6 +32,6 @@ bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponenti
 
 void weapon_prepareattack_do(entity actor, .entity weaponentity, float secondary, float attacktime);
 
-void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor, .entity weaponentity, int fire) func);
+void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor, .entity weaponentity, int fire) func);
 
 #endif