/*-
 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Revision History:
 *  3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
 * 26-Nov-2001: Random level generator added
 * 01-Nov-2000: Allocation checks
 * 04-Jun-1997: Compatible with xscreensaver
 *
 */

#define LEVHEIGHT 	32U
#define LEVWIDTH 	40U

#define GETNB(n) ((1 << (n)) - 1)
#define TESTNB(v, n) (((1 << (n)) & v) != 0x00)
#define SNB(v, n) ((v) |= (1 << (n)))
#define UNSNB(v, n) ((v) &= ~(1 << (n)))
#define GHOSTS 4U
#define MAXMOUTH 11
#define MAXGPOS 2
#define MINGRIDSIZE 4
#define MINSIZE 3
#define NOWHERE 16383
#define START ((LRAND() & 1) ? 1 : 3)
#define MINDOTPERC 10

#define RED (0)
#define ORANGE (3 * MI_NPIXELS(mi) / 32)
#define YELLOW (MI_NPIXELS(mi) / 6)
#define GREEN (23 * MI_NPIXELS(mi) / 64)
#define CYAN (MI_NPIXELS(mi) / 2)
#define BLUE (45 * MI_NPIXELS(mi) / 64)
#define MAGENTA (53 * MI_NPIXELS(mi) / 64)
#define WHITE (MI_NPIXELS(mi))

#define LINEWIDTH	4
#define HLINEWIDTH	1
#define JAILHEIGHT      7
#define JAILWIDTH       8

#define GETFACTOR(x, y) ((x) > (y) ? 1 : ((x) < (y) ? -1 : 0))
#define SIGN(x) GETFACTOR((x), 0)
#define TRACEVECS 40

#define DEF_TRACKMOUSE "False"
static Bool trackmouse;

typedef struct { int vx, vy; } tracevec_struct;

typedef enum { inbox = 0, goingout, randdir, chasing, hiding } GhostState;
typedef enum { ps_eating = 0, ps_chasing, ps_hiding, ps_random } PacmanState;
typedef enum { GHOST_DANGER, GHOST_EATEN } GameState;

typedef struct {
	unsigned int 	col, row;
	unsigned int 	lastbox, nextcol, nextrow;
	int		dead;
	int		cfactor, rfactor;
	int		cf, rf;
	int		oldcf, oldrf;
	int		timeleft;
	GhostState	aistate;
	int		color;
	int		speed;
	XPoint	    	delta;
	XPoint		err;
} ghoststruct;

typedef struct {
	unsigned int	col, row;
	unsigned int	lastbox, nextcol, nextrow;
	int		mouthstage, mouthdirection;
	int		cfactor, rfactor;
	int		cf, rf;
	int		oldcf, oldrf;
	int		oldlx, oldly;
	int		justate;
	PacmanState	aistate;
	tracevec_struct	trace[TRACEVECS];
	int		cur_trace;
	int		state_change;
	int		roundscore;
	int		speed;
	int		lastturn;
	XPoint	    	delta;
	XPoint		err;
} pacmanstruct;

typedef struct {
	unsigned short 	width, height;
	unsigned short 	nrows, ncols;
	short 		xs, ys, xb, yb;
	short 		incx, incy;
	GC          	stippledGC;
	int         	graphics_format;
	pacmanstruct	pacman;
	ghoststruct	*ghosts;
	unsigned int	nghosts;
	Pixmap      	pacmanPixmap[4][MAXMOUTH];
/*	Pixmap	    	ghostPixmap[4][MAXGPOS];*/
	Pixmap	    	ghostPixmap[2];
	char        	level[LEVHEIGHT * LEVWIDTH];
	unsigned int	wallwidth;
	unsigned int	dotsleft;
	int		spritexs, spriteys, spritedx, spritedy;

	GameState	gamestate;
	unsigned int	timeleft;
} pacmangamestruct;

#define DIRVECS 4
static const struct { int dx, dy; } dirvecs[DIRVECS] =
	{ {-1, 0}, {0, 1}, {1, 0}, {0, -1}};

static pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;

typedef char lev_t[LEVHEIGHT][LEVWIDTH + 1];

/* function type definitions */

	/* level generation */
static int 	creatlevelblock(lev_t *level, const unsigned x,
				const unsigned y);
static void 	setblockto(lev_t *level, const unsigned x, const unsigned y,
				const char c);
static int 	checkset(lev_t *level, const unsigned x, const unsigned y);
static int 	checksetout(lev_t *level, const unsigned x, const unsigned y);
static int 	checkunsetdef(lev_t *level, const unsigned x, const unsigned y);
static void 	clearlevel(lev_t *level);
static void 	copylevel(char *dest, lev_t *level);
static void 	createjail(lev_t *level, const unsigned width,
				const unsigned height);
static void 	finishjail(lev_t *level, const unsigned width,
				const unsigned height);
static int 	tryset(lev_t *level, const unsigned xpos, const unsigned ypos,
				const char *block);
static int 	creatlevelblock(lev_t *level, const unsigned x,
				const unsigned y);
static void 	filllevel(lev_t *level);
static void 	frmtlevel(lev_t *level);
static unsigned countdots(ModeInfo * mi);
static int 	createnewlevel(ModeInfo * mi);
static int	check_pos(pacmangamestruct *pp, int y, int x, int ghostpass);
static int 	check_dot(pacmangamestruct *pp, unsigned int x, unsigned int y);

	/* graphics rendering */
static void 	drawlevel(ModeInfo * mi);

	/* AI */
static int 	ghost_get_posdirs(pacmangamestruct *pp, int *posdirs,
				ghoststruct *g);
static void 	ghost_random(pacmangamestruct *pp, ghoststruct *g);
static void 	ghost_chasing(pacmangamestruct *pp, ghoststruct *g);
static void 	ghost_hiding(pacmangamestruct *pp, ghoststruct *g);
static void 	ghost_update(pacmangamestruct *pp, ghoststruct *g);

static void 	pac_dot_vec(pacmangamestruct *pp, pacmanstruct *p,
				long *vx, long *vy);
static int 	pac_ghost_prox_and_vector(pacmangamestruct *pp,
				pacmanstruct *p, long *vx, long *vy);
static int 	pac_get_posdirs(pacmangamestruct *pp, pacmanstruct *p,
				int *posdirs);
static void 	pac_clear_trace(pacmanstruct *p);
static void 	pac_save_trace(pacmanstruct *p, const int vx, const int vy);
static int 	pac_check_trace(const pacmanstruct *p, const int vx,
				const int vy);
static void 	pac_eating(pacmangamestruct *pp, pacmanstruct *p);
#if 0
static void 	pac_chasing(pacmangamestruct *pp, pacmanstruct *p);
#endif
static void 	pac_random(pacmangamestruct *pp, pacmanstruct *p);
static int	pac_get_vector_screen(pacmangamestruct *pp, pacmanstruct *p,
			const int x, const int y, int *vx, int *vy);
static int 	pac_trackmouse(ModeInfo * mi, pacmangamestruct *pp,
			pacmanstruct *p);
static void 	pac_update(ModeInfo * mi, pacmangamestruct *pp,
			pacmanstruct *p);

	/* generic functions */
static void 	repopulate(ModeInfo * mi);
