/*
==============================================================================
		      WordUp Graphics Toolkit Version 5.0
			     Demonstration Program 56

 This program uses the scrolling library to create a parallax scrolling      
 world with two layers.  There are three kinds of objects in the world;      
 clouds, yellow balls (wearing shades) that move behind the front layer,     
 and yellow balls that move in front.  Use the arrow keys to move around     
 and ESC will exit.                                                          

 *** PROJECT ***                                                             
 This program requires the files WGT5_WC.LIB and WSCR_WC.LIB to be linked.   
									      
 *** DATA FILES ***                                                          
 PARABACK.SPR, PARAFRON.SPR, PARASPR.SPR, PARAMAPB.WMP, PARAMAPF.WMP         
							   WATCOM C++ VERSION 
==============================================================================
*/

#include <stdlib.h>
#include <dos.h>
#include <wgt5.h>
#include <wgtscrol.h>


#define WIND_SIZEX 20 /* Set scrolling window dimensions (in tiles) */
#define WIND_SIZEY 13

#define SPEED 8       /* Speed of scrolling */

#define LEFT 75       /* Keyboard codes */
#define RIGHT 77
#define UP 72
#define DOWN 80
#define ESC 1
#define KEY_S 31
#define KEY_L 38


#define BACKWIN 0       /* The NORMAL layer */
#define FRONTWIN 1      /* The PARALLAX layer */

block backtiles[256];           /* Tiles, objects and tiles types for the */
scrollsprite backobject[200];   /* background window. */
short backtypes[256];

block fronttiles[256];          /* Tiles, objects and tiles types for the */
scrollsprite frontobject[200];  /* foreground window. */
short fronttypes[256];

block sprites[50];              /* The sprite images */

int timer;                      /* Counts timer ticks */

color pal[256];

short movex, movey;               /* Amount to scroll the front */
short bmovex, bmovey;             /* Amount to scroll the back */

short x, y;                       /* Current viewing coordinates */
                                  /* The window tries to center itself on
                                     this coordinate. */

short num_clouds;         /* Number of cloud sprites on background */
short num_ball_behind;    /* Number of yellow balls behind front layer.
			   All yellow balls with object numbers 100-199 are
			   drawn between the background and foreground 
			   layers. */
short num_ball_infront;   /* Number of yellow balls in front of every layer */

short cloud_speed[100];   /* Movement speed and direction for clouds */

short oldmode;            /* Old video mode */

wgtmap backmap;  /* Holds the scrolling map */
wgtmap frontmap;  /* Holds the scrolling map */


void find_sprites (void)
/* Searches for the maximum object numbers for each of the three categories
   of wobjects. (clouds, ball behind, ball in front) */
{
 short i;

 num_clouds = 0;
 for (i = 0; i < 100; i++)
   if (backobject[i].on)
      num_clouds = i;

 num_ball_behind = 0;
 for (i = 100; i < 200; i++)
   if (frontobject[i].on)
      num_ball_behind = i;

 num_ball_infront = 0;
 for (i = 0; i < 100; i++)
   if (frontobject[i].on)
      num_ball_infront = i;
}


void set_cloud_movements (void)
/* Sets up the movement and speed for clouds */
{
  short i;

  for (i = 0; i < num_clouds; i++)
  {
    cloud_speed[i] = (rand () % 6)-3;
    if (cloud_speed[i] == 0)
      cloud_speed[i] = 1;
  }
}


void move_clouds (scrollsprite *s)
/* Moves the clouds and wraps them around the map if needed */
{
  short i;
  short maxx;
 
  maxx = mapwidth[BACKWIN] * tilewidth[BACKWIN];

  for (i = 0; i < num_clouds; i++)
  {
    backobject[i].x += cloud_speed[i];
    if (backobject[i].x < -80)
       backobject[i].x = maxx;
    else if (backobject[i].x > maxx)
       backobject[i].x = -80;
   }
}


void move_balls (void)
/* Moves the balls around randomly */
{
  short i;
  short maxx, maxy;
 
  maxx = mapwidth[BACKWIN] * tilewidth[BACKWIN];
  maxy = mapheight[BACKWIN] * tileheight[BACKWIN];

  for (i = 0; i <= num_ball_infront; i++)
  {
    frontobject[i].x += (rand () % 17) - 8;    /* Randomly move the ball */
    frontobject[i].y += (rand () % 17) - 8;

    if (frontobject[i].x < 0)                   /* Check the X boundaries */
      frontobject[i].x = 0;
    else if (frontobject[i].x > maxx)
      frontobject[i].x = maxx;

    if (frontobject[i].y < 0)                   /* Check the Y boundaries */
      frontobject[i].y = 0;
    else if (frontobject[i].y > maxy)
      frontobject[i].y = maxy;
  }

  for (i = 100; i <= num_ball_behind; i++)
   /* Do the same with the balls behind the front layer. */
  {
    frontobject[i].x += (rand () % 17) - 8;
    frontobject[i].y += (rand () % 17) - 8;

    if (frontobject[i].x < 0)
      frontobject[i].x = 0;
    else if (frontobject[i].x > maxx)
      frontobject[i].x = maxx;
    if (frontobject[i].y < 0)
      frontobject[i].y = 0;
    else if (frontobject[i].y > maxy)
      frontobject[i].y = maxy;
  }
}


void timerctr (void)
{
  timer++;
}


void main (void)
{
  float seconds, fps;
  int frames = 0;  

  oldmode = wgetmode ();
  if (!vgadetected ())
  {
    printf ("VGA is required to run this program...");
    exit (1);
  }
  printf ("WGT Example #56\n\n");

  printf ("This program uses the scrolling library to create a parallax scrolling\n");
  printf ("world with two layers.  There are three kinds of objects in the world\n");
  printf ("clouds, yellow balls (wearing shades) that move behind the front layer,\n");
  printf ("and yellow balls that move in front.  Use the arrow keys to move around\n");
  printf ("and ESC will exit.\n");
  printf ("You can also save and load the map by pressing S and L.\n");
  printf ("\nPress any key to begin\n");
  getch ();


  vga256 ();

  wloadsprites (pal, "paraspr.spr", sprites, 0, 49);

  wloadsprites (pal, "paraback.spr", backtiles, 0, 255);
  wloadsprites (pal, "parafron.spr", fronttiles, 0, 255);
  wsetpalette (0, 255, pal);

  winitscroll (BACKWIN, NORMAL, 0, WIND_SIZEX / 2, WIND_SIZEY / 2, backtiles);
  /* Uses half the size since the background tiles are twice as big (32x32) */

  winitscroll (FRONTWIN, PARALLAX, BACKWIN, WIND_SIZEX, WIND_SIZEY, fronttiles);
  /* Link this window to the background one */

  backmap  = wloadmap (BACKWIN, "paramapb.wmp", backtypes, backobject);
  frontmap = wloadmap (FRONTWIN, "paramapf.wmp", fronttypes, frontobject);
  /* Load the maps in for both windows */

  wshowwindow (BACKWIN, 0, 0);
  wshowwindow (FRONTWIN, 0, 0);
  /* Set the initial viewing coordinates. */

  find_sprites ();
  set_cloud_movements ();

  installkbd ();                 /* Install the custom keyboard handler */
  timer = 0;
  winittimer ();
  wstarttimer (timerctr, TICKS(70));
  do {
    move_clouds (backobject);
    move_balls ();

    if (kbdon[LEFT])             /* Change the viewing coordinates */
    {
      x -= 8;
      if (x < 0)
	x = 0;
    }
    if (kbdon[RIGHT])
    {
      x += 8;
      if (x > worldmaxx[FRONTWIN]) 
	x = worldmaxx[FRONTWIN];
    }
    if (kbdon[UP])
    {
      y -= 8;
      if (y < 0) 
	y = 0;
    }
    if (kbdon[DOWN])
    {
      y += 8;
      if (y > worldmaxy[FRONTWIN]) 
	y = worldmaxy[FRONTWIN];
    }

    /* Move the foreground by finding the difference between the actual
       world coordinates and the desired world coordinates.  This will
       make sure the window is centered on the (x,y) coordinate. */
    movex = x - worldx[FRONTWIN]; 
    movey = y - worldy[FRONTWIN];

    bmovex = worldx[FRONTWIN] + movex;
    bmovey = worldy[FRONTWIN] + movey;
    /* World coordinates of the front windows, after the current move */

    /* We must 'clip' them */
    if (bmovex < 0)
        bmovex = 0;
    if (bmovey < 0)
        bmovey = 0;
    if (bmovex > worldmaxx[FRONTWIN] - 1)
        bmovex = worldmaxx[FRONTWIN] - 1;
    if (bmovey > worldmaxy[FRONTWIN] - 1)
        bmovey = worldmaxy[FRONTWIN] - 1;

    /* Now make the background coordinates a percentage of the front window */
    bmovex = bmovex * 4 / 5;
    bmovey = bmovey * 9 / 10;

    worldx[BACKWIN] = bmovex;    
    worldy[BACKWIN] = bmovey;    
    /* Set the new background window coordinates */

    wscrollwindow (BACKWIN, 0, 0);
    /* Display the background layer. (Layers are drawn from back to front.) */
    /* No need to scroll by a value, because we already moved it by
       changing worldx and worldy */


    wshowobjects(BACKWIN, 0, num_clouds, sprites, backobject);
    /* Show the clouds next */

    wshowobjects(BACKWIN, 100, num_ball_behind, sprites, frontobject);
    /* Show the balls that are behind the front layer. (They will be just in 
       front of the clouds) */
   
    wscrollwindow (FRONTWIN, movex, movey);
    /* Display the foreground layer */

    wshowobjects(FRONTWIN, 0, num_ball_infront, sprites, frontobject);
    /* Show the balls that are in front of everything else. */

    wnormscreen ();
    /* Uncomment the following line to time with the vertical retrace */
    // wretrace();
    wputblock (0, 0, scrollblock[BACKWIN], 0);
    /* Once the image is built, display the whole thing on the visual screen. */

    if (kbdon[KEY_S])      /* Save the map */
    {
      while (kbdon[KEY_S] == 1)
        wretrace ();      /* Wait until the user releases the S key */
      wsavemap (FRONTWIN, "savefron.wmp", frontmap, fronttypes, frontobject, num_ball_behind+1);
      wsavemap (BACKWIN, "saveback.wmp", backmap, backtypes, backobject, num_clouds+1);
    }
    if (kbdon[KEY_L])      /* Load the map */
    {
      while (kbdon[KEY_L])
        wretrace ();        /* Wait until the user releases the L key */
      wfreemap (frontmap);  /* Free the maps that are already in memory */
      wfreemap (backmap);
      frontmap = wloadmap (FRONTWIN, "savefron.wmp", fronttypes, frontobject);
      backmap = wloadmap (BACKWIN, "saveback.wmp", backtypes, backobject);
    }
    frames++;
  } while (kbdon[ESC] == 0);
  wstoptimer ();
  wdonetimer ();

  /* Now clean up everything */
  wendscroll (FRONTWIN);
  wendscroll (BACKWIN);

  uninstallkbd ();
  wfreesprites (fronttiles, 0, 255);
  wfreesprites (backtiles, 0, 255);
  wfreesprites (sprites, 0, 49);
  wfreemap (backmap);
  wfreemap (frontmap);
  wsetmode (oldmode);
  seconds = (float)(timer / 70.0);
  fps = (float)(frames) / seconds;
  printf ("Frames completed: %d\n", frames);
  printf ("Seconds elapsed:  %g\n", seconds);
  printf ("FPS: %g\n", fps);
}
