/**********************************

		パズル「石狩」

					メイン

 **********************************/

#include	"common.h"
#include	"ishikari.h"
#include	"select.h"
#include	"graph.h"
#include	"screen.h"
#include	"pad.h"
#include	"sound.h"


#define		PLAY_FILE	"ishikari.sav"		/* プレイデータファイル名 */


FIELD far*	field;							/* 現在のフィールド */
int		stone_cnt[FIELD_H][FIELD_W];		/* 石消去カウンタ */
RAY		ray[RAY_MAX];						/* 光線 */
int		cursor_x, cursor_y;					/* カーソル位置 */
Bool	cursor_disp;						/* カーソルを表示するか */
int		clear_flag;							/* クリア、失敗 */

int		area_num;							/* エリア番号 */
int		stage_num;							/* ステージ番号 */

u_short	play_data[AREA_MAX];				/* プレイデータ */

static u_char far*	stage_data;				/* 全ステージデータ */
static FIELD		field_data[STAGE_MAX];	/* エリアのフィールドデータ */

static
int	ray_dx[6] = {1, 1, 0, -1, -1,  0},		/* 光線の進む方向 */
	ray_dy[6] = {0, 1, 1,  0, -1, -1};


/******************
    光線待ち状態
 ******************/
static
void	wait_ray(void)
{
	static int	cnt = 0;
	RAY far*	p;
	int		i, k;

	for (i = 0, p = ray; i < RAY_MAX; i++, p++) {
		k = cnt - 10 - i;
		k = (k < -6) ? -6*2 : ((k > 6) ? 6*2 : k*2);
		p->x = field->sx*16 + ray_dx[p->dir]*k;			/* 位置設定 */
		p->y = field->sy*16 + ray_dy[p->dir]*k;
	}
	cnt = (cnt + 1) % (RAY_MAX + 20);
}

/************************************
    カーソル移動
		引数	pad = パッドデータ
 ************************************/
static
void	move_cursor(PAD pad)
{
	int		x, y;

	x = cursor_x;
	y = cursor_y;
	if ( pad & KEY_LEFT1 ) {				/* 左 */
		x--;
	}
	else if ( pad & KEY_RIGHT1 ) {			/* 右 */
		x++;
	}
	if ( pad & KEY_UP1 ) {					/* 上 */
		y--;
		x -= y % 2;
	}
	else if ( pad & KEY_DOWN1 ) {			/* 下 */
		x += y % 2;
		y++;
	}
	if ( x < (y + 1)/2 ) {
		x = (y + 1)/2;
	}
	else if ( x > FIELD_W - (FIELD_H - y + 1)/2  ) {
		x = FIELD_W - (FIELD_H - y + 1)/2;
	}
	if ( (y >= 0) && (y < FIELD_H) ) {
		cursor_x = x;
		cursor_y = y;
	}
}

/*************************************
    石の向きを設定
		戻り値	 0:設定終了
				 1:ステージ変更
				-1:エリア選択に戻る
 *************************************/
static
int		edit_stone(void)
{
	PAD		pad;
	STONE	c;

	cursor_disp = TRUE;						/* カーソル表示 */
	while (1) {
		draw_screen();						/* 画面描画 */
		wait_ray();							/* 光線待ち状態 */
		if ( get_push() & KEY_START ) {		/* エリア選択に戻る */
			return	-1;
		}
		if ( get_push() & KEY_B ) {			/* 設定終了 */
			return	0;
		}

		pad = get_rept();					/* 入力 */
		move_cursor(pad);					/* カーソル移動 */
		if ( pad & KEY_A ) {				/* 石の回転 */
			if ( (c = field->stone[cursor_y][cursor_x]) > 0 ) {
				field->stone[cursor_y][cursor_x] = (c % 3) + 1;
				play_se(SE_CLICK);			/* 効果音 */
			}
			else {
				return	0;					/* 設定終了 */
			}
		}
		if ( pad & KEY_LEFT2 ) {			/* ステージを戻す */
			stage_num--;
			return	1;
		}
		if ( pad & KEY_RIGHT2 ) {			/* ステージを進める */
			stage_num++;
			return	1;
		}
	}
	return	-1;
}


/********************************
    次の石があるか
		引数	p = 光線
		戻り値	 TRUE : 続く
				FALSE : 終わり
 ********************************/
static
Bool	next_jewel(RAY far* p)
{
	int		x, y;

	x = p->x/16 + ray_dx[p->dir];
	y = p->y/16 + ray_dy[p->dir];
	while ( (x >= 0) && (x < FIELD_W) && (y >= 0) && (y < FIELD_H) ) {
		if ( (field->stone[y][x] > 0) && (stone_cnt[y][x] == 0) ) {
			return	TRUE;				/* 石がある */
		}
		x += ray_dx[p->dir];			/* 移動 */
		y += ray_dy[p->dir];
	}
	return	FALSE;
}

/****************************
    光線衝突判定
		引数	p = 光線
		戻り値	衝突したか
 ****************************/
static
Bool	check_ray(RAY far* p)
{
	STONE	c;
	int		x, y;

	x = p->x/16;
	y = p->y/16;
	if ( (x < 0) || (x >= FIELD_W) || (y < 0) || (y >= FIELD_H) ) {
		return	FALSE;							/* 画面外 */
	}
	if ( ((c = field->stone[y][x]) > 0) && (stone_cnt[y][x] == 0) ) {
		p->dir = (6 + c*2 - p->dir) % 6;		/* 反射 */
		stone_cnt[y][x] = 1;					/* 石を消去 */
		return	TRUE;
	}
	return	FALSE;
}

/*************************************
    光線を動かす
		戻り値	 0:成功
				 1:失敗、キャンセル
				-1:エリア選択に戻る
 *************************************/
static
int		move_ray(void)
{
	int far*	p;
	RAY far*	rp;
	int		i, num;

	num = field->num;							/* 石の数 */
	cursor_disp = FALSE;						/* カーソル非表示 */
	while (1) {
		draw_screen();							/* 画面描画 */
		if ( get_push() & KEY_START ) {			/* エリア選択に戻る */
			return	-1;
		}
		if ( get_push() & KEY_A ) {				/* キャンセル */
			return	(clear_flag > 0) ? 0 : 1;
		}
		if ( ((clear_flag > 64) && (get_push() & KEY_B))
						|| (clear_flag > 10000) || (clear_flag < -10000) ) {
			return	1;
		}

		if ( !(get_pad() & (KEY_B | KEY_LEFT1 | KEY_RIGHT1 | KEY_UP1
															| KEY_DOWN1)) ) {
			if ( clear_flag > 0 ) {				/* クリア状態 */
				if ( ++clear_flag == 72 ) {
					play_music(BGM_CLEAR, PLAY_SINGLE);		/* 演奏開始 */
				}
			}
			else if ( clear_flag < 0 ) {		/* 失敗状態 */
				clear_flag--;
			}

			p = &stone_cnt[0][0];				/* 石消去カウンタ */
			for (i = 0; i < FIELD_W*FIELD_H; i++) {
				if ( (*p > 0) && (*p < FLASH_TIME) ) {
					(*p)++;
				}
				p++;
			}

			for (i = 0, rp = ray + RAY_MAX - 1; i < RAY_MAX - 1; i++, rp--) {
				rp->dir = (rp - 1)->dir;		/* 後ろの光線を移動 */
				rp->x   = (rp - 1)->x;
				rp->y   = (rp - 1)->y;
			}

			rp = ray;							/* 先頭 */
			rp->x += ray_dx[rp->dir]*2;			/* 光線移動 */
			rp->y += ray_dy[rp->dir]*2;
			if ( (rp->x % 16 == 0) && (rp->y % 16 == 0) ) {
				if ( check_ray(rp) ) {			/* 衝突判定 */
					play_se(SE_STONE1 + ((rp->x/16 + rp->y/16) % 3));
												/* 効果音再生 */
					if ( --num == 0 ) {			/* クリア */
						clear_flag = 1;
						if ( !check_stage(area_num, stage_num) ) {
							play_data[area_num] += (1 << stage_num) + (1 << 12);
						}
						fadeout_music();		/* ＢＧＭフェードアウト */
					}
					else if ( (clear_flag == 0) && !next_jewel(rp) ) {
						clear_flag = -1;		/* 失敗 */
					}
				}
			}
		}
	}
	return	-1;
}


/**********************
    フィールド初期化
 **********************/
static
void	clear_field(void)
{
	int far*	p;
	RAY far*	rp;
	int		i;

	p = &stone_cnt[0][0];				/* 石消去カウンタ */
	for (i = 0; i < FIELD_W*FIELD_H; i++) {
		*p++ = 0;						/* 初期化 */
	}

	rp = ray;							/* 光線 */
	for (i = 0; i < RAY_MAX; i++) {
		rp->dir = field->dir;			/* 方向 */
		rp->x   = field->sx*16;			/* 位置 */
		rp->y   = field->sy*16;
		rp++;
	}

	clear_flag = 0;						/* クリア、失敗 */
}

/**********************
    エリアデータ転送
 **********************/
static
void	init_area_data(void)
{
	u_char far*	p0;
	FIELD far*	fp;
	STONE far*	p1;
	int		i, j, k;
	u_char	c;

	p0 = stage_data + area_num*((4 + FIELD_W*FIELD_H/4)*STAGE_MAX);
										/* エリアデータ */
	for (i = 0, fp = field_data; i < STAGE_MAX; i++, fp++) {
		fp->num = (int)*p0++;			/* 石の数 */
		fp->dir = (int)*p0++;			/* 光線の方向 */
		fp->sx  = (int)*p0++;			/* 光線初期位置 */
		fp->sy  = (int)*p0++;
		p1 = &fp->stone[0][0];			/* 石配置 */
		for (j = 0; j < FIELD_W*FIELD_H/4; j++) {
			c = *p0++;
			for (k = 0; k < 4; k++) {
				*p1++ = (STONE)(c & 0x03);
				c >>= 2;
			}
		}
	}
	stage_num = 0;						/* ステージ番号 */
}

/******************
    ゲームメイン
 ******************/
static
void	game_main(void)
{
	Bool	loop;
	int		i;

	init_area_data();						/* エリアデータ転送 */

	play_music(BGM_MAIN1 + (area_num % 3), PLAY_LOOP);		/* 演奏開始 */
	fade_in();								/* フェードイン */
	cursor_x = 2;							/* カーソル位置 */
	cursor_y = 2;
	loop = TRUE;
	while ( loop ) {
		field = field_data + stage_num;		/* フィールド */
		clear_field();						/* フィールド初期化 */
		draw_stage();						/* ステージ描画 */

		switch ( edit_stone() ) {			/* 石の向きを設定 */
		  case 0 :
			switch ( (i = move_ray()) ) {	/* 光線移動 */
			  case 0 :						/* 成功 */
				stage_num++;
				break;
			  case 1 :						/* キャンセル */
				break;
			  default :						/* エリア選択に戻る */
				loop = FALSE;
				break;
			}
			if ( (clear_flag > 0) && (i >= 0) ) {		/* 演奏開始 */
				play_music(BGM_MAIN1 + (area_num % 3), PLAY_LOOP);
			}
			break;
		  case 1 :							/* ステージ変更 */
			break;
		  default :							/* エリア選択に戻る */
			loop = FALSE;
			break;
		}
		stage_num = (stage_num + STAGE_MAX) % STAGE_MAX;
	}
	fadeout_music();						/* ＢＧＭフェードアウト */
	fade_out();								/* フェードアウト */
	for (i = 0; i < FADE_COUNT; i++) {
		draw_screen();
	}
}


/************************
    プレイデータ初期化
 ************************/
static
void	init_play_data(void)
{
	FILE far*	fp;
	int			i;

	if ( (fp = fopen("/ram0/"PLAY_FILE, "rb")) ) {	/* データ読み込み */
		fread(play_data, sizeof(u_short), AREA_MAX, fp);
		fclose(fp);
	}
	else {
		for (i = 0; i < AREA_MAX; i++) {			/* プレイデータクリア */
			play_data[i] = 0x0000;
		}
		if ( (fp = fopen("/ram0/"PLAY_FILE, "wb")) ) {
			fclose(fp);
		}
		else {
			text_screen_init();
			text_put_string(4, 8, "セーブデータを作成できませんでした");
			key_wait();
		}
	}
}

/**************************
    プレイデータ書き込み
 **************************/
static
void	save_play_data(void)
{
	FILE far*	fp;

	if ( (fp = fopen("/ram0/"PLAY_FILE, "wb")) ) {		/* データ書き込み */
		fwrite(play_data, sizeof(u_short), AREA_MAX, fp);
		fclose(fp);
	}
}

/************
    メイン
 ************/
int		main(int argc, char* argv[])
{
	res_t far*	res;

	init_play_data();								/* プレイデータ初期化 */

	res = init_screen(_pc->_resource);				/* 画面初期化 */
	res = init_sound(res);							/* サウンド初期化 */
	stage_data = (u_char far*)res + sizeof(res_t);	/* ステージデータ */

	area_num = 0;
	while ( select_area() ) {						/* エリア選択 */
		game_main();								/* ゲームメイン */
	}
	stop_music();									/* ＢＧＭ停止 */
	end_sound();									/* サウンド終了 */
	save_play_data();								/* プレイデータ書き込み */

	return	0;
}

/**************** End of File **********************************************/