Fandom

Doom Wiki

Status bar face hysteresis

3,484pages on
this wiki
Add New Page
Talk0 Share

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

Bludface

Status bar face hysteresis demonstrated in MAP30: Icon of Sin

When the player's health moves between ranges corresponding to different status bar face animations, the red HEALTH percentage changes immediately, but the face itself is not updated until the end of the current animation sequence.

For example, if a severely injured player (below 20% health) gains a new weapon, followed closely by a large health pickup such as a megasphere, the status bar briefly displays a very high health percentage alongside an extremely bloodied face, as shown in the figure.

Technical

The phenomenon occurs because the engine chooses the next face graphic according to a strict hierarchy, and then tries to avoid updating the face again until a certain number of tics have passed. From st_stuff.c:

   #define ST_EVILGRINCOUNT                (2*TICRATE)
   #define ST_STRAIGHTFACECOUNT            (TICRATE/2)
   #define ST_TURNCOUNT                    (1*TICRATE)
   #define ST_OUCHCOUNT                    (1*TICRATE)
   #define ST_RAMPAGEDELAY                 (2*TICRATE)
   void ST_updateFaceWidget(void)        
   {        
       int                i;        
       angle_t            badguyangle;        
       angle_t            diffang;        
       static int         lastattackdown = -1;        
       static int         priority = 0;        
       boolean            doevilgrin;        
           
       if (priority < 10)        
       {        
           // dead        
           if (!plyr->health)        
           {        
               priority = 9;        
               st_faceindex = ST_DEADFACE;        
               st_facecount = 1;        
           }        
       }        
           
       if (priority < 9)        
       {        
           if (plyr->bonuscount)        
           {        
               // picking up bonus        
               doevilgrin = false;        
           
               for (i=0;i<NUMWEAPONS;i++)        
               {        
                   if (oldweaponsowned[i] != plyr->weaponowned[i])        
                   {        
                       doevilgrin = true;        
                       oldweaponsowned[i] = plyr->weaponowned[i];        
                   }        
               }        
               if (doevilgrin)         
               {        
                   // evil grin if just picked up weapon        
                   priority = 8;        
                   st_facecount = ST_EVILGRINCOUNT;        
                   st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET;        
               }        
           }        
       }        
             
       if (priority < 8)        
       {        
           if (plyr->damagecount        
               && plyr->attacker        
               && plyr->attacker != plyr->mo)        
           {        
               // being attacked        
               priority = 7;        
                       
               if (plyr->health - st_oldhealth > ST_MUCHPAIN)        
               {        
                   st_facecount = ST_TURNCOUNT;        
                   st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;        
               }        
               else        
               {        
                   badguyangle = R_PointToAngle2(plyr->mo->x,        
                                                 plyr->mo->y,        
                                                 plyr->attacker->x,        
                                                 plyr->attacker->y);        
                           
                   if (badguyangle > plyr->mo->angle)        
                   {        
                       // whether right or left        
                       diffang = badguyangle - plyr->mo->angle;        
                       i = diffang > ANG180;         
                   }        
                   else        
                   {        
                       // whether left or right        
                       diffang = plyr->mo->angle - badguyangle;        
                       i = diffang <= ANG180;         
                   } // confusing, aint it?        
           
                           
                   st_facecount = ST_TURNCOUNT;        
                   st_faceindex = ST_calcPainOffset();        
                           
                   if (diffang < ANG45)        
                   {        
                       // head-on            
                       st_faceindex += ST_RAMPAGEOFFSET;        
                   }        
                   else if (i)        
                   {        
                       // turn face right        
                       st_faceindex += ST_TURNOFFSET;        
                   }        
                   else        
                   {        
                       // turn face left        
                       st_faceindex += ST_TURNOFFSET+1;        
                   }        
               }        
           }        
       }        
             
       if (priority < 7)        
       {        
           // getting hurt because of your own damn stupidity        
           if (plyr->damagecount)        
           {        
               if (plyr->health - st_oldhealth > ST_MUCHPAIN)        
               {        
                   priority = 7;        
                   st_facecount = ST_TURNCOUNT;        
                   st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;        
               }        
               else        
               {        
                   priority = 6;        
                   st_facecount = ST_TURNCOUNT;        
                   st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;        
               }        
           }        
       }        
             
       if (priority < 6)        
       {        
           // rapid firing        
           if (plyr->attackdown)        
           {        
               if (lastattackdown==-1)        
                   lastattackdown = ST_RAMPAGEDELAY;        
               else if (!--lastattackdown)        
               {        
                   priority = 5;        
                   st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;        
                   st_facecount = 1;        
                   lastattackdown = 1;        
               }        
           }        
           else        
               lastattackdown = -1;        
       }        
             
       if (priority < 5)        
       {        
           // invulnerability        
           if ((plyr->cheats & CF_GODMODE)        
               || plyr->powers[pw_invulnerability])        
           {        
               priority = 4;        
           
               st_faceindex = ST_GODFACE;        
               st_facecount = 1;        
           }        
       }        
           
       // look left or look right if the facecount has timed out        
       if (!st_facecount)        
       {        
           st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3);        
           st_facecount = ST_STRAIGHTFACECOUNT;        
           priority = 0;        
       }        
           
       st_facecount--;        
           
   }        

Each change in the face graphic increases the counter variable st_facecount (bolded for emphasis), which decreases by 1 each tic. In order for the face to be refreshed, st_facecount must be zero or an in-game event with higher precedence must occur; for example, player death replaces a grinning face with the "death" face immediately.

Some face types, such as the default "looking ahead" face or the invulnerability face, can be overridden by damage or a weapon pickup on the very next call (st_facecount = 1), but others linger for half a second or so (TICRATE is 35); subsequent health pickups therefore cannot be represented in the face during that interval.

Also on Fandom

Random Wiki