/* *************************** *
 *                             *
 *     EASTER BUNNY            *
 *     by Daniel Bienvenu      *
 *                             *
 *     Minigame compo 2007     *
 *                             *
 * *************************** */

/* 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>

#define sprgen  0x1b00 /* sprite_attribute_table */

volatile byte color_offset;
volatile byte flag_effect;

extern const sound_t snd_table[];

extern byte read_next_code(byte *item, byte code);
extern void invincible(byte i);
extern void player_sprite (byte i);
extern void player_color (void);

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

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

#define NONE 0x00
#define ICE 0x01
#define GRASS 0x02
#define SAND 0x04
#define AIR 0x08

/* game levels */

static byte no_level;

extern const byte* levels[];

/* game properties */
byte speed;
const int gravity = 384;
const int friction[] = {0,80,320,200,500,290,410,300,1024};

const int vy_min = -2048;
const int vy_max = 2048;
const int vx_min = -1024;
const int vx_max = 1024;

typedef struct
{
        unsigned y;
        unsigned x;
        int vy;
        int vx;
        int speed;
        byte state;
} player_t;

player_t player;

byte nbr_keys_left;

static void updatey_(byte y)
{
        sprites[0].y = y;
        sprites[1].y = y;
        y-=8;
        sprites[2].y = y;
        sprites[3].y = y;
}

static void updatey(void)
{
        byte y;
        y = intdiv256(player.y);
        updatey_(y);
}

static void updatex_(byte x)
{
        sprites[0].x = x;
        sprites[1].x = x;
        sprites[2].x = x;
        sprites[3].x = x;
}

static void updatex(void)
{
        byte x;
        x = intdiv256(player.x);
        updatex_(x);
}

static void slide_sprite(void)
{
    byte i = sprites[0].x;
    byte j = intdiv256(player.x);
    if (i<j) i++;
    if (i>j) i--;
    updatex_(i);
    i = sprites[0].y;
    j = intdiv256(player.y);
    if (i<j) i++;
    if (i>j) i--;
    updatey_(i);
}

static void smoke(void)
{
        sprites[4].y = intdiv256(player.y)+2;
        sprites[4].pattern = 0x26;
        sprites[4].colour = 0x0f;
}

static void update_smoke(void)
{
        if (sprites[4].pattern == 0x2a)
        {
                sprites[4].y = 0xf0;
        }
        else
        {
                sprites[4].y -= 4;
                sprites[4].x = intdiv256(player.x);
                sprites[4].pattern++;
        }
}

static void bird(void)
{
        sprites[5].x = 128;
        sprites[5].y = 0;
        sprites[5].pattern = 44;
        sprites[5].colour = 0x0f;
}

static void update_bird(void)
{
    byte i;
    byte j;
    i = sprites[5].y;
    j = sprites[0].y;
    if (i<j) i++;
    if (i>j) i--;
    sprites[5].y=i;
    i = sprites[5].x;
    j = sprites[0].x;
    if (i<j) { i++; sprites[5].pattern=44+(i&1); }
    if (i>j) { i--; sprites[5].pattern=46+(i&1); }
    sprites[5].x=i;
}

char bird_kill(void)
{
    byte kill = 0;
    byte dx = absdiff_byte(sprites[0].x,sprites[5].x);
    byte dy = absdiff_byte(sprites[0].y,sprites[5].y); 
    if ( dx < 3 && dy < 5) kill = -1;
    return kill;
}

static void update_state()
{
        byte i;
        byte c[2];
        byte cx[2];
        byte cy;
        int rest_y;
        byte nbr = 2;

        cx[0] = sprites[0].x + 2;
        cx[0] >>= 3;
        cx[1] = sprites[0].x + 5;
        cx[1] >>= 3;

        if (cx[0]==cx[1]) nbr=1;

        cy = sprites[0].y+9;
        rest_y = cy & 7;
        rest_y <<=8;
        cy >>= 3;

        player.state = AIR;
        for (i=0;i<nbr;i++)
        {
                c[i] = get_char(cx[i],cy);
                if ((c[i]&0xf0)==0x10)
                {
                        c[i]++;
                        put_char(cx[i],cy,c[i]);
                        if ((player.state & AIR) && (player.vy>=0))
                        {
                                player.state = NONE;
                                player.y -= rest_y;
                                player.y &= 0xff00;
                                updatey();
                        }
                        player.state |= GRASS;
                }
                else
                {
                        if ((c[i]&0xfc)==0x0c)
                        {
                                if ((player.state & AIR) && (player.vy>=0))
                                {
                                        player.state = NONE;
                                        player.y -= rest_y;
                                        player.y &= 0xff00;
                                        updatey();
                                }
                                if (c[i]==0x0c)
                                {
                                        player.state |= ICE;
                                }
                                if (c[i]==0x0d)
                                {
                                        player.state |= GRASS;
                                }
                                if (c[i]==0x0e)
                                {
                                        player.state |= SAND;
                                }
                                if (c[i]==0x0f)
                                {
                                        player.vy = vy_min<<1;
                                        player.state = AIR;
                                        smoke();
                                        play_sound(3);
                                        i = nbr;
                                }
                        }
                }
        }
        i = sprites[0].x + 3;
        i >>= 3;
        cy = sprites[0].y - 6;
        cy >>= 3;
        c[0] = get_char(i,cy);
        c[1] = get_char(i,cy+1);
        if (c[0]==0x21)
        {
                put_char(i,cy,0x20);
                nbr_keys_left--;
                play_sound(1);
        }
        if (c[1]==0x21)
        {
                put_char(i,cy+1,0x20);
                nbr_keys_left--;
                play_sound(1);
        }
}

static void update_player_y(void)
{
        if (player.state & AIR)
        {
                player.vy += gravity;
                if (player.vy > vy_max) player.vy = vy_max;
        }
        else
        {
                player.vy = 0;
                if (joypad_1 & FIRE1)
                {
                        player.vy = vy_min;
                        player.state = AIR;
                        play_sound(2);
                }
        }
        player.y += player.vy;
        updatey();
}

static void update_player_x(void)
{
        int abs_vx = player.vx & 0x7fff;

        if (joypad_1 & (LEFT | RIGHT))
        {
                if (joypad_1 & LEFT) player.vx -= player.speed;
                if (joypad_1 & RIGHT) player.vx += player.speed;
        }
        else
        {
                if (abs_vx > 0)
                {
                        if ((player.state & AIR)!=AIR)
                        {
                                int f = friction[player.state];
                                if (player.vx<0)
                                {
                                        if (abs_vx > f)
                                        {
                                                player.vx += f;
                                        }
                                        else
                                        {
                                                player.vx = 0;
                                        }
                                }
                                else
                                {
                                        if (abs_vx > f)
                                        {
                                                player.vx -= f;
                                        }
                                        else
                                        {
                                                player.vx = 0;
                                        }
                                }
                        }
                }
        }

        if (player.vx < vx_min) player.vx = vx_min;
        if (player.vx > vx_max) player.vx = vx_max;

        player.x += player.vx;
        if (player.x < 0x1000 || player.x > 0xE800)
        {
                player.vx = -player.vx;
                player.x += player.vx;
        }
}

static void update_player(void)
{
        update_player_x();
        update_player_y();
        update_state();
        /* Set Player Sprite Pattern */
        if (player.vx>128)
        {
            player_sprite(4);
        }
        if (player.vx<128)
        {
            player_sprite(8);
        }
        if (player.vx==0)
        {
            player_sprite(0);
        }
        /* Set Player Sprite Position */
        updatey();
        updatex();
        /* update smoke animation */
        update_smoke();
        /* update bird animation */
        update_bird();
        /* update_eggs(); */
        /* Update Sprites on Screen */
        put_vram(sprgen, sprites, 25);
}

static const byte floor_pattern[] = {0x99,0x66,0xdd,0x77,0xdd,0x77,0x99,0x66};

/* Initialise characters graphics in VideoRAM */
static void init_graphics(void)
{
        /* Load characters pattern and color */
        load_patternrle(PATTERNRLE);
        put_vram_pattern(0x0060,floor_pattern,8,7);
        duplicate_pattern();
        rle2vram(COLORRLE,0x2000);
        /* Set sprites pattern as characters pattern */
        vdp_out(6,0);
}

const byte left_border[]={0x24,0x23};
const byte right_border[]={0x25,0x26};

static void decode_level(byte *level)
{
	byte x;
	byte count;
	byte item;
	byte y=0;
	byte *offset = level;
	nbr_keys_left = 0;
	count = read_next_code(&item, *offset++);
	delay(1);
	screen_on();
loop_y:
	delay(1);
	put_at(0,y,left_border,2);
	x = 2;
loop_x:
	if (y==23 && item==0x20) item=0xf;
	if (item==0x22)
	{
		put_char(x,y-1,0x21);
		nbr_keys_left++;
	}
	if (item==0)
	{
		item=0x20;
		player.x = (unsigned) x;
		player.x <<= 11;
		player.y = (unsigned) y;
		player.y <<= 3;
		player.y--;
		player.y <<= 8;
		player.vx = 0;
		player.vy = 0;
		player.speed = 128;
		player.state = NONE;
	}
	put_char(x,y,item);
	count--;
	if (count==0) count = read_next_code(&item, *offset++);
	x++;
	if (x<30) goto loop_x;
	put_at(30,y,right_border,2);
	y++;
	if (y<24) goto loop_y;
}

static void anim_getready(void)
{
    byte i;
    player_sprite(0);

    for (i=0;i<224;i++)
    {
        delay(1);
        slide_sprite();
        invincible(i);
        put_vram(sprgen, sprites, 25);
    }
    delay(1);
    sprites[4].y = 0xd0;
    sprites[5].y = 0xd0;
    put_vram(sprgen, sprites, 25);
}

static void game(void)
{
        byte level_done = 0;

        enable_nmi();

        decode_level((char*) levels[no_level&7]);
    
        play_sound(4);
        anim_getready();
    
        bird();

        /* Start BGSound */

        while(!level_done)
        {
                delay(speed);
                update_player();
                if (nbr_keys_left == 0) {no_level++; level_done = -1;}
                if (bird_kill()) {play_sound(5); level_done=-1;}
                /* if (player.y >= 0xE000 && player.y <= 0xF000) level_done = -1; */
        }
        delay(30);
        /* Stop All Sounds */
        stop_sound(1);
        stop_sound(2);
        stop_sound(3);
        delay(1);
        disable_nmi();
}

/* main function : starting point of any C program */
void main(void)
{
        set_snd_table(snd_table);
        /* Default screen mode 2 setting is done in crtcv.as file */
        screen_mode_2_text();
        /* Set Sprites to 8x8 pixels */
        sprites_8x8();
        /* Initialise graphics and sounds */
        init_graphics();

        rle2vram(NAMERLE,0x1800);
        screen_on();
    
        play_sound(6);
        play_sound(7);
        flag_effect = -1;
        speed = 5 - choice_keypad(1,3);
        flag_effect = 0;
        stop_sound(6);
        stop_sound(7);
    
        speed = 2;
        /* Set Player Sprite Color */
        player_color();
        /* Play game */
        no_level=0;
        while(1) game();
        /* END */
}

static const byte colors[] = {
    0xd1,0xd1,0xd1,0x51,
    0xd1,0x51,0x51,0x51,
    0xc1,0x51,0xc1,0xc1,
    0xc1,0x21,0xc1,0x21,
    0x21,0x21,0x31,0x21,
    0x31,0x31,0x31,0xa1,
    0x31,0xa1,0xa1,0xa1,
    0x91,0xa1,0x91,0x91,
    0x91,0x81,0x91,0x81,
    0x81,0x81,0xd1,0x81,
    0xd1,0xd1,0xd1,0x51,
    0xd1,0x51,0x51,0x51,
    0xc1,0x51,0xc1,0xc1,
    0xc1,0x21,0xc1,0x21,
    };

void nmi(void)
{
    if (flag_effect)
    {
        color_offset++;
        if (color_offset > 39) color_offset = 0;
        put_vram_pattern(0x2400,&colors[color_offset],8,12);
        put_vram_pattern(0x2460,&colors[color_offset+8],8,12);        
    }    
}
