#include <alloc.h>
#include <wgt45.h>

/*
==============================================================================
		      WordUp Graphics Toolkit Version 4.5
			     Demonstration Program 68d

 Same as wgt68d.c, but shows the shaded resize routine.
 This one requires a mouse.  Hit a key to cycle through each of the
 resize shading modes.  ESC or Q quits the program.

 *** PROJECT ***
 This program requires the WGT45.LIB file to be linked.

 *** DATA FILES ***
 STREET.PAL, RUN.SPR
==============================================================================
*/

unsigned char shadowtable[256]; /* Shadow table */
unsigned char *transparency_table; /* a 65536 byte table,
				      allocated dynamically */

color pal[256];         /* The palette used for every graphic image */
block sprites[200];     /* Array of images for the running robot */

block background;       /* Holds the background scrolling image */
block work;             /* Page for constructing each frame */

int oldmode;

int animation_count;    /* Used for animation timing */
int movement_multiplier;/* Number of times to move objects, ground, etc */
int resize_mode;        /* Shade technique used for resize */

/* A structure which holds the scrolling values for each horizontal line */
typedef struct
  {
   long x;                /* Current x value, shifted by 8 */
   long increment;        /* fixed point increment */
  } line_scroll;
line_scroll lines[80];  /* 80 scrolling lines along the ground */

/* A simple sprite structure */
typedef struct
  {
   int x;
   int y;
   int anm;              /* Sprite number */
  } sprite;
sprite people[5];


long backx = 0;          /* X value for the scrolling rocks */
long backinc;            /* X increment for scrolling rocks */


/* Loads the graphics files, and allocates buffers */
void load_graphics (void)
{
  work = wallocblock (320, 200);
  /* Allocate a work buffer */

  wems_loadsprites (pal, "run.spr", sprites, 0, 199);
  background = wloadpak ("street.pak");
  wsetpalette (0, 255, pal);

  transparency_table = (unsigned char *)farmalloc (256L * 255);
}



/* Frees the buffers and sprites */
void free_graphics (void)
{
  farfree (transparency_table);
  wfreeblock (background);
  wfreeblock (work);
}


/* Set up the initial scrolling values */
void init_lines (void)
{
int i;
int inc;

  inc = 128;     /* slowest scrolling speed (128/256 of a pixel */

  backx = 0;     /* rocks x value */
  backinc = inc; /* rocks same speed as the ground */

  for (i = 0; i < 80; i++)
    {
     lines[i].x = 0;              /* clear out the x value */
     lines[i].increment = inc;    /* set the scroll speed */
     inc += 32;                   /* Make the next row move faster */
    }
}



/* Construct the background image */
void animate_lines (void)
{
block source1, source2;
block dest1, dest2;
block origsource, origdest;
int i;
int x;
int mult;

  wcopyscreen (0, 0, 319, 51, background, 0, 0, work);
  /* Draw the moon stationary */
 
 
  x = backx >> 8;

  /* Copy the rocks */
  wcopyscreen (x, 52, 319, 119, background, 0, 52, work);
  if (x > 0)
    wcopyscreen (0, 52, x-1, 119, background, 320 - x, 52, work);

  /* Scroll the rocks */
  for (mult = 0; mult <= movement_multiplier; mult++)
    {
     backx += backinc;
     if (backx >= 81920L)   /* 81920 is 320 << 8 */
       backx -= 81920L;
    }

  origdest = abuf + 120 * 320l;           /* First row to copy */
  origsource = background + 120 * 320l;   /* First row to copy */

  for (i = 0; i < 80; i++)
    {
     /* Scroll this line */
     for (mult = 0; mult <= movement_multiplier; mult++)
       {
	lines[i].x += lines[i].increment;
	if (lines[i].x >= 81920)   /* 81920 is 320 << 8, wraps scroll around */
	  lines[i].x -= 81920;
       }

     x = lines[i].x >> 8;
     /* Get the x coord */

     dest1 = origdest + i * 320;
     dest2 = dest1 + (319 - x);
     source1 = origsource + i * 320;
     source2 = source1 + x;


     /* Fastcopy is a function similar to memcpy, only it uses an unrolled
	dword loop for faster copying.  Source code for this routine is
	inside the wnpblock.c source file */

     /* Copy the line in two steps */
     fastcopy (dest1, source2, 320 - x);
     if (x > 0)
       fastcopy (dest2, source1, x + 1);
    }
}



/* Animates and displays the running man */
void animate_man (void)
{
unsigned char monocolor;
block emsbl;
int mult;
short rx, ry; 

  for (mult = 0; mult <= movement_multiplier; mult++)
    {
     people[0].anm++;
     if (people[0].anm > 29)
       people[0].anm = 0;
    }

 rx = mx;
 ry = my;
 emsbl = wems_getsprite (people[0].anm, sprites);

 switch (resize_mode)
 {
  case 0:  wresize_shade (0, 0, rx, ry, emsbl, shadowtable, SHADE_NORMAL);
	   break;
  case 1:  wresize_shade (0, 0, rx, ry, emsbl, shadowtable, SHADE_XRAY);
	   break;
  case 2:  wresize_shade (0, 0, rx, ry, emsbl, shadowtable, SHADE_SHADOW);
	   break;
  case 3:  wresize_shade (0, 0, rx, ry, emsbl, transparency_table, SHADE_TRANSLUCENT);
	   break;
  case 4:  monocolor = 15;
	   wresize_shade (0, 0, rx, ry, emsbl, &monocolor, SHADE_MONO);
	   break;
  }


  /* display the shadow */
  emsbl = wems_getsprite (people[0].anm + 30, sprites);
  wputblock_shade (people[0].x, 158, emsbl, shadowtable, SHADE_SHADOW);

  /* Show the running robot */
  emsbl = wems_getsprite (people[0].anm, sprites);
  wputblock_shade (people[0].x, people[0].y, emsbl,
		  transparency_table, SHADE_TRANSLUCENT);
}


void wcreate_shadow_table (color *palette)
{
float fr, fg, fb;
long ir, ig, ib;

long absr, absg, absb;

int r,g,b;

short col;
short findcol;

unsigned long lowest;
unsigned char bestfit;
unsigned long coldif;

  for (col = 0; col < 256; col++)
    {
     fr = (float)palette[col].r * (0.5);
     fg = (float)palette[col].g * (0.5);
     fb = (float)palette[col].b * (0.5);

     ir = fr;
     ig = fg;
     ib = fb;

     lowest = 655350;
     for  (findcol = 0; findcol < 256; findcol++)
       {
	absr = abs ( (long)palette[findcol].r - ir);
	absg = abs ( (long)palette[findcol].g - ig);
	absb = abs ( (long)palette[findcol].b - ib);

	coldif = absr + absg + absb;
	if  ((coldif < lowest) && (findcol != col))
	  {
	   lowest = coldif;
	   bestfit = findcol;
	  }
       }
     shadowtable[col] = bestfit;
    }
}


void wcreate_transparency_table (color *pal)
{
float lightlevel1;
float lightlevel2;
float fr, fg, fb;
float fr2, fg2, fb2;
long ir, ig, ib;

long absr, absg, absb;

short col, col2;
short findcol;

unsigned long lowest;
unsigned char bestfit;
unsigned long coldif;


  lightlevel1 = 0.5;     /* Percent of color 1 */
  lightlevel2 = 0.5;     /* Percent of color 2 */

  /* Lightlevel1 and lightlevel2 must total to 1 */

  /* Transparency is created by taking two colors, multiplying the
  RGB values by a percentage, and adding the RGB values together.  The
  new color will contain a little bit of each oringal color. */


  /* For each of the 256 colors, we can mix with any other color, therefore
  we need a 256x256 table, however we can't allocate 64k, so we'll
  have to settle for 255x256. */

  wtextcolor (15);
  wtexttransparent (TEXTFGBG);
  wgtprintf (0, 0, NULL, "Making transparency table...", col2);

  for (col2 = 0; col2 < 255; col2++)
    {
     for (col = 0; col < 256; col++)
       {
	fr = (float)pal[col].r * lightlevel1;
	fg = (float)pal[col].g * lightlevel1;
	fb = (float)pal[col].b * lightlevel1;

	fr2 = (float)pal[col2].r * lightlevel2;
	fg2 = (float)pal[col2].g * lightlevel2;
	fb2 = (float)pal[col2].b * lightlevel2;

	ir = (fr + fr2);
	ig = (fg + fg2);
	ib = (fb + fb2);

	lowest = 655350;
	for  (findcol = 0; findcol < 256; findcol++)
	  {
	   absr = abs ( (long)pal[findcol].r - ir) * 30;
	   absg = abs ( (long)pal[findcol].g - ig) * 59;
	   absb = abs ( (long)pal[findcol].b - ib) * 11;

	   coldif = sqrt(absr*absr + absg*absg + absb*absb);
	   if (coldif < lowest)
	     {
	      lowest = coldif;
	      bestfit = findcol;
	     }
	  }

	transparency_table[col2 * 256L + col] = bestfit;
       }
     wgtprintf (0, 8, NULL, "Color %03hi", col2);
    }
}



int load_table (char *filename, block table, int size)
{
FILE *in;

  in = fopen (filename, "rb");
  if (in == NULL)
    return 0;
  fread (table, size, 1, in);
  fclose (in);
  return 1;
}


void save_table (char *filename, block table, int size)
{
FILE *out;

  out = fopen (filename, "wb");
  fwrite (table, size, 1, out);
  fclose (out);
}



void far animation_timer (void)
{
 animation_count++;
}



void main (void)
{
char c;

  oldmode = wgetmode ();

  if (!vgadetected ())
    {
     printf ("VGA is required to run this program...");
     exit (1);
    }

  printf ("WGT Example #68a\n\n");
  printf ("This example shows how to make a scrolling image with depth.  It scrolls\n");
  printf ("each horizontal line of the ground by different amounts, the fastest\n");
  printf ("being closest to you.\n");
  printf ("\nPress any key to begin.\n");
  getch ();

  vga256 ();

  if (!wems_present ())
    {
     printf ("\n EMS not found!\n");
     exit (1);
    }
  if (!wems_init ())
    {
     printf ("\nEMS initialization error!\n");
     exit (1);
    }

  if (!wems_open (700000L / 16384L))
    {
     wsetmode (oldmode);
     printf ("\nNot enough EMS memory!\n");
     exit (1);
    }

  load_graphics ();

  init_lines ();

  minit ();

  /* Set the position of the running man */
  people[0].x = 120;
  people[0].y = 50;
  people[0].anm = 0;

  wcreate_shadow_table (pal);

  if (!load_table ("trans.dat", transparency_table, 256L * 255))
    {
     wcreate_transparency_table (pal);
     save_table ("trans.dat", transparency_table, 256L * 255);
    }

  animation_count = 0;
  winittimer ();
  wstarttimer (animation_timer, 30);


  do {
   wsetscreen (work);

   movement_multiplier = animation_count;
   if (movement_multiplier > 0)
     {
      animation_count = 0;

      animate_lines ();
      animate_man ();
      wnormscreen ();

      wputblock (0, 0, work, 0);
     }

   if (kbhit ())
    {
     c = getch();
     resize_mode++;
     if (resize_mode > 4)
	 resize_mode = 0;
    }

  } while ((c != 27) && (c != 'q') && (c != 'Q'));

  wstoptimer ();
  wdonetimer ();

  free_graphics ();
  mdeinit ();
  wsetmode (oldmode);
}
