/* *************************** *
 *                             *
 *     DIAMOND DASH            *
 *     by Daniel Bienvenu      *
 *                             *
 *     Minigame compo 2004     *
 *                             *
 * *************************** */

/* No need for sprites support from Coleco library */
#define NO_SPRITES
/* Include Marcel de Kogel's Coleco library */
#include <coleco.h>
/* Include my own toolkit library */
#include <getput1.h>

extern const sound_t snd_table[];

/* Sprites table */
typedef struct
{
 byte y;
 byte x;
 byte pattern;
 byte colour;
} sprite_t;
sprite_t sprites[2];

/* Data from "tiles.c" to initialise graphics */
extern byte NAMERLE[];
extern byte PATTERNRLE[];
extern byte COLORRLE[];

/* Char array for "GAME OVER" */
const byte game_over[] = {33,34,35,36,37,38};
/* Char array for "DIAMOND DASH!!" */
const byte game_title[] = {40,41,42,43,44,45,46,47};

/* Data from "holes.c" to draw holes */
extern char *holes_dx[];
extern char *holes_dy[];
extern char holes_size;

/* Number of dynamite */
int dynamite;
/* Score */
unsigned int diamant;
/* Timer */
unsigned int timer;
/* Diamonds value */
const unsigned int diamant_score[] = {1,3,5};
/* Number of diamonds needed to finish the level */
byte nombre_diamants;

const byte minimum_diamants[]={85,75,60,45,35};

/* Classic coordinate structure */
typedef struct {
	char x;
	char y;
	char dx;
	char dy;
} coorxy;

/* Player : coordinate */
coorxy player;

/* Dynamite : coordinate */
coorxy one_dynamite;
/* Dynamite : countdown before explosion */
byte dynamite_countdown;

/* Initialise characters graphics in VideoRAM */
void init_graphics(void)
{
	/* Load characters pattern and color */ 
	rle2vram(PATTERNRLE,0x0000);
	duplicate_pattern();
	rle2vram(COLORRLE,0x2000);
	/* Set sprites pattern as characters pattern */
	vdp_out0(0,6);
}

/* Show score, timer and number of dynamites */
void show_score(void)
{
	put_at(6,23,str(diamant),5);
	put_at(14,23,str(timer),4);
	put_at(23,23,str(dynamite),5);
}

/* Add diamonds function */
void add_diamond(byte nombre, byte diamond_type)
{
	char x,y,k;
	char dx,dy;
loop2:
	x = rnd_byte(1,30);
	y = rnd_byte(4,21);
	if (get_char(x,y)!=1) goto loop2;
	for (dx=-1;dx<2;dx++)
	{
		for (dy=-1;dy<2;dy++)
		{
			k = get_char(x+dx,y+dy);
			if (k==2) goto loop2;
		}
	}
	put_char(x,y,2+diamond_type);
	nombre--;
	if (nombre!=0) goto loop2;
}

/* Add holes function */
void add_holes(byte nombre)
{
	char x,y,k,type;
	char dx,dy,size;
loop3:
	x = rnd_byte(3,28);
	y = rnd_byte(8,21);
	type = get_random()&1;
	size=holes_size;
	while(size--!=0)
	{
		dx=(holes_dx[type])[size];
		dy=(holes_dy[type])[size];
		k = get_char(x+dx,y+dy);
		if (k!=1) goto loop3;
	}
	size=holes_size;
	while(size--!=0)
	{
		dx=(holes_dx[type])[size];
		dy=(holes_dy[type])[size];
		put_char(x+dx,y+dy,2);
	}
	nombre--;
	/* add dynamite pack in the last two holes */
	if (nombre==2) put_char(x,y,7);
	if (nombre<2) put_char(x,y,31);
	/* loop */
	if (nombre!=0) goto loop3;
}

/* Draw a new mountain */
void new_mountain(void)
{
	/* Blank screen */
	screen_off();
	/* Load default mountain screen */
	rle2vram(NAMERLE,0x1800);
	/* Initialise player's coordinate and sprite pattern */
	player.x = 22;
	player.y = 2;
	player.dx = 0;
	player.dy = 0;
	sprites[0].pattern = 64;
	sprites[0].colour = 15;
	/* Add 6 holes */
	add_holes(6);
	/* Add 51 green diamonds */
	add_diamond(51,1);
	/* Add 23 red diamonds */
	add_diamond(23,2);
	/* Add 11 white diamonds */
	add_diamond(11,3);
	/* Show screen */
	screen_on();
}

/* Show sprite */
void show_sprite(void)
{
	/* wait 3 vertical retrace to slowdown the execution */
	enable_nmi();
	delay(3);
	disable_nmi();
	/* Put sprites table directly in VideoRAM */
	put_vram(0x1b00,sprites,8);
}

/* Update player on screen */
void show_player(byte n)
{
	byte x,y;
	/* convert player's x and y position for normal sprite's position */
	x = player.x<<3;
	y = (player.y<<3)-1;
	/* Switch player's sprite pattern */
	sprites[0].pattern ^=1;
	/* If it's during game */
	if (n==0)
	{
		/* Then animate sprites player movement */
		byte j;
		for (j=0;j<2;j++)
		{
			if (sprites[0].x<x) sprites[0].x +=4;
			if (sprites[0].x>x) sprites[0].x -=4;
			if (sprites[0].y<y) sprites[0].y +=4;
			if (sprites[0].y>y) sprites[0].y -=4;
			show_sprite();
		}
	}
	else
	{
		/* Otherwise, simply set sprites table with player's coordinate */
		sprites[0].x=x;
		sprites[0].y=y;
		show_sprite();
	}
}

char move_player(void)
{
	byte j,k;
	char x,y;
	/* Animate player */
	show_player(0);
	/* Put a "cavern" character on player */
	put_char(player.x, player.y,2);
	/* Get joystick in port#1 */
	j = joypad_1;
	/* Reset player's direction x and y */
	player.dx = 0;
	player.dy = 0;
	/* Set new player's direction x and y */ 
	if (j & UP) player.dy=-1;
	if (j & DOWN) player.dy=1;
	if (j & LEFT) player.dx=-1;
	if (j & RIGHT) player.dx=1;
	/* If player doesn't move then set 2nd player's sprite pattern, otherwise, set 1st player's sprite pattern */
	if (player.dx==0 && player.dy==0) sprites[0].pattern |= 2; else sprites[0].pattern &= 0xfd; 
	/* If player press fire */
	if (j & FIRE1 && dynamite_countdown==0)
	{
		/* Set new countdown and dynamite coordinate */
		dynamite_countdown=10;
		one_dynamite.x=player.x;
		one_dynamite.y=player.y;
		/* Decrease number of player's dynamite */
		dynamite--;
		/* If player had no dynamite then return "stop game" flag */
		if (dynamite<0) return 0;
		/* Play sound#4 : "schhhhhh" */
		play_sound(4);
	}
	/* Check if there is something in front of the player */
	x = player.x + player.dx;
	y = player.y + player.dy;
	k = get_char(x,y);
	if ((k>1 && k<7)|| k==31)
	{
		/* Update player's coordinate */
		player.x = x; player.y=y;
		/* If it's a diamond, increase score and play sound #1 : "beep" */
		if (k>2 && k<6)
		{
			diamant += diamant_score[k-3]; play_sound(1);
			if (--nombre_diamants==0)
			{
				put_char(23,2,6); play_sound(5); play_sound(6);
			} 
		}
		/* If it's a dynamite pack, increase number of player's dynamite by 3 and play sound #2 */
		if (k==31)
		{
			dynamite += 3; play_sound(2);
		}
		/* If it's the exit, return "stop game" flag */
		if (k==6)
		{
			return 0;
		}
	}
	/* Return "continue game" flag */
	return -1;
}

/* Do a square hole */
void square_hole(byte x, byte y, byte size)
{
	byte i,j,k;
	char dx,dy=0;
	char n = size >> 1;

	/* Play sound #2 : "bonk" */
	play_sound(2);

	/* Add rocks in mountain */ 
	enable_nmi();
	for (k=0;k<5;k++)
	{
loop1:
		dx = rnd_byte(0,31);
		dy = rnd_byte(2,22);
		if (get_char(dx,dy)!=2) goto loop1; 
		disable_nmi();
		put_char(dx,dy,1);
		enable_nmi();
	}
	disable_nmi();

	dx = 0;
	/* Erase whatever it was directly in the center of this futur hole */
	put_char(x,y,2);
	/* Do square hole */ 
	x -= n;
	y -= n;
	for (i=0;i<size;i++)
	{
		for (j=0;j<size;j++)
		{
			k = get_char(x,y);
			/* If detect a barrel of powder, keep note of its coordinate */
			if (k==7) {dx=x; dy=y;}
			if (k==1) put_char(x,y,2);
			y++;
		}
		x++;
		y-=size;
	}
	/* If a barrel of powder was detected, do a big square hole */
	if (dx!=0) square_hole(dx, dy, 7);
}

/* Update dynamite countdown and effect */
void update_dynamite(void)
{
	/* If a dynamite is on screen */
	if (dynamite_countdown!=0)
	{
		/* Decrease countdown counter */
		dynamite_countdown--;

		/* Update dynamite animation on screen */
		if (dynamite_countdown&1)
		{
			put_char(one_dynamite.x,one_dynamite.y,29);
		}
		else
		{
			put_char(one_dynamite.x,one_dynamite.y,30);
		}
		/* If dynamite countdown = 0, then do a small square hole */
		if (dynamite_countdown==0)
		{
			square_hole(one_dynamite.x,one_dynamite.y,3);
		}
	}
}

/* Game engine */
void game(void)
{
	byte mountains_limit = 5;
	dynamite=0; diamant=0;
	
next_mountain:
	dynamite = 20;
	new_mountain();
	/* Decrease number of mountain to do */
	mountains_limit--;
	/* Set number of diamonds needed to exit level */
	nombre_diamants = minimum_diamants[mountains_limit];
	timer=2000;
	show_player(1);

	/* Game loop */
	while(move_player())
	{
		if (timer!=0) timer--;
		show_score();
		update_dynamite();
	}

	/* Animate player for the last time */
	show_player(0);

	/* "Game Over" when player had no more dynamite */
	if (dynamite<0)
	{
		play_sound(3); 
		put_at(13,11,game_over,6);
		enable_nmi();
		pause();
		disable_nmi();
	}
	else
	{
		/* If there still a timer bonus */
		while (timer>9)
		{
		/* Update timer and score */
		timer-=10;
		diamant++;
		/* Play sound#1 : "beep" */
		play_sound(1);
		/* Update score and timer on screen */
		show_score();
		/* Wait a short time to slowdown the execution and to let the "beep" sound playing a bit */
		enable_nmi();
		delay(2);
		disable_nmi();
		} 

		/* If there still a timer bonus */
		while (dynamite--!=0)
		{
			/* Update score */
			diamant+=5;
			/* Play sound#1 : "bonk" */
			play_sound(2);
			/* Update score and timer on screen */
			show_score();
			/* Wait a short time to slowdown the execution and to let the "beep" sound playing a bit */
			enable_nmi();
			delay(12);
			disable_nmi();
		}

		/* Play winner music */
		play_sound(5);
		play_sound(6); 

		/* Wait until music ends */
		enable_nmi();
		delay(75);
		disable_nmi(); 

		/* Go to next mountain : if it's the last mountain, redo the last one */
		if (mountains_limit==0) mountains_limit++;
		goto next_mountain;
	}
}

/* main function : starting point of any C program */
void main(void)
{
	set_snd_table(snd_table);
	/* Default screen mode 2 */
	screen_mode_2_text();
	/* Set Sprites to 8x8 pixels */
	sprites_8x8(); 
	/* Initialise graphics and sounds */
	init_graphics();
	/* Set a "stop point" to let the Video Chip knows that there is only 1 sprite to show */ 
	sprites[1].y=208;
	/* Show a new mountain */
	new_mountain(); 
	/* Show "DIAMOND DASH!!" in center screen */
	put_at(12,11,game_title,8);
	/* Wait until player press fire */
	enable_nmi(); pause(); disable_nmi();
	/* Play game */
	game();
	/* END */
}

/* Non Maskable Interrupt */
void nmi(void)
{
}