]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into divVerent/csad
authorSamual Lenks <samual@xonotic.org>
Tue, 11 Dec 2012 22:17:48 +0000 (17:17 -0500)
committerSamual Lenks <samual@xonotic.org>
Tue, 11 Dec 2012 22:17:48 +0000 (17:17 -0500)
28 files changed:
defaultXonotic.cfg
qcsrc/client/Main.qc
qcsrc/client/autocvars.qh
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/player_skeleton.qc [new file with mode: 0644]
qcsrc/client/player_skeleton.qh [new file with mode: 0644]
qcsrc/client/progs.src
qcsrc/common/animdecide.qc [new file with mode: 0644]
qcsrc/common/animdecide.qh [new file with mode: 0644]
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/csqcmodellib/cl_model.qc
qcsrc/csqcmodellib/cl_model.qh
qcsrc/csqcmodellib/cl_player.qc
qcsrc/csqcmodellib/cl_player.qh
qcsrc/csqcmodellib/common.qh
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/defs.qh
qcsrc/server/g_subs.qc
qcsrc/server/mutators/mutator_dodging.qc
qcsrc/server/progs.src
qcsrc/server/t_jumppads.qc
qcsrc/server/w_electro.qc

index 9796e60027f44f1ca3308e856c002e03dc1f17a9..dc5a8d6e53a3203150b57687393b33858cb018c9 100644 (file)
@@ -247,7 +247,6 @@ set sv_doublejump 0 "allow Quake 2-style double jumps"
 set sv_jumpspeedcap_min "" "lower bound on the baseline velocity of a jump; final velocity will be >= (jumpheight * min + jumpheight)"
 set sv_jumpspeedcap_max "" "upper bound on the baseline velocity of a jump; final velocity will be <= (jumpheight * max + jumpheight)"
 set sv_jumpspeedcap_max_disable_on_ramps 0 "disable upper baseline velocity bound on ramps to preserve the old rampjump style"
-set sv_player_jumpanim_minfall 48 "minimum distance player has to have below their feet before the jump animation will be activated (only when falling, +jump will play anim instantly)"
 
 seta sv_precacheplayermodels 1
 seta sv_precacheweapons 0
index 835d2855ed78a866c8e0e15a806b63b6ff6b9766..663368c78a50beeee744c95838a113db370f552a 100644 (file)
@@ -792,6 +792,12 @@ void Ent_Remove()
        if(self.entremove)
                self.entremove();
 
+       if(self.skeletonindex)
+       {
+               skel_delete(self.skeletonindex);
+               self.skeletonindex = 0;
+       }
+
        self.enttype = 0;
        self.classname = "";
        self.draw = menu_sub_null;
index ed56f718381cc0e2ecfe61c29d8e3ed2a59a261c..3029dbe819a9b708f143379b78597f5c2e70bd4d 100644 (file)
@@ -404,3 +404,4 @@ string autocvar__cl_playermodel;
 float autocvar_cl_precacheplayermodels;
 float autocvar_cl_deathglow;
 float autocvar_developer_csqcentities;
+float autocvar__animblend;
index 9c87d13addf9949c671a4319da1e5c91f2f69b45..933cc237131fea711caee04edbc9cddf2d493630 100644 (file)
@@ -1,4 +1,4 @@
-void CSQCModel_Hook_PreDraw();
+void CSQCModel_Hook_PreDraw(float isplayer);
 
 .float isplayermodel;
 
@@ -270,8 +270,10 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer)
 // FEATURE: fallback frames
 .float csqcmodel_saveframe;
 .float csqcmodel_saveframe2;
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
 .float csqcmodel_saveframe3;
 .float csqcmodel_saveframe4;
+#endif
 .float csqcmodel_framecount;
 
 #define IS_DEAD_FRAME(f) ((f) == 0 || (f) == 1)
@@ -279,15 +281,19 @@ void CSQCPlayer_FallbackFrame_PreUpdate(void)
 {
        self.frame = self.csqcmodel_saveframe;
        self.frame2 = self.csqcmodel_saveframe2;
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        self.frame3 = self.csqcmodel_saveframe3;
        self.frame4 = self.csqcmodel_saveframe4;
+#endif
 }
 void CSQCPlayer_FallbackFrame_PostUpdate(float isnew)
 {
        self.csqcmodel_saveframe = self.frame;
        self.csqcmodel_saveframe2 = self.frame2;
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        self.csqcmodel_saveframe3 = self.frame3;
        self.csqcmodel_saveframe4 = self.frame4;
+#endif
 
        // hack for death animations: set their frametime to zero in case a
        // player "pops in"
@@ -300,8 +306,10 @@ void CSQCPlayer_FallbackFrame_PostUpdate(float isnew)
                }
                FIX_FRAMETIME(frame, frame1time)
                FIX_FRAMETIME(frame2, frame2time)
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
                FIX_FRAMETIME(frame3, frame3time)
                FIX_FRAMETIME(frame4, frame4time)
+#endif
        }
        self.csqcmodel_isdead = IS_DEAD_FRAME(self.frame);
 }
@@ -329,8 +337,10 @@ void CSQCPlayer_FallbackFrame_Apply(void)
 {
        self.frame = CSQCPlayer_FallbackFrame(self.frame);
        self.frame2 = CSQCPlayer_FallbackFrame(self.frame2);
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        self.frame3 = CSQCPlayer_FallbackFrame(self.frame3);
        self.frame4 = CSQCPlayer_FallbackFrame(self.frame4);
+#endif
 }
 
 // FEATURE: auto tag_index
@@ -357,7 +367,7 @@ void CSQCModel_AutoTagIndex_Apply(void)
                {
                        entity oldself = self;
                        self = self.tag_entity;
-                       CSQCModel_Hook_PreDraw();
+                       CSQCModel_Hook_PreDraw((self.entnum >= 1 && self.entnum <= maxclients));
                        self = oldself;
                }
 
@@ -419,6 +429,8 @@ float EF_DIMLIGHT   = 8;
 float EF_DOUBLESIDED = 32768;
 float EF_NOSELFSHADOW = 65536;
 float EF_DYNAMICMODELLIGHT = 131072;
+float EF_RESTARTANIM_BIT = 1048576;
+float EF_TELEPORT_BIT = 2097152;
 float MF_ROCKET  =   1; // leave a trail
 float MF_GRENADE =   2; // leave a trail
 float MF_GIB     =   4; // leave a trail
@@ -543,7 +555,16 @@ void CSQCPlayer_GlowMod_Apply(void)
 
 // general functions
 .float csqcmodel_predraw_run;
-void CSQCModel_Hook_PreDraw()
+.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)
 {
        if(self.csqcmodel_predraw_run == framecount)
                return;
@@ -562,7 +583,64 @@ void CSQCModel_Hook_PreDraw()
                CSQCPlayer_ForceModel_Apply(self.entnum == player_localnum + 1);
                CSQCPlayer_GlowMod_Apply();
                CSQCPlayer_LOD_Apply();
-               CSQCPlayer_FallbackFrame_Apply();
+               if(!isplayer)
+                       CSQCPlayer_FallbackFrame_Apply();
+               else
+               {
+                       // we know that frame3 and frame4 fields, used by InterpolateAnimation, are left alone - but that is all we know!
+                       float doblend = autocvar__animblend;
+                       float onground = 0;
+                       if(self == csqcplayer)
+                       {
+                               if(self.pmove_flags & PMF_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);
+                               else if(self.anim_state & ANIMSTATE_DUCK)
+                                       animdecide_setstate(self, self.anim_state - ANIMSTATE_DUCK, FALSE);
+                       }
+                       else
+                       {
+                               traceline(self.origin + '0 0 1' * self.maxs_z, self.origin + '0 0 1' * (self.mins_z - 4), MOVE_NOMONSTERS, self);
+                               if(trace_startsolid || trace_fraction < 1)
+                                       onground = 1;
+                       }
+                       animdecide_init(self); // FIXME only do this on model change
+                       animdecide_setimplicitstate(self, onground);
+                       animdecide_setframes(self, doblend, anim_frame, anim_frame1time, anim_frame2, anim_frame2time);
+                       float 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)
+                               sf |= CSQCMODEL_PROPERTY_FRAME2;
+                       self.anim_saveframe = self.anim_frame;
+                       self.anim_saveframe1time = self.anim_frame1time;
+                       self.anim_saveframe = 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);
+                       }
+                       CSQCModel_InterpolateAnimation_2To4_Do();
+                       if(doblend)
+                       {
+                               skeleton_from_frames(self);
+                       }
+                       else
+                       {
+                               free_skeleton_from_frames(self);
+                               // just in case, clear these
+                               self.lerpfrac3 = 0;
+                               self.lerpfrac4 = 0;
+                       }
+               }
        }
 
        CSQCModel_AutoTagIndex_Apply();
@@ -576,7 +654,8 @@ void CSQCModel_Hook_PreUpdate(float isnew, float isplayer, float islocalplayer)
        CSQCModel_Effects_PreUpdate();
        if(self.isplayermodel)
        {
-               CSQCPlayer_FallbackFrame_PreUpdate();
+               if(!isplayer)
+                       CSQCPlayer_FallbackFrame_PreUpdate();
                CSQCPlayer_ForceModel_PreUpdate();
        }
 }
@@ -590,7 +669,8 @@ void CSQCModel_Hook_PostUpdate(float isnew, float isplayer, float islocalplayer)
        if(self.isplayermodel)
        {
                CSQCPlayer_ForceModel_PostUpdate();
-               CSQCPlayer_FallbackFrame_PostUpdate(isnew);
+               if(!isplayer)
+                       CSQCPlayer_FallbackFrame_PostUpdate(isnew);
        }
        CSQCModel_Effects_PostUpdate();
 }
diff --git a/qcsrc/client/player_skeleton.qc b/qcsrc/client/player_skeleton.qc
new file mode 100644 (file)
index 0000000..48fb8a3
--- /dev/null
@@ -0,0 +1,91 @@
+.float skeleton_modelindex;
+#define BONETYPE_LOWER 0
+#define BONETYPE_UPPER 1
+#define MAX_BONES 128
+.float skeleton_bonetype[MAX_BONES];
+.float skeleton_aimbone;
+.float skeleton_numbones;
+
+void skeleton_identifybones(entity e)
+{
+       float s = e.skeletonindex;
+       float n = (e.skeleton_numbones = skel_get_numbones(s));
+       e.skeleton_aimbone = 0;
+       float i;
+       for(i = 1; i <= n; ++i)
+       {
+               float t = BONETYPE_LOWER;
+               float p = skel_get_boneparent(s, i);
+               if(p > 0)
+                       t = e.(skeleton_bonetype[p-1]);
+               string nm = skel_get_bonename(s, i);
+               if(nm == "spine2")
+                       t = BONETYPE_UPPER;
+               if(nm == "upperarm_R")
+                       e.skeleton_aimbone = i;
+               e.(skeleton_bonetype[i-1]) = t;
+       }
+}
+
+void free_skeleton_from_frames(entity e)
+{
+       if(e.skeletonindex)
+       {
+               skel_delete(e.skeletonindex);
+               e.skeletonindex = 0;
+       }
+}
+
+void skeleton_from_frames(entity e)
+{
+       float m = e.modelindex;
+       if(m != e.skeleton_modelindex)
+       {
+               if(e.skeletonindex)
+               {
+                       skel_delete(e.skeletonindex);
+                       e.skeletonindex = 0;
+               }
+               m = (e.skeleton_modelindex = e.modelindex);
+               if(m)
+               {
+                       e.skeletonindex = skel_create(m);
+                       skeleton_identifybones(e);
+               }
+       }
+       float s = e.skeletonindex;
+       if(!s)
+               return;
+       float bone;
+       float n = e.skeleton_numbones;
+       float savelerpfrac = e.lerpfrac;
+       float savelerpfrac3 = e.lerpfrac3;
+       float savelerpfrac4 = e.lerpfrac4;
+       for(bone = 0; bone < n; )
+       {
+               float firstbone = bone;
+               float bonetype = e.skeleton_bonetype[bone];
+               for(++bone; (bone < n) && (e.skeleton_bonetype[bone] == bonetype); ++bone)
+                       ;
+               if(bonetype == BONETYPE_UPPER)
+               {
+                       // only show frames 1+3 (upper body)
+                       e.lerpfrac = 0;
+                       e.lerpfrac3 = savelerpfrac3 * 2;
+                       e.lerpfrac4 = 0;
+               }
+               else
+               {
+                       // only show frames 2+4 (lower body)
+                       e.lerpfrac = savelerpfrac * 2;
+                       e.lerpfrac3 = 0;
+                       e.lerpfrac4 = savelerpfrac4 * 2;
+               }
+               //print(sprintf("Run: bone %d to %d, type %d\n", firstbone + 1, bone, bonetype));
+               //print(sprintf("frame %d %d %d %d lerpfrac * %d %d %d\n", e.frame, e.frame2, e.frame3, e.frame4, e.lerpfrac, e.lerpfrac3, e.lerpfrac4));
+               skel_build(s, e, m, 0, firstbone + 1, bone);
+       }
+       e.lerpfrac = savelerpfrac;
+       e.lerpfrac3 = savelerpfrac;
+       e.lerpfrac4 = savelerpfrac;
+}
diff --git a/qcsrc/client/player_skeleton.qh b/qcsrc/client/player_skeleton.qh
new file mode 100644 (file)
index 0000000..292cfca
--- /dev/null
@@ -0,0 +1,2 @@
+void free_skeleton_from_frames(entity e);
+void skeleton_from_frames(entity e);
index 0922433eebdecd2d6c3c229636162e1475c6e56b..9d968f1fe7d86e1ceeecf09b135c6dcc1e23d4f7 100644 (file)
@@ -23,6 +23,7 @@ Defs.qc
 ../common/command/generic.qh
 ../common/command/shared_defs.qh
 ../common/urllib.qh
+../common/animdecide.qh
 command/cl_cmd.qh
 
 autocvars.qh
@@ -49,6 +50,7 @@ vehicles/vehicles.qh
 ../csqcmodellib/cl_model.qh
 ../csqcmodellib/cl_player.qh
 projectile.qh
+player_skeleton.qh
 
 sortlist.qc
 miscfunctions.qc
@@ -111,4 +113,7 @@ command/cl_cmd.qc
 ../warpzonelib/client.qc
 tturrets.qc
 
+player_skeleton.qc
+../common/animdecide.qc
+
 ../common/if-this-file-errors-scroll-up-and-fix-the-warnings.fteqccfail
diff --git a/qcsrc/common/animdecide.qc b/qcsrc/common/animdecide.qc
new file mode 100644 (file)
index 0000000..17f8b57
--- /dev/null
@@ -0,0 +1,311 @@
+// player animation data for this model
+// each vector is as follows:
+// _x = startframe
+// _y = numframes
+// _z = framerate
+.vector anim_die1; // player dies
+.vector anim_die2; // player dies differently
+.vector anim_draw; // player pulls out a weapon
+.vector anim_duckwalk; // player walking while crouching
+.vector anim_duckjump; // player jumping from a crouch
+.vector anim_duckidle; // player idling while crouching
+.vector anim_idle; // player standing
+.vector anim_jump; // player jump
+.vector anim_pain1; // player flinches from pain
+.vector anim_pain2; // player flinches from pain, differently
+.vector anim_shoot; // player shoots
+.vector anim_taunt; // player taunts others (FIXME: no code references this)
+.vector anim_run; // player running forward
+.vector anim_runbackwards; // player running backward
+.vector anim_strafeleft; // player shuffling left quickly
+.vector anim_straferight; // player shuffling right quickly
+.vector anim_forwardright; // player running forward and right
+.vector anim_forwardleft; // player running forward and left
+.vector anim_backright; // player running backward and right
+.vector anim_backleft; // player running back and left
+.vector anim_melee; // player doing the melee action
+.vector anim_duck; // player doing the melee action
+.vector anim_duckwalkbackwards;
+.vector anim_duckwalkstrafeleft;
+.vector anim_duckwalkstraferight;
+.vector anim_duckwalkforwardright;
+.vector anim_duckwalkforwardleft;
+.vector anim_duckwalkbackright;
+.vector anim_duckwalkbackleft;
+
+void animdecide_init(entity e)
+{
+       vector none = '0 0 0';
+       e.anim_die1 = animfixfps(e, '0 1 0.5', none); // 2 seconds
+       e.anim_die2 = animfixfps(e, '1 1 0.5', none); // 2 seconds
+       e.anim_draw = animfixfps(e, '2 1 3', none);
+       e.anim_duckwalk = animfixfps(e, '4 1 1', none);
+       e.anim_duckjump = animfixfps(e, '5 1 10', none);
+       e.anim_duckidle = animfixfps(e, '6 1 1', none);
+       e.anim_idle = animfixfps(e, '7 1 1', none);
+       e.anim_jump = animfixfps(e, '8 1 10', none);
+       e.anim_pain1 = animfixfps(e, '9 1 2', none); // 0.5 seconds
+       e.anim_pain2 = animfixfps(e, '10 1 2', none); // 0.5 seconds
+       e.anim_shoot = animfixfps(e, '11 1 5', none); // analyze models and set framerate
+       e.anim_taunt = animfixfps(e, '12 1 0.33', none);
+       e.anim_run = animfixfps(e, '13 1 1', none);
+       e.anim_runbackwards = animfixfps(e, '14 1 1', none);
+       e.anim_strafeleft = animfixfps(e, '15 1 1', none);
+       e.anim_straferight = animfixfps(e, '16 1 1', none);
+       e.anim_forwardright = animfixfps(e, '19 1 1', '16 1 1');
+       e.anim_forwardleft = animfixfps(e, '20 1 1', '15 1 1');
+       e.anim_backright = animfixfps(e, '21 1 1', '16 1 1');
+       e.anim_backleft  = animfixfps(e, '22 1 1', '15 1 1');
+       e.anim_melee = animfixfps(e, '23 1 1', '11 1 1');
+       e.anim_duckwalkbackwards = animfixfps(e, '24 1 1', '4 1 1');
+       e.anim_duckwalkstrafeleft = animfixfps(e, '25 1 1', '4 1 1');
+       e.anim_duckwalkstraferight = animfixfps(e, '26 1 1', '4 1 1');
+       e.anim_duckwalkforwardright = animfixfps(e, '27 1 1', '4 1 1');
+       e.anim_duckwalkforwardleft = animfixfps(e, '28 1 1', '4 1 1');
+       e.anim_duckwalkbackright = animfixfps(e, '29 1 1', '4 1 1');
+       e.anim_duckwalkbackleft  = animfixfps(e, '30 1 1', '4 1 1');
+
+       // these anims ought to stay until stopped explicitly by weaponsystem
+       e.anim_shoot_z = 0.001;
+       e.anim_melee_z = 0.001;
+}
+
+#define ANIMPRIO_IDLE 0
+#define ANIMPRIO_ACTIVE 2
+#define ANIMPRIO_DEAD 3
+
+vector animdecide_getupperanim(entity e)
+{
+       // is there an action?
+       vector outframe = '-1 0 0';
+       float t, a;
+       if(e.anim_upper_time >= e.anim_upper_implicit_time)
+       {
+               a = e.anim_upper_action;
+               t = e.anim_upper_time;
+       }
+       else
+       {
+               a = e.anim_upper_implicit_action;
+               t = e.anim_upper_implicit_time;
+       }
+       switch(a)
+       {
+               case ANIMACTION_DRAW: outframe = e.anim_draw; break;
+               case ANIMACTION_PAIN1: outframe = e.anim_pain1; break;
+               case ANIMACTION_PAIN2: outframe = e.anim_pain2; break;
+               case ANIMACTION_SHOOT: outframe = e.anim_shoot; break;
+               case ANIMACTION_TAUNT: outframe = e.anim_taunt; break;
+               case ANIMACTION_MELEE: outframe = e.anim_melee; break;
+       }
+       if(outframe_x >= 0)
+       {
+               if(time <= t + outframe_y / outframe_z)
+               {
+                       // animation is running!
+                       return vec3(outframe_x, t, ANIMPRIO_ACTIVE);
+               }
+       }
+       // or, decide the anim by state
+       t = max(e.anim_time, e.anim_implicit_time);
+       // but all states are for lower body!
+       return vec3(e.anim_idle_x, t, ANIMPRIO_IDLE);
+}
+
+vector animdecide_getloweranim(entity e)
+{
+       // death etc.
+       if(e.anim_state & ANIMSTATE_FROZEN)
+               return vec3(e.anim_idle_x, e.anim_time, ANIMPRIO_DEAD);
+       if(e.anim_state & ANIMSTATE_DEAD1)
+               return vec3(e.anim_die1_x, e.anim_time, ANIMPRIO_DEAD);
+       if(e.anim_state & ANIMSTATE_DEAD2)
+               return vec3(e.anim_die2_x, e.anim_time, ANIMPRIO_DEAD);
+
+       // is there an action?
+       vector outframe = '-1 0 0';
+       float t, a;
+       if(e.anim_lower_time >= e.anim_lower_implicit_time)
+       {
+               a = e.anim_lower_action;
+               t = e.anim_lower_time;
+       }
+       else
+       {
+               a = e.anim_lower_implicit_action;
+               t = e.anim_lower_implicit_time;
+       }
+       switch(a)
+       {
+               case ANIMACTION_JUMP: if(e.anim_implicit_state & ANIMIMPLICITSTATE_INAIR) { if(e.anim_state & ANIMSTATE_DUCK) outframe = e.anim_duckjump; else outframe = e.anim_jump; } break;
+       }
+       if(outframe_x >= 0)
+       {
+               if(time <= t + outframe_y / outframe_z)
+               {
+                       // animation is running!
+                       return vec3(outframe_x, t, ANIMPRIO_ACTIVE);
+               }
+       }
+       // or, decide the anim by state
+       t = max(e.anim_time, e.anim_implicit_time);
+       if(e.anim_state & ANIMSTATE_DUCK)
+       {
+               if(e.anim_implicit_state & ANIMIMPLICITSTATE_INAIR)
+                       return vec3(e.anim_duckjump_x, 0, ANIMPRIO_ACTIVE); // play the END of the jump anim
+               else switch(e.anim_implicit_state & (ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT | ANIMIMPLICITSTATE_RIGHT))
+               {
+                       case ANIMIMPLICITSTATE_FORWARD:
+                               return vec3(e.anim_duckwalk_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS:
+                               return vec3(e.anim_duckwalkbackwards_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkstraferight_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkstrafeleft_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkforwardright_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkforwardleft_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkbackright_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkbackleft_x, t, ANIMPRIO_ACTIVE);
+                       default:
+                               return vec3(e.anim_duckidle_x, t, ANIMPRIO_ACTIVE);
+               }
+       }
+       else
+       {
+               if(e.anim_implicit_state & ANIMIMPLICITSTATE_INAIR)
+                       return vec3(e.anim_jump_x, 0, ANIMPRIO_ACTIVE); // play the END of the jump anim
+               else switch(e.anim_implicit_state & (ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT | ANIMIMPLICITSTATE_RIGHT))
+               {
+                       case ANIMIMPLICITSTATE_FORWARD:
+                               return vec3(e.anim_run_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS:
+                               return vec3(e.anim_runbackwards_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_straferight_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_strafeleft_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_forwardright_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_forwardleft_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_backright_x, t, ANIMPRIO_ACTIVE);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_backleft_x, t, ANIMPRIO_ACTIVE);
+                       default:
+                               return vec3(e.anim_idle_x, t, ANIMPRIO_IDLE);
+               }
+       }
+       // can't get here
+}
+
+void animdecide_setimplicitstate(entity e, float onground)
+{
+       float s;
+       s = 0;
+
+       makevectors(e.angles);
+       vector v;
+       v_x = e.velocity * v_forward;
+       v_y = e.velocity * v_right;
+
+       // we want to match like this:
+       // the 8 directions shall be "evenly spaced"
+       // that means, the forward key includes anything from -67.5 to +67.5 degrees
+       // which then means x > |y| * cot(3pi/8)
+       //
+       // BUT, the engine's clip-movement-to-keyboard function uses 0.5 here,
+       // which would be an angle range from -63.43 to +63.43 degrees, making
+       // it slightly less likely to "hit two keys at once", so let's do this
+       // here too
+
+       if(vlen(v) > 10)
+       {
+               if(v_x >  fabs(v_y) * 0.5)
+                       s |= ANIMIMPLICITSTATE_FORWARD;
+               if(v_x < -fabs(v_y) * 0.5)
+                       s |= ANIMIMPLICITSTATE_BACKWARDS;
+               if(v_y >  fabs(v_x) * 0.5)
+                       s |= ANIMIMPLICITSTATE_RIGHT;
+               if(v_y < -fabs(v_x) * 0.5)
+                       s |= ANIMIMPLICITSTATE_LEFT;
+       }
+       if(!onground)
+               s |= ANIMIMPLICITSTATE_INAIR;
+
+       // detect some kinds of otherwise misdetected jumps (ground to air transition)
+       // NOTE: currently, in CSQC this is the only jump detection, as the explicit jump action is never called!
+       if(!(e.anim_implicit_state & ANIMIMPLICITSTATE_INAIR) && (s & ANIMIMPLICITSTATE_INAIR))
+       {
+               e.anim_lower_implicit_action = ANIMACTION_JUMP;
+               e.anim_lower_implicit_time = time;
+       }
+
+       if(s != e.anim_implicit_state)
+       {
+               e.anim_implicit_state = s;
+               e.anim_implicit_time = time;
+       }
+}
+void animdecide_setframes(entity e, float support_blending, .float fld_frame, .float fld_frame1time, .float fld_frame2, .float fld_frame2time)
+{
+       // _x: frame
+       // _y: start time
+       // _z: priority
+       vector upper = animdecide_getupperanim(e);
+       vector lower = animdecide_getloweranim(e);
+       //print("UPPER: ", vtos(upper), ", LOWER: ", vtos(lower), "\n");
+       if(upper_z > lower_z)
+               lower = upper;
+       else if(lower_z > upper_z)
+               upper = lower;
+       if(support_blending)
+       {
+               if(e.frame1time != upper_y || e.frame2time != lower_y)
+                       BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
+               e.fld_frame = upper_x;
+               e.fld_frame1time = upper_y;
+               e.fld_frame2 = lower_x;
+               e.fld_frame2time = lower_y;
+       }
+       else
+       {
+               if(e.frame1time != upper_y)
+                       BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
+               e.fld_frame = upper_x;
+               e.fld_frame1time = upper_y;
+       }
+}
+
+void animdecide_setstate(entity e, float newstate, float restart)
+{
+       if(!restart)
+               if(newstate == e.anim_state)
+                       return;
+       e.anim_state = newstate;
+       e.anim_time = time;
+}
+void animdecide_setaction(entity e, float action, float restart)
+{
+       if(action < 0)
+       {
+               if(!restart)
+                       if(action == e.anim_lower_action)
+                               return;
+               e.anim_lower_action = action;
+               e.anim_lower_time = time;
+       }
+       else
+       {
+               if(!restart)
+                       if(action == e.anim_upper_action)
+                               return;
+               e.anim_upper_action = action;
+               e.anim_upper_time = time;
+       }
+}
diff --git a/qcsrc/common/animdecide.qh b/qcsrc/common/animdecide.qh
new file mode 100644 (file)
index 0000000..105dac7
--- /dev/null
@@ -0,0 +1,46 @@
+// client side frame inferring
+void animdecide_init(entity e);
+
+void animdecide_setimplicitstate(entity e, float onground);
+void animdecide_setframes(entity e, float support_blending, .float fld_frame, .float fld_frame1time, .float fld_frame2, .float fld_frame2time);
+
+// please network this one
+.float anim_state;
+.float anim_time;
+.float anim_lower_action;
+.float anim_lower_time;
+.float anim_upper_action;
+.float anim_upper_time;
+
+// when copying entities, copy these too
+.float anim_implicit_state;
+.float anim_implicit_time;
+.float anim_lower_implicit_action;
+.float anim_lower_implicit_time;
+.float anim_upper_implicit_action;
+.float anim_upper_implicit_time;
+
+// explicit anim states (networked)
+void animdecide_setstate(entity e, float newstate, float restart);
+#define ANIMSTATE_DEAD1 1 // base frames: die1
+#define ANIMSTATE_DEAD2 2 // base frames: die2
+#define ANIMSTATE_DUCK 4 // turns walk into duckwalk, jump into duckjump, etc.
+#define ANIMSTATE_FROZEN 8 // force idle
+
+// implicit anim states (inferred from velocity, etc.)
+#define ANIMIMPLICITSTATE_INAIR 1
+#define ANIMIMPLICITSTATE_FORWARD 2
+#define ANIMIMPLICITSTATE_BACKWARDS 4
+#define ANIMIMPLICITSTATE_LEFT 8
+#define ANIMIMPLICITSTATE_RIGHT 16
+#define ANIMIMPLICITSTATE_JUMPRELEASED 32
+
+// explicit actions (networked); negative values are for lower body
+void animdecide_setaction(entity e, float action, float restart);
+#define ANIMACTION_JUMP -1 // jump
+#define ANIMACTION_DRAW 1 // draw
+#define ANIMACTION_PAIN1 2 // pain
+#define ANIMACTION_PAIN2 3 // pain
+#define ANIMACTION_SHOOT 4 // shoot
+#define ANIMACTION_TAUNT 5 // taunt
+#define ANIMACTION_MELEE 6 // melee
index c07bbeebc37a99708ea84ecb4e3a1903b0a43fcb..028ce9b8cbcf9a64e9d14131e6c7f31bef663064 100644 (file)
@@ -1,5 +1,5 @@
 // define this if svqc code wants to use .frame2 and .lerpfrac
-#define CSQCMODEL_HAVE_TWO_FRAMES
+//#define CSQCMODEL_HAVE_TWO_FRAMES
 
 // don't define this ever
 //#define CSQCMODEL_SUPPORT_GETTAGINFO_BEFORE_DRAW
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_x, 255, 0, 255) \
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_y, 255, 0, 255) \
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_z, 255, 0, 255) \
+       CSQCMODEL_ENDIF \
+       CSQCMODEL_IF(isplayer) \
+               CSQCMODEL_PROPERTY(128, float, ReadByte, WriteByte, anim_state) \
+               CSQCMODEL_PROPERTY(128, float, ReadApproxPastTime, WriteApproxPastTime, anim_time) \
+               CSQCMODEL_IF(!islocalplayer) \
+                       CSQCMODEL_PROPERTY(256, float, ReadChar, WriteChar, anim_lower_action) \
+                       CSQCMODEL_PROPERTY(256, float, ReadApproxPastTime, WriteApproxPastTime, anim_lower_time) \
+               CSQCMODEL_ENDIF \
+               CSQCMODEL_PROPERTY(512, float, ReadChar, WriteChar, anim_upper_action) \
+               CSQCMODEL_PROPERTY(512, float, ReadApproxPastTime, WriteApproxPastTime, anim_upper_time) \
        CSQCMODEL_ENDIF
 // TODO get rid of colormod/glowmod here, find good solution for nex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody
 
@@ -37,7 +47,7 @@
 #define CSQCMODEL_HOOK_POSTUPDATE \
        CSQCModel_Hook_PostUpdate(isnew, isplayer, islocalplayer);
 #define CSQCMODEL_HOOK_PREDRAW \
-       CSQCModel_Hook_PreDraw();
+       CSQCModel_Hook_PreDraw(isplayer);
 #define CSQCPLAYER_HOOK_POSTCAMERASETUP
 
 // force updates of player entities that often even if unchanged
index 53c14cfb0cd1798a706bb96cb36bce8afd924ba8..98eecb3c61cdb6af600f2ec9999098a15f6be999 100644 (file)
@@ -2463,3 +2463,32 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
        for(queue_start = e; queue_start; queue_start = queue_start.fld)
                queue_start.FindConnectedComponent_processing = 0;
 }
+
+vector vec3(float x, float y, float z)
+{
+       vector v;
+       v_x = x;
+       v_y = y;
+       v_z = z;
+       return v;
+}
+
+#ifndef MENUQC
+vector animfixfps(entity e, vector a, vector b)
+{
+       // multi-frame anim: keep as-is
+       if(a_y == 1)
+       {
+               float dur;
+               dur = frameduration(e.modelindex, a_x);
+               if(dur <= 0 && b_y)
+               {
+                       a = b;
+                       dur = frameduration(e.modelindex, a_x);
+               }
+               if(dur > 0)
+                       a_z = 1.0 / dur;
+       }
+       return a;
+}
+#endif
index faa605f887598c0dba17c7a6d5ae99654eb8e409..b8c5502a846e6466af246144b42a04a475103c34 100644 (file)
@@ -361,3 +361,9 @@ float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor);
 typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
 typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
+
+vector vec3(float x, float y, float z);
+
+#ifndef MENUQC
+vector animfixfps(entity e, vector a, vector b);
+#endif
index 163a5126d3be8f34b5e96289d61740c42c2eae0d..b2157f5dd54139a538ce9fa5a77f66527b6d3bbc 100644 (file)
@@ -28,9 +28,8 @@ var float autocvar_cl_nolerp = 0;
 .float csqcmodel_lerpfractime;
 .float csqcmodel_lerpfrac2time;
 
-void CSQCModel_InterpolateAnimation_PreNote(float sf)
+void CSQCModel_InterpolateAnimation_2To4_PreNote(float sf)
 {
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        if(sf & CSQCMODEL_PROPERTY_FRAME)
        {
                self.frame3 = self.frame;
@@ -47,42 +46,62 @@ void CSQCModel_InterpolateAnimation_PreNote(float sf)
                self.csqcmodel_lerpfrac2time = self.csqcmodel_lerpfractime;
                self.lerpfrac = self.csqcmodel_lerpfrac;
        }
-#else
+}
+void CSQCModel_InterpolateAnimation_1To2_PreNote(float sf)
+{
        if(sf & CSQCMODEL_PROPERTY_FRAME)
        {
                self.frame2 = self.frame;
                self.frame2time = self.frame1time;
        }
+}
+void CSQCModel_InterpolateAnimation_PreNote(float sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+       CSQCModel_InterpolateAnimation_2To4_PreNote(sf);
+#else
+       CSQCModel_InterpolateAnimation_1To2_PreNote(sf);
 #endif
 }
 
-void CSQCModel_InterpolateAnimation_Note(float sf)
+void CSQCModel_InterpolateAnimation_2To4_Note(float sf, float set_times)
 {
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        if(sf & CSQCMODEL_PROPERTY_FRAME)
        {
-               self.frame1time = time;
+               if(set_times)
+                       self.frame1time = time;
        }
        if(sf & CSQCMODEL_PROPERTY_FRAME2)
        {
-               self.frame2time = time;
+               if(set_times)
+                       self.frame2time = time;
        }
        if(sf & CSQCMODEL_PROPERTY_LERPFRAC)
        {
                self.csqcmodel_lerpfrac = self.lerpfrac;
-               self.csqcmodel_lerpfractime = time;
+               if(set_times)
+                       self.csqcmodel_lerpfractime = time;
        }
-#else
+}
+void CSQCModel_InterpolateAnimation_1To2_Note(float sf, float set_times)
+{
        if(sf & CSQCMODEL_PROPERTY_FRAME)
        {
-               self.frame1time = time;
+               if(set_times)
+                       self.frame1time = time;
        }
+}
+void CSQCModel_InterpolateAnimation_Note(float sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+       CSQCModel_InterpolateAnimation_2To4_Note(sf, TRUE);
+#else
+       CSQCModel_InterpolateAnimation_1To2_Note(sf, TRUE);
 #endif
 }
 
-void CSQCModel_InterpolateAnimation_Do()
+void CSQCModel_InterpolateAnimation_2To4_Do()
 {
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
        if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
        {
                self.lerpfrac = self.csqcmodel_lerpfrac;
@@ -127,7 +146,9 @@ void CSQCModel_InterpolateAnimation_Do()
                        self.frame3time = 0;
                }
        }
-#else
+}
+void CSQCModel_InterpolateAnimation_1To2_Do()
+{
        if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
        {
                self.lerpfrac = 0;
@@ -139,6 +160,13 @@ void CSQCModel_InterpolateAnimation_Do()
                else
                        self.lerpfrac = 1 - bound(0, (time - self.frame1time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
        }
+}
+void CSQCModel_InterpolateAnimation_Do()
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+       CSQCModel_InterpolateAnimation_2To4_Do();
+#else
+       CSQCModel_InterpolateAnimation_1To2_Do();
 #endif
 }
 
index e8ea9012cb3e16ffc6b1f761c875da7672823ef3..ded7357419221c50305a1a907df5d355d995fb54 100644 (file)
@@ -35,3 +35,16 @@ void CSQCModel_Read(float isnew);
 
 entity CSQCModel_server2csqc(float pl);
 .float csqcmodel_teleported;
+
+// this is exported for custom frame animation code. Use with care.
+// to update frames, first call this:
+void CSQCModel_InterpolateAnimation_2To4_PreNote(float sf);
+void CSQCModel_InterpolateAnimation_1To2_PreNote(float sf);
+// then update frame, frame1time (and possibly frame2, frame2time, lerpfrac)
+// if set_times is not set, caller is responsible for frame1time, frame2time, csqcmodel_lerpfractime!
+void CSQCModel_InterpolateAnimation_2To4_Note(float sf, float set_times);
+void CSQCModel_InterpolateAnimation_1To2_Note(float sf, float set_times);
+// to retrieve animation state, call this
+void CSQCModel_InterpolateAnimation_2To4_Do();
+void CSQCModel_InterpolateAnimation_1To2_Do();
+// will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs
index 4aa8fdf92cdb89886784437e1ba471ca5b8b0d49..ac9373989cf1121e92747bfa54413ff8afe2e4a1 100644 (file)
 var float autocvar_cl_movement_errorcompensation = 0;
 
 // engine stuff
-.float pmove_flags;
-float pmove_onground; // weird engine flag we shouldn't really use but have to for now
-#define PMF_JUMP_HELD 1
-#define PMF_DUCKED 4
-#define PMF_ONGROUND 8
 #define REFDEFFLAG_TELEPORTED 1
 #define REFDEFFLAG_JUMPING 2
+float pmove_onground; // weird engine flag we shouldn't really use but have to for now
 
 vector csqcplayer_origin, csqcplayer_velocity;
 float csqcplayer_sequence, player_pmflags;
index 881ad3b329bc30100cc677b0214a72dfeb9454ec..1cb42f2a5ce8383b6345546a038766ebcee7a6e8 100644 (file)
@@ -26,6 +26,12 @@ float csqcplayer_status;
 #define CSQCPLAYERSTATUS_FROMSERVER 1
 #define CSQCPLAYERSTATUS_PREDICTED 2
 
+// only ever READ these!
+.float pmove_flags;
+#define PMF_JUMP_HELD 1
+#define PMF_DUCKED 4
+#define PMF_ONGROUND 8
+
 void CSQCPlayer_SetCamera();
 float CSQCPlayer_PreUpdate();
 float CSQCPlayer_PostUpdate();
index 587645cd4fc27b81e79e5423a6f8a765962954c3..f6044cb283f3cf3b9b87959121df6c9abfff81b2 100644 (file)
@@ -51,13 +51,13 @@ IN THE SOFTWARE.\
 .float lerpfrac;
 
 #define CSQCMODEL_PROPERTY_FRAME 32768
-#define CSQCMODEL_PROPERTY_FRAME2 16384
-#define CSQCMODEL_PROPERTY_LERPFRAC 8192
-#define CSQCMODEL_PROPERTY_TELEPORTED 4096 // the "teleport bit" cancelling interpolation
-#define CSQCMODEL_PROPERTY_MODELINDEX 2048
-#define CSQCMODEL_PROPERTY_ORIGIN 1024
-#define CSQCMODEL_PROPERTY_YAW 512
-#define CSQCMODEL_PROPERTY_PITCHROLL 256
+#define CSQCMODEL_PROPERTY_TELEPORTED 16384 // the "teleport bit" cancelling interpolation
+#define CSQCMODEL_PROPERTY_MODELINDEX 8192
+#define CSQCMODEL_PROPERTY_ORIGIN 4096
+#define CSQCMODEL_PROPERTY_YAW 2048
+#define CSQCMODEL_PROPERTY_PITCHROLL 1024
+#define CSQCMODEL_PROPERTY_FRAME2 512
+#define CSQCMODEL_PROPERTY_LERPFRAC 256
 
 #define ALLPROPERTIES_COMMON \
        CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_FRAME, float, ReadByte, WriteByte, frame) \
index 16a7193c13710903da8cd9ed2db5143802504508..d682e78f89c54f97d58afec8c44afd55df8de662 100644 (file)
@@ -1190,7 +1190,6 @@ float autocvar_sv_maxairspeed;
 float autocvar_sv_maxairstrafespeed;
 float autocvar_sv_maxspeed;
 string autocvar_sv_motd;
-float autocvar_sv_player_jumpanim_minfall;
 float autocvar_sv_precacheplayermodels;
 float autocvar_sv_precacheweapons;
 float autocvar_sv_q3acompat_machineshotgunswap;
index 101e35cae2718d4702f43301b7061a4dbd138ae8..7ccbacbf314fd191cde32f899bfa1c86e2f36593 100644 (file)
@@ -2796,7 +2796,7 @@ void PlayerPreThink (void)
                self.prevorigin = self.origin;
 
                if (!self.vehicle)
-               if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x && !self.freezetag_frozen) // prevent crouching if using melee attack
+               if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && !self.freezetag_frozen) // FIXME-CSAD: prevent crouching if using melee attack
                {
                        if (!self.crouch)
                        {
index 3e2268d89748d1eb193892407968c8ee721f66a5..95dba951df6efb1109ab3c1f29d810c8b348a099 100644 (file)
@@ -169,10 +169,7 @@ void PlayerJump (void)
        self.flags &~= FL_ONGROUND;
        self.flags &~= FL_JUMPRELEASED;
 
-       if (self.crouch)
-               setanim(self, self.anim_duckjump, FALSE, TRUE, TRUE);
-       else if (self.animstate_startframe != self.anim_melee_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // jump animation shouldn't override melee until we have animation blending (or until the anim finished, 21/20 = numframes/fps)
-               setanim(self, self.anim_jump, FALSE, TRUE, TRUE);
+       animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
 
        if(g_jump_grunt)
                PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
index dcf1ab76842afdd5a8885d68d4aacc16811c4627..876608745dce17b1dea69d02373d6feca26b14a2 100644 (file)
@@ -161,16 +161,20 @@ void CopyBody(float keepvelocity)
        self.effects = oldself.effects;
        self.glowmod = oldself.glowmod;
        self.event_damage = oldself.event_damage;
-       self.animstate_startframe = oldself.animstate_startframe;
-       self.animstate_numframes = oldself.animstate_numframes;
-       self.animstate_framerate = oldself.animstate_framerate;
-       self.animstate_starttime = oldself.animstate_starttime;
-       self.animstate_endtime = oldself.animstate_endtime;
-       self.animstate_override = oldself.animstate_override;
-       self.animstate_looping = oldself.animstate_looping;
+       self.anim_state = oldself.anim_state;
+       self.anim_time = oldself.anim_time;
+       self.anim_lower_action = oldself.anim_lower_action;
+       self.anim_lower_time = oldself.anim_lower_time;
+       self.anim_upper_action = oldself.anim_upper_action;
+       self.anim_upper_time = oldself.anim_upper_time;
+       self.anim_implicit_state = oldself.anim_implicit_state;
+       self.anim_implicit_time = oldself.anim_implicit_time;
+       self.anim_lower_implicit_action = oldself.anim_lower_implicit_action;
+       self.anim_lower_implicit_time = oldself.anim_lower_implicit_time;
+       self.anim_upper_implicit_action = oldself.anim_upper_implicit_action;
+       self.anim_upper_implicit_time = oldself.anim_upper_implicit_time;
        self.dphitcontentsmask = oldself.dphitcontentsmask;
        self.death_time = oldself.death_time;
-       self.frame = oldself.frame;
        self.pain_finished = oldself.pain_finished;
        self.health = oldself.health;
        self.armorvalue = oldself.armorvalue;
@@ -208,6 +212,8 @@ void CopyBody(float keepvelocity)
        self.CopyBody_think = oldself.think;
        self.nextthink = time;
        self.think = CopyBody_Think;
+       // "bake" the current animation frame for clones (they don't get clientside animation)
+       animdecide_setframes(self, FALSE, frame, frame1time, frame2, frame2time);
 
        self = oldself;
 }
@@ -225,132 +231,38 @@ float player_getspecies()
 
 void player_setupanimsformodel()
 {
-       // defaults for legacy .zym models without animinfo files
-       self.anim_die1 = animfixfps(self, '0 1 0.5'); // 2 seconds
-       self.anim_die2 = animfixfps(self, '1 1 0.5'); // 2 seconds
-       self.anim_draw = animfixfps(self, '2 1 3');
-       // self.anim_duck = '3 1 100'; // This anim is broken, use slot 3 as a new free slot in the future ;)
-       self.anim_duckwalk = animfixfps(self, '4 1 1');
-       self.anim_duckjump = '5 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_duckidle = animfixfps(self, '6 1 1');
-       self.anim_idle = animfixfps(self, '7 1 1');
-       self.anim_jump = '8 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_pain1 = animfixfps(self, '9 1 2'); // 0.5 seconds
-       self.anim_pain2 = animfixfps(self, '10 1 2'); // 0.5 seconds
-       self.anim_shoot = animfixfps(self, '11 1 5'); // analyze models and set framerate
-       self.anim_taunt = animfixfps(self, '12 1 0.33');
-       self.anim_run = animfixfps(self, '13 1 1');
-       self.anim_runbackwards = animfixfps(self, '14 1 1');
-       self.anim_strafeleft = animfixfps(self, '15 1 1');
-       self.anim_straferight = animfixfps(self, '16 1 1');
-       //self.anim_dead1 = animfixfps(self, '17 1 1');
-       //self.anim_dead2 = animfixfps(self, '18 1 1');
-       self.anim_forwardright = animfixfps(self, '19 1 1');
-       self.anim_forwardleft = animfixfps(self, '20 1 1');
-       self.anim_backright = animfixfps(self, '21 1 1');
-       self.anim_backleft  = animfixfps(self, '22 1 1');
-       self.anim_melee = animfixfps(self, '23 1 1');
-       self.anim_duckwalkbackwards = animfixfps(self, '24 1 1');
-       self.anim_duckwalkstrafeleft = animfixfps(self, '25 1 1');
-       self.anim_duckwalkstraferight = animfixfps(self, '26 1 1');
-       self.anim_duckwalkforwardright = animfixfps(self, '27 1 1');
-       self.anim_duckwalkforwardleft = animfixfps(self, '28 1 1');
-       self.anim_duckwalkbackright = animfixfps(self, '29 1 1');
-       self.anim_duckwalkbackleft  = animfixfps(self, '30 1 1');
-       // TODO introspect models for finding right "fps" value (1/duration)
-       // reset animstate now
-       setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
+       // load animation info
+       animdecide_init(self);
+       animdecide_setstate(self, 0, FALSE);
 }
 
 void player_anim (void)
 {
-       updateanim(self);
-       if (self.weaponentity)
-               updateanim(self.weaponentity);
-
-       if (self.deadflag != DEAD_NO)
-               return;
-
-       if (!self.animstate_override)
-       {
-               if (self.freezetag_frozen)
-                       setanim(self, self.anim_idle, TRUE, FALSE, FALSE);
-               else if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
-               {
-                       if (self.crouch)
-                       {
-                               if (self.animstate_startframe != self.anim_duckjump_x) // don't perform another trace if already playing the crouch jump anim
-                               {
-                                       traceline(self.origin + '0 0 1' * PL_CROUCH_MIN_z, self.origin + '0 0 1' * (PL_CROUCH_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
-                                       if(!trace_startsolid && trace_fraction == 1 || !(self.animstate_startframe == self.anim_duckwalk_x || self.animstate_startframe == self.anim_duckidle_x)) // don't get stuck on non-crouch anims
-                                       {
-                                               setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
-                                               self.restart_jump = FALSE;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                if (self.animstate_startframe != self.anim_jump_x) // don't perform another trace if already playing the jump anim
-                {
-                    traceline(self.origin + '0 0 1' * PL_MIN_z, self.origin + '0 0 1' * (PL_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
-                    if(!trace_startsolid && trace_fraction == 1 || self.animstate_startframe == self.anim_idle_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // don't get stuck on idle animation in midair, nor melee after it finished
-                    {
-                        setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
-                        self.restart_jump = FALSE;
-                    }
-                }
-                       }
-               }
-               else if (self.crouch)
-               {
-                       if (self.movement_x > 0 && self.movement_y == 0)
-                               setanim(self, self.anim_duckwalk, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y == 0)
-                               setanim(self, self.anim_duckwalkbackwards, TRUE, FALSE, FALSE);
-                       else if (self.movement_x == 0 && self.movement_y > 0)
-                               setanim(self, self.anim_duckwalkstraferight, TRUE, FALSE, FALSE);
-                       else if (self.movement_x == 0 && self.movement_y < 0)
-                               setanim(self, self.anim_duckwalkstrafeleft, TRUE, FALSE, FALSE);
-                       else if (self.movement_x > 0 && self.movement_y > 0)
-                               setanim(self, self.anim_duckwalkforwardright, TRUE, FALSE, FALSE);
-                       else if (self.movement_x > 0 && self.movement_y < 0)
-                               setanim(self, self.anim_duckwalkforwardleft, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y > 0)
-                               setanim(self, self.anim_duckwalkbackright, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y < 0)
-                               setanim(self, self.anim_duckwalkbackleft, TRUE, FALSE, FALSE);
-                       else
-                               setanim(self, self.anim_duckidle, TRUE, FALSE, FALSE);
-               }
-               else if ((self.movement_x * self.movement_x + self.movement_y * self.movement_y) > 20)
-               {
-                       if (self.movement_x > 0 && self.movement_y == 0)
-                               setanim(self, self.anim_run, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y == 0)
-                               setanim(self, self.anim_runbackwards, TRUE, FALSE, FALSE);
-                       else if (self.movement_x == 0 && self.movement_y > 0)
-                               setanim(self, self.anim_straferight, TRUE, FALSE, FALSE);
-                       else if (self.movement_x == 0 && self.movement_y < 0)
-                               setanim(self, self.anim_strafeleft, TRUE, FALSE, FALSE);
-                       else if (self.movement_x > 0 && self.movement_y > 0)
-                               setanim(self, self.anim_forwardright, TRUE, FALSE, FALSE);
-                       else if (self.movement_x > 0 && self.movement_y < 0)
-                               setanim(self, self.anim_forwardleft, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y > 0)
-                               setanim(self, self.anim_backright, TRUE, FALSE, FALSE);
-                       else if (self.movement_x < 0 && self.movement_y < 0)
-                               setanim(self, self.anim_backleft, TRUE, FALSE, FALSE);
-                       else
-                               setanim(self, self.anim_run, TRUE, FALSE, FALSE);
-               }
+       float deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
+       if(self.deadflag && !deadbits)
+               if(random() < 0.5)
+                       deadbits = ANIMSTATE_DEAD1;
                else
-                       setanim(self, self.anim_idle, TRUE, FALSE, FALSE);
-       }
+                       deadbits = ANIMSTATE_DEAD2;
+       float animbits = deadbits;
+       if(self.freezetag_frozen)
+               animbits |= ANIMSTATE_FROZEN;
+       if(self.crouch)
+               animbits |= ANIMSTATE_DUCK;
+       animdecide_setstate(self, animbits, FALSE);
+       animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
+
+#ifndef NO_LEGACY_NETWORKING
+       if(!self.iscsqcmodel)
+               animdecide_setframes(self, FALSE, frame, frame1time, frame2, frame2time);
+#endif
 
        if (self.weaponentity)
-       if (!self.weaponentity.animstate_override)
-               setanim(self.weaponentity, self.weaponentity.anim_idle, TRUE, FALSE, FALSE);
+       {
+               updateanim(self.weaponentity);
+               if (!self.weaponentity.animstate_override)
+                       setanim(self.weaponentity, self.weaponentity.anim_idle, TRUE, FALSE, FALSE);
+       }
 }
 
 void SpawnThrownWeapon (vector org, float w)
@@ -566,9 +478,9 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                                                if (!self.animstate_override)
                                                {
                                                        if (random() > 0.5)
-                                                               setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
+                                                               animdecide_setaction(self, ANIMACTION_PAIN1, TRUE);
                                                        else
-                                                               setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+                                                               animdecide_setaction(self, ANIMACTION_PAIN2, TRUE);
                                                }
                                        }
 
@@ -774,9 +686,9 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        self.respawn_countdown = -1; // do not count down
                self.death_time = time;
                if (random() < 0.5)
-                       setanim(self, self.anim_die1, FALSE, TRUE, TRUE);
+                       animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, TRUE);
                else
-                       setanim(self, self.anim_die2, FALSE, TRUE, TRUE);
+                       animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD2, TRUE);
                if (self.maxs_z > 5)
                {
                        self.maxs_z = 5;
@@ -1255,7 +1167,7 @@ void FakeGlobalSound(string sample, float chan, float voicetype)
                case VOICETYPE_TAUNT:
                        if(self.classname == "player")
                                if(self.deadflag == DEAD_NO)
-                                       setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
+                                       animdecide_setaction(self, ANIMACTION_TAUNT, TRUE);
                        if(!sv_taunt)
                                break;
                        if(sv_gentle)
@@ -1352,7 +1264,7 @@ void GlobalSound(string sample, float chan, float voicetype)
                case VOICETYPE_TAUNT:
                        if(self.classname == "player")
                                if(self.deadflag == DEAD_NO)
-                                       setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
+                                       animdecide_setaction(self, ANIMACTION_TAUNT, TRUE);
                        if(!sv_taunt)
                                break;
                        if(sv_gentle)
index 402b692af1230fba959a89c1fce3aef5a999ab13..690d9b5511d6d8291e80f44269f17be4efa6561e 100644 (file)
@@ -343,10 +343,10 @@ void CL_WeaponEntity_SetModel(string name)
 
                setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
                // preset some defaults that work great for renamed zym files (which don't need an animinfo)
-               self.anim_fire1  = animfixfps(self, '0 1 0.01');
-               self.anim_fire2  = animfixfps(self, '1 1 0.01');
-               self.anim_idle   = animfixfps(self, '2 1 0.01');
-               self.anim_reload = animfixfps(self, '3 1 0.01');
+               self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
+               self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
+               self.anim_idle   = animfixfps(self, '2 1 0.01', '0 0 0');
+               self.anim_reload = animfixfps(self, '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
@@ -1046,25 +1046,17 @@ void weapon_thinkf(float fr, float t, void() func)
        self.weapon_think = func;
        //dprint("next ", ftos(self.weapon_nextthink), "\n");
 
-       // The shoot animation looks TERRIBLE without animation blending! Yay for moonwalking while shooting!
-       //anim = self.anim_shoot;
-       if (restartanim)
-       if (t)
-       if (!self.crouch) // shoot anim stands up, this looks bad
+       if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
        {
-               vector anim;
                if(self.weapon == WEP_SHOTGUN && self.BUTTON_ATCK2)
-               {
-                       anim = self.anim_melee;
-                       anim_z = anim_y / (t + sys_frametime);
-                       setanim(self, anim, FALSE, TRUE, TRUE);
-               }
-               else if (self.animstate_startframe == self.anim_idle_x) // only allow shoot anim to override idle animation until we have animation blending
-               {
-                       anim = self.anim_shoot;
-                       anim_z = anim_y / (t + sys_frametime);
-                       setanim(self, anim, FALSE, TRUE, TRUE);
-               }
+                       animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
+               else
+                       animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
+       }
+       else
+       {
+               if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE)
+                       self.anim_upper_action = 0;
        }
 }
 
index d7772e5090d0fda499eb14eee8f3544b298bf007..8b6d65e1214d8e60cbd98578b4360ff7a5ec8437 100644 (file)
@@ -116,44 +116,6 @@ float maxclients;
 .float animstate_override;
 .float animstate_looping;
 
-// player animation data for this model
-// each vector is as follows:
-// _x = startframe
-// _y = numframes
-// _z = framerate
-.vector anim_die1; // player dies
-.vector anim_die2; // player dies differently
-.vector anim_draw; // player pulls out a weapon
-// .vector anim_duck; // player crouches (from idle to duckidle)
-.vector anim_duckwalk; // player walking while crouching
-.vector anim_duckjump; // player jumping from a crouch
-.vector anim_duckidle; // player idling while crouching
-.vector anim_idle; // player standing
-.vector anim_jump; // player jump
-.vector anim_pain1; // player flinches from pain
-.vector anim_pain2; // player flinches from pain, differently
-.vector anim_shoot; // player shoots
-.vector anim_taunt; // player taunts others (FIXME: no code references this)
-.vector anim_run; // player running forward
-.vector anim_runbackwards; // player running backward
-.vector anim_strafeleft; // player shuffling left quickly
-.vector anim_straferight; // player shuffling right quickly
-//.vector anim_dead1; // player dead (must be identical to last frame of die1)
-//.vector anim_dead2; // player dead (must be identical to last frame of die2)
-.vector anim_forwardright; // player running forward and right
-.vector anim_forwardleft; // player running forward and left
-.vector anim_backright; // player running backward and right
-.vector anim_backleft; // player running back and left
-.vector anim_melee; // player doing the melee action
-.vector anim_duck; // player doing the melee action
-.vector anim_duckwalkbackwards;
-.vector anim_duckwalkstrafeleft;
-.vector anim_duckwalkstraferight;
-.vector anim_duckwalkforwardright;
-.vector anim_duckwalkforwardleft;
-.vector anim_duckwalkbackright;
-.vector anim_duckwalkbackleft;
-
 // weapon animation vectors:
 .vector anim_fire1;
 .vector anim_fire2;
index b07cae2714e19770ca08d721d26fd0cec4885575..9f4c22a02697d9e72213b4fa0350a8c4b021bf91 100644 (file)
@@ -58,19 +58,6 @@ void updateanim(entity e)
        //print(ftos(time), " -> ", ftos(e.frame), "\n");
 }
 
-vector animfixfps(entity e, vector a)
-{
-       // multi-frame anim: keep as-is
-       if(a_y == 1)
-       {
-               float dur;
-               dur = frameduration(e.modelindex, a_x);
-               if(dur > 0)
-                       a_z = 1.0 / dur;
-       }
-       return a;
-}
-
 /*
 ==================
 SUB_Remove
index bdd39b1283f487fe0e74ab42135b6d66f4b180c7..969c980a298dd44d7f085672385b4cf28650b815 100644 (file)
@@ -117,7 +117,7 @@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
                if (autocvar_sv_dodging_sound == 1)
                        PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
 
-               setanim(self, self.anim_jump, TRUE, FALSE, TRUE);
+               animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
 
                self.dodging_single_action = 0;
        }
index 028519372f025ae24aa923d472ba0a6593de86b7..0ef33f85403daa2840106d6b6fb241e4dbf663ca 100644 (file)
@@ -22,6 +22,7 @@ sys-post.qh
 ../common/command/generic.qh
 ../common/command/shared_defs.qh
 ../common/net_notice.qh
+../common/animdecide.qh
 
 autocvars.qh
 constants.qh
@@ -229,6 +230,7 @@ mutators/mutator_superspec.qc
 ../warpzonelib/util_server.qc
 ../warpzonelib/server.qc
 
+../common/animdecide.qc
 ../common/util.qc
 
 ../common/if-this-file-errors-scroll-up-and-fix-the-warnings.fteqccfail
index 8923c19f18300e1f742acd0586e61539744c3c15..2458e262f27fae92a74d7ab48c1bced632ebcac4 100644 (file)
@@ -200,14 +200,8 @@ void trigger_push_touch()
                        else
                                other.lastteleporttime = time;
 
-                       if (!other.animstate_override)
                        if (other.deadflag == DEAD_NO)
-                       {
-                               if (other.crouch)
-                                       setanim(other, other.anim_duckjump, FALSE, TRUE, TRUE);
-                               else
-                                       setanim(other, other.anim_jump, FALSE, TRUE, TRUE);
-                       }
+                               animdecide_setaction(other, ANIMACTION_JUMP, TRUE);
                }
                else
                        other.jumppadcount = TRUE;
index 7a91cbd89cae366bfde23606899c9fd9807281a9..575ff677e2090caec7f14acb5477cacdc20d9a3d 100644 (file)
@@ -436,13 +436,8 @@ float w_electro(float req)
                {
                        if(autocvar_g_balance_electro_lightning)
                                if(self.BUTTON_ATCK_prev)
-                               {
-                                       // prolong the animtime while the gun is being fired
-                                       if(self.animstate_startframe == self.anim_shoot_x && self.animstate_numframes == self.anim_shoot_y)
-                                               weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_electro_primary_animtime, w_ready);
-                                       else
-                                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
-                               }
+                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
+
                        if (weapon_prepareattack(0, (autocvar_g_balance_electro_lightning ? 0 : autocvar_g_balance_electro_primary_refire)))
                        {
                                if(autocvar_g_balance_electro_lightning)