/*
==============================================================================
		      WordUp Graphics Toolkit Version 5.0                     
			     Demonstration Program 59                         
									      
 This is a PACMAN clone which has two player mode.                            
									      
 The right player uses the arrow keys, and the left player uses               
 the W, A, S, and D keys.                                                     

 *** PROJECT ***                                                             
 This program requires the files WGT5_WC.LIB and WSCR_WC.LIB to be linked.   
									      
 *** DATA FILES ***                                                          
 PACSPR.SPR, PACTILE.SPR, PACMAN.WMP                                         
							   WATCOM C++ VERSION 
==============================================================================
*/

#include <dos.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <wgt5.h>
#include <wgtscrol.h>

#define LEFT 0
#define RIGHT 1
#define UP 2
#define DOWN 3

#define ESC 1
#define WIN 0
#define WIN2 1

short keys[2][4] = {{30, 32, 17, 31}, {75, 77, 72, 80}};

#define NUM_LOAD 100
#define DIESPEED 40

int frames;                        /* Number of frames counted */
short spx,spy;                     /* Speed of scrolling window */
short pacanim, pacdir[2];          /* Controls animation frame and the direction
				      PACMAN is facing */
short ox[2],oy[2];                 /* Stores PACMAN's coordinate from previous
				      frame, in case you hit a wall */
wgtmap pacmap;                     /* our world map */
short dying[2];                    /* Counts down after dying.  Once it hits 0,
				      PACMAN starts a new life. */
color palette[256];                /* Our palette of colours */
block pactile[NUM_LOAD + 1];       /* Tiles for the map */
block pacspr[NUM_LOAD + 1];        /* Our sprites */
scrollsprite wobject[100];         /* A few objects for you, ghosts, etc */
short tiletypes[256];              /* Tile types */

short pacspeed = 4,ghostspeed = 4; /* Moving speed of you and the ghosts */
short oldmode;                     /* Previous video mode */

short bonus;                       /* Tells how many points eating a ghost will 
				      give you  0 = 100, 1 = 200, 2 = 400, 
				      3 = 800 */
short traptime[6];                 /* Time left until ghost reappears */
short moved[6], movedir[6];        /* Controls movement of ghosts */
short bluetime;                    /* Time left with blue ghosts */
short i;
short score[2];                    /* Your score */
short level;                       /* Current level */
short bonusscore[5] = {100, 200, 400, 800}; /* Bonus points for each ghost */
short total_dots;                  /* Dots not eaten */
int timer;

void checkpos (short);             /* checks PACMAN for hitting walls */
void moveghost (short);            /* checks ghosts for hitting walls */

short oldmode;


void timerctr (void)
{
  timer++;
}


void set_positions (void)
/* Sets the initial positions of the ghosts and pacman */
{
  short i;

  wobject[0].on = 1;             /* Set Pacman's position */
  wobject[0].x = 192; 
  wobject[0].y = 176;
  wobject[0].num = 1;

  wobject[1].on = 1;             /* Set Pacman's position */
  wobject[1].x = 208; 
  wobject[1].y = 176;
  wobject[1].num = 1;

  for (i = 2; i < 6; i++)
  {
    wobject[i].on = 1;
    wobject[i].x = 176;  /* Set up the ghosts */
    wobject[i].y = 112; 
    wobject[i].num = 19 + i;  /* Make the ghosts different colors */
  }

  for (i = 2; i < 6; i++) 
  {
    moved[i] = 16;
    movedir[i] = 4;
  }
  pacanim = 1;
}


void count_dots(void)
{
  unsigned short *mapptr;
  int size, i;

  mapptr = (unsigned short *)pacmap;
  total_dots = 0;

  for (i = 0; i < mapwidth[WIN] * mapheight[WIN]; i++)
  {
    if ((tiletypes[*mapptr] == 2) || (tiletypes[*mapptr] == 3))
      total_dots++;
    mapptr++;
  }
}



void incscore(short player, short amt)
/* Increases your score by amt and shows the change on the screen. */
{
  score[player] += amt;
  wgtprintf (10 + player * 160, 192, NULL, "%i", score[player]);
}



void check_dots_left (void)
/* Checks how many dots are left. */
{
  if (total_dots == 0)
  {
    nosound ();
    wfreemap (pacmap);
    pacmap = wloadmap (WIN, "pacman.wmp", tiletypes, wobject);
    set_positions ();
    wshowwindow (WIN, 5, 5);
    wshowwindow (WIN2, 6, 5);
    count_dots ();
    ghostspeed *= 2;
    if (ghostspeed > 16)
      ghostspeed = 16;
  }
}



void checkpos (short player)
/* Checks your position, and makes sure you don't go through a wall */
{
  short hit = 0;
  short nx;

  i = wgetworldblock (player, wobject[player].x, wobject[player].y);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (player, wobject[player].x+15, wobject[player].y);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (player, wobject[player].x, wobject[player].y + 15);
  if ((tiletypes[i] == 0) | (tiletypes[i] == 4))
    hit = 1;
  i = wgetworldblock (player, wobject[player].x + 15, wobject[player].y + 15);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (player, wobject[player].x + 7, wobject[player].y + 7);
  if (tiletypes[i] == 2)
  {
    sound (500);
    wputworldblock (WIN, wobject[player].x + 7, wobject[player].y + 7, 12);
    incscore (player, 10);
    total_dots--;
    check_dots_left ();
  }
  if (tiletypes[i] == 3)
  {
    sound (800);
    wputworldblock (WIN, wobject[player].x + 7, wobject[player].y + 7, 12);
    bluetime += 150;
    total_dots--;
    check_dots_left ();
  }
  if (wobject[player].x < 4)
  {
    wobject[player].x = mapwidth[WIN] * 16 - 32;
    wshowwindow (player, (worldx[player] + 144) / 16, worldy[player] / 16);
  }
  if (wobject[player].x > mapwidth[WIN] * 16 - 17)
  {
    wobject[player].x = 16;
    wshowwindow (player, 0, worldy[player] / 16);
  }
  if (hit == 1)
  {
    wobject[player].x = ox[player];
    wobject[player].y = oy[player];
    moved[player] = 0;
  }
}



void moveghost (short numb)
/* Checks the ghost's position, and makes sure it doesn't go through a wall */
{
  short gox, goy;          /* Coordinate of ghost */
  short hit = 0;
  short j;

  gox = wobject[numb].x;
  goy = wobject[numb].y;

  if (moved[numb] == 0)
    movedir[numb] = (rand () % 5) +1; /* Pick a random direction (1-5)  5 = still */

  switch (movedir[numb])
  {
    case 1: wobject[numb].x += ghostspeed; 
	    break;
    case 2: wobject[numb].x -= ghostspeed; 
	    break;
    case 3: wobject[numb].y += ghostspeed; 
	    break;
    case 4: wobject[numb].y -= ghostspeed; 
	    break;
  }

  moved[numb] += ghostspeed;
  if (moved[numb] >= 16)
    moved[numb] = 0;

  /* Make sure ghost doesn't move off the screen. */
  if (wobject[numb].x < 0)
    wobject[numb].x = 0;
  else if (wobject[numb].x > (mapwidth[WIN] - 1) * 16)
    wobject[numb].x = (mapwidth[WIN] - 1) * 16;

  if (wobject[numb].y < 0)
    wobject[numb].y = 0;
  else if (wobject[numb].y > (mapheight[WIN] - 1) * 16)
    wobject[numb].y = (mapheight[WIN] - 1) * 16;

  j = wgetworldblock (WIN, wobject[numb].x, wobject[numb].y);
  if (tiletypes[j] == 0)
    hit = 1;
  j = wgetworldblock (WIN, wobject[numb].x + 15, wobject[numb].y);
  if (tiletypes[j] == 0)
    hit = 1;
  j = wgetworldblock(WIN, wobject[numb].x, wobject[numb].y + 15);
  if (tiletypes[j] == 0)
    hit = 1;
  if ((tiletypes[j] == 4) & (movedir[numb] == 3))  /* Can't move down through
						      ghost's door */
    hit = 1;
  j = wgetworldblock(WIN, wobject[numb].x + 14, wobject[numb].y + 15);
  if (tiletypes[j] == 0)
    hit = 1;
  
  if (hit == 1)  /* A solid tile was run into */
  {
    wobject[numb].x = gox; /* Set to the old position */
    wobject[numb].y = goy;
    moved[numb] = 0;
  }
  if ((wobject[numb].x == gox) && (wobject[numb].y == goy))
    moveghost (numb);    /* Recursive call, makes sure ghost moves in a new
			    direction if it ran into a wall the first time. */
}


void do_player (short player)
{
  spx = 0;
  spy = 0;
  ox[player] = wobject[player].x;
  oy[player] = wobject[player].y;

  if (dying[player] == 0)
  {
    switch(movedir[player])
    {
      case 1: wobject[player].x += pacspeed; 
	      break;
      case 2: wobject[player].x -= pacspeed; 
	      break;
      case 3: wobject[player].y += pacspeed; 
	      break;
      case 4: wobject[player].y -= pacspeed; 
	      break;
    }
    if (movedir[player] != 0)
    {
      moved[player] += pacspeed;
      checkpos (player);
    }
    if (moved[player] >= 16)
      moved[player] = 0;
    if (moved[player] == 0)
    {
      if (kbdon[keys[player][RIGHT]])
      {
	movedir[player] = 1;
	pacdir[player] = 0;
      }
      else if (kbdon[keys[player][LEFT]])
      {
	movedir[player] = 2;
	pacdir[player] = 2;
      }
      else if (kbdon[keys[player][UP]])
      {
	movedir[player] = 4;
	pacdir[player] = 1;
      }
      else if (kbdon[keys[player][DOWN]])
      {
	movedir[player] = 3;
	pacdir[player] = 3;
      }
    }
    pacanim++;
    if (pacanim > 5)
      pacanim = 1;
    wobject[player].num = pacanim + (pacdir[player] * 5);
    /* Set the sprite image for pacman */
  }
  else  /* Dying loop */
  {
    wobject[player].num = (DIESPEED - dying[player] + 31);
    if (wobject[player].num > 34) 
      wobject[player].num = 34;
    dying[player]--;
    if (dying[player] == 0)
    {
      wobject[player].x = 192; 
      wobject[player].y = 176;
      wobject[player].num = 1;
    }
  }   
}


void main(void)
{
  short j;

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

  printf ("WGT Example #59\n\n");
  printf ("This is a PACMAN clone which has two player mode.\n");
  printf ("The right player uses the arrow keys, and the left player uses\n");
  printf ("the W, A, S, and D keys.\n");
  printf ("\nPress any key to begin\n");
  getch ();

  vga256 ();

  wtextcolor (1);
  wtexttransparent (TEXTFGBG);
  wtextbackground (254);

  /* Load the graphics */
  wloadsprites (palette, "pactile.spr", pactile, 1, NUM_LOAD);
  wloadsprites (palette, "pacspr.spr", pacspr, 1, NUM_LOAD);
  wsetpalette (0, 255, palette);

  /* load our pacman map */
  winitscroll (WIN, NORMAL, -1, 10, 12, pactile);
  winitscroll (WIN2, NORMAL, -1, 10, 12, pactile);
  pacmap = wloadmap (WIN, "pacman.wmp", tiletypes, wobject);
  wcopymap (WIN, WIN2);

  wcls (0);
  wbutt (0, 0, 319, 199);

  wshowwindow (WIN, 5, 5);
  wshowwindow (WIN2, 6, 5);
  count_dots ();

  installkbd ();

  set_positions ();

  frames = 0;
  timer = 0;
  winittimer ();
  wstarttimer (timerctr, TICKS(100));   /* Program runs at top speed */
  do {
    do_player (0);
    do_player (1);

    if (bluetime > 0)
      bluetime--;

    for (i = 2; i < 6; i++)              /* Ghost loop */
    {
      if (traptime[i] == 0)
      {
	moveghost (i);
	if (bluetime > 0)
	  wobject[i].num = 25;
	if ((bluetime == 1) || ((bluetime < 40) && (bluetime % 2 == 1)))
	  wobject[i].num = 19 + i;

	for (j = 0; j < 2; j++)
	  if ((soverlap (i, wobject, pacspr, j, wobject, pacspr) == 1) && 
	      (traptime[i] == 0))
	{
	  if (bluetime > 0)
	  {
	    wobject[i].num = 27 + bonus;
	    incscore (j, bonusscore[bonus]);
	    bonus++;
	    traptime[i] = 100;
	  }
	  else if (dying[j] == 0)               /* The ghost caught you */
	    dying[j] = DIESPEED;
	}
      }
      else traptime[i]--;
      if (traptime[i] == 1)
      {
	wobject[i].x = 192;
	wobject[i].y = 144;
	wobject[i].num = 19 + i;
	bonus = 0;
      }
    }

    /* Find out the scrolling speed based on where you are in the window. */
    if (wobject[0].x - worldx[WIN] < windowmaxx[WIN] / 2 - 1)
      spx = -pacspeed;
    else if (wobject[0].x - worldx[WIN] > windowmaxx[WIN] / 2 + 1)
      spx = pacspeed;
    
    if (wobject[0].y - worldy[WIN] < windowmaxy[WIN] / 2 - 1)
      spy = -pacspeed;
    else if (wobject[0].y - worldy[WIN] > windowmaxy[WIN] / 2 + 1)
      spy = pacspeed;
  
    nosound ();

    wscrollwindow (WIN, spx, spy);
    wshowobjects (WIN, 0, 8, pacspr, wobject);
    wputblock (0, 0, scrollblock[WIN], NORMAL);

    spx = 0;
    spy = 0;
    /* Do the same for the second player */
    if (wobject[1].x - worldx[WIN2] < windowmaxx[WIN2] / 2 - 1)
      spx = -pacspeed;
    else if (wobject[1].x - worldx[WIN2] > windowmaxx[WIN2] / 2 + 1)
    spx = pacspeed;
    if (wobject[1].y - worldy[WIN2] < windowmaxy[WIN2] / 2 - 1)
      spy = -pacspeed;
    else if (wobject[1].y - worldy[WIN2] > windowmaxy[WIN2] / 2 + 1)
    spy = pacspeed;
    wscrollwindow (WIN2, spx, spy);
    wshowobjects (WIN2, 0, 8, pacspr, wobject);
    wretrace ();
    wputblock (161, 0, scrollblock[WIN2], NORMAL);
    frames++;
  } while (!kbdon[ESC]);        /* until ESC is pressed */
  wstoptimer ();
  wdonetimer ();
 
  uninstallkbd ();
  wendscroll (WIN);
  wendscroll (WIN2);
  wfreesprites (pactile, 0, NUM_LOAD);
  wfreesprites (pacspr, 0, NUM_LOAD);
  wfreemap (pacmap);
  wsetmode (oldmode);

  printf ("Number of Frames: %i\n", frames);
  printf ("Time elapsed:     %i microseconds\n", timer);
  printf ("Frame Rate:       %f\n", (float)frames / (float)(timer / 100.0));
}

