]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into terencehill/ca_arena_mutators
authorSamual Lenks <samual@xonotic.org>
Sun, 10 Feb 2013 01:01:32 +0000 (20:01 -0500)
committerSamual Lenks <samual@xonotic.org>
Sun, 10 Feb 2013 01:01:32 +0000 (20:01 -0500)
37 files changed:
defaultXonotic.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/hud.qc
qcsrc/client/hud.qh
qcsrc/client/hud_config.qc
qcsrc/client/miscfunctions.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/client/scoreboard.qc
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/csqcmodellib/interpolate.qc
qcsrc/csqcmodellib/interpolate.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
qcsrc/server/w_shotgun.qc

index fc847e3aa15f0e203006d4a7cca179929985f6ae..bf7d85eb7aae233e48a936267d4b2d46a34586a0 100644 (file)
@@ -253,7 +253,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
@@ -951,6 +950,7 @@ seta scoreboard_columns default
 seta scoreboard_border_thickness 1 "scoreboard border thickness"
 seta scoreboard_accuracy_border_thickness 1 "accuracy stats border thickness"
 seta scoreboard_accuracy_doublerows 0 "use two rows instead of one"
+seta scoreboard_accuracy_nocolors 0 "don't use colors displaying accuracy stats"
 seta scoreboard_accuracy 1 "show weapon accuracy stats panel on scoreboard; colors can be configured with accuracy_color* cvars"
 seta scoreboard_color_bg_r 0 "red color component of the scoreboard background"
 seta scoreboard_color_bg_g 0.4 "green color component of the scoreboard background"
index 57cf97c998889ce4576be0b839a5d8618e50da53..47d0e37d6a903f78268480795a1d1579d7fbe471 100644 (file)
@@ -207,6 +207,7 @@ void CSQC_Init(void)
 
        WarpZone_Init();
 
+       hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
        hud_configure_prev = -1;
        tab_panel = -1;
 
index 8206c1f3a7a541129bb8f8f42b58a3fc3a0e1659..dac205e723f0bd607b0313b495a1afedf52d4b11 100644 (file)
@@ -1505,25 +1505,8 @@ void CSQC_UpdateView(float w, float h)
 
 void CSQC_common_hud(void)
 {
-    // do some accuracy var caching
-    float i;
-    if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS))
-    {
-        if(autocvar_accuracy_color_levels != acc_color_levels)
-        {
-            if(acc_color_levels)
-                strunzone(acc_color_levels);
-            acc_color_levels = strzone(autocvar_accuracy_color_levels);
-            acc_levels = tokenize_console(acc_color_levels);
-            if (acc_levels > MAX_ACCURACY_LEVELS)
-                acc_levels = MAX_ACCURACY_LEVELS;
-
-            for (i = 0; i < acc_levels; ++i)
-                acc_lev[i] = stof(argv(i)) / 100.0;
-        }
-        // let know that acc_col[] needs to be loaded
-        acc_col[0] = '-1 0 0';
-    }
+       if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS))
+               Accuracy_LoadLevels();
 
     HUD_Main(); // always run these functions for alpha checks
     HUD_DrawScoreboard();
index 506764227e7a969b090fcd10f2ab0bb8f315e7b4..9fedeffa949e442dd56174a88b8f260a5224c39c 100644 (file)
@@ -358,6 +358,7 @@ float autocvar_r_fullbright;
 float autocvar_r_letterbox;
 float autocvar_scoreboard_accuracy;
 float autocvar_scoreboard_accuracy_doublerows;
+float autocvar_scoreboard_accuracy_nocolors;
 float autocvar_scoreboard_alpha_bg;
 var float autocvar_scoreboard_alpha_fg = 1.0;
 var float autocvar_scoreboard_alpha_name = 0.9;
@@ -416,4 +417,6 @@ string autocvar__cl_playermodel;
 float autocvar_cl_precacheplayermodels;
 float autocvar_cl_deathglow;
 float autocvar_developer_csqcentities;
+float autocvar__animblend;
+float autocvar__animblend_fixbone;
 float autocvar_g_jetpack_attenuation;
index ccc5dc35475dcda19612e5d228bad1c508059d94..1d0c36ae9c1236302d77cc354511a88deeb3d31b 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
@@ -566,7 +578,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;
@@ -585,7 +606,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);
+                       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_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);
+                       }
+                       CSQCModel_InterpolateAnimation_2To4_Do();
+                       if(doblend)
+                       {
+                               skeleton_from_frames(self);
+                       }
+                       else
+                       {
+                               free_skeleton_from_frames(self);
+                               // just in case, clear these (we're animating in frame and frame3)
+                               self.lerpfrac = 0;
+                               self.lerpfrac4 = 0;
+                       }
+               }
        }
 
        CSQCModel_AutoTagIndex_Apply();
@@ -599,7 +677,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();
        }
 }
@@ -613,7 +692,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();
 }
index 4b06cbf019cb219122b9bafe41a58690bfe7b714..c9625ffd4af54a7a3bc063de48f9f08130867220 100644 (file)
@@ -437,13 +437,13 @@ void HUD_Weapons(void)
        // declarations
        WEPSET_DECLARE_A(weapons_stat);
        WEPSET_COPY_AS(weapons_stat);
-       float i, f, a, j, factor;
-       float screen_ar, center_x, center_y;
+       float i, f, a;
+       float screen_ar, center_x = 0, center_y;
        float weapon_count, weapon_id;
        float row, column, rows, columns;
        float aspect = autocvar_hud_panel_weapons_aspect;
 
-       float show_accuracy = false, panel_weapon_accuracy;
+       float panel_weapon_accuracy;
 
        float timeout = autocvar_hud_panel_weapons_timeout;
        float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0);
@@ -468,7 +468,8 @@ void HUD_Weapons(void)
        {
                if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
                        return;
-               else if(timeout && time >= weapontime + timeout + timeout_effect_length && ((autocvar_hud_panel_weapons_timeout_effect != 1) && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
+               if(timeout && time >= weapontime + timeout + timeout_effect_length)
+               if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
                {
                        weaponprevtime = time;
                        return;
@@ -598,6 +599,8 @@ void HUD_Weapons(void)
                                        else //top
                                                panel_pos_y -= f * (panel_pos_y + panel_size_y);
                                }
+                               if(f == 1)
+                                       center_x = -1; // mark the panel as off screen
                        }
                        weaponprevtime = time - (1 - f) * timein_effect_length;
                }
@@ -645,6 +648,10 @@ void HUD_Weapons(void)
 
        // draw the background, then change the virtual size of it to better fit other items inside
        HUD_Panel_DrawBg(1);
+
+       if(center_x == -1)
+               return;
+
        if(panel_bg_padding)
        {
                panel_pos += '1 1 0' * panel_bg_padding;
@@ -676,14 +683,8 @@ void HUD_Weapons(void)
                        baroffset_y = (weapon_size_y - barsize_y) / 2;
                }
        }
-
-       if(autocvar_hud_panel_weapons_accuracy && acc_levels)
-       {
-               show_accuracy = true;
-               if (acc_col[0] == '-1 0 0')
-                       for (i = 0; i < acc_levels; ++i)
-                               acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
-       }
+       if(autocvar_hud_panel_weapons_accuracy)
+               Accuracy_LoadColors();
 
        row = column = 0;
        for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
@@ -713,21 +714,12 @@ void HUD_Weapons(void)
                        drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 
                // draw the weapon accuracy
-               if(show_accuracy)
+               if(autocvar_hud_panel_weapons_accuracy)
                {
                        panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST];
                        if(panel_weapon_accuracy >= 0)
                        {
-                               // find the max level lower than weapon_accuracy
-                               j = acc_levels-1;
-                               while ( j && panel_weapon_accuracy < acc_lev[j] )
-                                       --j;
-
-                               // inject color j+1 in color j, how much depending on how much weapon_accuracy is higher than level j
-                               factor = (panel_weapon_accuracy - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
-                               color = acc_col[j];
-                               color = color + factor * (acc_col[j+1] - color);
-
+                               color = Accuracy_GetColor(panel_weapon_accuracy);
                                drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL);
                        }
                }
@@ -5150,11 +5142,14 @@ void HUD_Main (void)
                return;
 
        // Drawing stuff
-       if (hud_skin_path != autocvar_hud_skin)
+       if (hud_skin_prev != autocvar_hud_skin)
        {
                if (hud_skin_path)
                        strunzone(hud_skin_path);
                hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
+               if (hud_skin_prev)
+                       strunzone(hud_skin_prev);
+               hud_skin_prev = strzone(autocvar_hud_skin);
        }
 
        // HUD configure visible grid
@@ -5285,7 +5280,7 @@ void HUD_Main (void)
                        HUD_Panel_UpdatePosSizeForId(highlightedPanel);
                        HUD_Panel_HlBorder(panel_bg_border + 1.5 * hlBorderSize, '0 0.5 1', 0.25 * (1 - autocvar__menu_alpha));
                }
-               if (!hud_configure_prev)
+               if(!hud_configure_prev || hud_configure_prev == -1)
                {
                        if(autocvar_hud_cursormode) { setcursormode(1); }
                        hudShiftState = 0;
index 16ccd0c79c0d33aea202eaa3d7c2aab7b4b2721f..0da6ac5d087e5875bc3cc9efa5a66336593b35f6 100644 (file)
@@ -15,12 +15,6 @@ const float BORDER_MULTIPLIER = 0.25;
 float scoreboard_bottom;
 float weapon_accuracy[WEP_MAXCOUNT];
 
-#define MAX_ACCURACY_LEVELS 10
-float acc_lev[MAX_ACCURACY_LEVELS];
-vector acc_col[MAX_ACCURACY_LEVELS];
-float acc_levels;
-string acc_color_levels;
-
 float complain_weapon;
 string complain_weapon_name;
 float complain_weapon_type;
@@ -52,6 +46,7 @@ float menu_enabled_time;
 float hud_fade_alpha;
 
 string hud_skin_path;
+string hud_skin_prev;
 
 var vector progressbar_color;
 
index d21234ccecaf6bf868dd0ba786f0b2497dfe6b68..c831c17f58b3d0e7285fdb07998f5d3806bbc3d6 100644 (file)
@@ -144,8 +144,8 @@ void HUD_Panel_ExportCfg(string cfgname)
                                        HUD_Write_PanelCvar_q("_freezetag_layout");
                                        break;
                                case HUD_PANEL_PRESSEDKEYS:
-                                       HUD_Write_PanelCvar_q("_attack");
                                        HUD_Write_PanelCvar_q("_aspect");
+                                       HUD_Write_PanelCvar_q("_attack");
                                        break;
                                case HUD_PANEL_ENGINEINFO:
                                        HUD_Write_PanelCvar_q("_framecounter_time");
index 6cceb42bacda13964394e996318c8f37f0e8fb3f..9e5505b0a5bb5c47c0b1af68e5ff225623332985 100644 (file)
@@ -635,3 +635,61 @@ void draw_endBoldFont()
 {
        drawfont = FONT_USER+1;
 }
+
+
+#define MAX_ACCURACY_LEVELS 10
+float acc_lev[MAX_ACCURACY_LEVELS];
+vector acc_col[MAX_ACCURACY_LEVELS];
+float acc_col_loadtime;
+float acc_levels;
+string acc_color_levels;
+void Accuracy_LoadLevels()
+{
+       float i;
+       if(autocvar_accuracy_color_levels != acc_color_levels)
+       {
+               if(acc_color_levels)
+                       strunzone(acc_color_levels);
+               acc_color_levels = strzone(autocvar_accuracy_color_levels);
+               acc_levels = tokenize_console(acc_color_levels);
+               if(acc_levels > MAX_ACCURACY_LEVELS)
+                       acc_levels = MAX_ACCURACY_LEVELS;
+               if(acc_levels < 2)
+                       print("Warning: accuracy_color_levels must contain at least 2 values\n");
+
+               for(i = 0; i < acc_levels; ++i)
+                       acc_lev[i] = stof(argv(i)) / 100.0;
+       }
+}
+
+void Accuracy_LoadColors()
+{
+       float i;
+       if(time > acc_col_loadtime)
+       if(acc_levels >= 2)
+       {
+               for(i = 0; i < acc_levels; ++i)
+                       acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
+               acc_col_loadtime = time + 2;
+       }
+}
+
+vector Accuracy_GetColor(float accuracy)
+{
+       float j, factor;
+       vector color;
+       if(acc_levels < 2)
+               return '0 0 0'; // return black, can't determine the right color
+
+       // find the max level lower than acc
+       j = acc_levels-1;
+       while(j && accuracy < acc_lev[j])
+               --j;
+
+       // inject color j+1 in color j, how much depending on how much accuracy is higher than level j
+       factor = (accuracy - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
+       color = acc_col[j];
+       color = color + factor * (acc_col[j+1] - color);
+       return color;
+}
+
diff --git a/qcsrc/client/player_skeleton.qc b/qcsrc/client/player_skeleton.qc
new file mode 100644 (file)
index 0000000..cbed797
--- /dev/null
@@ -0,0 +1,171 @@
+.float skeleton_modelindex;
+#define BONETYPE_LOWER 0
+#define BONETYPE_UPPER 1
+#define MAX_BONES 128
+.float skeleton_bonetype[MAX_BONES];
+.float skeleton_fixrotatebone;
+.float skeleton_fixtargetbone;
+.float skeleton_bone;
+.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;
+       e.skeleton_fixrotatebone = 0;
+       e.skeleton_fixtargetbone = 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")
+               {
+                       e.skeleton_fixrotatebone = i;
+                       t = BONETYPE_UPPER;
+               }
+               if(nm == "weapon" || nm == "tag_weapon" || nm == "bip01 r hand")
+                       if(t == BONETYPE_UPPER)
+                               e.skeleton_fixtargetbone = i;
+               if(nm == "upperarm_R")
+                       e.skeleton_aimbone = i;
+               e.(skeleton_bonetype[i-1]) = t;
+       }
+}
+
+void skel_set_bone_lerp(float skel, float bone, vector org, float strength)
+{
+       if(strength >= 1)
+               return skel_set_bone(skel, bone, org);
+
+       vector fo = v_forward;
+       vector ri = v_right;
+       vector up = v_up;
+       vector oldorg = skel_get_bonerel(skel, bone);
+
+       org = org * strength + oldorg * (1 - strength);
+       v_forward = fo * strength + v_forward * (1 - strength);
+       v_right = ri * strength + v_right * (1 - strength);
+       v_up = up * strength + v_up * (1 - strength);
+       return skel_set_bone(skel, bone, org);
+}
+
+void skeleton_fixbone(entity e, float strength, float yawonly)
+{
+       if(!e.skeleton_fixrotatebone)
+               return;
+       if(strength <= 0)
+               return;
+       // model:
+       // T  = M_before_fixrotate * M_fixrotate  * M_after_fixrotate
+       // T' = M_before_fixrotate^-1 * M_fixrotate' * M_after_fixrotate
+       // M_fixrotate' = M_before_fixrotate^-1 * T' * M_after_fixrotate^-1
+       float s = e.skeletonindex;
+
+       skel_get_boneabs(s, skel_get_boneparent(s, e.skeleton_fixrotatebone));
+       vector M_before_fixrotate = fixedvectoangles2(v_forward, v_up);
+       skel_get_boneabs(s, e.skeleton_fixrotatebone);
+       vector M_including_fixrotate = fixedvectoangles2(v_forward, v_up);
+       skel_get_boneabs(s, e.skeleton_fixtargetbone);
+       vector T = fixedvectoangles2(v_forward, v_up);
+       vector M_after_fixrotate = AnglesTransform_LeftDivide(M_including_fixrotate, T);
+       vector T_ = T;
+       if(yawonly)
+               T__y = 0; // undo yaw
+       else
+               T_ = '0 0 0'; // undo all
+       vector M_fixrotate_ = AnglesTransform_LeftDivide(M_before_fixrotate, AnglesTransform_RightDivide(T_, M_after_fixrotate));
+       vector org = skel_get_bonerel(s, e.skeleton_fixrotatebone);
+       fixedmakevectors(M_fixrotate_);
+       skel_set_bone_lerp(s, e.skeleton_fixrotatebone, org, strength);
+}
+
+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 = savelerpfrac3;
+       e.lerpfrac4 = savelerpfrac4;
+
+       if(autocvar__animblend_fixbone)
+       {
+               float l4 = e.lerpfrac4;
+               float l3 = e.lerpfrac3;
+               float l2 = e.lerpfrac;
+               float l1 = 1 - l2 - l3 - l4;
+
+               // how much of upper body animates same way as lower body?
+               float equalamount =
+                       (e.frame == e.frame2) * (l1 * l2) +
+                       (e.frame == e.frame4) * (l1 * l4) +
+                       (e.frame3 == e.frame2) * (l3 * l2) +
+                       (e.frame3 == e.frame4) * (l3 * l4);
+               float maxequalamount = (l1 + l3) * (l2 + l4);
+
+               // now how strong is the lerp?
+               float lerpstrength = 1 - equalamount / maxequalamount;
+
+               // FIX IT
+               skeleton_fixbone(e, lerpstrength, autocvar__animblend_fixbone >= 2);
+       }
+}
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
index af982ad46547bd4d2435e56f40e7e929b6d38723..1aa1b6a435ac5d015e5c27f0d4cd24cbbfdc61bf 100644 (file)
@@ -998,12 +998,10 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
                g_minstagib = 1; // TODO: real detection for minstagib?
 
        float weapon_stats;
-
-       if (!acc_levels)
+       if(autocvar_scoreboard_accuracy_nocolors)
                rgb = '1 1 1';
-       else if (acc_col[0] == '-1 0 0')
-               for (i = 0; i < acc_levels; ++i)
-                       acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
+       else
+               Accuracy_LoadColors();
 
        for(i = WEP_FIRST; i <= WEP_LAST; ++i)
        {
@@ -1033,20 +1031,8 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
                        float padding;
                        padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
 
-                       if (acc_levels)
-                       {
-                               // find the max level lower than weapon_stats
-                               float j;
-                               j = acc_levels-1;
-                               while ( j && weapon_stats < acc_lev[j] )
-                                       --j;
-
-                               // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
-                               float factor;
-                               factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
-                               rgb = acc_col[j];
-                               rgb = rgb + factor * (acc_col[j+1] - rgb);
-                       }
+                       if(!autocvar_scoreboard_accuracy_nocolors)
+                               rgb = Accuracy_GetColor(weapon_stats);
 
                        drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
                }
@@ -1058,7 +1044,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
        }
 
        if(weapons_with_stats)
-               average_accuracy = floor(average_accuracy / weapons_with_stats);
+               average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
 
        if(rows == 2)
                pos_x -= weapon_width / 2;
diff --git a/qcsrc/common/animdecide.qc b/qcsrc/common/animdecide.qc
new file mode 100644 (file)
index 0000000..32ffef2
--- /dev/null
@@ -0,0 +1,333 @@
+// 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;
+.float animdecide_modelindex;
+
+void animdecide_init(entity e)
+{
+       if(e.modelindex == e.animdecide_modelindex)
+               return;
+       e.animdecide_modelindex = e.modelindex;
+
+       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 1
+#define ANIMPRIO_CROUCH 2
+#define ANIMPRIO_DEAD 3
+
+vector animdecide_getupperanim(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_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_CROUCH); // 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_CROUCH);
+                       case ANIMIMPLICITSTATE_BACKWARDS:
+                               return vec3(e.anim_duckwalkbackwards_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkstraferight_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkstrafeleft_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkforwardright_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_FORWARD | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkforwardleft_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_RIGHT:
+                               return vec3(e.anim_duckwalkbackright_x, t, ANIMPRIO_CROUCH);
+                       case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT:
+                               return vec3(e.anim_duckwalkbackleft_x, t, ANIMPRIO_CROUCH);
+                       default:
+                               return vec3(e.anim_duckidle_x, t, ANIMPRIO_CROUCH);
+               }
+       }
+       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
+#ifdef GMQCC
+       return vec3(e.anim_idle_x, t, ANIMPRIO_IDLE);
+#endif
+}
+
+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;
+       v_z = 0;
+
+       // 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(support_blending)
+       {
+               if(upper_z && !lower_z)
+                       lower = upper;
+               else if(lower_z && !upper_z)
+                       upper = lower;
+               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(upper_z > lower_z)
+                       lower = upper;
+               else if(lower_z > upper_z)
+                       upper = lower;
+               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..a0076825be402ec5e837a564686def6fe3deef0d 100644 (file)
@@ -1,9 +1,12 @@
 // 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
 
+// server decides crouching, this lags, but so be it
+#define CSQCMODEL_SERVERSIDE_CROUCH
+
 // a hack for Xonotic
 #ifdef CSQC
 # define TAG_ENTITY_NAME tag_networkentity
                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 +50,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 4d9ce517d1abcb0fccb0c57e14bba2e9d4314e6b..404bd7e5a486012deec784cb14be934f0c5cd85c 100644 (file)
@@ -2479,3 +2479,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 10e680e562615a818f62f7297294681158ed03aa..4e553e4ba5d1611fc73fa83289c45b0d0de27b57 100644 (file)
@@ -364,3 +364,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 8ccdf814e7362296b49209c647ed7ab0a0b6bf32..85e2f15ee99ca82f51732b0ff8ca4faaaf09d61b 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
 }
 
@@ -185,6 +213,7 @@ void CSQCModel_Read(float isnew)
 
        self.classname = "csqcmodel";
        self.iflags |= IFLAG_ANGLES; // interpolate angles too
+       self.iflags |= IFLAG_VELOCITY | IFLAG_AUTOVELOCITY; // let's calculate velocity automatically
 
        { CSQCMODEL_HOOK_PREUPDATE }
 
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..f77f9512623e099cea3f7f4fecff98e762e5019c 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;
@@ -244,6 +240,14 @@ void CSQCPlayer_SetCamera()
                        }
                        CSQCPlayer_PredictTo(clientcommandframe + 1, TRUE);
 
+#ifdef CSQCMODEL_SERVERSIDE_CROUCH
+                       // get crouch state from the server (LAG)
+                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
+                               self.pmove_flags &~= PMF_DUCKED;
+                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
+                               self.pmove_flags |= PMF_DUCKED;
+#endif
+
                        CSQCPlayer_SetMinsMaxs();
 
                        self.angles_y = input_angles_y;
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 55f134126fd7472eb1a33b1ba9a8a7d00d0b979a..2ed78fbd8c3b92247ecae15a36525358e9a2f7f7 100644 (file)
@@ -50,6 +50,10 @@ void InterpolateOrigin_Note()
                if(self.iorigin2 != self.iorigin1)
                        self.angles = vectoangles(self.iorigin2 - self.iorigin1);
 
+       if(self.iflags & IFLAG_AUTOVELOCITY)
+               if(self.itime2 != self.itime1)
+                       self.velocity = (self.iorigin2 - self.iorigin1) * (1.0 / (self.itime2 - self.itime1));
+
        if(self.iflags & IFLAG_ANGLES)
        {
                fixedmakevectors(self.angles);
index 8254fae8cfc6f44649b7dd158035d7749bec79aa..c69c90dde9f53f9dcd1eea3587c463a59baf8eab 100644 (file)
@@ -27,6 +27,7 @@
 #define IFLAG_VALID 8
 #define IFLAG_PREVALID 16
 #define IFLAG_TELEPORTED 32
+#define IFLAG_AUTOVELOCITY 64
 #define IFLAG_INTERNALMASK (IFLAG_VALID | IFLAG_PREVALID)
 
 // call this BEFORE reading an entity update
index c40419b57a724d2b1dc0d68b895a3f384dabc254..446696e9925a0cfad798089d4b3c780007874465 100644 (file)
@@ -1194,7 +1194,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 4e604cd04aa79b289c29c4f9165197b9dce55f82..cf59adeed6dd31ad37347e494ba2395fb1813136 100644 (file)
@@ -2732,8 +2732,19 @@ 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
+               float do_crouch = self.BUTTON_CROUCH;
+               if(self.hook.state)
+                       do_crouch = 0;
+               if(self.health <= g_bloodloss)
+                       do_crouch = 1;
+               if(self.vehicle)
+                       do_crouch = 0;
+               if(self.freezetag_frozen)
+                       do_crouch = 0;
+               if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
+                       do_crouch = 0;
+
+               if (do_crouch)
                {
                        if (!self.crouch)
                        {
index 4e5f007e3f17323ffca29b19b1bc8acb6bd309f0..50401604ef905313d521380d6995b0cf202d6bec 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 e564c3ac4f9e3f590be53b2d995d1dba807255d7..baca8e99358fc86172a2d10233a13c5181b3e0b4 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)
@@ -562,9 +474,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);
                                                }
                                        }
 
@@ -760,9 +672,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;
@@ -1241,7 +1153,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)
@@ -1338,7 +1250,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 8f510edff00bf700610370218907ffba6922f7ab..a75e2be5af16fee8e3758a60e0590a28f020ad0f 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
@@ -1047,25 +1047,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);
-               }
+               if(self.weapon == WEP_SHOTGUN && fr == WFRAME_FIRE2)
+                       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 991ad214787a071e02627d22e361545338b8b3b9..6c3b662d4c25a28ede9883e49d306644b0fca488 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 fd34e9ee17436cf9a3392b958372fabcd2edc7b2..371f9da399ee9988cd7982b9f887239d95dbe1cc 100644 (file)
@@ -56,19 +56,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 5cb328c2ea593f6ac43a08d3fba5ac54df4a9059..1d6dd911eb7f958fad79b957c120e06575433530 100644 (file)
@@ -102,7 +102,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 654b77f2936faf1dc61c0a6673a28dfd4909b07c..fcc82ed08557e6b0008acd229f2b482a46a515f0 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
@@ -239,6 +240,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 8d21766a950712365daa0a315ecdce6566da91b9..5a5042b3d48a63dc1a273feff50b33e767a7de8f 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 ad1fbf0fddcf2a02eb261cd7ec830b4f466c0662..082e454ace2de00516090a35270868346ba7a8c8 100644 (file)
@@ -437,13 +437,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)
index fcf9a27c27482a25432747d58cc37ee974fae513..fdfa6af382c27a48347689016cc92a161cb9c36d 100644 (file)
@@ -199,7 +199,7 @@ float w_shotgun(float req)
                        }
                }
                if (self.clip_load >= 0) // we are not currently reloading
-               if (!self.crouch) // we are not currently crouching; this fixes an exploit where your melee anim is not visible, and besides wouldn't make much sense
+               if (!self.crouch) // no crouchmelee please
                if (self.BUTTON_ATCK2 && autocvar_g_balance_shotgun_secondary)
                if (weapon_prepareattack(1, autocvar_g_balance_shotgun_secondary_refire))
                {