/*-----------------------------------------------------------------------------+
|									       |
| 				   ManIX				       |
|									       |
|	         	1989 Por Fernando J. G. Pereira .		       |
|									       |
|			   	 89-05-15				       |
|			   	 89-11-05				       |
|									       |
+-----------------------------------------------------------------------------*/

#include <stdio.h>
#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>


#define BITMAP_HEIGHT	16
#define BITMAP_WIDTH	16
#define NAME_LENGHT	20
#define MAX_LINES	22
#define MAX_COL		40
#define NIVEL_MAX	10
#define NMAX_TABLE	10
#define X_SCORE		8
#define Y_SCORE 	24
#define X_INICIAL	20
#define Y_INICIAL	17
#define X_ENTRADA	16
#define Y_ENTRADA	11
#define X_LENGHT	8
#define Y_LENGHT	3
#define N_VIDAS		5
#define VIDAS_POR_BONUS 8
#define SCORE_HUNT	500
#define SCORE_APPLE	400
#define SCORE_ORANGE	300
#define TIMER_VALUE_1	80
#define TIMER_VALUE_2	120
#define TEC_INICIAIS	{ 'O', 'P', 'Q', 'A', 'H', 'C' }
#define TEC_VI		{ 'H', 'L', 'K', 'J', 'A', 'Q' }

#define AT( n ) ((n)<<4)
#define LOW2UPPER(c) 	( (islower(c)) ? (toupper(c)) : (c) )
#define RANDOM() (rand() % 256)
    

/*---------+
| TypeDefs |
+---------*/
typedef struct Position
{
    int x,
	y ;
}Position ;

typedef char Ecra[MAX_LINES][MAX_COL] ;

typedef enum { Left, Right, Up, Down } Direct ;

struct _Player
{
    char name[NAME_LENGHT];
    int  score,
	 level,
	 bonus,
	 lives ;
    Position CurrPos,
	     ApagaPos ;
    Direct manDir ;
} Player ;

typedef struct Keyboard
{
    char left,
	 right,
	 up,
	 down,
	 pause,
	 stop ;
} Keyboard ;

struct _Ghost
{
    Position CurrPos,
	     ApagaPos ;
    Direct   OldMove ;
} Ghosts[2*NIVEL_MAX+5] ;

/*------------------+
| Variaveis Globais |
+------------------*/

char LastOne, Ultima ;
Keyboard TecCurr = TEC_INICIAIS ;
Keyboard TecIni = TEC_INICIAIS ;
struct _Player HighScores[NMAX_TABLE] ;
Ecra ecra ;
int total = 0 ;
char Tecla ;
int NivelInicial = 0 ;
int NGhosts ;
Direct Possibilidades[4] ;
int timer = 0, armado = False, morte = False, NivelMudou = False ;
int Score = 0 ;
char string[80] ;


/*----------------------------------+
| Variaveis relacionas com XWindows |
+----------------------------------*/

Display *display ;
int screen ;
Window window ;
Colormap cmap ;
Font ch_font ;
GC gc_yellow, gc_red, gc_green, gc_g_yellow, gc_blue, gc_orange, gc_cyan,
   gc_gray, gc_brown, gc_pink, gc_violet, gc_magenta, gc_black, gc_white ;
XSizeHints hints ;
XColor	yellow, red, green, g_yellow, blue, orange, cyan,
	gray, brown, pink, violet, magenta ;
unsigned long black_pixel, white_pixel ;
unsigned int depth ;
Pixmap	bm_userup, bm_userdown, bm_userright, bm_userleft, bm_usereat;
Pixmap	bm_ghost1, bm_ghost2, bm_ghost3 ;
Pixmap	bm_space, bm_apple, bm_orange, bm_wall, bm_dot ;


/*----------------------------+
| Inicio do codigo executavel |
+----------------------------*/


main( argc, argv )
int argc ;
char *argv[] ;
{
    char *display_name ;

    if( argc != 3 || strcmp( argv[1], "-display" ) ) display_name = NULL ;
    else display_name = argv[2] ;

    Inicializacao( display_name, argc, argv ) ;

    do
    {
	TopScores();
	switch( Menu() )
	{
	    default : Jogar();
		      break ;
	    case '1': MudarNivel();
		      break ;
	    case '2': Teclado();
		      break ;
	    case '3': finalizacao();
	}
    } while( True );
}



Inicializacao( disp_name, argc, argv )
int argc ;
char *argv[] ;
char disp_name[] ;
{
    Window root_window ;
    XColor exact ;
    XEvent ev ;

    srand( time( NULL ) ) ;

    display = XOpenDisplay( disp_name ) ;
    if( display == (Display *)NULL )
    {
	fprintf( stderr, "Nao consigo ligar-me ao server !\n" ) ;
	exit( 1 ) ;
    }
    screen = DefaultScreen( display ) ;
    root_window = DefaultRootWindow( display ) ;

    buscar_cores() ;

    ch_font = XLoadFont( display, "9x15" ) ;

    hints.x = hints.y = 50 ;
    hints.width = BITMAP_WIDTH * MAX_COL ;
    hints.height = BITMAP_HEIGHT * ( MAX_LINES + 3 ) ;
    hints.flags = PPosition | PSize ;
    window = XCreateSimpleWindow( display, root_window, hints.x, hints.y,
				  hints.width, hints.height, 2,
				  yellow.pixel,  black_pixel) ;
    XSetStandardProperties( display, window, "ManIX Game", "ManIX",
			    None, argv, argc, &hints ) ;

    criar_gcs() ;
    criar_cursor() ;
    criar_pixmaps() ;

    XSelectInput ( display, window, ExposureMask );
    XMapRaised( display, window ) ;
    do XNextEvent( display, &ev ) ;
    while( ev.type != Expose ) ;

    XSelectInput( display, window, KeyPressMask | ExposureMask |
				   EnterWindowMask | LeaveWindowMask );
}


buscar_cores()
{
    XColor exact ;

    depth = DefaultDepth( display, screen ) ;
    cmap = DefaultColormap( display, screen ) ;
    black_pixel = BlackPixel( display, screen ) ;
    white_pixel = WhitePixel( display, screen ) ;
    if( depth == 1 ) blue.pixel = red.pixel = green.pixel = cyan.pixel =
		     magenta.pixel = orange.pixel = pink.pixel = brown.pixel =
		     violet.pixel = g_yellow.pixel = gray.pixel =
		     yellow.pixel = white_pixel;
    else if( !XAllocNamedColor( display, cmap, "yellow", &exact, &yellow ) ||
        !XAllocNamedColor( display, cmap, "blue", &exact, &blue ) ||
        !XAllocNamedColor( display, cmap, "red", &exact, &red ) ||
        !XAllocNamedColor( display, cmap, "green", &exact, &green ) ||
        !XAllocNamedColor( display, cmap, "cyan", &exact, &cyan ) ||
        !XAllocNamedColor( display, cmap, "magenta", &exact, &magenta ) ||
        !XAllocNamedColor( display, cmap, "coral", &exact, &orange ) ||
        !XAllocNamedColor( display, cmap, "pink", &exact, &pink ) ||
        !XAllocNamedColor( display, cmap, "brown", &exact, &brown ) ||
        !XAllocNamedColor( display, cmap, "violet", &exact, &violet ) ||
        !XAllocNamedColor( display, cmap, "greenyellow", &exact, &g_yellow ) ||
        !XAllocNamedColor( display, cmap, "gray", &exact, &gray ) )
    {
	fprintf( stderr, "Nao consigo 'allocar' todas as cores !\n" ) ;
	exit( 1 ) ;
    }
}


criar_gcs()
{
    gc_yellow = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_yellow, yellow.pixel ) ;
    XSetBackground( display, gc_yellow, black_pixel ) ;
    XSetFont( display, gc_yellow, ch_font ) ;
    gc_red = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_red, red.pixel ) ;
    XSetBackground( display, gc_red, black_pixel ) ;
    XSetFont( display, gc_red, ch_font ) ;
    gc_green = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_green, green.pixel ) ;
    XSetBackground( display, gc_green, black_pixel ) ;
    XSetFont( display, gc_green, ch_font ) ;
    gc_blue = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_blue, blue.pixel ) ;
    XSetBackground( display, gc_blue, black_pixel ) ;
    XSetFont( display, gc_blue, ch_font ) ;
    gc_g_yellow = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_g_yellow, g_yellow.pixel ) ;
    XSetBackground( display, gc_g_yellow, black_pixel ) ;
    XSetFont( display, gc_g_yellow, ch_font ) ;
    gc_cyan = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_cyan, cyan.pixel ) ;
    XSetBackground( display, gc_cyan, black_pixel ) ;
    XSetFont( display, gc_cyan, ch_font ) ;
    gc_brown = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_brown, brown.pixel ) ;
    XSetBackground( display, gc_brown, black_pixel ) ;
    XSetFont( display, gc_brown, ch_font ) ;
    gc_white = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_white, white_pixel ) ;
    XSetBackground( display, gc_white, black_pixel ) ;
    XSetGraphicsExposures( display, gc_white, 0 ) ;
    XSetFont( display, gc_white, ch_font ) ;
    gc_black = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_black, black_pixel ) ;
    XSetBackground( display, gc_black, white_pixel ) ;
    XSetFont( display, gc_black, ch_font ) ;
    gc_gray = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_gray, gray.pixel ) ;
    XSetBackground( display, gc_gray, black_pixel ) ;
    XSetFont( display, gc_gray, ch_font ) ;
    gc_magenta = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_magenta, magenta.pixel ) ;
    XSetBackground( display, gc_magenta, black_pixel ) ;
    XSetFont( display, gc_magenta, ch_font ) ;
    gc_violet = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_violet, violet.pixel ) ;
    XSetBackground( display, gc_violet, black_pixel ) ;
    XSetFont( display, gc_violet, ch_font ) ;
    gc_orange = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_orange, orange.pixel ) ;
    XSetBackground( display, gc_orange, black_pixel ) ;
    XSetFont( display, gc_orange, ch_font ) ;
    gc_pink = XCreateGC( display, window, 0L, 0 ) ;
    XSetForeground( display, gc_pink, pink.pixel ) ;
    XSetBackground( display, gc_pink, black_pixel ) ;
    XSetFont( display, gc_pink, ch_font ) ;
}


criar_pixmaps()
{
#include "userup"
#include "userdown"
#include "userright"
#include "userleft"
#include "usereat"
#include "wall"
#include "dot"
#include "orange"
#include "apple"
#include "ghost1"
#include "ghost2"
#include "ghost3"
#include "space"

    bm_userup = XCreatePixmapFromBitmapData( display, window, userup_bits,
	userup_width, userup_height, yellow.pixel, black_pixel, depth );
    bm_userdown = XCreatePixmapFromBitmapData( display, window, userdown_bits,
	userdown_width, userdown_height, yellow.pixel, black_pixel, depth );
    bm_userleft = XCreatePixmapFromBitmapData( display, window, userleft_bits,
	userleft_width, userleft_height, yellow.pixel, black_pixel, depth );
    bm_userright = XCreatePixmapFromBitmapData( display, window, userright_bits,
	userright_width, userright_height, yellow.pixel, black_pixel, depth );
    bm_usereat = XCreatePixmapFromBitmapData( display, window, usereat_bits,
	usereat_width, usereat_height, yellow.pixel, black_pixel, depth );
    bm_ghost1 = XCreatePixmapFromBitmapData( display, window, ghost1_bits,
	ghost1_width, ghost1_height, red.pixel, black_pixel, depth );
    bm_ghost2 = XCreatePixmapFromBitmapData( display, window, ghost2_bits,
	ghost2_width, ghost2_height, cyan.pixel, black_pixel, depth );
    bm_ghost3 = XCreatePixmapFromBitmapData( display, window, ghost3_bits,
	ghost3_width, ghost3_height, green.pixel, black_pixel, depth );
    bm_dot = XCreatePixmapFromBitmapData( display, window, dot_bits,
	dot_width, dot_height, white_pixel, black_pixel, depth );
    bm_space = XCreatePixmapFromBitmapData( display, window, space_bits,
	space_width, space_height, white_pixel, black_pixel, depth );
    bm_apple = XCreatePixmapFromBitmapData( display, window, apple_bits,
	apple_width, apple_height, red.pixel, black_pixel, depth );
    bm_orange = XCreatePixmapFromBitmapData( display, window, orange_bits,
	orange_width, orange_height, orange.pixel, black_pixel, depth );
    bm_wall = XCreatePixmapFromBitmapData( display, window, wall_bits,
	wall_width, wall_height, gray.pixel, black_pixel, depth );
}

criar_cursor()
{
#include "cursor"
#include "cmask"

    Pixmap curs_bmap, cmask_bmap ;
    Cursor curs ;
    
    curs_bmap = XCreateBitmapFromData(  display, window, cursor_bits,
					cursor_width, cursor_height ) ;
    cmask_bmap = XCreateBitmapFromData( display, window, cmask_bits,
					cmask_width, cmask_height ) ;
    curs = XCreatePixmapCursor(display, curs_bmap, cmask_bmap, &yellow, &brown,
				cursor_x_hot, cursor_y_hot ) ;
    XDefineCursor( display, window, curs ) ;
}



put_object( x, y, object )
short x, y ;
Pixmap object ;
{
    XCopyArea( display, object, window, gc_white, 0, 0,
		BITMAP_WIDTH, BITMAP_HEIGHT, AT( x ) , AT( y ) ) ;
}


Apagar()
{
    XClearWindow( display, window ) ;
    XSync( display, 1 ) ;
}


PAUSA()
{
    XEvent ev ;
    KeySym mykey ;

    XSync( display, 1 ) ;

    do XNextEvent( display, &ev ) ;
    while(ev.type != KeyPress && ev.type != ButtonPress && ev.type != Expose) ;

    if( ev.type == KeyPress )
    {
	  XLookupString( &ev, string, 10, &mykey, 0 ) ;	
	  Tecla = string[0] ;
    }
    else Tecla = '\0' ;
}


x_print_string( x, y, str, gc )
short x, y ;
char str[] ;
GC gc ;
{
    int len ;

    len = strlen( str ) ;
    XDrawImageString( display, window, gc, AT( x ), AT( y ), str, len ) ;
}



x_get_string( x, y, str, len )
short x, y ;
char *str ;
int len ;
{
    int i = 0 ;

    while( True )
    {
	str[i] = '\0' ;
	sprintf( string, " %s_ ", str ) ;
	x_print_string( x, y, string, gc_white ) ;
	PAUSA() ;
	switch( Tecla )
	{
	    case '\b' : if( i ) --i ;
	    case '\0' : break ;
	    case '\n' :
	    case '\r' : return ;
	    default   : if( Tecla < ' ' || Tecla > 127 ) break ;
		        if( i > len - 2 ) break ;
			str[i++] = Tecla ;
	}
    }
}



finalizacao()
{
    XSync( display, 1 ) ;
    XCloseDisplay( display ) ;
    exit( 0 ) ;
}

/*---------------+
| Jogo em Si ... |
+---------------*/


TopScores()
{
 int i ;
 FILE *fptr ;

    if( !(fptr = fopen( HIGH_SCORE_FILE, "r" )) ) return;
    fread( HighScores, sizeof( struct _Player ), NMAX_TABLE, fptr );
    fclose( fptr );
    Apagar() ;
    sprintf( string, "ManIX");
    x_print_string( 18, 3, string, gc_red ) ;
    sprintf( string, " High Scores :");
    x_print_string( 14, 5, string, gc_yellow ) ;

    for( i=0 ; i<NMAX_TABLE ; ++i )
    {
        sprintf( string,  "%2d --- %-20s --- %05d", i+1,
		HighScores[i].name, HighScores[i].score );
        x_print_string( 8, 7 + i, string, gc_white ) ;
    }
    sprintf( string, "By Fernando J. G. Pereira .");
    x_print_string( 11, 20, string, gc_g_yellow ) ;
    sprintf( string, "Lisbon - PORTUGAL");
    x_print_string( 14, 22, string, gc_green ) ;

    PAUSA() ;
}



Menu()
{
    Apagar();
    sprintf( string, "ManIX");
    x_print_string( 18, 2, string, gc_red ) ;
    sprintf( string, "_________________________________________________");
    x_print_string( 7, 4, string, gc_blue ) ;
    sprintf( string, "Any Key - Play.");
    x_print_string( 12, 6, string, gc_magenta ) ;
    sprintf( string, "1 - Change Level.");
    x_print_string( 14, 7, string, gc_cyan ) ;
    sprintf( string, "2 - Configure Keyboard.");
    x_print_string( 14, 8, string, gc_cyan ) ;
    sprintf( string, "3 - Exit.");
    x_print_string( 14, 9, string, gc_cyan ) ;
    sprintf( string, "_________________________________________________");
    x_print_string( 7, 11, string, gc_blue ) ;
    sprintf( string, " LEVEL %02d .",NivelInicial );
    x_print_string( 12, 13, string, gc_pink ) ;
    sprintf( string, "_________________________________________________");
    x_print_string( 7, 15, string, gc_blue ) ;
    sprintf( string, "Keyboard :");
    x_print_string( 12, 17, string, gc_magenta ) ;
    sprintf( string, "Left     --- %c",TecCurr.left);
    x_print_string( 14, 18, string, gc_cyan ) ;
    sprintf( string, "Right    --- %c",TecCurr.right);
    x_print_string( 14, 19, string, gc_cyan ) ;
    sprintf( string, "Up       --- %c",TecCurr.up   );
    x_print_string( 14, 20, string, gc_cyan ) ;
    sprintf( string, "Down     --- %c",TecCurr.down );
    x_print_string( 14, 21, string, gc_cyan ) ;
    sprintf( string, "Pause    --- %c",TecCurr.pause );
    x_print_string( 14, 22, string, gc_cyan ) ;
    sprintf( string, "Break    --- %c",TecCurr.stop );
    x_print_string( 14, 23, string, gc_cyan ) ;
    PAUSA();
    return( Tecla );
}

MudarNivel()
{
    do
    {
        Apagar();
        sprintf( string, "Which Level ( 0..%d ) ?", NIVEL_MAX-1 );
        x_print_string( 16, 10, string, gc_red ) ;
        PAUSA() ;
        NivelInicial = Tecla - '0';
    }
    while( NivelInicial < 0 || NivelInicial > NIVEL_MAX-1 );
}


Teclado()
{
    Apagar();
    sprintf( string, " Default Keys / Redefine ?");
    x_print_string( 16, 4, string, gc_violet ) ;
    sprintf( string, " D / Any Key : ");
    x_print_string( 16, 5, string, gc_magenta ) ;
    PAUSA();
    if( (toupper(Tecla)) == 'D' )
    {
        TecCurr = TecIni ; 
	return ;
    }
    Apagar();
    sprintf( string, "Enter Keys :");
    x_print_string( 10, 8, string, gc_violet ) ;

    sprintf( string, " Up");
    x_print_string( 10, 10, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 10, string, gc_orange ) ;
    PAUSA();
    TecCurr.up = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.up ) ;
    x_print_string( 18, 10, string, gc_red ) ;

    sprintf( string, " Down");
    x_print_string( 10, 11, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 11, string, gc_orange ) ;
    PAUSA();
    TecCurr.down = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.down ) ;
    x_print_string( 18, 11, string, gc_red ) ;

    sprintf( string, " Left");
    x_print_string( 10, 12, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 12, string, gc_orange ) ;
    PAUSA();
    TecCurr.left = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.left ) ;
    x_print_string( 18, 12, string, gc_red ) ;

    sprintf( string, " Right");
    x_print_string( 10, 13, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 13, string, gc_orange ) ;
    PAUSA();
    TecCurr.right = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.right ) ;
    x_print_string( 18, 13, string, gc_red ) ;

    sprintf( string, " Pause");
    x_print_string( 10, 14, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 14, string, gc_orange ) ;
    PAUSA();
    TecCurr.pause = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.pause ) ;
    x_print_string( 18, 14, string, gc_red ) ;

    sprintf( string, " Break");
    x_print_string( 10, 15, string, gc_cyan ) ;
    sprintf( string, ">" ) ;
    x_print_string( 16, 15, string, gc_orange ) ;
    PAUSA();
    TecCurr.stop = LOW2UPPER(Tecla) ;
    sprintf( string, "%c", TecCurr.stop ) ;
    x_print_string( 18, 15, string, gc_red ) ;
}


ReadScreen( scr, level )
Ecra scr ;
int level ;
{
 FILE *fptr;
 char screen_file[80];
 int i,j ;

    sprintf(screen_file, "%s/screen.%d", GAME_DIRECTORY, level );

    if( !(fptr = fopen( screen_file, "r") ) )
    {
	fprintf( stderr, "Cannot open screen-file !\n");
        finalizacao() ;
    }
    total = 0 ;
    for( i=0; i< MAX_LINES; ++i )
    {
    	for( j=0; j< MAX_COL; ++j )
	    if( ( scr[i][j] = getc( fptr ) ) == '.' ) ++total ;
	getc( fptr );
    }
    fclose( fptr );
    total *= 10 ;
}


Jogar()
{
    IniciaJogo();
    while( Player.lives >= 0 )
    {
        morte = False ;
        NivelMudou = False ;
        armado = False ;

	Apagar();
	sprintf( string, " LEVEL : %d ",Player.level );
	x_print_string( 18, 10, string, gc_red ) ;
	PAUSA();
        ImprimirEcra() ;
	do
	{
	    ImprimirGraficos();
	    GuiarXMan();
	    VerChoque();
	    GerarMovimento();
	    VerChoque();
	    if( timer && !--timer ) armado = False ;
	} while( ! (morte || NivelMudou) );
	ActualizaNivel() ;
    }
    Player.score += Score ;
    sprintf( string,  " ******************* ");
    x_print_string( 14, 12, string, gc_orange ) ;
    sprintf( string,  " *** GAME OVER ! *** ");
    x_print_string( 14, 13, string, gc_orange ) ;
    sprintf( string,  " ******************* ");
    x_print_string( 14, 14, string, gc_orange ) ;
    PAUSA();
    VerificarRecords();
}


IniciaJogo()
{
 int i;

    ReadScreen( ecra, NivelInicial );
    Player.lives = N_VIDAS ;
    Player.score = 0 ;
    Score = 0 ;
    Player.bonus = 0 ;
    Player.level = NivelInicial ;
    Player.CurrPos.x = X_INICIAL ;
    Player.ApagaPos.x = X_INICIAL ;
    Player.CurrPos.y = Y_INICIAL ;
    Player.ApagaPos.y = Y_INICIAL ;
    NGhosts = 3 + ( Player.level +1 ) / 3 ;
    for( i = 0 ; i < NGhosts ; ++i ) PutGhost(i) ;
    timer = 0 ;
}


VerChoque()
{
    int i ;

    for( i = 0 ; i < NGhosts; ++i )
    if( Ghosts[i].CurrPos.x == Player.CurrPos.x &&
	Ghosts[i].CurrPos.y == Player.CurrPos.y )
    {
	XBell( display, 50 );
	XFlush( display );
	if( armado )
	{
	    ++(Player.bonus) ;
	    Player.score+=SCORE_HUNT ;
	    put_object( Ghosts[i].ApagaPos.x, Ghosts[i].ApagaPos.y, bm_space );
	    PutGhost( i );
	}
	else morte = True ;

        ImprimirGraficos();
    }
}


ActualizaNivel()
{
 int i;

    if( NivelMudou )
    {
	if( ++(Player.level) > NIVEL_MAX ) Player.level = 0 ;
        ReadScreen( ecra, Player.level );
    	Player.score += Score ;
        NGhosts = 3 + ( Player.level +1 ) / 3 ;
    	Score = 0 ;
    }
    else --(Player.lives) ;
    Player.lives += Player.bonus / VIDAS_POR_BONUS ;
    Player.bonus = 0 ;
    Player.CurrPos.x = X_INICIAL ;
    Player.CurrPos.y = Y_INICIAL ;
    Player.ApagaPos.x = X_INICIAL ;
    Player.ApagaPos.y = Y_INICIAL ;
    for( i = 0 ; i < NGhosts ; ++i ) PutGhost(i) ;
    LastOne = Ultima = '\0' ;
}


ImprimirEcra()
{
 int i,j;

    Apagar();
    for( i = 0; i < MAX_LINES; ++i )
    {
	for( j = 0; j< MAX_COL; ++j )
	switch( ecra[i][j] )
	{
	    case 'X' : put_object( j, i, bm_wall ) ;
		       break ;
	    case '.' : put_object( j, i, bm_dot ) ;
	    case ' ' : 
	    case 'A' : break ;
	    case 'B' : put_object( j, i, bm_apple ) ;
		       break ;
	    case 'C' : put_object( j, i, bm_orange ) ;
	    default  : break ;
	}
    }
    sprintf( string, "SCORE: %06d   LIVES: %3d  LEVEL: %3d",
	     Player.score+Score, Player.lives, Player.level );
    x_print_string( 4, Y_SCORE, string, gc_red ) ;
    XSync( display, 1 ) ;
}



PutGhost( i )
int i;
{
     Ghosts[i].CurrPos.x = X_ENTRADA + rand() % X_LENGHT ;
     Ghosts[i].CurrPos.y = Y_ENTRADA + rand() % Y_LENGHT ;
     Ghosts[i].ApagaPos.x = Ghosts[i].CurrPos.x;
     Ghosts[i].ApagaPos.y = Ghosts[i].CurrPos.y;
}



ImprimirGraficos()
{
 int i ;

    sprintf( string, "%06d",Player.score+Score);
    x_print_string( X_SCORE, Y_SCORE, string, gc_red );

    for( i=0 ; i<NGhosts; ++i )
    {
	put_object( Ghosts[i].CurrPos.x, Ghosts[i].CurrPos.y,
		    (armado) ? (timer > 10) ? bm_ghost3 : bm_ghost2 : bm_ghost1 );
	if( Ghosts[i].CurrPos.x != Ghosts[i].ApagaPos.x ||
	    Ghosts[i].CurrPos.y != Ghosts[i].ApagaPos.y )
	switch( ecra[Ghosts[i].ApagaPos.y][Ghosts[i].ApagaPos.x] )
	{
	    case 'A' :
	    case ' ' :
		put_object(Ghosts[i].ApagaPos.x,Ghosts[i].ApagaPos.y, bm_space);
		break ;
	    case '.' :
		put_object(Ghosts[i].ApagaPos.x,Ghosts[i].ApagaPos.y, bm_dot);
		break ;
	    case 'B' :
		put_object(Ghosts[i].ApagaPos.x,Ghosts[i].ApagaPos.y, bm_apple);
		break ;
	    case 'C' :
		put_object(Ghosts[i].ApagaPos.x,Ghosts[i].ApagaPos.y,bm_orange);
	    default  :
	        break ;
	}
    }

    switch( Player.manDir )
    {
	case Left : put_object(Player.CurrPos.x, Player.CurrPos.y, bm_userleft );
	            break ;
	case Right: put_object(Player.CurrPos.x, Player.CurrPos.y, bm_userright);
	            break ;
	case Up :   put_object(Player.CurrPos.x, Player.CurrPos.y, bm_userup );
		    break ;
	case Down : put_object(Player.CurrPos.x, Player.CurrPos.y, bm_userdown );
    }
    if(Player.ApagaPos.x!=Player.CurrPos.x||Player.ApagaPos.y!=Player.CurrPos.y)
	put_object( Player.ApagaPos.x, Player.ApagaPos.y, bm_space ) ;
}



GuiarXMan()
{
 Position manNP ;
 int Pausa = False ;

    Tecla = LerTeclado() ;
    if( Tecla<=4  ) Tecla = LastOne ;
    LastOne = Tecla ;
    do
    {
 	Player.ApagaPos.x = Player.CurrPos.x ;
	Player.ApagaPos.y = Player.CurrPos.y ;
	manNP.x = Player.CurrPos.x ;
	manNP.y = Player.CurrPos.y ;

	if( Tecla == TecCurr.left )
	{
	    if( --manNP.x < 0 ) manNP.x = MAX_COL -1 ;
	    Player.manDir = Left ;
	}
	if( Tecla == TecCurr.right )
	{
	    if( ++(manNP.x) >= MAX_COL ) manNP.x = 0 ;
	    Player.manDir = Right ;
	}
	if( Tecla == TecCurr.up )
	{
	    if( --(manNP.y) < 0 ) manNP.y = MAX_LINES - 1 ;
	    Player.manDir = Up ;
	}
	if( Tecla == TecCurr.down )
	{
	    if( ++(manNP.y) >= MAX_LINES ) manNP.y = 0 ; 
	    Player.manDir = Down ;
	}
	if( Tecla == TecCurr.pause )
	{
	    LastOne = Ultima = '\0' ;
	    PAUSA() ;
	}
	if( Tecla == TecCurr.stop )
	{
	    Tecla = LastOne = Ultima = '\0' ;
	    Player.lives = 0 ;
	    morte = 1 ;
	}



	if( ecra[manNP.y][manNP.x] != 'X' &&
	    ecra[manNP.y][manNP.x] != 'A'    )
	{
	    if( !Pausa ) Ultima = Tecla ;
	    Pausa = False ;
	    Player.CurrPos.x = manNP.x ;
	    Player.CurrPos.y = manNP.y ;
	    switch( ecra[manNP.y][manNP.x] )
	    {
		case '.' : if( ( Score += 10 ) >= total )
				NivelMudou = True ;
			   break ;
		case 'B' : armado = True ;
			   timer = TIMER_VALUE_1 ;
			   Player.score += SCORE_APPLE ;
			   break ;
		case 'C' : armado = True ;
			   timer = TIMER_VALUE_2 ;
			   Player.score += SCORE_ORANGE ;
		default :  break ;
	    }
	    ecra[manNP.y][manNP.x] = ' ' ;
	}
	else
	{
	    if( !Pausa ) Tecla = Ultima ;
	    Pausa = !Pausa ;
	}
     } while( Pausa ) ;
}


LerTeclado()
{
    XEvent ev ;
    KeySym mykey ;

    usleep( 100000 - 4000 * Player.level );
    while( XPending( display) )
    {
	XNextEvent( display, &ev ) ;
	switch( ev.type )
	{
	    case KeyPress :
		XLookupString( &ev, string, 10, &mykey, 0 ) ;	
		return Tecla = LOW2UPPER( string[0] ) ;
		break ;
	    case LeaveNotify :
	    case EnterNotify :
		return TecCurr.pause ;
	    case Expose :
		ImprimirEcra() ;
		ImprimirGraficos() ;
	    default : break ;
	}
    }
    return( '\0' );
}


int VerPossiveisMovimentos( i )
int i ;
{
 int x, y, np = 0 ;

    x = Ghosts[i].CurrPos.x ;
    y = Ghosts[i].CurrPos.y ;

    if( Ghosts[i].OldMove != Right && ( x == 0 || ecra[y][x-1] != 'X' ))
	 Possibilidades[np++] = Left ;
    if( Ghosts[i].OldMove != Left && ( x == MAX_COL-1 || ecra[y][x+1] != 'X' ))
	 Possibilidades[np++] = Right ;
    if( Ghosts[i].OldMove != Down && ( y == 0 || ecra[y-1][x] != 'X' ))
	 Possibilidades[np++] = Up ;
    if( Ghosts[i].OldMove != Up && ( y == MAX_LINES-1 || ecra[y+1][x] != 'X' ))
	 Possibilidades[np++] = Down ;

    return np ;
}
	

Direct MaxiMove( i, n )
int i, n ;
{
 int j ;

    if( rand() % 2 )
    {
	if( armado ) for( j = 0 ; j < n; ++j )
	{
	    if( Possibilidades[j] == Left &&
	        Ghosts[i].CurrPos.x<=Player.CurrPos.x) return Left ;
	    if( Possibilidades[j] == Right &&
	        Ghosts[i].CurrPos.x>=Player.CurrPos.x) return Right ;
            if( Possibilidades[j] == Up &&
	        Ghosts[i].CurrPos.y<=Player.CurrPos.y) return Up;
            if( Possibilidades[j] == Down &&
                Ghosts[i].CurrPos.y>=Player.CurrPos.y) return Down ;
	}
	else for( j = 0 ; j < n; ++j )
	{
	    if( Possibilidades[j] == Left &&
	        Ghosts[i].CurrPos.x>Player.CurrPos.x) return Left ;
	    if( Possibilidades[j] == Right &&
	        Ghosts[i].CurrPos.x<Player.CurrPos.x) return Right ;
            if( Possibilidades[j] == Up &&
	        Ghosts[i].CurrPos.y>Player.CurrPos.y) return Up;
            if( Possibilidades[j] == Down &&
                Ghosts[i].CurrPos.y<Player.CurrPos.y) return Down ;
	}
    }
    else
    {
	if( armado ) for( j = n-1 ; j >= 0 ; --j )
	{
            if( Possibilidades[j] == Up &&
	        Ghosts[i].CurrPos.y<=Player.CurrPos.y) return Up;
            if( Possibilidades[j] == Down &&
                Ghosts[i].CurrPos.y>=Player.CurrPos.y) return Down ;
	    if( Possibilidades[j] == Left &&
	        Ghosts[i].CurrPos.x<=Player.CurrPos.x) return Left ;
	    if( Possibilidades[j] == Right &&
	        Ghosts[i].CurrPos.x>=Player.CurrPos.x) return Right ;
	}
	else for( j = n-1 ; j >= 0 ; --j )
	{
            if( Possibilidades[j] == Up &&
	        Ghosts[i].CurrPos.y>Player.CurrPos.y) return Up;
            if( Possibilidades[j] == Down &&
                Ghosts[i].CurrPos.y<Player.CurrPos.y) return Down ;
	    if( Possibilidades[j] == Left &&
	        Ghosts[i].CurrPos.x>Player.CurrPos.x) return Left ;
	    if( Possibilidades[j] == Right &&
	        Ghosts[i].CurrPos.x<Player.CurrPos.x) return Right ;
	}
    }
    return Possibilidades[ rand() % n] ;
}


GerarMovimento()
{
 int i, NMovPossiveis ;
 Direct Move ;

    for( i=0; i<NGhosts; ++i )
    {
        Ghosts[i].ApagaPos.x = Ghosts[i].CurrPos.x ;
        Ghosts[i].ApagaPos.y = Ghosts[i].CurrPos.y ;

        if( RANDOM() > 80 - 5 * Player.level )
        {

	    if( ecra[Ghosts[i].CurrPos.y][Ghosts[i].CurrPos.x] == 'A' )
	    {
	        if( Ghosts[i].CurrPos.x < (MAX_COL-1)/2 ) Move = Right ;
	        else if( Ghosts[i].CurrPos.x > MAX_COL/2 ) Move = Left ;
	        else Move = Up ;
	    }
	    else
	    {
	        NMovPossiveis = VerPossiveisMovimentos( i );
	        Move = Possibilidades[rand() % NMovPossiveis] ;

                if( (NMovPossiveis > 1) &&
		    (RANDOM() > (100 - 8 * Player.level)) )
		    Move = MaxiMove( i, NMovPossiveis ) ;
	    }

	    switch( Move )
	    {
	        case Left : if( --(Ghosts[i].CurrPos.x)  < 0 )
			    Ghosts[i].CurrPos.x = MAX_COL - 1 ;
		            break ;
	        case Right : if( ++(Ghosts[i].CurrPos.x) >= MAX_COL )
		             Ghosts[i].CurrPos.x = 0 ;
		             break ;
	        case Up : if( --(Ghosts[i].CurrPos.y) < 0 )
			  Ghosts[i].CurrPos.y = MAX_LINES - 1 ;
		          break ;
	        case Down : if( ++(Ghosts[i].CurrPos.y) >= MAX_LINES )
		             Ghosts[i].CurrPos.y = 0 ;
	    }
	    Ghosts[i].OldMove = Move ;
        }
    }
}


VerificarRecords()
{
 int i, j ;
 FILE *fptr ;

    if( fptr = fopen( HIGH_SCORE_FILE, "r" ) ) 
    {
    	fread( HighScores, sizeof( struct _Player ), NMAX_TABLE, fptr );
    	fclose( fptr );
    }
    else for( i = 0 ; i< NMAX_TABLE; ++i )
    {
	strcpy( HighScores[i].name, "__________________" ) ;
	HighScores[i].score = 0 ;
    }

    for( i = NMAX_TABLE-1; i>=0 && Player.score>HighScores[i].score; --i ) ;
    if( ++i == NMAX_TABLE ) return ;

    Apagar();
    sprintf( string, "New HighScore !");
    x_print_string( 14, 12, string, gc_red ) ;
    sprintf( string, " Enter your Name > ");
    x_print_string( 12, 14, string, gc_magenta ) ;
    x_get_string( 22, 14, Player.name, NAME_LENGHT ) ;

    for( j = NMAX_TABLE-1; j > i ; --j )
         memcpy(HighScores[j].name,HighScores[j-1].name,sizeof(struct _Player));
    memcpy( HighScores[i].name, &Player, sizeof( struct _Player ) );

    if( (fptr = fopen( HIGH_SCORE_FILE, "w" )) ) 
    {
        fwrite( HighScores, sizeof( struct _Player ), NMAX_TABLE, fptr );
        fclose( fptr );
    }
}
