// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: hu_stuff.c,v 1.30 1998/09/07 20:05:25 jim Exp $
//
//  BOOM, a modified and improved DOOM engine
//  Copyright (C) 1999 by
//  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 
//  02111-1307, USA.
//
// DESCRIPTION:  Heads-up displays
//
//-----------------------------------------------------------------------------

static const char
rcsid[] = "$Id: hu_stuff.c,v 1.30 1998/09/07 20:05:25 jim Exp $";

// killough 5/3/98: remove unnecessary headers

#include "doomstat.h"
#include "hu_stuff.h"
#include "hu_lib.h"
#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */
#include "w_wad.h"
#include "s_sound.h"
#include "dstrings.h"
#include "sounds.h"
#include "d_deh.h"   /* Ty 03/27/98 - externalization of mapnamesx arrays */

// global heads up display controls

int hud_active;       //jff 2/17/98 controls heads-up display mode 
int hud_displayed;    //jff 2/23/98 turns heads-up display on/off
int hud_nosecrets;    //jff 2/18/98 allows secrets line to be disabled in HUD
int hud_distributed;  //jff 3/4/98 display HUD in different places on screen
int hud_graph_keys=1; //jff 3/7/98 display HUD keys as graphics

//
// Locally used constants, shortcuts.
//
// Ty 03/28/98 -
// These four shortcuts modifed to reflect char ** of mapnamesx[]
#define HU_TITLE  (*mapnames[(gameepisode-1)*9+gamemap-1])
#define HU_TITLE2 (*mapnames2[gamemap-1])
#define HU_TITLEP (*mapnamesp[gamemap-1])
#define HU_TITLET (*mapnamest[gamemap-1])
#define HU_TITLEHEIGHT  1
#define HU_TITLEX 0
//jff 2/16/98 change 167 to ST_Y-1
#define HU_TITLEY (ST_Y - 1 - SHORT(hu_font[0]->height)) 

//jff 2/16/98 add coord text widget coordinates
#define HU_COORDX (SCREENWIDTH - 13*SHORT(hu_font2['A'-HU_FONTSTART]->width))
//jff 3/3/98 split coord widget into three lines in upper right of screen
#define HU_COORDX_Y (1 + 0*SHORT(hu_font['A'-HU_FONTSTART]->height))
#define HU_COORDY_Y (2 + 1*SHORT(hu_font['A'-HU_FONTSTART]->height))
#define HU_COORDZ_Y (3 + 2*SHORT(hu_font['A'-HU_FONTSTART]->height))

//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap
#define HU_GAPY 8
#define HU_HUDHEIGHT (6*HU_GAPY)
#define HU_HUDX 2
#define HU_HUDY (SCREENHEIGHT-HU_HUDHEIGHT-1)
#define HU_MONSECX (HU_HUDX)
#define HU_MONSECY (HU_HUDY+0*HU_GAPY)
#define HU_KEYSX   (HU_HUDX) 
//jff 3/7/98 add offset for graphic key widget
#define HU_KEYSGX  (HU_HUDX+4*SHORT(hu_font2['A'-HU_FONTSTART]->width))
#define HU_KEYSY   (HU_HUDY+1*HU_GAPY)
#define HU_WEAPX   (HU_HUDX)
#define HU_WEAPY   (HU_HUDY+2*HU_GAPY)
#define HU_AMMOX   (HU_HUDX)
#define HU_AMMOY   (HU_HUDY+3*HU_GAPY)
#define HU_HEALTHX (HU_HUDX)
#define HU_HEALTHY (HU_HUDY+4*HU_GAPY)
#define HU_ARMORX  (HU_HUDX)
#define HU_ARMORY  (HU_HUDY+5*HU_GAPY)

//jff 3/4/98 distributed HUD positions
#define HU_HUDX_LL 2
#define HU_HUDY_LL (SCREENHEIGHT-2*HU_GAPY-1)
#define HU_HUDX_LR 200
#define HU_HUDY_LR (SCREENHEIGHT-2*HU_GAPY-1)
#define HU_HUDX_UR 224
#define HU_HUDY_UR 2
#define HU_MONSECX_D (HU_HUDX_LL)
#define HU_MONSECY_D (HU_HUDY_LL+0*HU_GAPY)
#define HU_KEYSX_D   (HU_HUDX_LL)
#define HU_KEYSGX_D  (HU_HUDX_LL+4*SHORT(hu_font2['A'-HU_FONTSTART]->width))
#define HU_KEYSY_D   (HU_HUDY_LL+1*HU_GAPY)
#define HU_WEAPX_D   (HU_HUDX_LR)
#define HU_WEAPY_D   (HU_HUDY_LR+0*HU_GAPY)
#define HU_AMMOX_D   (HU_HUDX_LR)
#define HU_AMMOY_D   (HU_HUDY_LR+1*HU_GAPY)
#define HU_HEALTHX_D (HU_HUDX_UR)
#define HU_HEALTHY_D (HU_HUDY_UR+0*HU_GAPY)
#define HU_ARMORX_D  (HU_HUDX_UR)
#define HU_ARMORY_D  (HU_HUDY_UR+1*HU_GAPY)

//#define HU_INPUTTOGGLE  't' // not used                           // phares
#define HU_INPUTX HU_MSGX
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1))
#define HU_INPUTWIDTH 64
#define HU_INPUTHEIGHT  1

#define key_alt   key_strafe                                        // phares
#define key_shift key_speed                                         //    |
                                                                    //    V
extern int  key_chat;
extern int  key_escape;
extern int  key_enter;
extern int  key_shift;                                              //    ^
extern int  key_alt;                                                //    |
extern int  destination_keys[MAXPLAYERS];                           // phares

char* chat_macros[] =    // Ty 03/27/98 - *not* externalized
{
  HUSTR_CHATMACRO0,
  HUSTR_CHATMACRO1,
  HUSTR_CHATMACRO2,
  HUSTR_CHATMACRO3,
  HUSTR_CHATMACRO4,
  HUSTR_CHATMACRO5,
  HUSTR_CHATMACRO6,
  HUSTR_CHATMACRO7,
  HUSTR_CHATMACRO8,
  HUSTR_CHATMACRO9
};

char* player_names[] =     // Ty 03/27/98 - *not* externalized
{
  HUSTR_PLRGREEN,
  HUSTR_PLRINDIGO,
  HUSTR_PLRBROWN,
  HUSTR_PLRRED
};

//jff 3/17/98 translate player colmap to text color ranges
int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED};

char chat_char;                 // remove later.
static player_t*  plr;

// font sets
patch_t* hu_font[HU_FONTSIZE];
patch_t* hu_font2[HU_FONTSIZE];
patch_t* hu_fontk[HU_FONTSIZE];//jff 3/7/98 added for graphic key indicators
patch_t* hu_msgbg[9];          //jff 2/26/98 add patches for message background

// widgets
static hu_textline_t  w_title;
static hu_stext_t     w_message;
static hu_itext_t     w_chat;
static hu_itext_t     w_inputbuffer[MAXPLAYERS];
static hu_textline_t  w_coordx; //jff 2/16/98 new coord widget for automap
static hu_textline_t  w_coordy; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_coordz; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_ammo;   //jff 2/16/98 new ammo widget for hud
static hu_textline_t  w_health; //jff 2/16/98 new health widget for hud
static hu_textline_t  w_armor;  //jff 2/16/98 new armor widget for hud
static hu_textline_t  w_weapon; //jff 2/16/98 new weapon widget for hud
static hu_textline_t  w_keys;   //jff 2/16/98 new keys widget for hud
static hu_textline_t  w_gkeys;  //jff 3/7/98 graphic keys widget for hud
static hu_textline_t  w_monsec; //jff 2/16/98 new kill/secret widget for hud
static hu_mtext_t     w_rtext;  //jff 2/26/98 text message refresh widget

static boolean    always_off = false;
static char       chat_dest[MAXPLAYERS];
boolean           chat_on;
static boolean    message_on;
static boolean    message_list; //2/26/98 enable showing list of messages
boolean           message_dontfuckwithme;
static boolean    message_nottobefuckedwith;
static int        message_counter;
extern int        showMessages;
extern boolean    automapactive;
static boolean    headsupactive = false;

//jff 2/16/98 hud supported automap colors added
int hudcolor_titl;  // color range of automap level title
int hudcolor_xyco;  // color range of new coords on automap
//jff 2/16/98 hud text colors, controls added
int hudcolor_mesg;  // color range of scrolling messages
int hudcolor_chat;  // color range of chat lines
int hud_msg_lines;  // number of message lines in window
//jff 2/26/98 hud text colors, controls added
int hudcolor_list;  // list of messages color
int hud_list_bgon;  // enable for solid window background for message list

//jff 2/16/98 initialization strings for ammo, health, armor widgets
static char hud_coordstrx[32];
static char hud_coordstry[32];
static char hud_coordstrz[32];
static char hud_ammostr[80];
static char hud_healthstr[80];
static char hud_armorstr[80];
static char hud_weapstr[80];
static char hud_keysstr[80];
static char hud_gkeysstr[80]; //jff 3/7/98 add support for graphic key display
static char hud_monsecstr[80];

//jff 2/16/98 declaration of color switch points
extern int ammo_red;
extern int ammo_yellow;
extern int health_red;
extern int health_yellow;
extern int health_green;
extern int armor_red;
extern int armor_yellow;
extern int armor_green;

//
// Builtin map names.
// The actual names can be found in DStrings.h.
//
// Ty 03/27/98 - externalized map name arrays - now in d_deh.c
// and converted to arrays of pointers to char *
// See modified HUTITLEx macros
extern char **mapnames[];
extern char **mapnames2[];
extern char **mapnamesp[];
extern char **mapnamest[];

// key tables
// jff 5/10/98 french support removed, 
// as it was not being used and couldn't be easily tested
//
const char* shiftxform;

const char english_shiftxform[] =
{
  0,
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
  31,
  ' ', '!', '"', '#', '$', '%', '&',
  '"', // shift-'
  '(', ')', '*', '+',
  '<', // shift-,
  '_', // shift--
  '>', // shift-.
  '?', // shift-/
  ')', // shift-0
  '!', // shift-1
  '@', // shift-2
  '#', // shift-3
  '$', // shift-4
  '%', // shift-5
  '^', // shift-6
  '&', // shift-7
  '*', // shift-8
  '(', // shift-9
  ':',
  ':', // shift-;
  '<',
  '+', // shift-=
  '>', '?', '@',
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  '[', // shift-[
  '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
  ']', // shift-]
  '"', '_',
  '\'', // shift-`
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  '{', '|', '}', '~', 127
};

//
// HU_Init()
//
// Initialize the heads-up display, text that overwrites the primary display
//
// Passed nothing, returns nothing
//
void HU_Init(void)
{

  int   i;
  int   j;
  char  buffer[9];

  shiftxform = english_shiftxform;

  // load the heads-up font
  j = HU_FONTSTART;
  for (i=0;i<HU_FONTSIZE;i++,j++)
  {
    if ('0'<=j && j<='9')
    {
      sprintf(buffer, "DIG%.1d",j-48);
      hu_font2[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
      sprintf(buffer, "STCFN%.3d",j);
      hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
    }
    else if ('A'<=j && j<='Z')
    {
      sprintf(buffer, "DIG%c",j);
      hu_font2[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
      sprintf(buffer, "STCFN%.3d",j);
      hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
    }
    else if (j=='-')
    {
      hu_font2[i] = (patch_t *) W_CacheLumpName("DIG45", PU_STATIC);
      hu_font[i] = (patch_t *) W_CacheLumpName("STCFN045", PU_STATIC);
    }
    else if (j=='/')
    {
      hu_font2[i] = (patch_t *) W_CacheLumpName("DIG47", PU_STATIC);
      hu_font[i] = (patch_t *) W_CacheLumpName("STCFN047", PU_STATIC);
    }
    else if (j==':')
    {
      hu_font2[i] = (patch_t *) W_CacheLumpName("DIG58", PU_STATIC);
      hu_font[i] = (patch_t *) W_CacheLumpName("STCFN058", PU_STATIC);
    }
    else if (j=='[')
    {
      hu_font2[i] = (patch_t *) W_CacheLumpName("DIG91", PU_STATIC);
      hu_font[i] = (patch_t *) W_CacheLumpName("STCFN091", PU_STATIC);
    }
    else if (j==']')
    {
      hu_font2[i] = (patch_t *) W_CacheLumpName("DIG93", PU_STATIC);
      hu_font[i] = (patch_t *) W_CacheLumpName("STCFN093", PU_STATIC);
    }
    else if (j<97)
    {
      sprintf(buffer, "STCFN%.3d",j);
      hu_font2[i] = hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
      //jff 2/23/98 make all font chars defined, useful or not
    }
    else if (j>122)
    {
      sprintf(buffer, "STBR%.3d",j);
      hu_font2[i] = hu_font[i] =
        (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
    }
    else
      hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap
  }

  //jff 2/26/98 load patches for message background
  hu_msgbg[0] = (patch_t *) W_CacheLumpName("BOXUL", PU_STATIC);
  hu_msgbg[1] = (patch_t *) W_CacheLumpName("BOXUC", PU_STATIC);
  hu_msgbg[2] = (patch_t *) W_CacheLumpName("BOXUR", PU_STATIC);
  hu_msgbg[3] = (patch_t *) W_CacheLumpName("BOXCL", PU_STATIC);
  hu_msgbg[4] = (patch_t *) W_CacheLumpName("BOXCC", PU_STATIC);
  hu_msgbg[5] = (patch_t *) W_CacheLumpName("BOXCR", PU_STATIC);
  hu_msgbg[6] = (patch_t *) W_CacheLumpName("BOXLL", PU_STATIC);
  hu_msgbg[7] = (patch_t *) W_CacheLumpName("BOXLC", PU_STATIC);
  hu_msgbg[8] = (patch_t *) W_CacheLumpName("BOXLR", PU_STATIC);

  //jff 2/26/98 load patches for keys and double keys
  hu_fontk[0] = (patch_t *) W_CacheLumpName("STKEYS0", PU_STATIC);
  hu_fontk[1] = (patch_t *) W_CacheLumpName("STKEYS1", PU_STATIC);
  hu_fontk[2] = (patch_t *) W_CacheLumpName("STKEYS2", PU_STATIC);
  hu_fontk[3] = (patch_t *) W_CacheLumpName("STKEYS3", PU_STATIC);
  hu_fontk[4] = (patch_t *) W_CacheLumpName("STKEYS4", PU_STATIC);
  hu_fontk[5] = (patch_t *) W_CacheLumpName("STKEYS5", PU_STATIC);
}

//
// HU_Stop()
//
// Make the heads-up displays inactive
//
// Passed nothing, returns nothing
//
void HU_Stop(void)
{
  headsupactive = false;
}

//
// HU_Start(void)
//
// Create and initialize the heads-up widgets, software machines to
// maintain, update, and display information over the primary display
//
// This routine must be called after any change to the heads up configuration
// in order for the changes to take effect in the actual displays
//
// Passed nothing, returns nothing
//
void HU_Start(void)
{

  int   i;
  char* s;

  if (headsupactive)                    // stop before starting
    HU_Stop();

  plr = &players[displayplayer];        // killough 3/7/98
  message_on = false;
  message_dontfuckwithme = false;
  message_nottobefuckedwith = false;
  chat_on = false;

  // create the message widget
  // messages to player in upper-left of screen
  HUlib_initSText
  (
    &w_message,
    HU_MSGX,
    HU_MSGY,
    HU_MSGHEIGHT,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_mesg],
    &message_on
  );

  //jff 2/16/98 added some HUD widgets
  // create the map title widget - map title display in lower left of automap
  HUlib_initTextLine
  (
    &w_title,
    HU_TITLEX,
    HU_TITLEY,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_titl]
  );

  // create the hud health widget
  // bargraph and number for amount of health, 
  // lower left or upper right of screen
  HUlib_initTextLine
  (
    &w_health,
    hud_distributed? HU_HEALTHX_D : HU_HEALTHX,  //3/4/98 distribute
    hud_distributed? HU_HEALTHY_D : HU_HEALTHY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GREEN]
  );

  // create the hud armor widget
  // bargraph and number for amount of armor, 
  // lower left or upper right of screen
  HUlib_initTextLine
  (
    &w_armor,
    hud_distributed? HU_ARMORX_D : HU_ARMORX,    //3/4/98 distribute
    hud_distributed? HU_ARMORY_D : HU_ARMORY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GREEN]
  );

  // create the hud ammo widget
  // bargraph and number for amount of ammo for current weapon, 
  // lower left or lower right of screen
  HUlib_initTextLine
  (
    &w_ammo,
    hud_distributed? HU_AMMOX_D : HU_AMMOX,      //3/4/98 distribute
    hud_distributed? HU_AMMOY_D : HU_AMMOY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GOLD]
  );

  // create the hud weapons widget
  // list of numbers of weapons possessed
  // lower left or lower right of screen
  HUlib_initTextLine
  (
    &w_weapon,
    hud_distributed? HU_WEAPX_D : HU_WEAPX,      //3/4/98 distribute
    hud_distributed? HU_WEAPY_D : HU_WEAPY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GRAY]
  );

  // create the hud keys widget
  // display of key letters possessed
  // lower left of screen
  HUlib_initTextLine
  (
    &w_keys,
    hud_distributed? HU_KEYSX_D : HU_KEYSX,      //3/4/98 distribute
    hud_distributed? HU_KEYSY_D : HU_KEYSY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GRAY]
  );

  // create the hud graphic keys widget
  // display of key graphics possessed
  // lower left of screen
  HUlib_initTextLine
  (
    &w_gkeys,
    hud_distributed? HU_KEYSGX_D : HU_KEYSGX,    //3/4/98 distribute
    hud_distributed? HU_KEYSY_D : HU_KEYSY,
    hu_fontk,
    HU_FONTSTART,
    colrngs[CR_RED]
  );

  // create the hud monster/secret widget
  // totals and current values for kills, items, secrets
  // lower left of screen
  HUlib_initTextLine
  (
    &w_monsec,
    hud_distributed? HU_MONSECX_D : HU_MONSECX,  //3/4/98 distribute
    hud_distributed? HU_MONSECY_D : HU_MONSECY,
    hu_font2,
    HU_FONTSTART,
    colrngs[CR_GRAY]
  );

  // create the hud text refresh widget
  // scrolling display of last hud_msg_lines messages received
  if (hud_msg_lines>HU_MAXMESSAGES)
    hud_msg_lines=HU_MAXMESSAGES;
  //jff 4/21/98 if setup has disabled message list while active, turn it off
  message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways
  //jff 2/26/98 add the text refresh widget initialization
  HUlib_initMText
  (
    &w_rtext,
    0,
    0,
    SCREENWIDTH,
    (hud_msg_lines+2)*HU_REFRESHSPACING,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_list],
    hu_msgbg,
    &message_list
  );

  // initialize the automap's level title widget
  switch (gamemode)
  {
    case shareware:
    case registered:
    case retail:
      s = HU_TITLE;
      break;

    case commercial:
    default:  // Ty 08/27/98 - modified to check mission for TNT/Plutonia
      s = (gamemission==pack_tnt)  ? HU_TITLET : 
          (gamemission==pack_plut) ? HU_TITLEP : HU_TITLE2;
      break;
  }
  while (*s)
    HUlib_addCharToTextLine(&w_title, *(s++));

  // create the automaps coordinate widget
  // jff 3/3/98 split coord widget into three lines: x,y,z
  // jff 2/16/98 added
  HUlib_initTextLine
  (
    &w_coordx,
    HU_COORDX,
    HU_COORDX_Y,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_xyco]
  );
  HUlib_initTextLine
  (
    &w_coordy,
    HU_COORDX,
    HU_COORDY_Y,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_xyco]
  );
  HUlib_initTextLine
  (
    &w_coordz,
    HU_COORDX,
    HU_COORDZ_Y,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_xyco]
  );
  
  // initialize the automaps coordinate widget
  //jff 3/3/98 split coordstr widget into 3 parts
  sprintf(hud_coordstrx,"X: %-5d",0); //jff 2/22/98 added z
  s = hud_coordstrx;
  while (*s)
    HUlib_addCharToTextLine(&w_coordx, *(s++));
  sprintf(hud_coordstry,"Y: %-5d",0); //jff 3/3/98 split x,y,z
  s = hud_coordstry;
  while (*s)
    HUlib_addCharToTextLine(&w_coordy, *(s++));
  sprintf(hud_coordstrz,"Z: %-5d",0); //jff 3/3/98 split x,y,z
  s = hud_coordstrz;
  while (*s)
    HUlib_addCharToTextLine(&w_coordz, *(s++));

  //jff 2/16/98 initialize ammo widget
  sprintf(hud_ammostr,"AMM ");
  s = hud_ammostr;
  while (*s)
    HUlib_addCharToTextLine(&w_ammo, *(s++));

  //jff 2/16/98 initialize health widget
  sprintf(hud_healthstr,"HEL ");
  s = hud_healthstr;
  while (*s)
    HUlib_addCharToTextLine(&w_health, *(s++));

  //jff 2/16/98 initialize armor widget
  sprintf(hud_armorstr,"ARM ");
  s = hud_armorstr;
  while (*s)
    HUlib_addCharToTextLine(&w_armor, *(s++));

  //jff 2/17/98 initialize weapons widget
  sprintf(hud_weapstr,"WEA ");
  s = hud_weapstr;
  while (*s)
    HUlib_addCharToTextLine(&w_weapon, *(s++));

  //jff 2/17/98 initialize keys widget
  if (!deathmatch) //jff 3/17/98 show frags in deathmatch mode
    sprintf(hud_keysstr,"KEY ");
  else
    sprintf(hud_keysstr,"FRG ");
  s = hud_keysstr;
  while (*s)
    HUlib_addCharToTextLine(&w_keys, *(s++));

  //jff 2/17/98 initialize graphic keys widget
  sprintf(hud_gkeysstr," ");
  s = hud_gkeysstr;
  while (*s)
    HUlib_addCharToTextLine(&w_gkeys, *(s++));

  //jff 2/17/98 initialize kills/items/secret widget
  sprintf(hud_monsecstr,"STS ");
  s = hud_monsecstr;
  while (*s)
    HUlib_addCharToTextLine(&w_monsec, *(s++));

  // create the chat widget
  HUlib_initIText
  (
    &w_chat,
    HU_INPUTX,
    HU_INPUTY,
    hu_font,
    HU_FONTSTART,
    colrngs[hudcolor_chat],
    &chat_on
  );

  // create the inputbuffer widgets, one per player
  for (i=0 ; i<MAXPLAYERS ; i++)
    HUlib_initIText
    (
      &w_inputbuffer[i],
      0,
      0,
      0,
      0,
      colrngs[hudcolor_chat],
      &always_off
    );

  // now allow the heads-up display to run
  headsupactive = true;
}

//
// HU_MoveHud()
//
// Move the HUD display from distributed to compact mode or vice-versa
//
// Passed nothing, returns nothing
//
//jff 3/9/98 create this externally callable to avoid glitch
// when menu scatter's HUD due to delay in change of position
//
void HU_MoveHud(void)
{
  static int ohud_distributed=-1;

  //jff 3/4/98 move displays around on F5 changing hud_distributed
  if (hud_distributed!=ohud_distributed)
  {
    w_ammo.x =    hud_distributed? HU_AMMOX_D   : HU_AMMOX; 
    w_ammo.y =    hud_distributed? HU_AMMOY_D   : HU_AMMOY;
    w_weapon.x =  hud_distributed? HU_WEAPX_D   : HU_WEAPX; 
    w_weapon.y =  hud_distributed? HU_WEAPY_D   : HU_WEAPY;
    w_keys.x =    hud_distributed? HU_KEYSX_D   : HU_KEYSX; 
    w_keys.y =    hud_distributed? HU_KEYSY_D   : HU_KEYSY;
    w_gkeys.x =   hud_distributed? HU_KEYSGX_D  : HU_KEYSGX; 
    w_gkeys.y =   hud_distributed? HU_KEYSY_D   : HU_KEYSY;
    w_monsec.x =  hud_distributed? HU_MONSECX_D : HU_MONSECX; 
    w_monsec.y =  hud_distributed? HU_MONSECY_D : HU_MONSECY;
    w_health.x =  hud_distributed? HU_HEALTHX_D : HU_HEALTHX; 
    w_health.y =  hud_distributed? HU_HEALTHY_D : HU_HEALTHY;
    w_armor.x =   hud_distributed? HU_ARMORX_D  : HU_ARMORX; 
    w_armor.y =   hud_distributed? HU_ARMORY_D  : HU_ARMORY;
  }
  ohud_distributed = hud_distributed;
}

//
// HU_Drawer()
//
// Draw all the pieces of the heads-up display
//
// Passed nothing, returns nothing
//
void HU_Drawer(void)
{
  char *s;
  player_t *plr;
  char ammostr[80];  //jff 3/8/98 allow plenty room for dehacked mods
  char healthstr[80];//jff
  char armorstr[80]; //jff
  int i,doit;

  plr = &players[displayplayer];         // killough 3/7/98
  // draw the automap widgets if automap is displayed
  if (automapactive)
  {
    // map title
    HUlib_drawTextLine(&w_title, false);

    //jff 2/16/98 output new coord display
    // x-coord
    sprintf(hud_coordstrx,"X: %-5d", (plr->mo->x)>>FRACBITS);
    HUlib_clearTextLine(&w_coordx);
    s = hud_coordstrx;
    while (*s)
      HUlib_addCharToTextLine(&w_coordx, *(s++));
    HUlib_drawTextLine(&w_coordx, false);

    //jff 3/3/98 split coord display into x,y,z lines
    // y-coord
    sprintf(hud_coordstry,"Y: %-5d", (plr->mo->y)>>FRACBITS);
    HUlib_clearTextLine(&w_coordy);
    s = hud_coordstry;
    while (*s)
      HUlib_addCharToTextLine(&w_coordy, *(s++));
    HUlib_drawTextLine(&w_coordy, false);

    //jff 3/3/98 split coord display into x,y,z lines  
    //jff 2/22/98 added z
    // z-coord
    sprintf(hud_coordstrz,"Z: %-5d", (plr->mo->z)>>FRACBITS);  
    HUlib_clearTextLine(&w_coordz);
    s = hud_coordstrz;
    while (*s)
      HUlib_addCharToTextLine(&w_coordz, *(s++));
    HUlib_drawTextLine(&w_coordz, false);
  }

  // draw the weapon/health/ammo/armor/kills/keys displays if optioned
  //jff 2/17/98 allow new hud stuff to be turned off
  // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY  
  if
  (
    hud_active>0 &&                  // hud optioned on
    hud_displayed &&                 // hud on from fullscreen key
    viewheight==SCREENHEIGHT &&      // fullscreen mode is active
    !automapactive                   // automap is not active
  )
  {
    doit = !(gametic&1); //jff 3/4/98 speed update up for slow systems
    if (doit)            //jff 8/7/98 update every time, avoid lag in update
    {
      HU_MoveHud();                  // insure HUD display coords are correct

      // do the hud ammo display
      // clear the widgets internal line
      HUlib_clearTextLine(&w_ammo);  
      strcpy(hud_ammostr,"AMM ");    
      if (weaponinfo[plr->readyweapon].ammo == am_noammo)
      { // special case for weapon with no ammo selected - blank bargraph + N/A
        strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A");
        w_ammo.cr = colrngs[CR_GRAY];
      }
      else
      {
        int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo];
        int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo];
        int ammopct = (100*ammo)/fullammo;
        int ammobars = ammopct/4;

        // build the numeric amount init string
        sprintf(ammostr,"%d/%d",ammo,fullammo);
        // build the bargraph string
        // full bargraph chars
        for (i=4;i<4+ammobars/4;)
          hud_ammostr[i++] = 123;     
        // plus one last character with 0,1,2,3 bars
        switch(ammobars%4)
        {
          case 0:
            break;
          case 1:
            hud_ammostr[i++] = 126;
            break;
          case 2:
            hud_ammostr[i++] = 125;
            break;
          case 3:
            hud_ammostr[i++] = 124;
            break;
        }
        // pad string with blank bar characters
        while(i<4+7)
          hud_ammostr[i++] = 127;
        hud_ammostr[i] = '\0';
        strcat(hud_ammostr,ammostr);

        // set the display color from the percentage of total ammo held
        if (ammopct<ammo_red)
          w_ammo.cr = colrngs[CR_RED];
        else if (ammopct<ammo_yellow)
          w_ammo.cr = colrngs[CR_GOLD];
        else
          w_ammo.cr = colrngs[CR_GREEN];
      }
      // transfer the init string to the widget
      s = hud_ammostr;
      while (*s)
        HUlib_addCharToTextLine(&w_ammo, *(s++));
    }
    // display the ammo widget every frame
    HUlib_drawTextLine(&w_ammo, false);
    
    // do the hud health display
    if (doit)
    {
      int health = plr->health;
      int healthbars = health>100? 25 : health/4;

      // clear the widgets internal line
      HUlib_clearTextLine(&w_health);    

      // build the numeric amount init string
      sprintf(healthstr,"%3d",health);
      // build the bargraph string
      // full bargraph chars
      for (i=4;i<4+healthbars/4;)
        hud_healthstr[i++] = 123;
      // plus one last character with 0,1,2,3 bars
      switch(healthbars%4)
      {
        case 0:
          break;
        case 1:
          hud_healthstr[i++] = 126;
          break;
        case 2:
          hud_healthstr[i++] = 125;
          break;
        case 3:
          hud_healthstr[i++] = 124;
          break;
      }
      // pad string with blank bar characters
      while(i<4+7)
        hud_healthstr[i++] = 127;
      hud_healthstr[i] = '\0';
      strcat(hud_healthstr,healthstr);

      // set the display color from the amount of health posessed
      if (health<health_red)
        w_health.cr = colrngs[CR_RED];
      else if (health<health_yellow)
        w_health.cr = colrngs[CR_GOLD];
      else if (health<=health_green)
        w_health.cr = colrngs[CR_GREEN];
      else
        w_health.cr = colrngs[CR_BLUE];

      // transfer the init string to the widget
      s = hud_healthstr;
      while (*s)
        HUlib_addCharToTextLine(&w_health, *(s++));
    }
    // display the health widget every frame
    HUlib_drawTextLine(&w_health, false);

    // do the hud armor display
    if (doit)
    {
      int armor = plr->armorpoints;
      int armorbars = armor>100? 25 : armor/4;

      // clear the widgets internal line
      HUlib_clearTextLine(&w_armor);
      // build the numeric amount init string
      sprintf(armorstr,"%3d",armor);
      // build the bargraph string
      // full bargraph chars
      for (i=4;i<4+armorbars/4;)
        hud_armorstr[i++] = 123;
      // plus one last character with 0,1,2,3 bars
      switch(armorbars%4)
      {
        case 0:
          break;
        case 1:
          hud_armorstr[i++] = 126;
          break;
        case 2:
          hud_armorstr[i++] = 125;
          break;
        case 3:
          hud_armorstr[i++] = 124;
          break;
      }
      // pad string with blank bar characters
      while(i<4+7)
        hud_armorstr[i++] = 127;
      hud_armorstr[i] = '\0';
      strcat(hud_armorstr,armorstr);

      // set the display color from the amount of armor posessed
      if (armor<armor_red)
        w_armor.cr = colrngs[CR_RED];
      else if (armor<armor_yellow)
        w_armor.cr = colrngs[CR_GOLD];
      else if (armor<=armor_green)
        w_armor.cr = colrngs[CR_GREEN];
      else
        w_armor.cr = colrngs[CR_BLUE];

      // transfer the init string to the widget
      s = hud_armorstr;
      while (*s)
        HUlib_addCharToTextLine(&w_armor, *(s++));
    }
    // display the armor widget every frame
    HUlib_drawTextLine(&w_armor, false);

    // do the hud weapon display
    if (doit)
    {
      int w;
      int ammo,fullammo,ammopct;

      // clear the widgets internal line
      HUlib_clearTextLine(&w_weapon);
      i=4; hud_weapstr[i] = '\0';      //jff 3/7/98 make sure ammo goes away

      // do each weapon that exists in current gamemode
      for (w=0;w<=wp_supershotgun;w++) //jff 3/4/98 show fists too, why not?
      {
        int ok=1;
        //jff avoid executing for weapons that do not exist
        switch (gamemode)
        {
          case shareware:
            if (w>=wp_plasma && w!=wp_chainsaw)
              ok=0;
            break;
          case retail:
          case registered:
            if (w>=wp_supershotgun)
              ok=0;
            break;
          default:
          case commercial:
            break;
        }
        if (!ok) continue;

        ammo = plr->ammo[weaponinfo[w].ammo];
        fullammo = plr->maxammo[weaponinfo[w].ammo];
        ammopct=0;
        
        // skip weapons not currently posessed
        if (!plr->weaponowned[w])
          continue;

        ammopct = fullammo? (100*ammo)/fullammo : 100;

        // display each weapon number in a color related to the ammo for it
        hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD
          hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY;
        else if (ammopct<ammo_red)
          hud_weapstr[i++] = '0'+CR_RED;
        else if (ammopct<ammo_yellow)
          hud_weapstr[i++] = '0'+CR_GOLD;
        else
          hud_weapstr[i++] = '0'+CR_GREEN;
        hud_weapstr[i++] = '0'+w+1;
        hud_weapstr[i++] = ' ';
        hud_weapstr[i] = '\0';
      }

      // transfer the init string to the widget
      s = hud_weapstr;
      while (*s)
        HUlib_addCharToTextLine(&w_weapon, *(s++));
    }
    // display the weapon widget every frame
    HUlib_drawTextLine(&w_weapon, false);

    if (doit && hud_active>1)
    {
      int k;

      hud_keysstr[4] = '\0';    //jff 3/7/98 make sure deleted keys go away
      //jff add case for graphic key display
      if (!deathmatch && hud_graph_keys)
      {
        i=0;
        hud_gkeysstr[i] = '\0'; //jff 3/7/98 init graphic keys widget string
        // build text string whose characters call out graphic keys from fontk
        for (k=0;k<6;k++)
        {
          // skip keys not possessed
          if (!plr->cards[k])
            continue;

          hud_gkeysstr[i++] = '!'+k;   // key number plus '!' is char for key
          hud_gkeysstr[i++] = ' ';     // spacing
          hud_gkeysstr[i++] = ' ';
        }
        hud_gkeysstr[i]='\0';
      }
      else // not possible in current code, unless deathmatching, 
      {
        i=4;
        hud_keysstr[i] = '\0';  //jff 3/7/98 make sure deleted keys go away

        // if deathmatch, build string showing top four frag counts
        if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch
        {
          int top1=-999,top2=-999,top3=-999,top4=-999;
          int idx1=-1,idx2=-1,idx3=-1,idx4=-1;
          int fragcount,m;
          char numbuf[32];

          // scan thru players
          for (k=0;k<MAXPLAYERS;k++)
          {
            // skip players not in game
            if (!playeringame[k])
              continue;

            fragcount = 0;
            // compute number of times they've fragged each player
            // minus number of times they've been fragged by them
            for (m=0;m<MAXPLAYERS;m++)
            {
              if (!playeringame[m]) continue;
              fragcount += (m!=k)?  players[k].frags[m] : -players[k].frags[m];
            }

            // very primitive sort of frags to find top four
            if (fragcount>top1)
            {
              top4=top3; top3=top2; top2 = top1; top1=fragcount;
              idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k;
            }
            else if (fragcount>top2)
            {
              top4=top3; top3=top2; top2=fragcount;
              idx4=idx3; idx3=idx2; idx2=k;
            }
            else if (fragcount>top3)
            {
              top4=top3; top3=fragcount;
              idx4=idx3; idx3=k;
            }
            else if (fragcount>top4)
            {
              top4=fragcount;
              idx4=k;
            }
          }
          // if the biggest number exists, put it in the init string
          if (idx1>-1)
          {
            sprintf(numbuf,"%5d",top1);
            // make frag count in player's color via escape code
            hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            hud_keysstr[i++] = '0'+plyrcoltran[idx1&3];
            s = numbuf;
            while (*s)
              hud_keysstr[i++] = *(s++);
          }
          // if the second biggest number exists, put it in the init string
          if (idx2>-1)
          {
            sprintf(numbuf,"%5d",top2);
            // make frag count in player's color via escape code
            hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            hud_keysstr[i++] = '0'+plyrcoltran[idx2&3];
            s = numbuf;
            while (*s)
              hud_keysstr[i++] = *(s++);
          }
          // if the third biggest number exists, put it in the init string
          if (idx3>-1)
          {
            sprintf(numbuf,"%5d",top3);
            // make frag count in player's color via escape code
            hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            hud_keysstr[i++] = '0'+plyrcoltran[idx3&3];
            s = numbuf;
            while (*s)
              hud_keysstr[i++] = *(s++);
          }
          // if the fourth biggest number exists, put it in the init string
          if (idx4>-1)
          {
            sprintf(numbuf,"%5d",top4);
            // make frag count in player's color via escape code
            hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            hud_keysstr[i++] = '0'+plyrcoltran[idx4&3];
            s = numbuf;
            while (*s)
              hud_keysstr[i++] = *(s++);
          }
          hud_keysstr[i] = '\0';
        } //jff 3/17/98 end of deathmatch clause
        else // build alphabetical key display (not used currently)
        {
          // scan the keys
          for (k=0;k<6;k++)
          {
            // skip any not possessed by the displayed player's stats
            if (!plr->cards[k])
              continue;

            // use color escapes to make text in key's color
            hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            switch(k)
            {
              case 0:
                hud_keysstr[i++] = '0'+CR_BLUE;
                hud_keysstr[i++] = 'B';
                hud_keysstr[i++] = 'C';
                hud_keysstr[i++] = ' ';
                break;
              case 1:
                hud_keysstr[i++] = '0'+CR_GOLD;
                hud_keysstr[i++] = 'Y';
                hud_keysstr[i++] = 'C';
                hud_keysstr[i++] = ' ';
                break;
              case 2:
                hud_keysstr[i++] = '0'+CR_RED;
                hud_keysstr[i++] = 'R';
                hud_keysstr[i++] = 'C';
                hud_keysstr[i++] = ' ';
                break;
              case 3:
                hud_keysstr[i++] = '0'+CR_BLUE;
                hud_keysstr[i++] = 'B';
                hud_keysstr[i++] = 'S';
                hud_keysstr[i++] = ' ';
                break;
            case 4:
                hud_keysstr[i++] = '0'+CR_GOLD;
                hud_keysstr[i++] = 'Y';
                hud_keysstr[i++] = 'S';
                hud_keysstr[i++] = ' ';
                break;
              case 5:
                hud_keysstr[i++] = '0'+CR_RED;
                hud_keysstr[i++] = 'R';
                hud_keysstr[i++] = 'S';
                hud_keysstr[i++] = ' ';
                break;
            }
            hud_keysstr[i]='\0';
          }
        }
      }
    }
    // display the keys/frags line each frame
    if (hud_active>1)
    {
      HUlib_clearTextLine(&w_keys);      // clear the widget strings
      HUlib_clearTextLine(&w_gkeys);

      // transfer the built string (frags or key title) to the widget
      s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags
      while (*s)
        HUlib_addCharToTextLine(&w_keys, *(s++));
      HUlib_drawTextLine(&w_keys, false);

      //jff 3/17/98 show graphic keys in non-DM only
      if (!deathmatch) //jff 3/7/98 display graphic keys
      {
        // transfer the graphic key text to the widget
        s = hud_gkeysstr;
        while (*s)
          HUlib_addCharToTextLine(&w_gkeys, *(s++));
        // display the widget
        HUlib_drawTextLine(&w_gkeys, false);
      }
    }

    // display the hud kills/items/secret display if optioned
    if (!hud_nosecrets)
    {
      if (hud_active>1 && doit)
      {
        // clear the internal widget text buffer
        HUlib_clearTextLine(&w_monsec);
        //jff 3/26/98 use ESC not '\' for paths
        // build the init string with fixed colors
        sprintf
        (
          hud_monsecstr,
          "STS \x1b\x36K \x1b\x33%d/%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d",
          plr->killcount,totalkills,
          plr->itemcount,totalitems,
          plr->secretcount,totalsecret
        );
        // transfer the init string to the widget
        s = hud_monsecstr;
        while (*s)
          HUlib_addCharToTextLine(&w_monsec, *(s++));
      }
      // display the kills/items/secrets each frame, if optioned
      if (hud_active>1)
        HUlib_drawTextLine(&w_monsec, false);
    }
  }

  //jff 3/4/98 display last to give priority
  HU_Erase(); // jff 4/24/98 Erase current lines before drawing current
              // needed when screen not fullsize

  //jff 4/21/98 if setup has disabled message list while active, turn it off
  if (hud_msg_lines<=1)
    message_list = false;

  // if the message review not enabled, show the standard message widget
  if (!message_list)
    HUlib_drawSText(&w_message);

  // if the message review is enabled show the scrolling message review
  if (hud_msg_lines>1 && message_list)
    HUlib_drawMText(&w_rtext);

  // display the interactive buffer for chat entry
  HUlib_drawIText(&w_chat);
}

//
// HU_Erase()
//
// Erase hud display lines that can be trashed by small screen display
//
// Passed nothing, returns nothing
//
void HU_Erase(void)
{
  // erase the message display or the message review display
  if (!message_list)
    HUlib_eraseSText(&w_message);
  else
    HUlib_eraseMText(&w_rtext);
  
  // erase the interactive text buffer for chat entry
  HUlib_eraseIText(&w_chat);

  // erase the automap title
  HUlib_eraseTextLine(&w_title);
}

//
// HU_Ticker()
//
// Update the hud displays once per frame
//
// Passed nothing, returns nothing
//
void HU_Ticker(void)
{
  int i, rc;
  char c;

  // tick down message counter if message is up
  if (message_counter && !--message_counter)
  {
    message_on = false;
    message_nottobefuckedwith = false;
  }

  // if messages on, or "Messages Off" is being displayed
  // this allows the notification of turning messages off to be seen
  if (showMessages || message_dontfuckwithme)
  {
    // display message if necessary
    if ((plr->message && !message_nottobefuckedwith)
        || (plr->message && message_dontfuckwithme))
    {
      //post the message to the message widget
      HUlib_addMessageToSText(&w_message, 0, plr->message);
      //jff 2/26/98 add message to refresh text widget too
      HUlib_addMessageToMText(&w_rtext, 0, plr->message);

      // clear the message to avoid posting multiple times
      plr->message = 0;
      // note a message is displayed
      message_on = true;
      // start the message persistence counter
      message_counter = HU_MSGTIMEOUT;
      // transfer "Messages Off" exception to the "being displayed" variable
      message_nottobefuckedwith = message_dontfuckwithme;
      // clear the flag that "Messages Off" is being posted
      message_dontfuckwithme = 0;
    }
  }

  // check for incoming chat characters
  if (netgame)
  {
    for (i=0; i<MAXPLAYERS; i++)
    {
      if (!playeringame[i])
        continue;
      if (i != consoleplayer
          && (c = players[i].cmd.chatchar))
      {
        if (c <= HU_BROADCAST)
          chat_dest[i] = c;
        else
        {
          if (c >= 'a' && c <= 'z')
            c = (char) shiftxform[(unsigned char) c];
          rc = HUlib_keyInIText(&w_inputbuffer[i], c);
          if (rc && c == KEYD_ENTER)
          {
            if (w_inputbuffer[i].l.len
                && (chat_dest[i] == consoleplayer+1
                || chat_dest[i] == HU_BROADCAST))
            {
              HUlib_addMessageToSText(&w_message,
                                      player_names[i],
                                      w_inputbuffer[i].l.l);

              message_nottobefuckedwith = true;
              message_on = true;
              message_counter = HU_MSGTIMEOUT;
              if ( gamemode == commercial )
                S_StartSound(0, sfx_radio);
              else
                S_StartSound(0, sfx_tink);
            }
            HUlib_resetIText(&w_inputbuffer[i]);
          }
        }
        players[i].cmd.chatchar = 0;
      }
    }
  }
}

#define QUEUESIZE   128

static char chatchars[QUEUESIZE];
static int  head = 0;
static int  tail = 0;

//
// HU_queueChatChar()
//
// Add an incoming character to the circular chat queue
//
// Passed the character to queue, returns nothing
//
void HU_queueChatChar(char c)
{
  if (((head + 1) & (QUEUESIZE-1)) == tail)
  {
    plr->message = HUSTR_MSGU;
  }
  else
  {
    chatchars[head] = c;
    head = (head + 1) & (QUEUESIZE-1);
  }
}

//
// HU_dequeueChatChar()
//
// Remove the earliest added character from the circular chat queue
//
// Passed nothing, returns the character dequeued
//
char HU_dequeueChatChar(void)
{
  char c;

  if (head != tail)
  {
    c = chatchars[tail];
    tail = (tail + 1) & (QUEUESIZE-1);
  }
  else
  {
    c = 0;
  }
  return c;
}

//
// HU_Responder()
//
// Responds to input events that affect the heads up displays
//
// Passed the event to respond to, returns true if the event was handled
//
boolean HU_Responder(event_t *ev)
{

  static char   lastmessage[HU_MAXLINELENGTH+1];
  char*   macromessage;
  boolean   eatkey = false;
  static boolean  shiftdown = false;
  static boolean  altdown = false;
  unsigned char   c;
  int     i;
  int     numplayers;

  static int    num_nobrainers = 0;

  numplayers = 0;
  for (i=0 ; i<MAXPLAYERS ; i++)
    numplayers += playeringame[i];

  if (ev->data1 == key_shift)
  {
    shiftdown = ev->type == ev_keydown;
    return false;
  }
  else if (ev->data1 == key_alt)
  {
    altdown = ev->type == ev_keydown;
    return false;
  }

  if (ev->type != ev_keydown)
    return false;

  if (!chat_on)
  {
    if (ev->data1 == key_enter)                                 // phares
    {
#ifndef INSTRUMENTED  // never turn on message review if INSTRUMENTED defined
      if (hud_msg_lines>1)  // it posts multi-line messages that will trash
      {
        if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages
        message_list = !message_list; //jff 2/26/98 toggle list of messages
      }
#endif
      if (!message_list)              // if not message list, refresh message
      {
        message_on = true;
        message_counter = HU_MSGTIMEOUT;
      }
      eatkey = true;
    }//jff 2/26/98 no chat if message review is displayed
    else if (!message_list && netgame && ev->data1 == key_chat)
    {
      eatkey = chat_on = true;
      HUlib_resetIText(&w_chat);
      HU_queueChatChar(HU_BROADCAST);
    }//jff 2/26/98
    else if (!message_list && netgame && numplayers > 2)
    {
      for (i=0; i<MAXPLAYERS ; i++)
      {
        if (ev->data1 == destination_keys[i])
        {
          if (playeringame[i] && i!=consoleplayer)
          {
            eatkey = chat_on = true;
            HUlib_resetIText(&w_chat);
            HU_queueChatChar(i+1);
            break;
          }
          else if (i == consoleplayer)
          {
            num_nobrainers++;
            if (num_nobrainers < 3)
                plr->message = HUSTR_TALKTOSELF1;
            else if (num_nobrainers < 6)
                plr->message = HUSTR_TALKTOSELF2;
            else if (num_nobrainers < 9)
                plr->message = HUSTR_TALKTOSELF3;
            else if (num_nobrainers < 32)
                plr->message = HUSTR_TALKTOSELF4;
            else
                plr->message = HUSTR_TALKTOSELF5;
          }
        }
      }
    }
  }//jff 2/26/98 no chat functions if message review is displayed
  else if (!message_list)
  {
    c = ev->data1;
    // send a macro
    if (altdown)
    {
      c = c - '0';
      if (c > 9)
        return false;
      macromessage = chat_macros[c];
      
      // kill last message with a '\n'
        HU_queueChatChar(key_enter); // DEBUG!!!                // phares
      
      // send the macro message
      while (*macromessage)
        HU_queueChatChar(*macromessage++);
      HU_queueChatChar(key_enter);                            // phares
      
      // leave chat mode and notify that it was sent
      chat_on = false;
      strcpy(lastmessage, chat_macros[c]);
      plr->message = lastmessage;
      eatkey = true;
    }
    else
    {
      if (shiftdown || (c >= 'a' && c <= 'z'))
        c = shiftxform[c];
      eatkey = HUlib_keyInIText(&w_chat, c);
      if (eatkey)
        HU_queueChatChar(c);

      if (c == key_enter)                                     // phares
      {
        chat_on = false;
        if (w_chat.l.len)
        {
          strcpy(lastmessage, w_chat.l.l);
          plr->message = lastmessage;
        }
      }
      else if (c == key_escape)                               // phares
        chat_on = false;
    }
  }
  return eatkey;
}


//----------------------------------------------------------------------------
//
// $Log: hu_stuff.c,v $
// Revision 1.30  1998/09/07  20:05:25  jim
// Added logical output routine
//
// Revision 1.29  1998/08/29  23:01:05  thldrmn
// Gamemission fixes for TNT and Plutonia
//
// Revision 1.28  1998/08/08  15:17:58  jim
// HUD update fixes
//
// Revision 1.27  1998/05/10  19:03:41  jim
// formatted/documented hu_stuff
//
// Revision 1.26  1998/05/03  22:25:24  killough
// Provide minimal headers at top; nothing else
//
// Revision 1.25  1998/04/28  15:53:58  jim
// Fix message list bug in small screen mode
//
// Revision 1.24  1998/04/22  12:50:14  jim
// Fix lockout from dynamic message change
//
// Revision 1.23  1998/04/05  10:09:51  jim
// added STCFN096 lump
//
// Revision 1.22  1998/03/28  05:32:12  jim
// Text enabling changes for DEH
//
// Revision 1.19  1998/03/17  20:45:23  jim
// added frags to HUD
//
// Revision 1.18  1998/03/15  14:42:16  jim
// added green fist/chainsaw in HUD when berserk
//
// Revision 1.17  1998/03/10  07:07:15  jim
// Fixed display glitch in HUD cycle
//
// Revision 1.16  1998/03/09  11:01:48  jim
// fixed string overflow for DEH, added graphic keys
//
// Revision 1.15  1998/03/09  07:10:09  killough
// Use displayplayer instead of consoleplayer
//
// Revision 1.14  1998/03/05  00:57:37  jim
// Scattered HUD
//
// Revision 1.13  1998/03/04  11:50:48  jim
// Change automap coord display
//
// Revision 1.12  1998/02/26  22:58:26  jim
// Added message review display to HUD
//
// Revision 1.11  1998/02/23  14:20:51  jim
// Merged HUD stuff, fixed p_plats.c to support elevators again
//
// Revision 1.10  1998/02/23  04:26:07  killough
// really allow new hud stuff to be turned off COMPLETELY
//
// Revision 1.9  1998/02/22  12:51:26  jim
// HUD control on F5, z coord, spacing change
//
// Revision 1.7  1998/02/20  18:46:51  jim
// cleanup of HUD control
//
// Revision 1.6  1998/02/19  16:54:53  jim
// Optimized HUD and made more configurable
//
// Revision 1.5  1998/02/18  11:55:55  jim
// Fixed issues with HUD and reduced screen size
//
// Revision 1.3  1998/02/15  02:47:47  phares
// User-defined keys
//
// Revision 1.2  1998/01/26  19:23:22  phares
// First rev with no ^Ms
//
// Revision 1.1.1.1  1998/01/19  14:02:55  rand
// Lee's Jan 19 sources
//
//
//----------------------------------------------------------------------------
