/***********************************************************
*                      K O U L E S                         *
*----------------------------------------------------------*
*  C1995 JAHUSOFT                                          *
*        Jan Hubicka                                       *
*        Dukelskych Bojovniku 1944                         *
*        390 03 Tabor                                      *
*        Czech Republic                                    *
*        Telefon: (048-I think) (0361) 32613               *
*        eMail: hubicka@limax.paru.cas.cz                  *
*----------------------------------------------------------*
* Copyright(c)1995 by Jan Hubicka.See README for license   *
*                          details.                        *
*----------------------------------------------------------*
*  koules.c initialization intro and main game routines    *
***********************************************************/

#define VARIABLES_HERE
#include "koules.h"
#include "font.h"
#include "text.h"
#include <alloca.h>
#include <sys/time.h>
#include <unistd.h>
#include <asm/io.h>		/*for waiting for retrace */
#define USE_PAGE_FLIPPING
#define SCANCODE_P 25

int             nobjects = 8;
int             nrockets = 0;
int             cit = 0;
int             fadedout = 0;
Object          object[MAXOBJECT];
Point           point[MAXPOINT];


GraphicsContext *physicalscreen;
GraphicsContext *backscreen;
GraphicsContext *starbackground;
GraphicsContext *background;


void           *fontblack;
void           *fontwhite;

char           *bball_bitmap;
char           *apple_bitmap;
char           *inspector_bitmap;
char           *mouse_bitmap;
char           *lball_bitmap[2];
char           *circle_bitmap;
char           *hole_bitmap;
char           *ehole_bitmap;
char           *ball_bitmap;
char           *eye_bitmap[MAXROCKETS];
char           *rocket_bitmap[MAXROCKETS];
char            hole_data[HOLE_RADIUS * 2][HOLE_RADIUS * 2];
char            ehole_data[HOLE_RADIUS * 2][HOLE_RADIUS * 2];
char            rocketcolor[5] =
{96, 160, 64, 96, 128};


int             textcolor;
int             sound;
int             tbreak;
int             gameplan = COOPERATIVE;
int             npoint = 0;
int             gamemode;
int             keys[5][4];
int             rotation[MAXROCKETS];
int             a_bballs, a_rockets, a_balls, a_holes, a_apples, a_inspectors,
                a_eholes;

int             mouseplayer = -1;
#ifdef JOYSTICK
int             joystickplayer[2] =
{-1, -1};
int             joystickdevice[2] =
{-1, -1};
int             calibrated[2];
int             center[2][2];
#endif

int             nmenu;
Menu            menu[20];

/*float           ROCKET_SPEED = 0.64;
   float           BALL_SPEED = 0.64;
   float           BBALL_SPEED = 0.64;
   float           SLOWDOWN = 0.92;
   float           GUMM = 13; */
float           ROCKET_SPEED = 1.2;
float           BALL_SPEED = 1.2;
float           BBALL_SPEED = 1.2;
float           SLOWDOWN = 0.8;
float           GUMM = 20;


float           BALLM = 3;
float           LBALLM = 3;
float           BBALLM = 8;
float           APPLEM = 34;
float           INSPECTORM = 2;
float           ROCKETM = 4;


#define NCOLORS 32

#define HOLE_XCENTER (2*HOLE_RADIUS-3*HOLE_RADIUS/4)
#define HOLE_YCENTER (2*HOLE_RADIUS-HOLE_RADIUS/4)
#define HOLE_MAX_RADIUS (HOLE_RADIUS/DIV+0.5*HOLE_RADIUS/DIV)
#define HOLE_SIZE_MAX (radius*radius)



char           *files[NSAMPLES] =
{
  SOUNDDIR "/start.raw",
  SOUNDDIR "/end.raw",
  SOUNDDIR "/colize.raw",
  SOUNDDIR "/destroy1.raw",
  SOUNDDIR "/destroy2.raw",
  SOUNDDIR "/creator1.raw",
  SOUNDDIR "/creator2.raw"
};
void
addpoint (int x, int y, int xp, int yp, int color, int time)
{
  point[npoint].x = x / DIV;
  point[npoint].y = y / DIV;
  point[npoint].xp = xp / DIV;
  point[npoint].yp = yp / DIV;
  point[npoint].time = time;

  point[npoint].color = color;
  npoint++;
  if (npoint >= MAXPOINT)
    npoint = 0;
}
static void     fadein1 ();

/*

 *  intro code
 *
 */
void
draw_koules (int c, int s, int r)
{
  int             i;
  for (i = 0; i < 360; i += 60)
    switch (c)
      {
      case 0:
	gl_putboxmaskcompiled ((int) (MAPWIDTH / 2 - BALL_RADIUS / DIV + sin (RAD (i + s)) * r),
	  (int) (MAPHEIGHT / 2 - BALL_RADIUS / DIV + cos (RAD (i + s)) * r),
		 BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, ball_bitmap);
	break;
      case 1:
	gl_putboxmaskcompiled ((int) (MAPWIDTH / 2 - BALL_RADIUS / DIV + sin (RAD (i + s)) * r),
	  (int) (MAPHEIGHT / 2 - BALL_RADIUS / DIV + cos (RAD (i + s)) * r),
	     BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, lball_bitmap[0]);
	break;
      default:
	gl_putboxmaskcompiled ((int) (MAPWIDTH / 2 - BALL_RADIUS / DIV + sin (RAD (i + s)) * r),
	  (int) (MAPHEIGHT / 2 - BALL_RADIUS / DIV + cos (RAD (i + s)) * r),
	     BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, lball_bitmap[1]);
	break;
      }

}
void
koulescreator (int r)
{
  int             time = 100;
  int             z = 0, z1;
  int             x, y;
  int             x1, y1, i;
  if (sound)
    Snd_effect (S_CREATOR1, next);
  for (i = 0; i < 360; i += 60)
    {
      x1 = (MAPWIDTH / 2 - +sin (RAD (i)) * r);
      y1 = (MAPHEIGHT / 2 - +cos (RAD (i)) * r);
      for (z1 = 0; z1 < BALL_RADIUS * BALL_RADIUS * M_PI / DIV / DIV; z1++)
	{
	  z++;
	  x = rand () % GAMEWIDTH;
	  y = rand () % GAMEHEIGHT;
	  addpoint (x * 256 * DIV, y * 256 * DIV,
		    (x1 - x) * 256 / (time) * DIV,
		    (y1 - y) * 256 / (time) * DIV,
		    ball (rand () % 32),
		    time);
	}
    }
}
void
starcreator ()
{
  int             time = 100;
  int             z;
  int             x, y;
  if (sound)
    Snd_effect (S_CREATOR1, next);
  for (z = 0; z < ROCKET_RADIUS * ROCKET_RADIUS * M_PI / DIV / DIV; z++)
    {
      x = rand () % GAMEWIDTH;
      y = rand () % GAMEHEIGHT;
      addpoint (x * 256 * DIV, y * 256 * DIV,
		(MAPWIDTH / 2 - x) * 256 / (time) * DIV,
		(MAPHEIGHT / 2 - y) * 256 / (time) * DIV,
		(rand () % 32),
		time);
    }
}
#if DIV==1
#define my_setpixel(x,y,color) *(backscreen->vbuf+(x)+(((y)&0xffffff00)>>1)+(((y)&0xffffff00)<<1))=color
#else
#define my_setpixel(x,y,color) *(backscreen->vbuf+(x)+((y)&0xffffff00)+(((y)&0xffffff00)>>2))=color
#endif
static void
points ()
{
  register unsigned int x, y;
  Point          *p, *lp;
  lp = &point[MAXPOINT];
  for (p = point; p < lp; p++)
    {
      if (p->time > 0)
	{
	  p->time--;
	  x = (p->x += p->xp) >> 8;
	  y = (p->y += p->yp);
	  if (x > 0 && x < MAPWIDTH &&
	      y > 0 && y >> 8 < MAPHEIGHT)
	    my_setpixel (x, y, p->color);
	  else
	    p->time = 0;
	}
    }
}


static void
points1 ()
{
  Point          *p, *lp;
  lp = &point[MAXPOINT];
  for (p = point; p < lp; p++)
    {
      if (p->time > 0)
	{
	  p->time--;
	  p->x += p->xp;
	  p->y += p->yp;
	}
    }
}
void
draw_player (int x, int y, float r, int z, int r1)
{
  int             x1, y1;
  gl_putboxmaskcompiled ((int) x - ROCKET_RADIUS / DIV, (int) y - ROCKET_RADIUS / DIV,
	ROCKET_RADIUS * 2 / DIV, ROCKET_RADIUS * 2 / DIV, rocket_bitmap[z]);
  x1 = x * DIV + sin (r - RAD (30)) * r1 - EYE_RADIUS;
  y1 = y * DIV + cos (r - RAD (30)) * r1 - EYE_RADIUS;
  gl_putboxmaskcompiled (x1 / DIV, y1 / DIV,
		 EYE_RADIUS * 2 / DIV, EYE_RADIUS * 2 / DIV, eye_bitmap[z]);
  x1 = x * DIV + sin (r + RAD (30)) * r1 - EYE_RADIUS;
  y1 = y * DIV + cos (r + RAD (30)) * r1 - EYE_RADIUS;
  gl_putboxmaskcompiled (x1 / DIV, y1 / DIV,
		 EYE_RADIUS * 2 / DIV, EYE_RADIUS * 2 / DIV, eye_bitmap[z]);

}

#define TEXTW 200
#define TEXTH 25
void
starwars ()
{
  int             y, i, z;
  float           r[3];
  float           angle = 0;

  int             actu = 0;
  int             time = 0;
  int             time1 = 0;

  int             playx = 0, playy = 0;
  int             bballx = 0, bbally = 0;
  float           playr = RAD (0), playp = 0.03;
  long            VfTime = 0;
  long            VendSleep = 0;
  int             sizes[TEXTSIZE];
  struct timeval  VlastClk;
  struct timeval  VnewClk;
  int             wait = 0;

  float           r1;
  float           rp[3];

  fadeout ();
  for (y = 0; y < TEXTSIZE; y++)
    sizes[y] = -vgatextsize (TEXTH, text[y]) / 2;
  gettimeofday (&VlastClk, NULL);
  gettimeofday (&VnewClk, NULL);
  VendSleep = VlastClk.tv_usec;
  VfTime = 1000000 / 65;

  r[1] = r[2] = r[0] = sqrt ((MAPWIDTH / 2) * (MAPWIDTH / 2) +
			     (MAPHEIGHT / 2) * (MAPHEIGHT / 2));
  rp[0] = 0.0 / DIV;
  rp[1] = 0.6 / DIV;
  rp[2] = 0.6 / DIV;
  r1 = r[0];


  if (sound)
    Snd_effect (S_START, 0);
  for (i = -600; i < (TEXTSIZE + 10) * TEXTW; i += 1)
    {
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (wait >= 0 || tbreak)
	{
	  gl_setcontext (starbackground);
	  gl_copyscreen (backscreen);
	  gl_setcontext (backscreen);
	  gl_enableclipping ();
	  for (z = 0; z < 3; z++)
	    {
	      if (r[z] <= ROCKET_RADIUS / DIV + BALL_RADIUS / DIV)
		{
		  rp[z] = -6 / DIV;
		  if (sound)
		    Snd_effect (S_COLIZE, next);
		}
	      if (r[z] < r1)
		draw_koules (rp[z] > 0 ? z : 0, (int) angle, r[z]);
	    }
	  if (bbally > -20 && bballx)
	    gl_putboxmaskcompiled ((int) bballx - (BBALL_RADIUS) / DIV, bbally,
	      BBALL_RADIUS * 2 / DIV, BBALL_RADIUS * 2 / DIV, bball_bitmap);

	  points ();
	  for (y = 0; y < TEXTSIZE; y++)
	    {
	      if (y * TEXTW - i + 2 * TEXTW < 1000 && y * TEXTW - i + TEXTW > -1500)
		{
		  textcolor = (1200 + (y * TEXTW - i)) * 32 / 2500;
		  if (textcolor <= 0)
		    continue;
		  actu = y;
		  vgadrawtext (sizes[y], y * TEXTW - i, TEXTH, text[y]);
		}
	    }
	  if (playx)
	    draw_player (playx, playy, playr, 0, EYE_RADIUS1);
	  if (actu == PLAYERLINE && !time)
	    starcreator (), time = 1;
	  gl_copyscreen (physicalscreen);
	  fadein1 ();
	}
      else
	points1 ();


      if (actu >= KOULESLINE && !time1)
	koulescreator (MAPHEIGHT / 2 - 20), time1 = 1;
      if (time1)
	time1++;
      if (time1 == 100)
	r[0] = MAPHEIGHT / 2 - 20;
      if (time1 > 100)
	r[0] -= rp[0], angle += 0.3;


      playr += playp;
      if (playr < RAD (-45))
	playp = 0.015, playr = RAD (-45);
      if (playr > RAD (45))
	playp = -0.03, playr = RAD (45);
      if (actu >= D1LINE)
	r[1] -= rp[1];
      if (actu >= D2LINE)
	r[2] -= rp[2];
      if (actu >= BLINE && !bballx)
	bballx = MAPWIDTH / 2,
	  bbally = MAPHEIGHT + 30;
      if (bballx)
	bbally--;
      if (bbally > 0 && bbally < MAPHEIGHT / 2 + ROCKET_RADIUS / 2)
	{
	  if (playy == MAPHEIGHT / 2 && sound)
	    Snd_effect (S_END, next);
	  playy -= 10;
	}
      if (time)
	time++;
      if (time == 100)
	{
	  playx = MAPWIDTH / 2, playy = MAPHEIGHT / 2, playr = RAD (180), rp[0] = 1.5 / DIV;
	  if (sound)
	    Snd_effect (S_CREATOR2, next);
	}
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (tbreak)
	wait = VfTime, tbreak = 0;
      usleep (wait < 0 ? 0 : wait);
      VendSleep = VnewClk.tv_usec + wait;
      gettimeofday (&VlastClk, NULL);
      keyboard_update ();
      mouse_update ();
      for (z = 0; z < 127; z++)
	if (keyboard_keypressed (z) || mouse_getbutton ())
	  {
	    fadeout ();
	    /*printf ("%i\n", z); */
	    return;
	  }
    }
  fadeout ();
}
void
clearpoints ()
{
  int             i;
  for (i = 0; i < MAXPOINT; i++)
    point[i].time = 0;
}
void
outro (int size, char *text[])
{
  int             y, i, z;

  int             actu = 0;
  int             lkey = 1;
  int             skey = 0;
  int             key = 1;

  long            VfTime = 0;
  long            VendSleep = 0;
  int             sizes[size];
  struct timeval  VlastClk;
  struct timeval  VnewClk;
  int             wait = 0;

  fadeout ();
  clearpoints ();
  for (y = 0; y < size; y++)
    sizes[y] = -vgatextsize (TEXTH, text[y]) / 2;
  gettimeofday (&VlastClk, NULL);
  gettimeofday (&VnewClk, NULL);
  VendSleep = VlastClk.tv_usec;
  VfTime = 1000000 / 65;

  for (i = -600; i < (size + 4) * TEXTW; i += 1)
    {
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (wait >= 0 || tbreak)
	{
	  gl_setcontext (starbackground);
	  gl_copyscreen (backscreen);
	  gl_setcontext (backscreen);
	  gl_enableclipping ();
	  points ();
	  for (y = 0; y < size; y++)
	    {
	      if (y * TEXTW - i + 2 * TEXTW < 1000 && y * TEXTW - i + TEXTW > -1500)
		{
		  textcolor = (1200 + (y * TEXTW - i)) * 32 / 2500;
		  if (textcolor <= 0)
		    continue;
		  actu = y;
		  vgadrawtext (sizes[y], y * TEXTW - i, TEXTH, text[y]);
		}
	    }
	  gl_copyscreen (physicalscreen);
	  fadein1 ();
	}
      else
	points1 ();


      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      if (tbreak)
	wait = VfTime, tbreak = 0;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      usleep (wait < 0 ? 0 : wait);
      VendSleep = VnewClk.tv_usec + wait;
      gettimeofday (&VlastClk, NULL);
      keyboard_update ();
      mouse_update ();
      if (actu > 0)
	{
	  lkey = key;
	  key = 0;
	  for (z = 0; z < 127; z++)
	    if (keyboard_keypressed (z) || mouse_getbutton ())
	      key = 1;
	  if (skey && !key && lkey)
	    {
	      fadeout ();
	      return;
	    }
	  if (!key && lkey)
	    skey = 1;
	}
    }
  fadeout ();
}
void
staraccel (float x1, float y1, float r)
{
  int             y;
  for (y = 0; y < 5 / DIV / DIV; y++)
    {
      float           p;
      p = RAD (rand () % 45 - 22);
      addpoint (x1 * 256,
		y1 * 256,
		(-sin (r + p) * 0.08 * 10) * (rand () % 512),
		(-cos (r + p) * 0.08 * 10) * (rand () % 512),
		rocket (rand () % 16), 30);
    }
}
void
outro2 ()
{
  int             y, i, z;
  int             lkey = 1;
  int             skey = 0;
  int             key = 1;

  int             actu = 0;
  float           width = 0;

  long            VfTime = 0;
  long            VendSleep = 0;
  int             size = TEXTSIZE2;
  int             sizes[size], nrockets1;
  struct timeval  VlastClk;
  struct timeval  VnewClk;
  int             wait = 0;
  float           playy = MAPHEIGHT + 20;
  float           playr = RAD (180);
  float           playp = 0;
  float           er = EYE_RADIUS1;
  float           erp = 2;


  fadeout ();
  clearpoints ();
  nrockets1 = nrockets - 1;
  if (nrockets1 == 0)
    nrockets1 = 1;
  for (y = 0; y < size; y++)
    sizes[y] = -vgatextsize (TEXTH, text2[y]) / 2;
  gettimeofday (&VlastClk, NULL);
  gettimeofday (&VnewClk, NULL);
  VendSleep = VlastClk.tv_usec;
  VfTime = 1000000 / 65;
  if (sound)
    Snd_effect (S_START, next);
  for (i = -600; i < (size + 5) * TEXTW; i += 1)
    {
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (wait >= 0 || tbreak)
	{
	  gl_setcontext (starbackground);
	  gl_copyscreen (backscreen);
	  gl_setcontext (backscreen);
	  gl_enableclipping ();
	  points ();
	  for (y = 0; y < size; y++)
	    {
	      if (y * TEXTW - i + 2 * TEXTW < 1000 && y * TEXTW - i + TEXTW > -1500)
		{
		  textcolor = (1200 + (y * TEXTW - i)) * 32 / 2500;
		  if (textcolor <= 0)
		    continue;
		  actu = y;
		  vgadrawtext (sizes[y], y * TEXTW - i, TEXTH, text2[y]);
		}
	    }
	  for (z = 0; z < nrockets; z++)
	    {
	      draw_player (MAPWIDTH / 2 - width / 2 + z * width / (nrockets1), playy, playr, z, (int) er);
	    }
	  gl_copyscreen (physicalscreen);
	  fadein1 ();
	}
      else
	points1 ();
      width = (200 * (nrockets - 1) * playy / MAPHEIGHT + 20 * (nrockets - 1)) / DIV;
      if (playy > MAPHEIGHT / 2 || actu > CONTLINE)
	{
	  playy -= 0.6 / DIV;
	  playr = RAD (180);
	  for (z = 0; z < nrockets; z++)
	    {
	      staraccel ((MAPWIDTH / 2 - width / 2 + z * width / (nrockets1)) * DIV, (playy) * DIV, playr);
	    }
	  er = EYE_RADIUS1;
	}
      else
	{
	  playr += playp;
	  if (playr < RAD (-45))
	    playp = 0.015, playr = RAD (-45);
	  if (playr > RAD (45))
	    playp = -0.03, playr = RAD (45);
	  if (actu > UDIVLINE)
	    {
	      /*if (er < 2 * EYE_RADIUS1)
	         erp += 0.1;
	         if (er > 2 * EYE_RADIUS1)
	         erp -= 0.1; */
	      erp += (2 * EYE_RADIUS1 - er) / 10;
	      erp *= 0.98;
	      er += erp;
	    }
	}
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (tbreak)
	wait = VfTime, tbreak = 0;
      usleep (wait < 0 ? 0 : wait);
      VendSleep = VnewClk.tv_usec + wait;
      gettimeofday (&VlastClk, NULL);
      keyboard_update ();
      mouse_update ();
      if (actu > 0)
	{
	  lkey = key;
	  key = 0;
	  for (z = 0; z < 127; z++)
	    if (keyboard_keypressed (z) || mouse_getbutton ())
	      key = 1;
	  if (skey && !key && lkey)
	    {
	      fadeout ();
	      return;
	    }
	  if (!key && lkey)
	    skey = 1;
	}
    }
  fadeout ();
}


void
outro1 ()
{
  outro (TEXTSIZE1, text1);
}

void
intro_intro ()
{
  outro (INTROSIZE, introtext);
}

void
hole_intro ()
{
  outro (HOLESIZE, holetext);
}

void
inspector_intro ()
{
  outro (INSPECTORSIZE, inspectortext);
}

void
bball_intro ()
{
  outro (BBALLSIZE, bballtext);
}

void
bbball_intro ()
{
  outro (BBBALLSIZE, bbballtext);
}

void
maghole_intro ()
{
  outro (MAGSIZE, magholetext);
}

/*
 * main game code
 */

inline int
radius (int type)
{
  switch (type)
    {
    case EHOLE:
    case HOLE:
      return (HOLE_RADIUS);
    case ROCKET:
      return (ROCKET_RADIUS);
    case BALL:
    case LBALL:
      return (BALL_RADIUS);
    case BBALL:
      return (BBALL_RADIUS);
    case APPLE:
      return (APPLE_RADIUS);
    case INSPECTOR:
      return (INSPECTOR_RADIUS);
    }
  return (0);
}
static inline int
color (int type, int i, int letter)
{
  switch (type)
    {
    case EHOLE:
      return (128);
    case HOLE:
      return (64);
    case ROCKET:
      return (rocketcolor[i]);
    case BALL:
      return (64);
    case LBALL:
      if (letter == L_ACCEL)
	return (128);
      if (letter == L_GUMM)
	return (160);
    case BBALL:
      return (128);
    case APPLE:
      return (64);
    case INSPECTOR:
      return (160);
    }
  return (0);
}
inline float
M (int type)
{
  switch (type)
    {
    case APPLE:
      return (APPLEM);
    case INSPECTOR:
      return (INSPECTORM);
    case HOLE:
    case EHOLE:
      return (BBALLM);
    case ROCKET:
      return (ROCKETM);
    case BALL:
    case LBALL:
      return (BALLM);
    case BBALL:
      return (BBALLM);
    }
  return (0);
}

int
find_possition (float *x, float *y, float radius)
{
  int             x1, y1, i, y2 = 0;
  float           xp, yp;
rerand:;
  x1 = rand () % (GAMEWIDTH - 60) + 30;
  y1 = rand () % (GAMEHEIGHT - 60) + 30;
  for (i = 0; i < nobjects; i++)
    {
      xp = x1 - object[i].x;
      yp = y1 - object[i].y;
      if (xp * xp + yp * yp < (radius + object[i].radius) *
	  (radius + object[i].radius))
	{
	  y2++;
	  if (y2 > 10000)
	    return (0);
	  goto rerand;

	}
    }
  *x = (float) x1;
  *y = (float) y1;
  return (1);
}




static char    *
draw_ball_bitmap (int radius, int color)
{
  char           *bitmap = NULL, *cbitmap, *point;
  int             x, y, r, size;
  radius /= DIV;
  if ((bitmap = alloca ((radius * 2) * (radius * 2) + 2)) == NULL)
    perror ("create_ball_bitmap"), exit (-1);
  point = bitmap;
  for (y = 0; y < radius * 2; y++)
    for (x = 0; x < radius * 2; x++, point++)
      {
	if ((x - radius) * (x - radius) + (y - radius) * (y - radius)
	    < (radius - 0.5) * (radius - 0.5))
	  {
	    r = (x - 3 * radius / 4) * (x - 3 * radius / 4) +
	      (y - radius / 4) * (y - radius / 4);
	    r = r * 32 / (1.5 * radius) / (1.5 * radius);
	    if (r > 31)
	      r = 31;
	    *point = color + r;
	  }
	else
	  *point = 0;
      }
  size = gl_compiledboxmasksize (radius * 2, radius * 2, bitmap);
  if ((cbitmap = malloc (size)) == NULL)
    perror ("create_bitmap"), exit (1);
  gl_compileboxmask (radius * 2, radius * 2, bitmap, cbitmap);
  return (cbitmap);
}
static char    *
draw_apple_bitmap (int radius, int color)
{
  char           *bitmap = NULL, *cbitmap, *point;
  int             x, y, r, size;
  int             radius1;
  radius /= DIV;
  if ((bitmap = alloca ((radius * 2) * (radius * 2) + 2)) == NULL)
    perror ("create_ball_bitmap"), exit (-1);
  point = bitmap;
  for (y = 0; y < radius * 2; y++)
    for (x = 0; x < radius * 2; x++, point++)
      {
#if DIV==2
	radius1 = radius * (abs (x - radius) / 2 + 25) / 30;
#else
	radius1 = radius * (abs (x - radius) / 2 + 50) / 60;
#endif
	if (radius1 > radius)
	  radius1 = radius;
	if ((x - radius) * (x - radius) + (y - radius) * (y - radius)
	    < ((radius1) * (radius1)))
	  {
	    r = (x - 3 * radius / 4) * (x - 3 * radius / 4) +
	      (y - radius / 4) * (y - radius / 4);
	    r = 3 + r * 22 / (1.5 * radius1) / (1.5 * radius1);
	    if (r > 31)
	      r = 31;
	    *point = color + r;
	  }
	else
	  *point = 0;
      }
  size = gl_compiledboxmasksize (radius * 2, radius * 2, bitmap);
  if ((cbitmap = malloc (size)) == NULL)
    perror ("create_bitmap"), exit (1);
  gl_compileboxmask (radius * 2, radius * 2, bitmap, cbitmap);
  return (cbitmap);
}

static void
create_bitmap ()
{
  int             x, y, r, size, po, radius;
  printf ("creating bitmaps...\n");
  for (x = 0; x < HOLE_RADIUS * 2; x++)
    for (y = 0; y < HOLE_RADIUS * 2; y++)
      {
	if (DIV == 1)
	  radius = HOLE_RADIUS / 2 + (int) (atan (fabs (x - HOLE_RADIUS + 0.5) / fabs (y - HOLE_RADIUS + 0.5)) * HOLE_RADIUS / 2) % (HOLE_RADIUS / 2);
	else
	  radius = HOLE_RADIUS / 4;
	if ((x - HOLE_RADIUS / DIV) * (x - HOLE_RADIUS / DIV) + (y - HOLE_RADIUS / DIV) * (y - HOLE_RADIUS / DIV)
	    < radius * radius)
	  {
	    r = (x - HOLE_RADIUS / DIV) * (x - HOLE_RADIUS / DIV) +
	      (y - HOLE_RADIUS / DIV) * (y - HOLE_RADIUS / DIV);
	    r = r * 24 / HOLE_SIZE_MAX;
	    if (r > 23)
	      r = 23;
	    hole_data[x][y] = 64 + r + 1;
	    ehole_data[x][y] = 128 + r + 1;
	  }
	else
	  hole_data[x][y] = 0,
	    ehole_data[x][y] = 0;
      }
  printf ("compiling bitmaps...\n");
  size = gl_compiledboxmasksize (HOLE_RADIUS * 2, HOLE_RADIUS * 2, hole_data);
  if ((hole_bitmap = malloc (size)) == NULL)
    perror ("hole"), exit (1);
  gl_compileboxmask (HOLE_RADIUS * 2, HOLE_RADIUS * 2, hole_data, hole_bitmap);
  printf ("compiling bitmaps...\n");
  size = gl_compiledboxmasksize (HOLE_RADIUS * 2, HOLE_RADIUS * 2, ehole_data);
  if ((ehole_bitmap = malloc (size)) == NULL)
    perror ("hole"), exit (1);
  gl_compileboxmask (HOLE_RADIUS * 2, HOLE_RADIUS * 2, ehole_data, ehole_bitmap);


  ball_bitmap = draw_ball_bitmap (BALL_RADIUS, ball (0));
  mouse_bitmap = draw_ball_bitmap (MOUSE_RADIUS * DIV, 32 + 32 * 2);
  bball_bitmap = draw_ball_bitmap (BBALL_RADIUS, 4 * 32);
  apple_bitmap = draw_apple_bitmap (APPLE_RADIUS, ball (0));
  inspector_bitmap = draw_ball_bitmap (INSPECTOR_RADIUS, 160);
  lball_bitmap[0] = draw_ball_bitmap (BALL_RADIUS, 4 * 32);
  lball_bitmap[1] = draw_ball_bitmap (BALL_RADIUS, 5 * 32);
  for (x = 0; x < 5; x++)
    rocket_bitmap[x] = draw_ball_bitmap (ROCKET_RADIUS, rocketcolor[x]);


  for (po = 0; po < MAXROCKETS; po++)
    {
      eye_bitmap[po] = draw_ball_bitmap (EYE_RADIUS, 32 + 32 * po);
    }


}





inline void
normalize (float *x, float *y, float size)
{
  float           length = sqrt ((*x) * (*x) + (*y) * (*y));
  if (length == 0)
    length = 1;
  *x *= size / length;
  *y *= size / length;
}


static inline int
col (int p, float p1)
{
  p *= p1;
  if (p > 63)
    return (63);
  if (p < 0)
    return (0);
  return (p);
}


static void
setcustompalette (int p, float p1)
{
  /* 0-31 black to yellow for starwars scroller */
  /* 32-63    black to red */
  /* 64-96    for red koules */
  /* 96-128   for yellow rockets */
  /* 128-160   for green rockets */
  /* 160-192   for blue rockets */
  /* 192-256   gray cmap for stars */
  Palette         pal;
  int             i;
  for (i = 0; i < 64; i++)
    {
      int             r, g, b;
      r = g = b = 0;
      if ((i & 32) > 0)
	b = (i & 31) << 1;
      if (i < 32)
	{
	  r = (i & 3) << 4;	/* 2 bits */
	  g = (i & 4) << 3;	/* 1 bit */
	  b = (i & 24) << 1;	/* 2 bits */
	}
      pal.color[i].red = col (r + p, p1);
      pal.color[i].green = col (g + p, p1);
      pal.color[i].blue = col (b + p, p1);
    }
  for (i = 64; i < 64 + 32; i++)
    {
      int             r, g, b;
      r = g = b = 0;
      r = (32 - (i - 63)) << 1;
      if (i < 64 + 8)
	{
	  b = g = (((8 - (i - 63))) << 5) / 5;
	}
      pal.color[i].red = col (r + p, p1);
      pal.color[i].green = col (g + p, p1);
      pal.color[i].blue = col (b + p, p1);
    }
  for (i = 96; i < 96 + 32; i++)
    {
      int             r, g, b;
      r = g = b = 0;
      r = g = (32 - (i - 95)) << 1;
      if (i < 96 + 8)
	{
	  b = ((8 - (i - 95))) << 3;
	}
      pal.color[i].red = col (r + p, p1);
      pal.color[i].green = col (g + p, p1);
      pal.color[i].blue = col (b + p, p1);
    }
  for (i = 128; i < 128 + 32; i++)
    {
      int             r, g, b;
      r = g = b = 0;
      g = (32 - (i - 127)) << 1;
      if (i < 128 + 8)
	{
	  r = b = ((8 - (i - 127))) << 3;
	}
      pal.color[i].red = col (r + p, p1);
      pal.color[i].green = col (g + p, p1);
      pal.color[i].blue = col (b + p, p1);
    }
  for (i = 160; i < 160 + 32; i++)
    {
      int             r, g, b;
      r = g = b = 0;
      b = (32 - (i - 159)) << 1;
      if (i < 160 + 8)
	{
	  r = g = (((8 - (i - 159))) << 3) / 2;
	}
      pal.color[i].red = col (r + p, p1);
      pal.color[i].green = col (g + p, p1);
      pal.color[i].blue = col (b + p, p1);
    }
  for (i = 0; i < 32; i++)
    {
      pal.color[i].red = col (i * 2 + p, p1);
      pal.color[i].green = col (i * 2 + p, p1);
      pal.color[i].blue = col (p, p1);
    }
  for (i = 0; i < 32; i++)
    {
      pal.color[192 + i].red = col (i * 2 + p, p1);
      pal.color[192 + i].green = col (i * 2 + p, p1);
      pal.color[192 + i].blue = col (i * 2 + p, p1);
    }
  pal.color[0].red = 0;
  pal.color[0].green = 0;
  pal.color[0].blue = 0;
  pal.color[255].red = col (64 + p, p1);
  pal.color[255].green = col (64 + p, p1);
  pal.color[255].blue = col (64 + p, p1);
  vga_waitretrace ();
  gl_setpalette (&pal);
}
void
fadeout ()
{
  if (!fadedout)
    {
      float           i;
      for (i = 1; i >= 0; i -= 0.1)
	{
	  setcustompalette (0, i);
	  usleep (200), tbreak = 1;

	}
      setcustompalette (-65, 0);
      fadedout = 1;
    }
}
void
fadein ()
{
  if (fadedout)
    {
      float           i;
      for (i = 0; i <= 1; i += 0.1)
	{
	  setcustompalette (0, i);
	  usleep (200), tbreak = 1;

	}
      setcustompalette (0, 1);
      fadedout = 0;
    }
}
static void
fadein1 ()			/*better for star background */
{
  if (fadedout)
    {
      int             i;
      for (i = -64; i <= 0; i += 6)
	{
	  setcustompalette (i, 1);
	  usleep (200), tbreak = 1;

	}
      setcustompalette (0, 1);
      fadedout = 0;
    }
}





Sample          sa[NSAMPLES];
void
loadsamples ()
{
  int             index;
  for (index = 0; index < NSAMPLES; index++)
    {
      if (Snd_loadRawSample (files[index], &sa[index]))
	perror (files[index]), printf ("sound will not be played!\n");
    }
  Snd_init (NSAMPLES, sa, 11000, NTRACKS * 2, "/dev/dsp");
}







static void
initialize ()
{
  printf ("Initializing sound server...\n");
  loadsamples ();
  printf ("Autoprobing hardware\n");
  printf ("Initializing joystick driver\n");
#ifdef JOYSTICK
  joystickdevice[0] = open ("/dev/js0", O_RDONLY);
  if (joystickdevice[0] < 0)
    {
      perror ("Joystick driver");
      printf ("Joystick 1 not avaiable..\n");
      joystickplayer[0] = -1;
    }
  else
    printf ("Joystick 1 initialized\n");
  joystickdevice[1] = open ("/dev/js1", O_RDONLY);
  if (joystickdevice[1] < 0)
    {
      perror ("Joystick driver");
      printf ("Joystick 2 not avaiable..\n");
      joystickplayer[1] = -1;
    }
  else
    printf ("Joystick 2 initialized\n");

#else
  printf ("Joystick driver not avaiable(recompile koules with JOYSTICK enabled )\n");
#endif
  printf ("Initializing mouse server\n");
  vga_setmousesupport (1);
  printf ("Initializing graphics server\n");
  vga_init ();
  vga_setmode (VGAMODE);



  printf ("Initializing video memory\n");
  setcustompalette (0, 1);
  gl_setcontextvga (VGAMODE);


  physicalscreen = gl_allocatecontext ();
  gl_getcontext (physicalscreen);


  printf ("Initializing graphics font\n");
  fontblack = malloc (256 * 8 * 8);
  gl_expandfont (8, 8, back (3), gl_font8x8, fontblack);
  fontwhite = malloc (256 * 8 * 8);
  gl_setfont (8, 8, fontwhite);
  gl_expandfont (8, 8, 255, gl_font8x8, fontwhite);


  gl_write (0, 0, "Graphics daemons fired up");
  gl_write (0, 8, "Checking system consitency....virus not found..");
  gl_setcontextvgavirtual (VGAMODE);
  backscreen = gl_allocatecontext ();
  gl_getcontext (backscreen);
  gl_setcontext (physicalscreen);
  gl_write (0, 16, "Calibrating delay loop");


  gl_setcontextvgavirtual (VGAMODE);
  background = gl_allocatecontext ();
  gl_getcontext (background);
  gl_setcontextvgavirtual (VGAMODE);
  starbackground = gl_allocatecontext ();
  gl_getcontext (starbackground);
  gl_setcontext (physicalscreen);


  gl_write (0, 24, "Initializing keyboard daemons");
  if (keyboard_init ())
    {
      printf ("Could not initialize keyboard.\n");
      exit (-1);
    }
  /*keyboard_translatekeys(TRANSLATE_CURSORKEYS | TRANSLATE_KEYPADENTER); */
  keyboard_translatekeys (0);
  gl_write (0, 32, "1 pc capable keyboard found");
}





static void
createbackground ()
{
/* Create fancy dark red background */
  int             x, y;
  char           *pixel = background->vbuf;
  for (y = 0; y < MAPHEIGHT + 20; y++)
    for (x = 0; x < MAPWIDTH; x++)
      {
	int             i = 0;
	int             n = 0;
	int             c;
	if (x > 0)
	  {
	    i += /*gl_getpixel (x - 1, y) */ *(pixel - 1) - back (0);
	    n++;
	  }
	if (y > 0)
	  {
	    i += /*gl_getpixel (x, y - 1) */ *(pixel - MAPWIDTH) - back (0);
	    n++;
	  }
	c = (i + (random () % 16)) / (n + 1);
	if (c > 9)
	  c = 9;
	/*gl_setpixel (x, y, back (0) + c); */
	*pixel = back (0) + c;
	pixel++;
      }
}
static void
drawstarbackground ()
{
/* Create fancy dark red background */
  int             x;
  gl_setcontext (starbackground);
  gl_clearscreen (191);
  for (x = 0; x < 700 / DIV / DIV; x++)
    gl_setpixel (rand () % MAPWIDTH, rand () % (MAPHEIGHT + 20), rand () % 32 + 192);
}




static void
drawbackground ()
{				/*int i; */
/* Build up background from map data */
  gl_setcontext (background);
  gl_clearscreen (0);
  createbackground ();
  gl_enableclipping ();
  gl_line (0, MAPHEIGHT, MAPWIDTH - 1, MAPHEIGHT, back (16));
  /*for(i=0;i<MAPWIDTH/2;i++)
     gl_fillbox(i,(int)(MAPHEIGHT+(i*10.0/MAPWIDTH*2.0)),
     MAPWIDTH-2*i,(int)(((MAPWIDTH/2-i)*20.0/MAPWIDTH*2.0)),
     back((int)(16-i*16.0/MAPWIDTH*2.0))); */
  gl_disableclipping ();
}





void
uninitialize ()
{
  int             h, i;
  float           p = 0;
  char            bitmap1[MAPWIDTH][MAPHEIGHT + 20];
  char            bitmap2[MAPWIDTH][MAPHEIGHT + 20];
  gl_enableclipping ();
  gl_setcontext (physicalscreen);
  gl_getbox (0, 0, MAPWIDTH, MAPHEIGHT + 20, bitmap1);
  for (h = (MAPHEIGHT + 20) / 2 - (MAPHEIGHT + 18) / 16; h >= 2; h -= (MAPHEIGHT + 18) / 16)
    {
      p += 64.0 / 8;
      gl_scalebox (MAPWIDTH, MAPHEIGHT + 20, bitmap1,
		   MAPWIDTH, h * 2, bitmap2);
      gl_putbox (0, (MAPHEIGHT + 20) / 2 - h, MAPWIDTH, h * 2, bitmap2);
      gl_fillbox (0, (MAPHEIGHT + 20) / 2 - h - (MAPHEIGHT + 20) / 16, MAPWIDTH, (MAPHEIGHT + 20) / 16, 0);
      gl_fillbox (0, (MAPHEIGHT + 20) / 2 + h, MAPWIDTH, (MAPHEIGHT + 20) / 16, 0);
      setcustompalette ((int) p, 1);
    }
  gl_fillbox (0, (MAPHEIGHT + 20) / 2 - 50 - 2 / DIV, MAPWIDTH, 50, 0);
  gl_fillbox (0, (MAPHEIGHT + 20) / 2 + 2 / DIV, MAPWIDTH, 50, 0);
  for (i = MAPWIDTH / 2; i >= 5; i -= 5 / DIV)
    gl_fillbox (0, (MAPHEIGHT + 20) / 2 - 2, MAPWIDTH / 2 - i, 20, 0),
      gl_fillbox (MAPWIDTH / 2 + i, (MAPHEIGHT + 20) / 2 - 2, MAPWIDTH / 2 - i, 20, 0),
      usleep (500);
  keyboard_close ();
  vga_setmode (TEXT);
  Snd_restore ();
  printf ("Life support systems disconected\n"
	  "\n\nHave a nice LINUX!\n");
}




static void
move_objects ()
{
  int             i;
  for (i = 0; i < nobjects; i++)
    if (object[i].type == CREATOR)
      {
	object[i].time--;
	if (object[i].time <= 0)
	  {
	    if (sound)
	      Snd_effect (S_CREATOR2, next);
	    object[i].live = object[i].live1;
	    object[i].type = object[i].ctype;
	    if (object[i].type == ROCKET)
	      object[i].time = 200;
	    object[i].radius = radius (object[i].ctype);
	    object[i].M = M (object[i].ctype);
	  }
      }
    else if (object[i].live)
      {
	object[i].x += object[i].fx;
	object[i].y += object[i].fy;
      }
}







char            str[2];
static void
draw_objects (int draw)
{
  char            s[80];
  int             i;
  if (draw)
    {
      gl_setcontext (background),
	gl_copyscreen (backscreen),
	gl_setcontext (backscreen);

      /* Now draw the objects in backscreen. */

      points ();
      for (i = 0; i < nobjects; i++)
	if (object[i].live)
	  {

	    switch (object[i].type)
	      {
	      case BALL:
		gl_putboxmaskcompiled ((int) (object[i].x - BALL_RADIUS) / DIV, (int) (object[i].y - BALL_RADIUS) / DIV,
		 BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, ball_bitmap);
		break;
	      case LBALL:
		if (object[i].letter == L_ACCEL)
		  gl_putboxmaskcompiled ((int) (object[i].x - BALL_RADIUS) / DIV, (int) (object[i].y - BALL_RADIUS) / DIV,
					 BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, lball_bitmap[0]);
		else
		  gl_putboxmaskcompiled ((int) (object[i].x - BALL_RADIUS) / DIV, (int) (object[i].y - BALL_RADIUS) / DIV,
					 BALL_RADIUS * 2 / DIV, BALL_RADIUS * 2 / DIV, lball_bitmap[1]);
		if (DIV == 1)
		  {
		    gl_setwritemode (WRITEMODE_MASKED);
		    gl_setfont (8, 8, fontblack);
		    str[0] = object[i].letter;
		    gl_write ((int) object[i].x / DIV - 4, (int) object[i].y / DIV - 4, str);
		  }
		break;
	      case HOLE:
		gl_enableclipping ();
		gl_putboxmaskcompiled ((int) (object[i].x - HOLE_RADIUS) / DIV, (int) (object[i].y - HOLE_RADIUS) / DIV,
			     HOLE_RADIUS * 2, HOLE_RADIUS * 2, hole_bitmap);
		gl_disableclipping ();
		break;
	      case EHOLE:
		gl_enableclipping ();
		gl_putboxmaskcompiled ((int) (object[i].x - HOLE_RADIUS) / DIV, (int) (object[i].y - HOLE_RADIUS) / DIV,
			    HOLE_RADIUS * 2, HOLE_RADIUS * 2, ehole_bitmap);
		gl_disableclipping ();
		break;
	      case BBALL:
		gl_putboxmaskcompiled ((int) (object[i].x - BBALL_RADIUS) / DIV, (int) (object[i].y - BBALL_RADIUS) / DIV,
				       BBALL_RADIUS * 2 / DIV, BBALL_RADIUS * 2 / DIV, bball_bitmap);
		break;
	      case INSPECTOR:
		gl_putboxmaskcompiled ((int) (object[i].x - INSPECTOR_RADIUS) / DIV, (int) (object[i].y - INSPECTOR_RADIUS) / DIV,
				       INSPECTOR_RADIUS * 2 / DIV, INSPECTOR_RADIUS * 2 / DIV, inspector_bitmap);
		break;
	      case APPLE:
		gl_putboxmaskcompiled ((int) (object[i].x - APPLE_RADIUS) / DIV, (int) (object[i].y - APPLE_RADIUS) / DIV,
				       APPLE_RADIUS * 2 / DIV, APPLE_RADIUS * 2 / DIV, apple_bitmap);
		gl_enableclipping ();
		gl_line ((int) (object[i].x + 10) / DIV, (int) (object[i].y - APPLE_RADIUS - 10) / DIV,
			 (int) (object[i].x) / DIV, (int) (object[i].y - APPLE_RADIUS + 10) / DIV, 150);
		gl_line ((int) (object[i].x + 10) / DIV + 1, (int) (object[i].y - APPLE_RADIUS - 10) / DIV,
			 (int) (object[i].x) / DIV + 1, (int) (object[i].y - APPLE_RADIUS + 10) / DIV, 150);
		if (DIV == 1)
		  gl_line ((int) (object[i].x + 10) / DIV + 2, (int) (object[i].y - APPLE_RADIUS - 10) / DIV,
			   (int) (object[i].x) / DIV + 2, (int) (object[i].y - APPLE_RADIUS + 10) / DIV, 150);
		gl_disableclipping ();
		gl_putboxmaskcompiled ((int) (object[i].x - EYE_RADIUS) / DIV,
			      (int) (object[i].y + APPLE_RADIUS - 15) / DIV,
		 EYE_RADIUS * 2 / DIV, EYE_RADIUS * 2 / DIV, eye_bitmap[0]);
		break;
	      case ROCKET:
		{
		  int             x1, y1;
		  gl_putboxmaskcompiled ((int) (object[i].x - ROCKET_RADIUS) / DIV, (int) (object[i].y - ROCKET_RADIUS) / DIV,
					 ROCKET_RADIUS * 2 / DIV, ROCKET_RADIUS * 2 / DIV, rocket_bitmap[i]);
		  x1 = object[i].x + sin (object[i].rotation - RAD (30)) * EYE_RADIUS1 - EYE_RADIUS;
		  y1 = object[i].y + cos (object[i].rotation - RAD (30)) * EYE_RADIUS1 - EYE_RADIUS;
		  gl_enableclipping ();
		  gl_putboxmaskcompiled (x1 / DIV, y1 / DIV,
		  EYE_RADIUS * 2 / DIV, EYE_RADIUS * 2 / DIV, eye_bitmap[i]);
		  x1 = object[i].x + sin (object[i].rotation + RAD (30)) * EYE_RADIUS1 - EYE_RADIUS;
		  y1 = object[i].y + cos (object[i].rotation + RAD (30)) * EYE_RADIUS1 - EYE_RADIUS;
		  gl_putboxmaskcompiled (x1 / DIV, y1 / DIV,
		  EYE_RADIUS * 2 / DIV, EYE_RADIUS * 2 / DIV, eye_bitmap[i]);
		  gl_disableclipping ();
		}
		break;
	      }
	  }
    }
  /*if draw */
  else
    points1 ();
  switch (gamemode)
    {
    case MENU:
      draw_menu (draw);
      break;
    case KEYS:
      draw_keys (draw);
      break;
#ifdef JOYSTICK
    case JOY:
      draw_joy (draw);
      break;
#endif
    }


  if (draw && (gamemode == MENU || (gamemode == GAME && mouseplayer != -1)))
    {
      gl_enableclipping ();
      gl_putboxmaskcompiled (mouse_getx () - MOUSE_RADIUS, mouse_gety () - MOUSE_RADIUS,
			  MOUSE_RADIUS * 2, MOUSE_RADIUS * 2, mouse_bitmap);
      gl_disableclipping ();
    }
  if (draw)
    {
      gl_enableclipping ();
      sprintf (s, " lives: %6i%6i%6i%6i%6i",
	       nrockets >= 1 ? object[0].live1 : 0,
	       nrockets >= 2 ? object[1].live1 : 0,
	       nrockets >= 3 ? object[2].live1 : 0,
	       nrockets >= 4 ? object[3].live1 : 0,
	       nrockets >= 5 ? object[4].live1 : 0);
      gl_setwritemode (WRITEMODE_MASKED);
      gl_setfont (8, 8, fontwhite);
      gl_write (MAPWIDTH / 2 - strlen (s) * 4, MAPHEIGHT + 2, s);
      sprintf (s, "scores: %6i%6i%6i%6i%6i",
	       object[0].score,
	       object[1].score,
	       object[2].score,
	       object[3].score,
	       object[4].score);
      gl_write (MAPWIDTH / 2 - strlen (s) * 4, MAPHEIGHT + 11, s);
      gl_setfont (8, 8, fontblack);

      /* Copy backscreen to physical screen. */
      gl_copyscreen (physicalscreen);
      fadein ();
      gl_disableclipping ();
    }
}




static void
explosion (int x, int y, int type, int letter, int n)
{
  float           i;
  int             speed;
  int             color1;
  int             radius1 = radius (type);
  for (i = 0; i < RAD (360); i += RAD (360.0) * DIV * DIV / radius1 / radius1 / M_PI)
    {
      speed = rand () % 3096 + 10;
      color1 = color (type, n, letter) + (rand () % 32);
      addpoint (x * 256, y * 256,
		sin (i) * (speed),
		cos (i) * (speed),
		color1,
		rand () % 100 + 10);
    }
}






void
destroy (int i)
{
  int             y;
  if (object[i].x - object[i].radius < 0)
    object[i].x = object[i].radius + 1, object[i].fx *= -1;
  if (object[i].y - object[i].radius < 0)
    object[i].y = object[i].radius + 1, object[i].fy *= -1;
  if (object[i].x + object[i].radius > GAMEWIDTH)
    object[i].x = GAMEWIDTH - object[i].radius - 1, object[i].fx *= -1;
  if (object[i].y + object[i].radius > GAMEHEIGHT)
    object[i].y = GAMEHEIGHT - object[i].radius - 1, object[i].fy *= -1;
  switch (object[i].type)
    {
    case LBALL:
      if (sound)
	Snd_effect (S_DESTROY_BALL, next);
      object[i].live = 0, explosion (object[i].x, object[i].y, object[i].type, object[i].letter, i);
      break;
    case APPLE:
      if (sound)
	Snd_effect (S_DESTROY_ROCKET, 0);
    case BALL:
    case EHOLE:
    case BBALL:
    case INSPECTOR:
      if (sound)
	Snd_effect (S_DESTROY_BALL, next);
      if ((y = create_letter ()) != 0)
	{
	  object[i].type = LBALL;
	  object[i].M = LBALLM;
	  switch (y)
	    {
	    case 1:
	      object[i].letter = L_ACCEL;
	      break;
	    case 2:
	      object[i].letter = L_GUMM;
	      break;
	    }
	}
      else
	object[i].live = 0, explosion (object[i].x, object[i].y, object[i].type, object[i].letter, i);
      break;
    case ROCKET:
      if (sound)
	Snd_effect (S_DESTROY_ROCKET, 0);
      object[i].live1--, object[i].live--, explosion (object[i].x, object[i].y, object[i].type, object[i].letter, i);
      rocket_destroyed (i);
      if (object[i].live)
	{
	  /*object[i].x = rand () % (GAMEWIDTH-60)+30;
	     object[i].y = rand () % (GAMEHEIGHT-60)+30; */
	  object[i].fx = 0;
	  object[i].fy = 0;
	  object[i].rotation = 0;
	  object[i].type = ROCKET;
	  object[i].accel = ROCKET_SPEED;
	  creator_rocket (i);
	}
      break;
    }
}




static void
check_limit ()
{
  int             i;
  for (i = 0; i < nobjects; i++)
    if (object[i].live)
      {
	if (object[i].x - object[i].radius < 0 || object[i].x + object[i].radius > GAMEWIDTH ||
	    object[i].y - object[i].radius < 0 || object[i].y + object[i].radius > GAMEHEIGHT)
	  {
	    destroy (i);
	  }
      }
}




static void
update_values ()
{
  int             i;
  a_holes = 0;
  a_rockets = 0;
  a_balls = 0;
  a_bballs = 0;
  a_apples = 0;
  a_eholes = 0;
  a_inspectors = 0;
  for (i = 0; i < nobjects; i++)
    {
      if (object[i].live)
	{
	  switch (object[i].type)
	    {
	    case HOLE:
	      a_holes++;
	      break;
	    case EHOLE:
	      a_eholes++;
	      break;
	    case ROCKET:
	      a_rockets++;
	      break;
	    case LBALL:
	    case BALL:
	      a_balls++;
	      break;
	    case BBALL:
	      a_bballs++;
	      break;
	    case APPLE:
	      a_apples++;
	      break;
	    case INSPECTOR:
	      a_inspectors++;
	      break;
	    }
	}
      if (object[i].type == CREATOR)
	{
	  switch (object[i].ctype)
	    {
	    case BBALL:
	      a_bballs++;
	      break;
	    case HOLE:
	      a_holes++;
	      break;
	    case EHOLE:
	      a_eholes++;
	      break;
	    case ROCKET:
	      a_rockets++;
	      break;
	    case LBALL:
	    case BALL:
	      a_balls++;
	      break;
	    case APPLE:
	      a_apples++;
	      break;
	    case INSPECTOR:
	      a_inspectors++;
	      break;
	    }
	}
    }

}




void
accel (int i)
{
  int             y;
  object[i].time = 0;
  object[i].fx += sin (object[i].rotation) * object[i].accel,
    object[i].fy += cos (object[i].rotation) * object[i].accel;
  for (y = 0; y < 5 / DIV / DIV; y++)
    {
      float           p;
      p = RAD (rand () % 45 - 22);
      addpoint (object[i].x * 256,
		object[i].y * 256,
		(object[i].fx - sin (object[i].rotation + p) * object[i].accel * 10) * (rand () % 512),
		(object[i].fy - cos (object[i].rotation + p) * object[i].accel * 10) * (rand () % 512),
		rocket (rand () % 16), 10);
    }
}




static void
process_keys ()
{
  int             i;
#ifdef JOYSTICK
  int             status;
  struct JS_DATA_TYPE js;
#endif


  keyboard_update ();
  if (keyboard_keypressed (SCANCODE_P))
    {
      int             k = 1, i;
      gl_setcontext (physicalscreen);
      gl_setwritemode (WRITEMODE_OVERWRITE);
      gl_setfont (8, 8, fontwhite);
      gl_write (MAPWIDTH / 2 - 20, MAPHEIGHT / 2 - 4, "PAUSE");
      tbreak = 1;
      while (k)
	{
	  keyboard_update ();
	  k = 0;
	  for (i = 0; i < 128; i++)
	    if (keyboard_keypressed (i))
	      k = 1;
	}
      while (!k)
	{
	  keyboard_update ();
	  k = 0;
	  for (i = 0; i < 128; i++)
	    if (keyboard_keypressed (i))
	      k = 1;
	}
      gl_setwritemode (WRITEMODE_MASKED);
    }
  mouse_update ();
  switch (gamemode)
    {
    case MENU:
      menu_keys ();
      break;
    case KEYS:
      keys_keys ();
      break;
#ifdef JOYSTICK
    case JOY:
      joy_keys ();
      break;
#endif
    case GAME:
#ifdef JOYSTICK
      for (i = 0; i < 2; i++)
	{
	  double          x, y, a;
	  if (joystickplayer[i] >= 0)
	    {
	      if (!object[joystickplayer[i]].live)
		continue;
	      if (object[joystickplayer[i]].type != ROCKET)
		continue;
	      status = read (joystickdevice[i], &js, JS_RETURN);
	      if (status != JS_RETURN)
		{
		  perror ("js");
		  break;
		}
	      x = center[i][0] - js.x;
	      y = center[i][0] - js.y;
	      if (x == 0)
		x = 0.001;
	      a = atan (fabs (y) / fabs (x));
	      if (x < 0 && y >= 0)
		object[joystickplayer[i]].rotation = a + RAD (90);
	      else if (x < 0 && y < 0)
		object[joystickplayer[i]].rotation = RAD (90) - a;
	      else if (x >= 0 && y < 0)
		object[joystickplayer[i]].rotation = a + RAD (270);
	      else if (x >= 0 && y >= 0)
		object[joystickplayer[i]].rotation = RAD (270) - a;
	      if (js.buttons)
		accel (joystickplayer[i]);

	    }
	}
#endif
      /* Move. */
      if (mouseplayer != -1 && object[mouseplayer].live && object[mouseplayer].type == ROCKET)
	{
	  double          dx, dy, a;
	  dx = object[mouseplayer].x / DIV - mouse_getx ();
	  dy = object[mouseplayer].y / DIV - mouse_gety ();
	  if (dx == 0)
	    dx = 0.001;
	  a = atan (fabs (dy) / fabs (dx));
	  if (dx < 0 && dy >= 0)
	    object[mouseplayer].rotation = a + RAD (90);
	  else if (dx < 0 && dy < 0)
	    object[mouseplayer].rotation = RAD (90) - a;
	  else if (dx >= 0 && dy < 0)
	    object[mouseplayer].rotation = a + RAD (270);
	  else if (dx >= 0 && dy >= 0)
	    object[mouseplayer].rotation = RAD (270) - a;
	  if (mouse_getbutton ())
	    accel (mouseplayer);

	}
      if (keyboard_keypressed (SCANCODE_ESCAPE))
	{
	  gamemode = MENU;
	  while (keyboard_keypressed (SCANCODE_ESCAPE))
	    keyboard_update ();
	}
      for (i = 0; i < nrockets; i++)
	{
	  if (object[i].type != ROCKET || !object[i].live)
	    continue;
	  if (rotation[i])
	    {
	      if (keyboard_keypressed (keys[i][1]))
		object[i].rotation += ROTSTEP;
	      if (keyboard_keypressed (keys[i][2]))
		object[i].rotation -= ROTSTEP;
	      if (keyboard_keypressed (keys[i][0]))
		accel (i);
	    }
	  else
	    {
	      if (keyboard_keypressed (keys[i][2]) && keyboard_keypressed (keys[i][0]))
		object[i].rotation = RAD (-135), accel (i);
	      else if (keyboard_keypressed (keys[i][3]) && keyboard_keypressed (keys[i][0]))
		object[i].rotation = RAD (135), accel (i);
	      else if (keyboard_keypressed (keys[i][1]) && keyboard_keypressed (keys[i][3]))
		object[i].rotation = RAD (45), accel (i);
	      else if (keyboard_keypressed (keys[i][1]) && keyboard_keypressed (keys[i][2]))
		object[i].rotation = RAD (-45), accel (i);
	      else if (keyboard_keypressed (keys[i][2]))
		object[i].rotation = RAD (-90), accel (i);
	      else if (keyboard_keypressed (keys[i][3]))
		object[i].rotation = RAD (90), accel (i);
	      else if (keyboard_keypressed (keys[i][0]))
		object[i].rotation = RAD (180), accel (i);
	      else if (keyboard_keypressed (keys[i][1]))
		object[i].rotation = RAD (0), accel (i);
	    }
	}
      break;
    }



}




void
creator (int type)
{
  int             time = 50;
  int             i;
  int             z;
  int             x, y;
  int             color1 = color (type, 0, 0);
  for (i = nrockets; i < nobjects && (object[i].live ||
				      object[i].type == CREATOR);
       i++);
  if (i >= MAXOBJECT)
    return;
  if (!find_possition (&object[i].x, &object[i].y, radius (type)))
    return;
  if (i >= nobjects)
    nobjects = i + 1;
  object[i].live = 0;
  object[i].live1 = 1;
  object[i].ctype = type;
  object[i].fx = 0.0;
  object[i].fy = 0.0;
  object[i].time = time;
  object[i].rotation = 0;
  object[i].type = CREATOR;
  object[i].M = M (type);
  object[i].radius = radius (type);
  object[i].accel = ROCKET_SPEED;
  object[i].letter = ' ';
  if (sound)
    Snd_effect (S_CREATOR1, 0);
  for (z = 0; z < object[i].radius * object[i].radius * M_PI / DIV / DIV; z++)
    {
      x = rand () % GAMEWIDTH;
      y = rand () % GAMEHEIGHT;
      addpoint (x * 256, y * 256,
		(object[i].x - x) * 256 / (time),
		(object[i].y - y) * 256 / (time),
		color1 + (rand () % 32),
		time);
    }
}
void
creator_rocket (int i)
{
  int             time = 50;
  int             type = ROCKET;
  int             z;
  int             x, y;
  int             color1 = color (ROCKET, i, 0);
  if (!find_possition (&object[i].x, &object[i].y, radius (type)))
    return;
  if (sound)
    object[i].live1 = object[i].live;
  object[i].live = 0;
  object[i].ctype = type;
  object[i].fx = 0.0;
  object[i].fy = 0.0;
  object[i].time = time;
  object[i].rotation = 0;
  object[i].type = CREATOR;
  object[i].M = ROCKETM;
  object[i].radius = ROCKET_RADIUS;
  object[i].accel = ROCKET_SPEED;
  object[i].letter = ' ';
  for (z = 0; z < ROCKET_RADIUS * ROCKET_RADIUS * M_PI / DIV / DIV; z++)
    {
      x = rand () % GAMEWIDTH;
      y = rand () % GAMEHEIGHT;
      addpoint (x * 256, y * 256,
		(object[i].x - x) * 256 / (time),
		(object[i].y - y) * 256 / (time),
		color1 + rand () % 32,
		time);
    }
}






static void
update_forces ()
{
  int             i;
  int             rocket = 0;
  int             r;
  float           d;
  float           xp, yp;
  for (i = 0; i < nobjects; i++)
    {
      if (object[i].live)
	{
	  if (object[i].type == ROCKET && object[i].time)
	    object[i].time--;
	  if (object[i].type == ROCKET && !object[i].time)
	    {
	      d = 640 * 640;
	      rocket = -1;
	      for (r = 0; r < nobjects; r++)
		{
		  if (object[r].live && !object[r].time && object[r].type == EHOLE)
		    {
		      int             distance;
		      float           gravity;
		      xp = object[r].x - object[i].x;
		      yp = object[r].y - object[i].y;
		      distance = sqrt (xp * xp + yp * yp);
		      gravity = BALL_SPEED * 200 / distance;
		      if (gravity > BALL_SPEED * 4 / 5)
			gravity = BALL_SPEED * 4 / 5;
		      normalize (&xp, &yp, gravity);
		      object[i].fx += xp;
		      object[i].fy += yp;
		    }
		}

	    }
	  if (object[i].type == BALL || object[i].type == LBALL || object[i].type == BBALL)
	    {
	      d = 640 * 640;
	      rocket = -1;
	      if (object[i].type == BALL && !rand () % 2048)
		{
		  object[i].type = LBALL;
		  switch (rand () % 2)
		    {
		    case 0:
		      object[i].letter = L_ACCEL;
		      break;
		    case 1:
		      object[i].letter = L_GUMM;
		      break;
		    }
		}
	      for (r = 0; r < nrockets; r++)
		{
		  if (object[r].live && !object[r].time)
		    {
		      xp = object[r].x - object[i].x;
		      yp = object[r].y - object[i].y;
		      if (xp * xp + yp * yp < d)
			d = xp * xp + yp * yp, rocket = r;
		    }
		}
	      if (rocket != -1)
		xp = object[rocket].x - object[i].x,
		  yp = object[rocket].y - object[i].y;
	      else
		xp = GAMEWIDTH / 2 - object[i].x,
		  yp = GAMEHEIGHT / 2 - object[i].y;
	      switch (object[i].type)
		{
		case BBALL:
		  normalize (&xp, &yp, BBALL_SPEED);
		  break;
		case BALL:
		case LBALL:
		  normalize (&xp, &yp, BALL_SPEED);
		  break;
		}
	      object[i].fx += xp;
	      object[i].fy += yp;
	    }
	  object[i].fx *= SLOWDOWN,
	    object[i].fy *= SLOWDOWN;
	}
    }
}




static void
colisions ()
{
  int             i, y;
  int             colize = 0;
  float           xp, yp;
  for (i = 0; i < nobjects; i++)
    if (object[i].live)
      for (y = i + 1; y < nobjects; y++)
	if (object[y].live)
	  {
	    xp = object[y].x - object[i].x;
	    yp = object[y].y - object[i].y;
	    if (xp * xp + yp * yp < (object[y].radius + object[i].radius) *
		(object[y].radius + object[i].radius))
	      {
		colize = 1;
		if (object[i].type == HOLE || object[i].type == EHOLE)
		  {
		    if (object[y].type != APPLE)
		      destroy (y);
		    if (object[i].type == EHOLE)
		      destroy (i);
		    continue;
		  }
		if (object[y].type == HOLE || object[y].type == EHOLE)
		  {
		    if (object[i].type != APPLE)
		      destroy (i);
		    if (object[y].type == EHOLE)
		      destroy (y);
		    continue;
		  }
		if (object[i].type == ROCKET)
		  {
		    object[i].score++;
		    if (object[y].letter == L_ACCEL)
		      object[i].accel += A_ADD,
			object[i].score += 10;
		    if (object[y].letter == L_GUMM)
		      object[i].M += M_ADD,
			object[i].score += 10;
		    object[y].letter = ' ';
		    if (object[y].type == LBALL)
		      object[y].type = BALL;
		  }
		normalize (&xp, &yp, object[i].M / object[y].M * GUMM);
		object[y].fx += xp;
		object[y].fy += yp;
		normalize (&xp, &yp, object[y].M / object[i].M * GUMM);
		object[i].fx -= xp;
		object[i].fy -= yp;
		if (object[i].type == ROCKET && object[i].time)
		  object[i].fx = 0,
		    object[i].fy = 0;
		if (object[y].type == ROCKET && object[y].time)
		  object[y].fx = 0,
		    object[y].fy = 0;
		if (object[y].type == INSPECTOR && object[i].type == ROCKET)
		  {
		    object[y].fx = 0,
		      object[y].fy = 0;
		    object[i].fx *= -2,
		      object[i].fy *= -2;
		  }
	      }
	  }
  if (colize && sound)
    Snd_effect (S_COLIZE, next);
}



static void
game ()
{
  long            VfTime = 0;
  long            VendSleep = 0;
  struct timeval  VlastClk;
  struct timeval  VnewClk;
  int             wait = 0;


  gettimeofday (&VlastClk, NULL);
  gettimeofday (&VnewClk, NULL);
  VendSleep = VlastClk.tv_usec;
  VfTime = 1000000 / 25;


  while (1)
    {
      process_keys ();
      update_values ();
      update_game ();
      update_forces ();
      colisions ();
      move_objects ();
      check_limit ();
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (wait > 0 || tbreak)
	draw_objects (1);
      else
	draw_objects (0);
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      if (tbreak)
	wait = VfTime, tbreak = 0;
      usleep (wait < 0 ? 0 : wait);
      VendSleep = VnewClk.tv_usec + wait;
      gettimeofday (&VlastClk, NULL);

    }
  if (sound)
    Snd_effect (S_END, 0);
}



int
main (int argc, char **argv)
{
  nrockets = 1;
  printf ("\n\n\n\n"
	  "                                The  game\n"
	  "                               K O U L E S\n"
	  "\n\n\n\n"
	  "                       Copyright(c) Jan Hubicka 1995\n\n\n");

  printf ("LINUX4GW 1.12.45b professional\n");
  printf ("Copyright(c)1991,1992,1993,1994,1995 Jan Hubicka(JAHUSOFT)\n");
  create_bitmap ();
  initialize ();
  gl_write (0, 40, "Initializing GUI user interface");
  init_menu ();
  gamemode = MENU;
  gl_write (0, 48, "Initializing 4d rotation tables");
  init_objects ();
  printf ("creating bitmaps\n");
  gl_write (0, 56, "Initializing refresh daemon ");
  gl_write (0, 66, "please wait 12043.21 Bogomipseconds");
  drawbackground ();
  drawstarbackground ();
  gl_setfont (8, 8, fontblack);

  keys[0][0] = SCANCODE_CURSORBLOCKUP;
  keys[0][1] = SCANCODE_CURSORBLOCKDOWN;
  keys[0][2] = SCANCODE_CURSORBLOCKLEFT;
  keys[0][3] = SCANCODE_CURSORBLOCKRIGHT;

  keys[1][0] = SCANCODE_CURSORUP;
  keys[1][1] = SCANCODE_CURSORDOWN;
  keys[1][2] = SCANCODE_CURSORLEFT;
  keys[1][3] = SCANCODE_CURSORRIGHT;
  starwars ();



  game ();
  printf ("uninitializing\n");
  uninitialize ();
  return 0;
}
