/*-
 * Copyright (c) 2000
 * Tatsuya Kudoh(CDR/TK),ROYALPANDA.    All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include<stdio.h>
#include<stdlib.h>
#include<sys/bios.h>

#include"jump.h"
#include"fontdata.h"

static hero_t Hero;
static floor_t Floor[32];

static int Xzero,Yzero;

static int Hiscore = 0;
static int Score = 0;

static int Level = 1;
static unsigned T_speed,T_count;
static unsigned T_min;
static int T_dir;


static const unsigned Spr_clear[16] = {
	4<<9|32, 224<<8|224, 4<<9|32, 224<<8|224,
	4<<9|32, 224<<8|224, 4<<9|32, 224<<8|224,
	4<<9|32, 224<<8|224, 4<<9|32, 224<<8|224,
	4<<9|32, 224<<8|224, 4<<9|32, 224<<8|224,
};


static void draw_floor( int y )
{
	floor_t *fp;
	int i;
	int x;
	unsigned buf[16];
	unsigned *src;

	y &= 31;
	fp = Floor + y;

	if( fp->x >= 256 )
		return;

	src = CT_floor[fp->pic];

	if( fp->sprite ){
		x = (fp->x - Xzero) & 255;
		y = (y*8 - Yzero) & 255;
		if( fp->viewable ){
			for( i = 0 ; i < fp->width ; i++ ){
				buf[i*2] = src[i] & ~(8<<9);
				buf[i*2+1] = x<<8 | y;
				x += 8;
			}
			sprite_set_data(fp->sprite,fp->width,(void far*)buf);
		}else{
			sprite_set_data(fp->sprite,fp->width,
						(void far*)Spr_clear);
		}
	}else{
		x = fp->x / 8;
		if( fp->viewable )
			screen_set_char(1,x,y,fp->width,1,src);
		else
			screen_fill_char(1,x,y,fp->width,1,12<<9|32);
	}
}


static void draw_all_floors( void )
{
	int y;
	int i;

	y = Yzero / 8;
	for( i = 0 ; i < 20 ; i++ ){
		draw_floor(y);
		y = (y+1) & 31;
	}
}


static void erase_floor( int y )
{
	floor_t *fp;

	y &= 31;

	fp = Floor + y;

	if( fp->x >= 256 )
		return;

	if( fp->sprite ){
		sprite_set_data(fp->sprite,fp->width,(void far*)Spr_clear);
	}else{
		screen_fill_char(1,fp->x/8,y,fp->width,1,12<<9|32);
	}
}


static void draw_hero( hero_t *hp )
{
	int x0,x1;
	int y0,y1,y2;

	unsigned buf[12];
	unsigned *src;

	x0 = (hp->x - Xzero) & 255;
	y0 = (hp->y - Yzero) & 255;
	x1 = (x0+8) & 255;
	y1 = (y0+8) & 255;
	y2 = (y0+16) & 255;
	src = CT_hero[hp->pic + hp->dir * 4];

	buf[0] = src[0];
	buf[1] = x0<<8 | y0;
	buf[2] = src[1];
	buf[3] = x1<<8 | y0;
	buf[4] = src[2];
	buf[5] = x0<<8 | y1;
	buf[6] = src[3];
	buf[7] = x1<<8 | y1;
	buf[8] = src[4];
	buf[9] = x0<<8 | y2;
	buf[10] = src[5];
	buf[11] = x1<<8 | y2;
	sprite_set_data(hp->sprite,6,(void far*)buf);
}


static void erase_hero( hero_t *hp )
{
	sprite_set_data(hp->sprite,6,(void far*)Spr_clear);
}


floor_t *GAME_get_floor( int y )
{
	return Floor + (y & 31);
}


static void init( void )
{
	int i;

	Xzero = 16;
	Yzero = 0;

	DISP_disable();

	DISP_clear();
	screen_set_scroll(1,16,0);
	screen_set_scroll(0,8,0);

	FLOOR_init();

	for( i = 16 ; i < 32 ; i++ )
		FLOOR_delete(Floor+i);

	for( i = 15 ; i >= 0 ; i-- )
		FLOOR_create(Floor+i,1);

	Hero.sprite = 32;
	Hero.x = 120;
	Hero.y = 96;
	Hero.xx = Hero.x * COORD_SCALE;
	Hero.yy = Hero.y * COORD_SCALE;
	Hero.vx = 0;
	Hero.vy = 0;
	Hero.jump = 0;
	Hero.pic = 0;
	Hero.count = 0;

	Score = 0;
	Level = 1;
	T_speed = 400;
	T_min = (5000+T_speed*7)/8;
	T_count = 0;
	T_dir = 1;

	draw_hero(&Hero);
	draw_all_floors();

	DISP_wait_vblank();
	DISP_enable();
}


static int fall( void )
{
	if( T_dir ){
		T_speed += (T_speed+999)/1000;
		if( T_speed > 15000 ){
			T_speed = 15000;
			T_dir = 0;
		}
	}else{
		T_speed -= (T_speed+19) / 20;
		if( T_speed < T_min ){
			T_speed = T_min;
			T_min = (5000+T_min*7)/8;
			if( T_min > 2000 )
				T_min = 2000;
			T_dir = 1;
			if( Level < 8 )
				Level++;
		}
	}

	if( T_speed > 5000 ){
		T_count = 0;
		return 1;
	}
        T_count += T_speed;
        if( T_count > 5000 ){
                T_count -= 5000;
		return 1;
        }
	return 0;
}


static void scroll( hero_t *hp )
{
	int x,y;
	int ynew;
	int step;

	if( hp ){
		x = (hp->x - Xzero) & 255;
		y = (hp->y - Yzero) & 255;

		if(x < 112 && Xzero > 0 )
			Xzero-=2;
		else if( x > 128 && Xzero < 32 )
			Xzero+=2;

		if( y < 32 )
			step = (32 - y + 7) / 8;
		else if( fall() )
			step = 1;
		else
			return;
	}else if( fall() ){
		step = 1;
	}else{
		return;
	}

	ynew = (Yzero - step) & 255;
	if( Yzero/8 != ynew/8 ){
		y = (Yzero/8 - 3) & 31;
		erase_floor(y);
		FLOOR_delete(Floor+y);

		y = (ynew/8 - 1) & 31;
		FLOOR_create(Floor+y,Level);
	}
	Yzero = ynew;
}



static void move_floor( void )
{
	int y;
	int i;

	y = Yzero / 8;
	for( i = 0 ; i < 19 ; i++ ){
		FLOOR_move(Floor+y);
		y = (y+1) & 31;
	}
}


static void put_score( int score )
{
	unsigned buf[10];
	int i;

	for( i = 0 ; i < 5 ; i++ ){
		buf[i*2] = 1<<13 | 6 << 9 | ((score % 10) + '0');
		buf[i*2+1] = (196-i*8)<<8 | 132;
		score /= 10;
	}
	sprite_set_data(0,5,(void far*)buf);
}

static void pause( void )
{
	unsigned buf[10];
	int i;

	for( i = 0 ; i < 5 ; i++ ){
		buf[i*2] = 1<<13 | 6 << 9 | "PAUSE"[i];
		buf[i*2+1] = (72+i*16)<<8 | 68;
	}
	sprite_set_data(16,5,(void far*)buf);

	for( i = 0 ; i < 5 ; i++ ){
		buf[i*2] = 1<<13 | 6 << 9 | 32;
		buf[i*2+1] = 224<<8 | 224;
	}

	KEY_wait_pause();

	sprite_set_data(16,5,(void far*)buf);
}


int GAME_title( void )
{
	char buf[32];
	int k;
	int y;
	int result;

	DISP_clear();
	screen_set_scroll(1,16,0);
	screen_set_scroll(0,8,0);

	sprintf(buf,"Floor %05d   Top %05d",Score,Hiscore);
	DISP_put_str(4,0,buf);
	DISP_put_str(6,4,"FALLING TOWER  mini");
	DISP_put_str(20,6,"Ver.");
	DISP_put_str(24,6,VERSION);
	DISP_put_str(8,10,"Push A to start.");
	DISP_put_str(8,12,"Push Y1 to exit.");
	DISP_put_str(6,17,"(C) 2000 ROYALPANDA");

	y = 0;
	for(;;){
		k = KEY_read();
		if( k & K_JUMP ){
			result = 1;
			break;
		}else if ( k & K_RESET ){
			result = 0;
			break;
		}
		DISP_wait_vblank();
		y = (y-1) & 2047;
		screen_set_scroll(0,8,y/4);
	}

	while( KEY_read() ){
		DISP_wait_vblank();
		y = (y-1) & 2047;
		screen_set_scroll(0,8,y/4);
	}
	return result;
}


static void gameover( void )
{
	unsigned buf[18];
	int i;

	for( i = 0 ; i < 9 ; i++ ){
		buf[i*2] = 1<<13 | 6<<9 | "GAME OVER"[i];
		buf[i*2+1] = (76 + i*8) << 8 | 68;
	}
	sprite_set_data(16,9,buf);
}



static int live( hero_t *hp )
{
	if( ((hp->y - Yzero) & 255) < 144 )
		return 1;
	else
		return 0;
}


void GAME_main( void )
{
	int k;
	int t;
	int fl;

	init();

	put_score(1);

	do{
		k = KEY_read();
		if( k & K_START )
			pause();
		fl = HERO_move(&Hero,k);
		scroll(&Hero);
		move_floor();

		DISP_wait_vblank();

		draw_hero(&Hero);
		draw_all_floors();
		if( fl > Score ){
			Score = fl;
			if( Score > Hiscore )
				Hiscore = Score;
			put_score(fl);
		}
		screen_set_scroll(1,Xzero,Yzero);
		screen_set_scroll(0,Xzero/2,Yzero/2);
	}while( live(&Hero) );

	gameover();

	t = 0;
	k = 0;
	while( k < 3 ){
		if( t < 750 )
			t++;
		else
			k = 2;

		switch( k ){
		case 0:
		case 2:
			if( !(KEY_read() & K_JUMP) )
				k++;
			break;

		case 1:
			if( KEY_read() & K_JUMP )
				k++;
			break;
		}
		scroll(NULL);
		move_floor();
		DISP_wait_vblank();
		draw_all_floors();
		screen_set_scroll(1,Xzero,Yzero);
		screen_set_scroll(0,Xzero/2,Yzero/2);
	}
}
