]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/csqcmodel_hooks.qc
Merge branch 'master' into terencehill/hud_cleanups
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / csqcmodel_hooks.qc
index 077b7d9720a1501e6d9eb72e3a36dabd3c0ec54c..6e7e5a8de77444946bec59756357d218a838975c 100644 (file)
@@ -1,13 +1,26 @@
-void CSQCModel_Hook_PreDraw(float isplayer);
+#include "mutators/events.qh"
+#include "player_skeleton.qh"
+#include "weapons/projectile.qh"
+#include "../common/animdecide.qh"
+#include "../common/movetypes/movetypes.qh"
+#include "../common/viewloc.qh"
+#include "../lib/csqcmodel/cl_model.qh"
+#include "../lib/csqcmodel/cl_player.qh"
+#include "../lib/csqcmodel/interpolate.qh"
 
-.float isplayermodel;
+.float death_time;
+.int modelflags;
+
+void CSQCModel_Hook_PreDraw(bool isplayer);
+
+.bool isplayermodel;
 
 // FEATURE: LOD
-.float lodmodelindex0;
-.float lodmodelindex1;
-.float lodmodelindex2;
+.int lodmodelindex0;
+.int lodmodelindex1;
+.int lodmodelindex2;
 void CSQCPlayer_LOD_Apply(void)
-{
+{SELFPARAM();
        // LOD model loading
        if(self.lodmodelindex0 != self.modelindex)
        {
@@ -27,7 +40,7 @@ void CSQCPlayer_LOD_Apply(void)
                if(fexists(s))
                {
                        precache_model(s);
-                       setmodel(self, s);
+                       _setmodel(self, s);
                        if(self.modelindex)
                                self.lodmodelindex1 = self.modelindex;
                }
@@ -36,12 +49,12 @@ void CSQCPlayer_LOD_Apply(void)
                if(fexists(s))
                {
                        precache_model(s);
-                       setmodel(self, s);
+                       _setmodel(self, s);
                        if(self.modelindex)
                                self.lodmodelindex2 = self.modelindex;
                }
 
-               setmodel(self, modelname); // make everything normal again
+               _setmodel(self, modelname); // make everything normal again
                setsize(self, mi, ma);
        }
 
@@ -71,39 +84,39 @@ void CSQCPlayer_LOD_Apply(void)
 
 // FEATURE: forcemodel and model color selection (MUST be called BEFORE LOD!)
 string forceplayermodels_model;
-float forceplayermodels_modelisgoodmodel;
-float forceplayermodels_modelindex;
-float forceplayermodels_skin;
+bool forceplayermodels_modelisgoodmodel;
+int forceplayermodels_modelindex;
+int forceplayermodels_skin;
 
 string forceplayermodels_mymodel;
-float forceplayermodels_myisgoodmodel;
-float forceplayermodels_mymodelindex;
+bool forceplayermodels_myisgoodmodel;
+int forceplayermodels_mymodelindex;
 
-float forceplayermodels_attempted;
+bool forceplayermodels_attempted;
 
 .string forceplayermodels_savemodel;
-.float forceplayermodels_savemodelindex;
-.float forceplayermodels_saveskin;
-.float forceplayermodels_savecolormap;
+.int forceplayermodels_savemodelindex;
+.int forceplayermodels_saveskin;
+.int forceplayermodels_savecolormap;
 
 .string forceplayermodels_isgoodmodel_mdl;
-.float forceplayermodels_isgoodmodel;
+.bool forceplayermodels_isgoodmodel;
 
 string forceplayermodels_goodmodel;
-float forceplayermodels_goodmodelindex;
+int forceplayermodels_goodmodelindex;
 
 .vector glowmod;
 .vector old_glowmod;
 
 void CSQCPlayer_ModelAppearance_PreUpdate(void)
-{
+{SELFPARAM();
        self.model = self.forceplayermodels_savemodel;
        self.modelindex = self.forceplayermodels_savemodelindex;
        self.skin = self.forceplayermodels_saveskin;
        self.colormap = self.forceplayermodels_savecolormap;
 }
 void CSQCPlayer_ModelAppearance_PostUpdate(void)
-{
+{SELFPARAM();
        self.forceplayermodels_savemodel = self.model;
        self.forceplayermodels_savemodelindex = self.modelindex;
        self.forceplayermodels_saveskin = self.skin;
@@ -114,11 +127,11 @@ void CSQCPlayer_ModelAppearance_PostUpdate(void)
                self.forceplayermodels_isgoodmodel = fexists(self.forceplayermodels_savemodel);
                self.forceplayermodels_isgoodmodel_mdl = self.forceplayermodels_savemodel;
                if(!self.forceplayermodels_isgoodmodel)
-                       printf("Warning: missing model %s has been used\n", self.forceplayermodels_savemodel);
+                       LOG_INFOF("Warning: missing model %s has been used\n", self.forceplayermodels_savemodel);
        }
 }
-void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
-{
+void CSQCPlayer_ModelAppearance_Apply(bool islocalplayer)
+{SELFPARAM();
        // FORCEMODEL
        // which one is ALWAYS good?
        if (!forceplayermodels_goodmodel)
@@ -126,7 +139,7 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
                entity e;
                e = spawn();
                precache_model(cvar_defstring("_cl_playermodel"));
-               setmodel(e, cvar_defstring("_cl_playermodel"));
+               _setmodel(e, cvar_defstring("_cl_playermodel"));
                forceplayermodels_goodmodel = e.model;
                forceplayermodels_goodmodelindex = e.modelindex;
                remove(e);
@@ -157,7 +170,7 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
                // only if this failed, find it out on our own
                entity e;
                e = spawn();
-               setmodel(e, autocvar__cl_playermodel); // this is harmless, see below
+               _setmodel(e, autocvar__cl_playermodel); // this is harmless, see below
                forceplayermodels_modelisgoodmodel = fexists(e.model);
                forceplayermodels_model = e.model;
                forceplayermodels_modelindex = e.modelindex;
@@ -169,7 +182,7 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
        {
                entity e;
                e = spawn();
-               setmodel(e, autocvar_cl_forcemyplayermodel); // this is harmless, see below
+               _setmodel(e, autocvar_cl_forcemyplayermodel); // this is harmless, see below
                forceplayermodels_myisgoodmodel = fexists(e.model);
                forceplayermodels_mymodel = e.model;
                forceplayermodels_mymodelindex = e.modelindex;
@@ -177,8 +190,8 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
        }
 
        // apply it
-       float isfriend;
-       float cm;
+       bool isfriend;
+       int cm;
        cm = self.forceplayermodels_savecolormap;
        cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(cm - 1, "colors")) + 1024);
 
@@ -216,8 +229,8 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
        if(teamplay)
        {
                // own team's color is never forced
-               float forcecolor_friend = 0;
-               float forcecolor_enemy = 0;
+               int forcecolor_friend = 0;
+               int forcecolor_enemy = 0;
                entity tm;
 
                if(autocvar_cl_forcemyplayercolors)
@@ -267,7 +280,7 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
 
        // GLOWMOD AND DEATH FADING
        if(self.colormap > 0)
-               self.glowmod = colormapPaletteColor(((self.colormap >= 1024) ? self.colormap : stof(getplayerkeyvalue(self.colormap - 1, "colors"))) & 0x0F, TRUE) * 2;
+               self.glowmod = colormapPaletteColor(((self.colormap >= 1024) ? self.colormap : stof(getplayerkeyvalue(self.colormap - 1, "colors"))) & 0x0F, true) * 2;
        else
                self.glowmod = '1 1 1';
 
@@ -280,9 +293,9 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
                        self.colormap = 0;
 
                        self.glowmod = self.old_glowmod * bound(0, 1 - (time - self.death_time) / autocvar_cl_deathglow, 1);
-                       self.glowmod_x = max(self.glowmod_x, 0.0001);
-                       self.glowmod_y = max(self.glowmod_y, 0.0001);
-                       self.glowmod_z = max(self.glowmod_z, 0.0001);
+                       self.glowmod_x = max(self.glowmod.x, 0.0001);
+                       self.glowmod_y = max(self.glowmod.y, 0.0001);
+                       self.glowmod_z = max(self.glowmod.z, 0.0001);
                }
                else if(self.old_glowmod != '0 0 0') { self.old_glowmod = '0 0 0'; }
        }
@@ -291,17 +304,17 @@ void CSQCPlayer_ModelAppearance_Apply(float islocalplayer)
 }
 
 // FEATURE: fallback frames
-.float csqcmodel_saveframe;
-.float csqcmodel_saveframe2;
+.int csqcmodel_saveframe;
+.int csqcmodel_saveframe2;
 #ifdef CSQCMODEL_HAVE_TWO_FRAMES
-.float csqcmodel_saveframe3;
-.float csqcmodel_saveframe4;
+.int csqcmodel_saveframe3;
+.int csqcmodel_saveframe4;
 #endif
-.float csqcmodel_framecount;
+.int csqcmodel_framecount;
 
 #define IS_DEAD_FRAME(f) ((f) == 0 || (f) == 1)
 void CSQCPlayer_FallbackFrame_PreUpdate(void)
-{
+{SELFPARAM();
        self.frame = self.csqcmodel_saveframe;
        self.frame2 = self.csqcmodel_saveframe2;
 #ifdef CSQCMODEL_HAVE_TWO_FRAMES
@@ -309,8 +322,8 @@ void CSQCPlayer_FallbackFrame_PreUpdate(void)
        self.frame4 = self.csqcmodel_saveframe4;
 #endif
 }
-void CSQCPlayer_FallbackFrame_PostUpdate(float isnew)
-{
+void CSQCPlayer_FallbackFrame_PostUpdate(bool isnew)
+{SELFPARAM();
        self.csqcmodel_saveframe = self.frame;
        self.csqcmodel_saveframe2 = self.frame2;
 #ifdef CSQCMODEL_HAVE_TWO_FRAMES
@@ -322,26 +335,25 @@ void CSQCPlayer_FallbackFrame_PostUpdate(float isnew)
        // player "pops in"
        if(isnew)
        {
-#define FIX_FRAMETIME(f,ft) \
-               if(IS_DEAD_FRAME(self.f) && self.ft != 0 && self.death_time != 0) \
-               { \
-                       self.ft = self.death_time; \
-               }
-               FIX_FRAMETIME(frame, frame1time)
-               FIX_FRAMETIME(frame2, frame2time)
+#define FIX_FRAMETIME(f,ft) do {                                                                                                                                                                       \
+               if(IS_DEAD_FRAME(self.f) && self.ft != 0 && self.death_time != 0)                                                                                       \
+                       self.ft = self.death_time;                                                                                                                                                              \
+} while(0)
+               FIX_FRAMETIME(frame, frame1time);
+               FIX_FRAMETIME(frame2, frame2time);
 #ifdef CSQCMODEL_HAVE_TWO_FRAMES
-               FIX_FRAMETIME(frame3, frame3time)
-               FIX_FRAMETIME(frame4, frame4time)
+               FIX_FRAMETIME(frame3, frame3time);
+               FIX_FRAMETIME(frame4, frame4time);
 #endif
        }
        self.csqcmodel_isdead = IS_DEAD_FRAME(self.frame);
 }
-void CSQCPlayer_AnimDecide_PostUpdate(float isnew)
-{
+void CSQCPlayer_AnimDecide_PostUpdate(bool isnew)
+{SELFPARAM();
        self.csqcmodel_isdead = !!(self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
 }
-float CSQCPlayer_FallbackFrame(float f)
-{
+int CSQCPlayer_FallbackFrame(int f)
+{SELFPARAM();
        if(frameduration(self.modelindex, f) > 0)
                return f; // goooooood
        if(frameduration(self.modelindex, 1) <= 0)
@@ -357,11 +369,11 @@ float CSQCPlayer_FallbackFrame(float f)
                case 29: return 4; // anim_duckwalkbackright -> anim_duckwalk
                case 30: return 4; // anim_duckwalkbackleft -> anim_duckwalk
        }
-       printf("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, self.model);
+       LOG_INFOF("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, self.model);
        return f;
 }
 void CSQCPlayer_FallbackFrame_Apply(void)
-{
+{SELFPARAM();
        self.frame = CSQCPlayer_FallbackFrame(self.frame);
        self.frame2 = CSQCPlayer_FallbackFrame(self.frame2);
 #ifdef CSQCMODEL_HAVE_TWO_FRAMES
@@ -372,17 +384,21 @@ void CSQCPlayer_FallbackFrame_Apply(void)
 
 // FEATURE: auto tag_index
 .entity tag_entity;
-.float tag_entity_lastmodelindex;
-.float tag_index;
+.int tag_entity_lastmodelindex;
+.int tag_index;
 void CSQCModel_AutoTagIndex_Apply(void)
-{
+{SELFPARAM();
        if(self.tag_entity && wasfreed(self.tag_entity))
                self.tag_entity = world;
 
+       viewloc_SetTags();
+
+       MUTATOR_CALLHOOK(TagIndex_Update, self);
+
        if(self.tag_networkentity)
        {
                // we are ATTACHED!
-               float changed = 0;
+               bool changed = 0;
                if(self.tag_entity.entnum != self.tag_networkentity)
                {
                        self.tag_entity = findfloat(world, entnum, self.tag_networkentity);
@@ -392,10 +408,7 @@ void CSQCModel_AutoTagIndex_Apply(void)
                // recursive predraw call to fix issues with forcemodels and LOD if bone indexes mismatch
                if(self.tag_entity.classname == "csqcmodel")
                {
-                       entity oldself = self;
-                       self = self.tag_entity;
-                       CSQCModel_Hook_PreDraw((self.entnum >= 1 && self.entnum <= maxclients));
-                       self = oldself;
+                       WITH(entity, self, self.tag_entity, CSQCModel_Hook_PreDraw((self.entnum >= 1 && self.entnum <= maxclients)));
                }
 
                if(self.tag_entity.modelindex != self.tag_entity_lastmodelindex)
@@ -408,9 +421,9 @@ void CSQCModel_AutoTagIndex_Apply(void)
                        if(self.tag_entity)
                        {
                                // the best part is: IT EXISTS
-                               if(substring(self.model, 0, 17) == "models/weapons/v_")
+                               if(substring(self.model, 0, 14) == "models/weapons")
                                {
-                                       if(substring(self.tag_entity.model, 0, 17) == "models/weapons/h_")
+                                       if(substring(self.tag_entity.model, 0, 14) == "models/weapons")
                                        {
                                                self.tag_index = gettagindex(self.tag_entity, "weapon");
                                                if(!self.tag_index)
@@ -420,7 +433,7 @@ void CSQCModel_AutoTagIndex_Apply(void)
                                                        // we need to prevent this from 'appening
                                                        self.tag_entity = world;
                                                        self.drawmask = 0;
-                                                       dprint("h_ model lacks weapon attachment, but v_ model is attached to it\n");
+                                                       LOG_TRACE("h_ model lacks weapon attachment, but v_ model is attached to it\n");
                                                }
                                        }
                                        else if(self.tag_entity.isplayermodel)
@@ -430,12 +443,14 @@ void CSQCModel_AutoTagIndex_Apply(void)
                                        }
                                }
 
-                               if(substring(self.tag_entity.model, 0, 17) == "models/weapons/v_")
+                               if(substring(self.tag_entity.model, 0, 14) == "models/weapons")
                                {
                                        self.tag_index = gettagindex(self.tag_entity, "shot");
                                        if(!self.tag_index)
                                                self.tag_index = gettagindex(self.tag_entity, "tag_shot");
                                }
+
+                               MUTATOR_CALLHOOK(TagIndex_Apply, self);
                        }
                        else
                        {
@@ -447,32 +462,34 @@ void CSQCModel_AutoTagIndex_Apply(void)
 }
 
 // FEATURE: EF_NODRAW workalike
-const float EF_BRIGHTFIELD     = 1;
-const float EF_BRIGHTLIGHT     = 4;
-const float EF_DIMLIGHT        = 8;
-const float EF_DOUBLESIDED = 32768;
-const float EF_NOSELFSHADOW = 65536;
-const float EF_DYNAMICMODELLIGHT = 131072;
-const float EF_RESTARTANIM_BIT = 1048576;
-const float EF_TELEPORT_BIT = 2097152;
-const float MF_ROCKET  =   1; // leave a trail
-const float MF_GRENADE =   2; // leave a trail
-const float MF_GIB     =   4; // leave a trail
-const float MF_ROTATE  =   8; // rotate (bonus items)
-const float MF_TRACER  =  16; // green split trail
-const float MF_ZOMGIB  =  32; // small blood trail
-const float MF_TRACER2 =  64; // orange split trail
-const float MF_TRACER3 = 128; // purple trail
-.float csqcmodel_effects;
-.float csqcmodel_modelflags;
+const int EF_BRIGHTFIELD       = 1;
+const int EF_BRIGHTLIGHT       = 4;
+const int EF_DIMLIGHT          = 8;
+const int EF_DOUBLESIDED       = 32768;
+const int EF_NOSELFSHADOW      = 65536;
+const int EF_DYNAMICMODELLIGHT = 131072;
+const int EF_RESTARTANIM_BIT = 1048576;
+const int EF_TELEPORT_BIT = 2097152;
+const int MF_ROCKET  =   1; // leave a trail
+const int MF_GRENADE =   2; // leave a trail
+const int MF_GIB     =   4; // leave a trail
+const int MF_ROTATE  =   8; // rotate (bonus items)
+const int MF_TRACER  =  16; // green split trail
+const int MF_ZOMGIB  =  32; // small blood trail
+const int MF_TRACER2 =  64; // orange split trail
+const int MF_TRACER3 = 128; // purple trail
+.int csqcmodel_effects;
+.int csqcmodel_modelflags;
+.int csqcmodel_traileffect;
 void CSQCModel_Effects_PreUpdate(void)
-{
+{SELFPARAM();
        self.effects = self.csqcmodel_effects;
        self.modelflags = self.csqcmodel_modelflags;
+       self.traileffect = self.csqcmodel_traileffect;
 }
 void Reset_ArcBeam(void);
 void CSQCModel_Effects_PostUpdate(void)
-{
+{SELFPARAM();
        if (self == csqcplayer) {
                if (self.csqcmodel_teleported) {
                        Reset_ArcBeam();
@@ -480,23 +497,24 @@ void CSQCModel_Effects_PostUpdate(void)
        }
        self.csqcmodel_effects = self.effects;
        self.csqcmodel_modelflags = self.modelflags;
+       self.csqcmodel_traileffect = self.traileffect;
        self.effects = 0;
        self.modelflags = 0;
        if(self.csqcmodel_teleported)
-               Projectile_ResetTrail(self.origin);
+               Projectile_ResetTrail(self, self.origin);
 }
-.float snd_looping;
+.int snd_looping;
 void CSQCModel_Effects_Apply(void)
-{
-       float eff = self.csqcmodel_effects;
-       eff &= ~CSQCMODEL_EF_RESPAWNGHOST;
+{SELFPARAM();
+       int eff = self.csqcmodel_effects & ~CSQCMODEL_EF_RESPAWNGHOST;
+       int tref = self.csqcmodel_traileffect;
 
        self.renderflags &= ~(RF_DEPTHHACK | RF_ADDITIVE | RF_FULLBRIGHT | EF_NOSHADOW | RF_USEAXIS);
        self.effects = 0;
        self.traileffect = 0;
 
        if(eff & EF_BRIGHTFIELD)
-               self.traileffect = particleeffectnum("TR_NEXUIZPLASMA");
+               tref = EFFECT_TR_NEXUIZPLASMA.m_id;
        // ignoring EF_MUZZLEFLASH
        if(eff & EF_BRIGHTLIGHT)
                adddynamiclight(self.origin, 400, '3 3 3');
@@ -514,9 +532,9 @@ void CSQCModel_Effects_Apply(void)
        if(eff & EF_FULLBRIGHT)
                self.renderflags |= RF_FULLBRIGHT;
        if(eff & EF_FLAME)
-               pointparticles(particleeffectnum("EF_FLAME"), self.origin, '0 0 0', bound(0, frametime, 0.1));
+               pointparticles(particleeffectnum(EFFECT_EF_FLAME), self.origin, '0 0 0', bound(0, frametime, 0.1));
        if(eff & EF_STARDUST)
-               pointparticles(particleeffectnum("EF_STARDUST"), self.origin, '0 0 0', bound(0, frametime, 0.1));
+               pointparticles(particleeffectnum(EFFECT_EF_STARDUST), self.origin, '0 0 0', bound(0, frametime, 0.1));
        if(eff & EF_NOSHADOW)
                self.renderflags |= RF_NOSHADOW;
        if(eff & EF_NODEPTHTEST)
@@ -530,29 +548,31 @@ void CSQCModel_Effects_Apply(void)
                self.renderflags |= RF_DYNAMICMODELLIGHT;
        // ignoring EF_UNUSED18, EF_UNUSED19, EF_RESTARTANIM_BIT, EF_TELEPORT_BIT, EF_LOWPRECISION
        if(self.csqcmodel_modelflags & MF_ROCKET)
-               self.traileffect = particleeffectnum("TR_ROCKET");
+               tref = EFFECT_TR_ROCKET.m_id;
        if(self.csqcmodel_modelflags & MF_GRENADE)
-               self.traileffect = particleeffectnum("TR_GRENADE");
+               tref = EFFECT_TR_GRENADE.m_id;
        if(self.csqcmodel_modelflags & MF_GIB)
-               self.traileffect = particleeffectnum("TR_BLOOD");
+               tref = EFFECT_TR_BLOOD.m_id;
        if(self.csqcmodel_modelflags & MF_ROTATE)
        {
                self.renderflags |= RF_USEAXIS;
                makevectors(self.angles + '0 100 0' * fmod(time, 3.6));
        }
        if(self.csqcmodel_modelflags & MF_TRACER)
-               self.traileffect = particleeffectnum("TR_WIZSPIKE");
+               tref = EFFECT_TR_WIZSPIKE.m_id;
        if(self.csqcmodel_modelflags & MF_ZOMGIB)
-               self.traileffect = particleeffectnum("TR_SLIGHTBLOOD");
+               tref = EFFECT_TR_SLIGHTBLOOD.m_id;
        if(self.csqcmodel_modelflags & MF_TRACER2)
-               self.traileffect = particleeffectnum("TR_KNIGHTSPIKE");
+               tref = EFFECT_TR_KNIGHTSPIKE.m_id;
        if(self.csqcmodel_modelflags & MF_TRACER3)
-               self.traileffect = particleeffectnum("TR_VORESPIKE");
+               tref = EFFECT_TR_VORESPIKE.m_id;
+
+       self.traileffect = tref;
 
        if(self.drawmask)
-               Projectile_DrawTrail(self.origin);
+               Projectile_DrawTrail(self, self.origin);
        else
-               Projectile_ResetTrail(self.origin);
+               Projectile_ResetTrail(self, self.origin);
 
        if(self.csqcmodel_effects & CSQCMODEL_EF_RESPAWNGHOST)
                self.renderflags |= RF_ADDITIVE;
@@ -562,7 +582,7 @@ void CSQCModel_Effects_Apply(void)
        {
                if(!self.snd_looping)
                {
-                       sound(self, CH_TRIGGER_SINGLE, "misc/jetpack_fly.wav", VOL_BASE, autocvar_g_jetpack_attenuation);
+                       sound(self, CH_TRIGGER_SINGLE, SND_JETPACK_FLY, VOL_BASE, autocvar_g_jetpack_attenuation);
                        self.snd_looping = CH_TRIGGER_SINGLE;
                }
        }
@@ -570,30 +590,25 @@ void CSQCModel_Effects_Apply(void)
        {
                if(self.snd_looping)
                {
-                       sound(self, self.snd_looping, "misc/null.wav", VOL_BASE, autocvar_g_jetpack_attenuation);
+                       sound(self, self.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation);
                        self.snd_looping = 0;
                }
        }
 }
 
-void CSQCPlayer_Precache()
-{
-       precache_sound("misc/jetpack_fly.wav");
-}
-
 // general functions
-.float csqcmodel_predraw_run;
-.float anim_frame;
-.float anim_frame1time;
-.float anim_frame2;
-.float anim_frame2time;
-.float anim_saveframe;
-.float anim_saveframe1time;
-.float anim_saveframe2;
-.float anim_saveframe2time;
-.float anim_prev_pmove_flags;
-void CSQCModel_Hook_PreDraw(float isplayer)
-{
+.int csqcmodel_predraw_run;
+.int anim_frame;
+.int anim_frame1time;
+.int anim_frame2;
+.int anim_frame2time;
+.int anim_saveframe;
+.int anim_saveframe1time;
+.int anim_saveframe2;
+.int anim_saveframe2time;
+.int anim_prev_pmove_flags;
+void CSQCModel_Hook_PreDraw(bool isplayer)
+{SELFPARAM();
        if(self.csqcmodel_predraw_run == framecount)
                return;
        self.csqcmodel_predraw_run = framecount;
@@ -610,10 +625,11 @@ void CSQCModel_Hook_PreDraw(float isplayer)
        {
                CSQCPlayer_ModelAppearance_Apply(self.entnum == player_localnum + 1);
                CSQCPlayer_LOD_Apply();
+
                if(!isplayer)
                {
                        skeleton_loadinfo(self);
-                       float doblend = (self.bone_upperbody >= 0);
+                       bool doblend = (self.bone_upperbody >= 0);
                        CSQCPlayer_FallbackFrame_Apply();
                        if(doblend)
                        {
@@ -631,17 +647,17 @@ void CSQCModel_Hook_PreDraw(float isplayer)
                {
                        // we know that frame3 and frame4 fields, used by InterpolateAnimation, are left alone - but that is all we know!
                        skeleton_loadinfo(self);
-                       float doblend = (self.bone_upperbody >= 0);
-                       float onground = 0;
+                       bool doblend = (self.bone_upperbody >= 0);
+                       bool onground = 0;
                        if(self == csqcplayer)
                        {
-                               if(self.pmove_flags & PMF_ONGROUND)
+                               if(self.flags & FL_ONGROUND)
                                        onground = 1;
-                               self.anim_prev_pmove_flags = self.pmove_flags;
-                               if(self.pmove_flags & PMF_DUCKED)
-                                       animdecide_setstate(self, self.anim_state | ANIMSTATE_DUCK, FALSE);
+                               self.anim_prev_pmove_flags = self.flags;
+                               if(self.flags & FL_DUCKED)
+                                       animdecide_setstate(self, self.anim_state | ANIMSTATE_DUCK, false);
                                else if(self.anim_state & ANIMSTATE_DUCK)
-                                       animdecide_setstate(self, self.anim_state - ANIMSTATE_DUCK, FALSE);
+                                       animdecide_setstate(self, self.anim_state - ANIMSTATE_DUCK, false);
                        }
                        else
                        {
@@ -649,10 +665,10 @@ void CSQCModel_Hook_PreDraw(float isplayer)
                                if(trace_startsolid || trace_fraction < 1)
                                        onground = 1;
                        }
-                       animdecide_init(self);
+                       animdecide_load_if_needed(self);
                        animdecide_setimplicitstate(self, onground);
                        animdecide_setframes(self, doblend, anim_frame, anim_frame1time, anim_frame2, anim_frame2time);
-                       float sf = 0;
+                       int sf = 0;
                        if(self.anim_saveframe != self.anim_frame || self.anim_saveframe1time != self.anim_frame1time)
                                sf |= CSQCMODEL_PROPERTY_FRAME;
                        if(self.anim_saveframe2 != self.anim_frame2 || self.anim_saveframe2time != self.anim_frame2time)
@@ -661,16 +677,15 @@ void CSQCModel_Hook_PreDraw(float isplayer)
                        self.anim_saveframe1time = self.anim_frame1time;
                        self.anim_saveframe2 = self.anim_frame2;
                        self.anim_saveframe2time = self.anim_frame2time;
-                       if(sf)
-                       {
-                               CSQCModel_InterpolateAnimation_2To4_PreNote(sf | CSQCMODEL_PROPERTY_LERPFRAC);
-                               self.lerpfrac = (doblend ? 0.5 : 0);
-                               self.frame = self.anim_frame;
-                               self.frame1time = self.anim_frame1time;
-                               self.frame2 = self.anim_frame2;
-                               self.frame2time = self.anim_frame2time;
-                               CSQCModel_InterpolateAnimation_2To4_Note(sf | CSQCMODEL_PROPERTY_LERPFRAC, FALSE);
-                       }
+                       // Note: we always consider lerpfrac "changed", as it uses fixed values every time anyway.
+                       // This ensures that .frame etc. are always written.
+                       CSQCModel_InterpolateAnimation_2To4_PreNote(sf | CSQCMODEL_PROPERTY_LERPFRAC);
+                       self.lerpfrac = (doblend ? 0.5 : 0);
+                       self.frame = self.anim_frame;
+                       self.frame1time = self.anim_frame1time;
+                       self.frame2 = self.anim_frame2;
+                       self.frame2time = self.anim_frame2time;
+                       CSQCModel_InterpolateAnimation_2To4_Note(sf | CSQCMODEL_PROPERTY_LERPFRAC, false);
                        CSQCModel_InterpolateAnimation_2To4_Do();
                        if(doblend)
                        {
@@ -691,8 +706,8 @@ void CSQCModel_Hook_PreDraw(float isplayer)
        CSQCModel_Effects_Apply();
 }
 
-void CSQCModel_Hook_PreUpdate(float isnew, float isplayer, float islocalplayer)
-{
+void CSQCModel_Hook_PreUpdate(bool isnew, bool isplayer, bool islocalplayer)
+{SELFPARAM();
        // interpolate v_angle
        self.iflags |= IFLAG_V_ANGLE_X;
        // revert to values from server
@@ -705,10 +720,10 @@ void CSQCModel_Hook_PreUpdate(float isnew, float isplayer, float islocalplayer)
        }
 }
 
-void CSQCModel_Hook_PostUpdate(float isnew, float isplayer, float islocalplayer)
-{
+void CSQCModel_Hook_PostUpdate(bool isnew, bool isplayer, bool islocalplayer)
+{SELFPARAM();
        // is it a player model? (shared state)
-       self.isplayermodel = (substring(self.model, 0, 14) == "models/player/" || substring(self.model, 0, 17) == "models/ok_player/");
+       self.isplayermodel = (substring(self.model, 0, 14) == "models/player/" || substring(self.model, 0, 17) == "models/ok_player/" || (substring(self.model, 0, 16) == "models/monsters/" && (self.entnum >= 1 && self.entnum <= maxclients)));
 
        // save values set by server
        if(self.isplayermodel)