/*
==============================================================================
		      WordUp Graphics Toolkit Version 4.5
			     Demonstration Program 58

 This is a PACMAN clone which uses the scrolling library.  Use the arrow      
 keys to move around and eat the pellets.  Hit ESC to quit.                   
									      
 The ghosts move randomly, and you have unlimited lives.  As an exercise,     
 complete the game by adding lives, fruit, intelligent ghosts, and levels.    

 *** PROJECT ***                                                             
 This program requires the files WGT45.LIB and WSCR_WC.LIB to be linked.

 *** DATA FILES ***
 PACSPR.SPR,  PACTILE.SPR, PACMAN.WMP

==============================================================================
*/

#include <dos.h>
#include <alloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <wgt45.h>
#include <wgtscrol.h>

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ESC 1
#define WIN 0

#define DESIRED_FRAMERATE 60


#define NUM_LOAD 100
#define DIESPEED 40

short spx, spy;                    /* Speed of scrolling window */
short pacanim, pacdir;             /* Controls animation frame and the direction
				      PACMAN is facing */
short ox, oy;                      /* Stores PACMAN's coordinate from previous
				      frame, in case you hit a wall */
wgtmap pacmap;                     /* our world map */
short dying;                       /* 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 = 2, ghostspeed = 1;/* 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[5];                 /* Time left until ghost reappears */
static short moved[5], movedir[5]; /* Controls movement of ghosts */
short bluetime;                    /* Time left with blue ghosts */
long timer, totaltime = 0;
short i;
short score;                       /* Your score */
short level;                       /* Current level */
short bonusscore[5] = {100, 200, 400, 800}; /* Bonus points for each ghost */
short total_dots;                  /* Dots not eaten */

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

short oldmode;


void far timerctr (void)
{
  timer++;
  totaltime++;
}


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;

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

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


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

  mapptr = 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 amt)
/* Increases your score by amt and shows the change on the screen. */
{
  score += amt;
  wgtprintf (260, 40, NULL, "%i", score);
}


void show_dots_left (void)
/* Displays how many dots are left. */
{
  wgtprintf (260, 80, NULL, "%3i", total_dots);
  if (total_dots == 0)
  {
    nosound ();
    wfreemap (pacmap);
    pacmap = wloadmap (WIN, "pacman.wmp", tiletypes, wobject);
    set_positions ();
    wshowwindow (WIN, 5, 5);
    count_dots ();
    ghostspeed *= 2;
    if (ghostspeed > 16)
      ghostspeed = 16;
  }
}


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

  i = wgetworldblock (WIN, wobject[0].x, wobject[0].y);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (WIN, wobject[0].x + 15, wobject[0].y);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (WIN, wobject[0].x, wobject[0].y + 15);
  if ((tiletypes[i] == 0) || (tiletypes[i] == 4))
    hit = 1;
  i = wgetworldblock (WIN, wobject[0].x + 15, wobject[0].y + 15);
  if (tiletypes[i] == 0)
    hit = 1;
  i = wgetworldblock (WIN, wobject[0].x + 7, wobject[0].y + 7);
  if (tiletypes[i] == 2)
  {
    sound (500);
    wputworldblock (WIN, wobject[0].x + 7, wobject[0].y + 7, 12);
    incscore (10);
    total_dots--;
    show_dots_left ();
  }
  if (tiletypes[i] == 3)
  {
    sound (800);
    wputworldblock (WIN, wobject[0].x + 7, wobject[0].y + 7, 12);
    bluetime += 5 * DESIRED_FRAMERATE; /* 5 Seconds */
    total_dots--;
    show_dots_left ();
  }

  if (wobject[0].x < 4)
  {
    wobject[0].x = mapwidth[WIN] * 16 - 32;
    wshowwindow(WIN, (worldx[WIN] + 144) / 16, worldy[WIN] / 16);
  }

  if (wobject[0].x > mapwidth[WIN] * 16 - 17)
  {
    wobject[0].x = 16;
    wshowwindow (WIN, 0, worldy[WIN] / 16);
  }

  if (hit == 1)
  {
    wobject[0].x = ox;
    wobject[0].y = oy;
    moved[0] = 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;

  /* Makes sure ghost moves in a new direction if it ran into a wall the first time. */
  while ((wobject[numb].x == gox) && (wobject[numb].y == goy))
  {
    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;
      case 5: break;
      default: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;
      hit = 0;
    }
  }
}


void main(void)
{
  int framectr;

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

  printf ("WGT Example #58\n\n");
  printf ("This is a PACMAN clone which uses the scrolling library.  Use the arrow\n");
  printf ("keys to move around and eat the pellets.  Hit ESC to quit.\n");
  printf ("The ghosts move randomly, and you have unlimited lives.  As an exercise,\n");
  printf ("complete the game by adding lives, fruit, intelligent ghosts, and levels.\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, 15, 12, pactile);
  pacmap = wloadmap (WIN, "pacman.wmp", tiletypes, wobject);

  wcls (0);
  wbutt (0, 0, 319, 199);
  wgtprintf (250, 30, NULL, "SCORE");
  wgtprintf (245, 70, NULL, "DOTS LEFT");

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

  installkbd ();

  set_positions ();
  framectr = 0;
  timer = 0;
  winittimer ();
  wstarttimer (timerctr, DESIRED_FRAMERATE);

  do {
    while (framectr == 0)
      framectr = timer;
    timer = 0;
    do {
    spx = 0;
    spy = 0;
    ox = wobject[0].x;
    oy = wobject[0].y;

    if (dying == 0)
    {
      switch(movedir[0])
      {
	case 1: wobject[0].x += pacspeed; 
		break;
	case 2: wobject[0].x -= pacspeed; 
		break;
	case 3: wobject[0].y += pacspeed; 
		break;
	case 4: wobject[0].y -= pacspeed; 
		break;
      }

      if (movedir[0] != 0)
      {
	moved[0] += pacspeed;
	checkpos ();
      }

      if (moved[0] >=16)
	moved[0] = 0;

      if (moved[0] == 0)
      {
	if (kbdon[RIGHT])
	{
	  movedir[0] = 1;
	  pacdir = 0;
	}
	else if (kbdon[LEFT])
	{
	  movedir[0] = 2;
	  pacdir = 2;
	}
	else if (kbdon[UP])
	{
	  movedir[0] = 4;
	  pacdir = 1;
	}
	else if (kbdon[DOWN])
	{
	  movedir[0] = 3;
	  pacdir = 3;
	}
      }

      pacanim++;
      if (pacanim > 5)
	pacanim = 1;
      wobject[0].num = pacanim + (pacdir * 5);     /* Set the sprite image for
						      pacman */
    }
    else  /* Dying loop */
    {
      wobject[0].num = (DIESPEED - dying + 31);
      if (wobject[0].num > 34) 
	wobject[0].num = 34;
      dying--;
      if (dying == 0)
      {
	wobject[0].x = 192; 
	wobject[0].y = 176;
	wobject[0].num = 1;
      }
    }   

    if (bluetime > 0)
      bluetime--;

    for (i = 1; i < 5; 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 = 20 + i;

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

    nosound ();

    framectr--;
    } while (framectr > 0);
    
    /* Find out the scrolling speed based on where you are in the window. */
    spx = wobject[0].x -  worldx[WIN] - windowmaxx[WIN] / 2 - 1;
    spy = wobject[0].y -  worldy[WIN] - windowmaxy[WIN] / 2 - 1;
    
    wscrollwindow (WIN, spx, spy);
    wshowobjects (WIN, 0, 7, pacspr, wobject);
    
    wputblock (4, 4, scrollblock[0], NORMAL);
  } while (!kbdon[ESC]);                 // until ESC is pressed

  wstoptimer ();
  wdonetimer ();

  uninstallkbd ();
  wendscroll (WIN);
  wfreesprites (pactile, 0, NUM_LOAD);
  wfreesprites (pacspr, 0, NUM_LOAD);
  wfreemap (pacmap);
  wsetmode (oldmode);

}

