/*	display.c */
#include "header.h"

#define makecode(_a,_b,_c) (((long)(_a)<<16) + ((_b)<<8) + (_c))

extern int dropflag;           /* in main.c; yuck ! */

static int minx,maxx,miny,maxy,k,m;
static char bot1f=0,bot2f=0,bot3f=0;
char always=0;

#if DEBLVL > 0
static FILE* debfd = NULL;

deb_log(char * msg)
{
  if ( debfd == NULL ){
    debfd=fopen("zzzLOGU", "a");
  };
  fprintf(debfd, "%s\n", msg);
  fflush(debfd);
}
#endif

/*
  bottomline()
  
  for the bottom line of the display
  */
bottomline()
{
  DEB1("Abottomline");	
  recalc();	
  bot1f=1;	
  DEB1("Ebottomline");
}
bottomhp()
{	
  bot2f=1;	
}
bottomspell()
{	
  bot3f=1;	
}
bottomdo()
{
  if (bot1f) { 
    bot3f=bot1f=bot2f=0; 
    bot_linex(); 
    return; 
  }
  if (bot2f) { 
    bot2f=0; 
    bot_hpx(); 
  }
  if (bot3f) { 
    bot3f=0; 
    bot_spellx(); 
  }
}

bot_linex()
{
  register int i;
  if (cbak[SPELLS] <= -50 || (always)) {
    cursor( 1,18);
    if (c[SPELLMAX]>99)  
      lprintf("Spells:%3d(%3d)", c[SPELLS],c[SPELLMAX]);
    else 
      lprintf("Spells:%3d(%2d) ",c[SPELLS],c[SPELLMAX]);
    lprintf(" AC: %-3d  WC: %-3d  Level",c[AC],c[WCLASS]);
    if (c[LEVEL]>99) 
      lprintf("%3d",c[LEVEL]);
    else 
      lprintf(" %-2d",c[LEVEL]);
    lprintf(" Exp: %-9d %s\n",c[EXPERIENCE],class[c[LEVEL]-1]);
    lprintf("HP: %3d(%3d) STR=%-2d INT=%-2d ",
	    c[HP],c[HPMAX],c[STRENGTH]+c[STREXTRA],
	    c[INTELLIGENCE]);
    lprintf("WIS=%-2d CON=%-2d DEX=%-2d CHA=%-2d LV:",
	    c[WISDOM],c[CONSTITUTION],c[DEXTERITY],c[CHARISMA]);
    
    if ((level==0) || (wizard))  
      c[TELEFLAG]=0;
    if (c[TELEFLAG])  
      lprcat(" ?");  
    else  
      lprcat(levelname[level]);
    lprintf("  Gold: %-6d",c[GOLD]);
    always=1;  
    botside();
    c[TMP] = c[STRENGTH]+c[STREXTRA];
    for (i=0; i<100; i++) cbak[i]=c[i];
    return;
  }
  
  botsub(makecode(SPELLS,8,18),"%3d");
  if (c[SPELLMAX]>99)  
    botsub(makecode(SPELLMAX,12,18),"%3d)");
  else 
    botsub(makecode(SPELLMAX,12,18),"%2d) ");
  botsub(makecode(HP,5,19),"%3d");
  botsub(makecode(HPMAX,9,19),"%3d");
  botsub(makecode(AC,21,18),"%-3d");
  botsub(makecode(WCLASS,30,18),"%-3d");
  botsub(makecode(EXPERIENCE,49,18),"%-9d");
  if (c[LEVEL] != cbak[LEVEL]) { 
    cursor(59,18);	
    lprcat(class[c[LEVEL]-1]);  
  }
  if (c[LEVEL]>99) 
    botsub(makecode(LEVEL,40,18),"%3d");
  else 
    botsub(makecode(LEVEL,40,18)," %-2d");
  c[TMP] = c[STRENGTH]+c[STREXTRA];	
  botsub(makecode(TMP,18,19),"%-2d");
  botsub(makecode(INTELLIGENCE,25,19),"%-2d");
  botsub(makecode(WISDOM,32,19),"%-2d");
  botsub(makecode(CONSTITUTION,39,19),"%-2d");
  botsub(makecode(DEXTERITY,46,19),"%-2d");
  botsub(makecode(CHARISMA,53,19),"%-2d");
  if ((level != cbak[CAVELEVEL]) || (c[TELEFLAG] != cbak[TELEFLAG])) {
    if ((level==0) || (wizard))  c[TELEFLAG]=0;
    cbak[TELEFLAG] = c[TELEFLAG];
    cbak[CAVELEVEL] = level;	
    cursor(59,19);
    if (c[TELEFLAG])  
      lprcat(" ?");  
    else  
      lprcat(levelname[level]);
  }
  botsub(makecode(GOLD,69,19),"%-6d");
  botside();
}

/*
  special subroutine to update only the gold number on the bottomlines
  called from ogold()
  */
bottomgold()
{
  botsub(makecode(GOLD,69,19),"%-6d");
}

/*
  special routine to update hp and level fields on bottom lines
  called in monster.c hitplayer() and spattack()
  */
bot_hpx()
{
  if (c[EXPERIENCE] != cbak[EXPERIENCE]) {
    recalc();
    bot_linex();
  }
  else botsub(makecode(HP,5,19),"%3d");	
}

/*
  special routine to update number of spells called from regen()
  */
bot_spellx()
{
  botsub(makecode(SPELLS,9,18),"%2d");
}

/*
  common subroutine for a more economical bottomline()
  */
static struct bot_side_def
{
  int typ;
  char *string;
}
bot_data[] =
{
  STEALTH,"stealth",		
  UNDEADPRO,"undead pro",		
  SPIRITPRO,"spirit pro",
  CHARMCOUNT,"Charm",		
  TIMESTOP,"Time Stop",		
  HOLDMONST,"Hold Monst",
  GIANTSTR,"Giant Str",	
  FIRERESISTANCE,"Fire Resit", 
  DEXCOUNT,"Dexterity",
  STRCOUNT,"Strength",	
  SCAREMONST,"Scare",			
  HASTESELF,"Haste Self",
  CANCELLATION,"Cancel",	
  INVISIBILITY,"Invisible",	
  ALTPRO,"Protect 5",
  PROTECTIONTIME,"Protect 2", 
  WTW,"Wall-Walk"
  };

botside()
{
  register int i,idx;
  
  for (i=0; i<17; i++) {
    idx = bot_data[i].typ;
    if ((always) || (c[idx] != cbak[idx])) {
      if ((always) || (cbak[idx] == 0)) { 
	if (c[idx]) { 
	  cursor(70,i+1); 
	  lprcat(bot_data[i].string); 
	} 
      }  
      else
	if (c[idx]==0)     { 
	  cursor(70,i+1); 
	  lprcat("          "); 
	}
      cbak[idx]=c[idx];
    }
  }
  always=0;
}

botsub(idx,str)
     register long idx;
     char *str;
{
  register int x,y;
  
  y = idx & 0xff;
  x = (idx>>8) & 0xff;
  idx >>= 16;
  if (c[idx] != cbak[idx]) { 
    cbak[idx]=c[idx];  
    cursor(x,y);  
    lprintf(str,c[idx]); 
  }
}

/*
 *	subroutine to draw only a section of the screen
 *	only the top section of the screen is updated.  If entire lines are being
 *	drawn, then they will be cleared first.
 */
int d_xmin=0,d_xmax=MAXX,d_ymin=0,d_ymax=MAXY;	/* for limited screen drawing */
draws(xmin,xmax,ymin,ymax)
     int xmin,xmax,ymin,ymax;
{
  register int i,idx;
  if (xmin==0 && xmax==MAXX) /* clear section of screen as needed */
  {
    if (ymin==0) cl_up(79,ymax);
    else for (i=ymin; i<ymin; i++)  cl_line(1,i+1);
    xmin = -1;
  }
  d_xmin=xmin;	
  d_xmax=xmax;	
  d_ymin=ymin;	
  d_ymax=ymax;	/* for limited screen drawing */
  drawscreen();
  if (xmin<=0 && xmax==MAXX)  {
    /* draw stuff on right side of screen as needed*/
    for (i=ymin; i<ymax; i++) {
      idx = bot_data[i].typ;
      if (c[idx]) {
	cursor(70,i+1); 
	lprcat(bot_data[i].string);
      }
      cbak[idx]=c[idx];
    }
  }
}

/*
  drawscreen()
  
  subroutine to redraw the whole screen as the player knows it
  */
char screen[MAXX][MAXY],d_flag;	/* template for the screen */
drawscreen()
{
  register int i,j,k;
  
  int lastx,lasty;  /* variables used to optimize the object printing */
  DEB1("Adrawscreen");
  if (d_xmin==0 && d_xmax==MAXX && d_ymin==0 && d_ymax==MAXY) {
    d_flag=1;  
    clear(); /* clear the screen */
  }
  else {
    d_flag=0;  
    cursor(1,1);
  }
  if (d_xmin<0)
    d_xmin=0; /* d_xmin=-1 means display all without bottomline */
  
  for (i=d_ymin; i<d_ymax; i++)
    for (j=d_xmin; j<d_xmax; j++)
      if (know[j][i]==0)  screen[j][i] = ',';  
      else if (know[j][i]==2)
	screen[j][i] = objnamelist[0];
      else
	if ((k=mitem[j][i])!=0)  {
	  if (k==MIMIC)
	    screen[j][i] = 
	      monstnamelist[rund(MAXMONST)];
	  else
	    screen[j][i] = monstnamelist[k];  
	}
	else
	  if ((k=item[j][i])==OWALL) 
	    screen[j][i] = objnamelist[OWALL];
	  else
	    screen[j][i] = objnamelist[0];
  
  for (i=d_ymin; i<d_ymax; i++) {
    j=d_xmin;  
    while ((screen[j][i]==' ') && (j<d_xmax)) j++;
    if (j >= d_xmax)  
      m=d_xmin; /* don't search backwards if blank line */
    else {	/* search backwards for end of line */
      m=d_xmax-1;  
      while ((screen[m][i]==' ') && (m>d_xmin)) --m;
      if (j<=m)  cursor(j+1,i+1);  
      else continue;
    }
    while (j <= m) {
      if (j <= m-3) {
	for (k=j; k<=j+3; k++) 
	  if (screen[k][i] != ' ') 
	    k=1000;
	if (k < 1000) { 
	  while(screen[j][i]==' ' && j<=m) 
	    j++;  
	  cursor(j+1,i+1); 
	}
      }
      lprc(screen[j++][i]);
    }
  }
  if (boldon) setbold();		/* print out only bold objects now */
  
  for (lastx=lasty=127, i=d_ymin; i<d_ymax; i++)
    for (j=d_xmin; j<d_xmax; j++) {
      if ((k=item[j][i])!=0)
	if (k != OWALL)
	  if ((know[j][i]) && (mitem[j][i]==0))
	    if (objnamelist[k]!=objnamelist[0]) {
	      if (lasty!=i+1 || lastx!=j)
		cursor(lastx=j+1,lasty=i+1);
	      else lastx++;
	      lprc(objnamelist[k]);
	    }
    }
  
  if (boldon) resetbold();  
  if (d_flag)  { 
    always=1; 
    botside(); 
    always=1; 
    bot_linex(); 
  }
  oldx=99;
  /* for limited screen drawing */
  d_xmin = 0 , d_xmax = MAXX , d_ymin = 0 , d_ymax = MAXY; 
  DEB1("Edrawscreen");
}

/*
  showcell(x,y)
  
  subroutine to display a cell location on the screen
  */
showcell(x,y)
     int x,y;
{
  register int i,j,k,m;
  
  if (c[BLINDCOUNT])  return;	/* see nothing if blind		*/
  if (c[AWARENESS]) { 
    minx = x-3;	
    maxx = x+3;	
    miny = y-3;	
    maxy = y+3; 
  }
  else	  { 
    minx = x-1;	
    maxx = x+1;	
    miny = y-1;	
    maxy = y+1; 
  }
  
  if (minx < 0) minx=0;		
  if (maxx > MAXX-1) maxx = MAXX-1;
  if (miny < 0) miny=0;		
  if (maxy > MAXY-1) maxy = MAXY-1;
  
  for (j=miny; j<=maxy; j++)
    for (m=minx; m<=maxx; m++)
      if ((know[m][j] & 1)==0) {
	cursor(m+1,j+1);
	x=maxx;  
	while (know[x][j] & 1) --x;
	for (i=m; i<=x; i++) {
	  if ((k=mitem[i][j]) != 0) {
	    if (k==MIMIC)
	      lprc(monstnamelist[rund(MAXMONST)]);
	    else
	      lprc(monstnamelist[k]);
	  }
	  else switch(k=item[i][j]) {
	  case 0: 
	  case OWALL:  
	  case OIVTELETRAP:  
	  case OTRAPARROWIV:
	  case OIVDARTRAP: 
	  case OIVTRAPDOOR:	
	    lprc(objnamelist[k]);	
	    break;
	    
	  default: 
	    if (boldon) setbold(); 
	    lprc(objnamelist[k]); 
	    if (boldon) resetbold();
	  };
	  know[i][j]=1;
	}
	m = maxx;
      }
}

/*
  this routine shows only the spot that is given it.  the spaces around
  these coordinated are not shown
  used in godirect() in monster.c for missile weapons display
  */
show1cell(x,y)
     int x,y;
{
  if (c[BLINDCOUNT])  return;	/* see nothing if blind		*/
  cursor(x+1,y+1);
  if ((k=mitem[x][y]) != 0)  {
    if (k==MIMIC)
      lprc(monstnamelist[rund(MAXMONST)]);
    else
      lprc(monstnamelist[k]);
  }
  else switch(k=item[x][y]) {
  case OWALL:  
  case 0:  
  case OIVTELETRAP:  
  case OTRAPARROWIV: 
  case OIVDARTRAP: 
  case OIVTRAPDOOR:	
    lprc(objnamelist[k]);	
    break;
    
  default: 
    if (boldon) setbold(); 
    lprc(objnamelist[k]); 
    if (boldon) resetbold();
  };
  know[x][y]|=1;	/* we end up knowing about it */
}

/*
  showplayer()
  
  subroutine to show where the player is on the screen
  cursor values start from 1 up
  */
showplayer()
{
  cursor(playerx+1,playery+1);
  oldx=playerx;  
  oldy=playery;
}

/*
  moveplayer(dir)
  
  subroutine to move the player from one room to another
  returns 0 if can't move in that direction or hit a monster or on an object
  else returns 1
  nomove is set to 1 to stop the next move (inadvertent monsters hitting
  players when walking into walls) if player walks off screen or into wall
  */
short diroffx[] = { 0,  0, 1,  0, -1,  1, -1, 1, -1 };
short diroffy[] = { 0,  1, 0, -1,  0, -1, -1, 1,  1 };
static int last_dir = 0;
static int next1_dir = 0; /* for zig-zag */
static int next2_dir = 0; /* for zig-zig-zag-zag */

get_nxt1_dir()
{
  return(next1_dir);
}

get_nxt2_dir()
{
  return(next2_dir);
}

moveplayer(dir)
     int dir;	/*	from = present room #  direction = [1-north]
			[2-east] [3-south] [4-west] [5-northeast]
			[6-northwest] [7-southeast] [8-southwest]
			if direction=0, don't move--just show where he is */
{
  register int k,m,i,j;
  
  /* last_dir = 0; not used, it's better like this */
  if( (dir >= 5) && (dir <= 8) ){                    /* diagonals ? */
    next1_dir   = (dir <= 6 ? dir+2 : dir-2);
    if( dir == last_dir ){                           /* zig-zig ?   */
      next2_dir = (dir <= 6 ? dir+2 : dir-2);
    };
    last_dir = dir;
  };
  
  if (c[CONFUSE]) 
    if (c[LEVEL]<rnd(30)) dir=rund(9); /*if confused any dir*/
  k = playerx + diroffx[dir];		
  m = playery + diroffy[dir];
  
  if (k<0 || k>=MAXX || m<0 || m>=MAXY) { 
    nomove=1; 
    return(yrepcount = 0); 
  }
  
  i = item[k][m];
  j = mitem[k][m];
  
  /*	hit a wall	*/
  if (i==OWALL && c[WTW]==0) { 
    nomove=1;  
    return(yrepcount = 0); 
  }		
  
  if (k==33 && m==MAXY-1 && level==1) {
    newcavelevel(0); 
    for (k=0; k<MAXX; k++) for (m=0; m<MAXY; m++)
      if (item[k][m]==OENTRANCE) { 
	playerx=k; 
	playery=m; 
	/* deleted, ws, 08.08.93	positionplayer();  */ 
	drawscreen(); 
	dropflag=1; /* no stupid questions */
	return(0); 
      }
  }
  if (j>0)     { 	
    hitmonster(k,m);	
    return(yrepcount = 0); 
  } /* hit a monster*/
  
  lastpx = playerx;			
  lastpy = playery;
  playerx = k;		
  playery = m;
  
  if (i && i!=OTRAPARROWIV && i!=OIVTELETRAP && i!=OIVDARTRAP 
      && i!=OIVTRAPDOOR) 
    return(yrepcount = 0);  
  else return(1);
}

/*
 *	function to show what magic items have been discovered thus far
 *	enter with -1 for just spells, 99 gives all spells in game (for when 
 *	genie asks you what you want). anything else will give scrolls & potions
 */
static int lincount,count;

seemagic(arg)
     int arg;
{
  register int i,number;
  count = lincount = 0;  
  nosignal=1;
  
  if (arg == 99) {
    number = (SPNUM+2)/3 + 4;	/* # lines needed to display */
    cl_up(79,number);  
    cursor(1,1);
    lprcat("Availible spells are:\n\n");
    for (i=0; i<SPNUM; i++) {
      lprintf("%s %-20s ",spelcode[i],spelname[i]);  
      seepage(); 
    }
    seepage();  
    more();	 
    nosignal=0;
    draws(0,MAXX,0,number);  
    return;
  }
  if (arg== -1) /* if display spells while casting one */
  {
    for (number=i=0; i<SPNUM; i++) if (spelknow[i]) number++;
    number = (number+2)/3 + 4;	/* # lines needed to display */
    cl_up(79,number);  
    cursor(1,1);
  }
  else {
    resetscroll();  
    clear();
  }
  
  lprcat("The magic spells you have discovered thus far:\n\n");
  for (i=0; i<SPNUM; i++)
    if (spelknow[i]) { 
      lprintf("%s %-20s ",spelcode[i],spelname[i]);  
      seepage(); 
    }
  
  if (arg== -1) {
    seepage();  
    more();	 
    nosignal=0;
    draws(0,MAXX,0,number);  
    return;
  }
  
  lincount += 3;  
  if (count!=0) { 
    count=2;  
    seepage(); 
  }
  
  lprcat("\nThe magic scrolls currently known to you are:\n\n");
  count=0;
  for (i=0; i<MAXSCROLL; i++)
    if (scrollname[i][0])
      if (scrollname[i][1]!=' ') { 
	lprintf("%-26s",&scrollname[i][1]);  
	seepage(); 
      }
  
  lincount += 3;  
  if (count!=0) { 
    count=2;  
    seepage(); 
  }
  
  lprcat("\nThe magic potions currently known to you are:\n\n");
  count=0;
  for (i=0; i<MAXPOTION; i++)
    if (potionname[i][0])
      if (potionname[i][1]!=' ') { 
	lprintf("%-26s",&potionname[i][1]);  
	seepage(); 
      }
  
  if (lincount!=0) more();	
  nosignal=0;  
  setscroll();	
  drawscreen();
}

/*
 *	subroutine to paginate the seemagic function
 */
seepage()
{
  if (++count==3) {
    lincount++;	
    count=0;	
    lprc('\n');
    if (lincount>17) {	
      lincount=0;  
      more();  
      clear();	
    }
  }
}






