/*********************************************/
/* you just keep on pushing my luck over the */
/*          BOULDER        DASH              */
/*                                           */
/*     Jeroen Houttuin, ETH Zurich, 1990     */
/*********************************************/

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include "xbd.h"

/* Manufaction a 32x32 graphics cursor used in a XFill... operation. */
GC
makegc(func, bits)
  int             func;		/* Drawing function such as GXcopy or GXor. */
  char            bits[];	/* Bits describing fill pattern.  Produced in
				 * an X11 */
/* bitmap file usually. */
{
  static XGCValues gcv;
  Pixmap          pmap;

  /* Build X11 bitmap from data in bits */
  pmap = XCreatePixmapFromBitmapData(disp, wind, bits, 32, 32, BlackPixel(disp, 0),
			       WhitePixel(disp, 0), DisplayPlanes(disp, 0));
  /* Assign the graphics cursor parameters */
  gcv.function = func;
  gcv.foreground = BlackPixel(disp, 0);
  gcv.background = WhitePixel(disp, 0);
  gcv.tile = pmap;
  gcv.fill_style = FillTiled;
  /* Return the created graphics cursor */
  return (XCreateGC(disp, wind, GCFunction | GCForeground | GCBackground |
		    GCTile | GCFillStyle, &gcv));
}

void
make_gcs()
{
  pgc = makegc(GXcopy, player_bits);
  pgc1 = makegc(GXcopy, player_bits);
  pgc2 = makegc(GXcopy, player2_bits);
  wgc = makegc(GXcopy, wall_bits);
  Wgc = makegc(GXcopy, wall_bits);
  Wgc2 = makegc(GXcopy, wall2_bits);
  sgc = makegc(GXcopy, space_bits);
  ggc = makegc(GXcopy, grass_bits);
  dgc = makegc(GXcopy, diamond_bits);
  dgc1 = makegc(GXcopy, diamond_bits);
  dgc2 = makegc(GXcopy, diamond2_bits);
  Sgc = makegc(GXcopy, steel_bits);
  bgc = makegc(GXcopy, boulder_bits);
  xgc = makegc(GXand, explosion_bits);
  lgc = makegc(GXcopy, lmonster_bits);
  lgc1 = makegc(GXcopy, lmonster_bits);
  lgc2 = makegc(GXcopy, lmonster2_bits);
  rgc = makegc(GXcopy, rmonster_bits);
  rgc1 = makegc(GXcopy, rmonster_bits);
  rgc2 = makegc(GXcopy, rmonster2_bits);
  egc = makegc(GXcopy, eater_bits);
  egc1 = makegc(GXcopy, eater_bits);
  egc2 = makegc(GXcopy, eater2_bits);
  Egc = makegc(GXcopy, steel_bits);
  Egc1 = makegc(GXcopy, steel_bits);
  Egc2 = makegc(GXcopy, exit2_bits);
  ngc = makegc(GXcopy, nucbal_bits);
  Bgc = makegc(GXcopy, blob_bits);
  Bgc1 = makegc(GXcopy, blob_bits);
  Bgc2 = makegc(GXcopy, blob2_bits);
  tgc = makegc(GXcopy, wall_bits);
  tgc1 = makegc(GXcopy, wall_bits);
  tgc2 = makegc(GXcopy, tinkle1_bits);
  tgc3 = makegc(GXcopy, tinkle2_bits);
}

void
init_level(levelnum)
  int             levelnum;
{

  FILE           *levelfile;
  char            buf[300], *ptr;
  extern char    *getenv();

  Egc = Egc1;			/* don't blink EXIT */
  blobcollapse = False;
  blobcells = 0;
  scoreobs = True;
  tinkact = False;
  levincreased = False;
  strcpy(levname, "No_name_for_this_level_yet");

  /* Manufaction the file name by starting with the world name and */
  /* appending the level number to it. */
  if (ptr = getenv("XBDLIB"))
    strcpy(filename, ptr);
  else
    strcpy(filename, LIB);
  strcat(filename, "/");
  strcat(filename, LEVELPREFIX);
  sprintf(filename + strlen(filename), "%03d", levelnum);
  /* Open level file for reading */
  levelfile = fopen(filename, "r");
  /* If level file does not exist, use the default level file. */
  if (levelfile == NULL)
  {
    /* Build the default level name */
    if (ptr = getenv("XBDLIB"))
      strcpy(buf, ptr);
    else
      strcpy(buf, LIB);
    strcat(buf, "/");
    strcat(buf, "/default");
    /* Open default level file for reading */
    levelfile = fopen(buf, "r");
    if (levelfile == NULL)
    {
      perror(LEVELPREFIX);
      exit(1);
    }
  }
  /* Load the first line of the level file */
  if (fgets(buf, 300, levelfile) == NULL)
  {
    x = w;
    y = h;
    speed = 15;
    diareq = 12;
    diapoints = 0;
    extradiapoints = 0;
    blobbreak = 200;
    tleft = 1000;
  } else
  {
    /* Extract the level parameters */
    sscanf(buf, "%d %d %d %d %d %d %d %d %d %s", &y, &x, &speed, &diareq,
	 &diapoints, &extradiapoints, &blobbreak, &tinkdur, &tleft, levname);
  }

  if (xin && yin)
  {
    x = xin;
    y = yin;
  }				/* read in from editor command line */
  /*
   * if (x > w) x = w; if (y > h) y = h; if (x < 2) x = 2; if (y < 1) y = 1;
   * 
  /*
   * Iterate through each horizontal line
   */
  for (i = 0; i < y; ++i)
  {
    /* Load the next line from the file */
    if (fgets(buf, 300, levelfile) != NULL)
    {
      /* Go through each horizontal position and copy the data into */
      /* the level array. */
      for (j = 0; j < x; ++j)
      {
	/* Break out if line ends prematurely */
	if (buf[j] == '\n' || buf[j] == '\0')
	  field[i][j].content = STEEL;	/* break; */
	field[i][j].content = buf[j];
	field[i][j].changed = True;
	field[i][j].dir = N;
	field[i][j].speed = 0;
	field[i][j].stage = 0;
	field[i][j].caught = True;
	field[i][j].checked = False;
      }
    } else
      j = 0;
    for (; j < x; ++j)
      field[i][j].content = STEEL;
  }
  /* Close the level file */
  fclose(levelfile);
}

/* Draw the score and level number */
void
draw_score()
{
  char            buf[200];

  /* Build the output string */
  sprintf(buf, "sc:%d lv:%d ls:%d ds:%d dp:%d ti:%d       %s", score, levelnum,
	  lives, diareq, diapoints, tleft / 10, levname);
  /* Clear the current score line */
  XFillRectangle(disp, wind, whitegc, 0, y << 5, x << 5, SCORESIZE);
  /* Actually draw the text */
  XDrawString(disp, wind, scoregc, 0, (y << 5) + SCORESIZE - 1, buf,
	      strlen(buf));
  scoreobs = False;
}

void
xstart(evmask)
  long            evmask;	/* Event mask which will be used in
				 * XSelectInput */
{
  XGCValues       xgcv;
  XWMHints        wmhints;

  /* create X window */
  disp = XOpenDisplay(NULL);
  /* Check to see if the open display succeeded */
  if (disp == NULL)
  {
    fprintf(stderr, "Display open failed.  Check DISPLAY environment variable.\n"
      );
    exit(-1);
  }
  wind = XCreateSimpleWindow(disp, DefaultRootWindow(disp), 0, 1,
			     x << 5, (y << 5) + SCORESIZE, 1,
			     WhitePixel(disp, 0), BlackPixel(disp, 0));
  /* Check to see if the open window succeeded */
  if (wind == 0)
  {
    fprintf(stderr, "Window open failed.\n");
    XCloseDisplay(disp);
    exit(-1);
  }
  /* Load in the font used to display the score */
  scorefont = XLoadFont(disp, SCOREFONT);
  /* Create GC which will be used from drawing score */
  xgcv.function = GXcopy;
  xgcv.font = scorefont;
  xgcv.foreground = BlackPixel(disp, 0);
  xgcv.background = WhitePixel(disp, 0);
  scoregc = XCreateGC(disp, wind,
		      GCFunction | GCFont | GCForeground | GCBackground,
		      &xgcv);
  /* Create GC which will be used for clearing score line */
  xgcv.function = GXcopy;
  scorefont = XLoadFont(disp, SCOREFONT);
  xgcv.foreground = WhitePixel(disp, 0);
  xgcv.background = BlackPixel(disp, 0);
  whitegc = XCreateGC(disp, wind,
		      GCFunction | GCForeground | GCBackground,
		      &xgcv);
  wmhints.flags = InputHint;
  wmhints.input = True;
  XSetWMHints(disp, wind, &wmhints);

  XSelectInput(disp, wind, evmask);
  XMapRaised(disp, wind);
  XWarpPointer(disp, None, wind, 0, 0, 0, 0, 11, 1);
}

/* Gracefully shut X windows down.  It is not strictly necessary to */
/* call this function. */
void
xend()
{
  gamestop = True;		/* tricky; prevent ticker function from using
				 * Xlib fcts */
  XUnloadFont(disp, scorefont);
  XUnmapWindow(disp, wind);
  XDestroyWindow(disp, wind);
  XCloseDisplay(disp);
}

void
draw_field(redrawall)
  short           redrawall;
{
  char            c;

  /* Iterate through each horizontal line */
  for (i = y - 1; i >= 0; --i)
  {
    for (j = 0; j < x; ++j)
    {
      if (field[i][j].changed || redrawall)	/* only redraw changed cells */
      {
	c = field[i][j].content;
	switch (c)
	{
	case GRASS:
	  XFillRectangle(disp, wind, ggc, j << 5, i << 5, 32, 32);
	  break;
	case SPACE:
	  XFillRectangle(disp, wind, sgc, j << 5, i << 5, 32, 32);
	  break;
	case PLAYER:
	  XFillRectangle(disp, wind, pgc, j << 5, i << 5, 32, 32);
	  break;
	case WALL:
	  XFillRectangle(disp, wind, wgc, j << 5, i << 5, 32, 32);
	  break;
	case MAGICWALL:
	  XFillRectangle(disp, wind, Wgc, j << 5, i << 5, 32, 32);
	  break;
	case DIAMOND:
	  XFillRectangle(disp, wind, dgc, j << 5, i << 5, 32, 32);
	  break;
	case BOULDER:
	  XFillRectangle(disp, wind, bgc, j << 5, i << 5, 32, 32);
	  break;
	case EXPLOSION:
	  XFillRectangle(disp, wind, xgc, j << 5, i << 5, 32, 32);
	  break;
	case LMONSTER:
	  XFillRectangle(disp, wind, lgc, j << 5, i << 5, 32, 32);
	  break;
	case RMONSTER:
	  XFillRectangle(disp, wind, rgc, j << 5, i << 5, 32, 32);
	  break;
	case NUCBAL:
	  XFillRectangle(disp, wind, ngc, j << 5, i << 5, 32, 32);
	  break;
	case BLOB:
	  XFillRectangle(disp, wind, Bgc, j << 5, i << 5, 32, 32);
	  break;
	case TINKLE:
	  XFillRectangle(disp, wind, tgc, j << 5, i << 5, 32, 32);
	  break;
	case EATER:
	  XFillRectangle(disp, wind, egc, j << 5, i << 5, 32, 32);
	  break;
	case EXIT:
	  XFillRectangle(disp, wind, Egc, j << 5, i << 5, 32, 32);
	  break;
	case STEEL:
	default:
	  field[i][j].content = STEEL;
	  XFillRectangle(disp, wind, Sgc, j << 5, i << 5, 32, 32);
	  break;
	}
	field[i][j].changed = False;
      }
    }
  }
  if (scoreobs)
    draw_score();
}

void
set_cell(i, j, content)
  int             i, j;
  char            content;
{
  field[i][j].content = content;
  field[i][j].speed = 0;
  field[i][j].changed = True;
  field[i][j].stage = 0;
  field[i][j].caught = True;
  field[i][j].checked = False;
}
