/*
 *	game.c
 *
 *	2Fuseもどき
 *
 *	* Wed Aug 09 21:35:35 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"
//*****************************************************************************
//	
//*****************************************************************************
//ST_Game.stat
#define GAME_STAT_READY		0				//ゲーム開始前		⇒SELECT1
#define GAME_STAT_SELECT1	1				//一つ目選択中		⇒SELECT2,GAMEOVER
#define GAME_STAT_SELECT2	2				//二つ目選択中		⇒SELECT1,MISS,GAMEOVER
#define GAME_STAT_MISS		3				//ミス揺れ中		⇒SELECT1,GAMEOVER
#define GAME_STAT_GAMEOVER	4				//ゲームオーバー
//-----------------------------------------------------------------------------
//ST_Cell.stat
#define CELL_STAT_WIRE		1				//ワイヤーフレーム出現中
#define CELL_STAT_APPEAR	2				//出現中
#define CELL_STAT_NORMAL	3				//通常状態
#define CELL_STAT_SELECT	4				//選択状態
#define CELL_STAT_CHANGE	5				//変化中
#define CELL_STAT_MISS		6				//ミス揺れ中
//ST_Cell.panel_color
//#define PANEL_COLOR_A		0				//緑	星パネルを消した時のアイテム効果=パネルの出現速度が早くなる。
//#define PANEL_COLOR_B		1				//赤	星パネルを消した時のアイテム効果=得点倍率がアップする。
//#define PANEL_COLOR_C		2				//青	星パネルを消した時のアイテム効果=時間の減りが遅くなる。
//ST_Cell.effect
#define EFFECT_S		1				//小エフェクト
#define EFFECT_M		2				//中エフェクト
#define EFFECT_L		3				//大エフェクト
//-----------------------------------------------------------------------------
#define FIRE_CNT_DIV		4/*調整可*/			//炎にアニメーションカウンタの除数
#define FIRE_MAX		(16*FIRE_CNT_DIV)		//炎の最大出現数
//-----------------------------------------------------------------------------
//ST_Game.game_time
#define GAME_TIME_MAX		SEC(60.0)			//ゲーム時間の初期値
#define GAME_TIME_WARN		SEC(5.0)			//警告開始ゲーム時間
//ST_Game.item_time
#define ITEM_TIME_MAX		SEC(6.0)			//アイテム効果時間の初期値
#define COMBO_TIME_MAX		SEC(1.5)			//コンボ継続時間の初期値
//-----------------------------------------------------------------------------
#define FIELD_COLS		4				//フィールド列数
#define FIELD_ROWS		4				//フィールド行数
//-----------------------------------------------------------------------------
#define WIRE_CNT_DIV		SEC(0.67)			//ワイヤーフレーム出現中のカウンタの除数
//-----------------------------------------------------------------------------
#define READY_CNT_MAX		SEC(2.0)			//ゲーム開始前演出時間
//-----------------------------------------------------------------------------
#define GAMEOVER_CNT_MAX	SEC(1.5)			//ゲームオーバー演出時間
#define GAMEOVER_CNT_MID	SEC(0.5)			//ゲームオーバー演出中にパネルが集まる時間
//-----------------------------------------------------------------------------
#define MISS_CNT_MAX		SEC(0.5)			//ミス演出時間
//-----------------------------------------------------------------------------
#define EFFECT_S_CNT_MAX	SEC(0.4)			//小エフェクト継続時間
#define EFFECT_M_CNT_MAX	SEC(0.5)			//中エフェクト継続時間
#define EFFECT_L_CNT_MAX	SEC(0.5)			//大エフェクト継続時間
#define EFFECT_G_CNT_MAX	SEC(0.75)			//ゲームオーバーエフェクト継続時間
//*****************************************************************************
//	
//*****************************************************************************
typedef struct _ST_Cell {
	uint8_t		stat;					//状態	CELL_STAT_*
	uint8_t		panel_color;				//PANEL_COLOR_*
	uint8_t		panel_number;				//0=[１],1=[２],2=[★]
	uint8_t		cnt;					//汎用カウンタ
	uint8_t		effect;					//エフェクト	EFFECT_*
	uint8_t		effect_cnt;				//エフェクトカウンタ
	int		effect_seed;				//エフェクトの乱数シード
} ST_Cell;
//-----------------------------------------------------------------------------
typedef struct _ST_Fire {
	uint8_t		stat;					//状態	0=空き,1=左側,2=左側(上下反転),3=右側,4=右側(上下反転)
	 int8_t		x, y;					//座標
	uint8_t		cnt;					//アニメーションカウンタ
} ST_Fire;
//-----------------------------------------------------------------------------
typedef struct _ST_Game {
	int		stat;					//状態	GAME_STAT_*
	int		score;					//スコア
	int		score_disp;				//表示スコア
	int		combo;					//コンボ数
	int		combo_time;				//コンボ継続残り時間
	int		game_time;				//ゲーム残り時間
	int		item_time[3];				//アイテム効果残り時間
	int		game_warn;				//警告音インターバル
	//
	int		cursor_col;				//カーソル列
	int		cursor_row;				//カーソル行
	int		select_col;				//選択列
	int		select_row;				//選択行
	//
	ST_Cell		TBL_Cell[FIELD_ROWS][FIELD_COLS];	//フィールド
	//--- アイテムエフェクト ---
	//フレームアニメーション
	int		frame_anm_cnt;				//フレームアニメーションカウンタ
	//炎アニメーション
	ST_Fire		TBL_Fire[FIRE_MAX];			//炎
	//氷アニメーション
	int		ice_anm_cnt;				//氷アニメーションカウンタ
	//--- ゲーム開始前演出 ---
	int		ready_cnt;				//ゲーム開始前演出カウンタ
	//--- ミス演出 ---
	int		miss_cnt;				//ミス演出カウンタ
	//--- ゲームオーバー演出 ---
	int		gameover_cnt;				//ゲームオーバー演出カウンタ
} ST_Game;
//*****************************************************************************
//	
//*****************************************************************************
ST_Game		stGame;
//*****************************************************************************
//	
//*****************************************************************************
const uint16_t TBL_ItemSpr[3]={
	SPR_ITEM_A_0,	//緑
	SPR_ITEM_B_0,	//赤
	SPR_ITEM_C_0,	//青
};
//-----------------------------------------------------------------------------
const uint16_t TBL_WireSpr[7/*cnt/WIRE_CNT_DIV*/]={
	SPR_WIRE_A_0,	//4x4
	SPR_WIRE_B_0,	//6x6
	SPR_WIRE_C_0,	//8x8
	SPR_WIRE_D_0,	//10x10
	SPR_WIRE_E_0,	//12x12
	SPR_WIRE_F_0,	//14x14
	SPR_WIRE_G_0,	//16x16
};
//-----------------------------------------------------------------------------
const uint16_t TBL_PanelSmallSpr[3/*panel_color*/][3/*panel_number*/]={
	{SPR_PANEL_SMALL_A_1_0,SPR_PANEL_SMALL_A_2_0,SPR_PANEL_SMALL_A_S_0},
	{SPR_PANEL_SMALL_B_1_0,SPR_PANEL_SMALL_B_2_0,SPR_PANEL_SMALL_B_S_0},
	{SPR_PANEL_SMALL_C_1_0,SPR_PANEL_SMALL_C_2_0,SPR_PANEL_SMALL_C_S_0},
};
const uint16_t TBL_PanelNormalSpr[3/*panel_color*/][3/*panel_number*/]={
	{SPR_PANEL_NORMAL_A_1_0,SPR_PANEL_NORMAL_A_2_0,SPR_PANEL_NORMAL_A_S_0},
	{SPR_PANEL_NORMAL_B_1_0,SPR_PANEL_NORMAL_B_2_0,SPR_PANEL_NORMAL_B_S_0},
	{SPR_PANEL_NORMAL_C_1_0,SPR_PANEL_NORMAL_C_2_0,SPR_PANEL_NORMAL_C_S_0},
};
const uint16_t TBL_PanelSelectSpr[3/*panel_color*/][3/*panel_number*/]={
	{SPR_PANEL_SELECT_A_1_0,SPR_PANEL_SELECT_A_2_0,SPR_PANEL_SELECT_A_S_0},
	{SPR_PANEL_SELECT_B_1_0,SPR_PANEL_SELECT_B_2_0,SPR_PANEL_SELECT_B_S_0},
	{SPR_PANEL_SELECT_C_1_0,SPR_PANEL_SELECT_C_2_0,SPR_PANEL_SELECT_C_S_0},
};
//*****************************************************************************
//	
//*****************************************************************************
static void fnRenderCircle(int x, int y, int r, void* arg) {
	int c = rnd8((int*)arg) & 1;	//0〜1					複数の色で描くためのシードです。単色で良い場合はこれは不要です。
	CbkQue_render_ellipse_fill(pCbkQue, PRI__EFFECT, &render, x - (r - 1), y - (r - 1), (r * 2) - 1, (r * 2) - 1, c);
}
static void fnRenderPolygon(const int xy[/*n*/][2/*x,y*/], int n/*3or4*/, void* arg) {
	VERTEX v[]={{xy[0][0],xy[0][1]},{xy[1][0],xy[1][1]},{xy[2][0],xy[2][1]},{xy[3][0],xy[3][1]}};
	CbkQue_render_polygon(pCbkQue, PRI__EFFECT, &render, v, n, (int)arg);
}
//*****************************************************************************
//	
//*****************************************************************************
void game_draw_ui() {
	//LOGO
	{
		CbkQue_sprite_draw(pCbkQue,
			PRI__LOGO,
			  X__LOGO,
			  Y__LOGO,
			 SPR_LOGO, 0);
	}
	//HI
	{
		CbkQue_render_font(pCbkQue,
			PRI__HI,
			&render,
			  X__HI__H,
			  Y__HI,
			'H',
			2,
			0);
		CbkQue_render_font(pCbkQue,
			PRI__HI,
			&render,
			  X__HI__I,
			  Y__HI,
			'i',
			2,
			0);
		CbkQue_render_printf(pCbkQue,
			PRI__HI,
			&render,
			  X__HI__SCORE,
			  Y__HI,
			2,
			2,
			"%06d", hi_score);
		CbkQue_render_printf(pCbkQue,
			PRI__HI,
			&render,
			  X__HI__SCORE,
			  Y__HI,
			2,
			1,
			"%6d", hi_score);
	}
	//1P
	{
		CbkQue_render_printf(pCbkQue,
			PRI__1P,
			&render,
			  X__1P__SCORE,
			  Y__1P,
			0,
			2,
			"%06d", stGame.score_disp);
		CbkQue_render_printf(pCbkQue,
			PRI__1P,
			&render,
			  X__1P__SCORE,
			  Y__1P,
			0,
			0,
			"%6d", stGame.score_disp);
	}
	//TIME
	{
		div_t d = div(stGame.game_time, FPS);
		int sec = d.quot;
		int csec = (d.rem * 100 + (FPS - 1)) / FPS;
		int w = (stGame.game_time * W__TIME__BAR + (GAME_TIME_MAX - 1)) / GAME_TIME_MAX;
		CbkQue_render_printf(pCbkQue,
			PRI__TIME,
			&render,
			  X__TIME__SEC,
			  Y__TIME,
			2,
			0,
			"%02d", sec);
		CbkQue_render_font(pCbkQue,
			PRI__TIME,
			&render,
			  X__TIME__COLON,
			  Y__TIME,
			':',
			2,
			0);
		CbkQue_render_printf(pCbkQue,
			PRI__TIME,
			&render,
			  X__TIME__CSEC,
			  Y__TIME,
			2,
			0,
			"%02d", csec);
		CbkQue_render_rectangle(pCbkQue,
			PRI__TIME,
			&render,
			  X__TIME__BAR - 1,
			  Y__TIME__BAR - 1,
			  W__TIME__BAR + 2,
			  H__TIME__BAR + 2,
			2);
		CbkQue_render_rectangle_fill(pCbkQue,
			PRI__TIME,
			&render,
			  X__TIME__BAR,
			  Y__TIME__BAR,
			  w,
			  H__TIME__BAR,
			0);
	}
	//COMBO
	{
		int x;
		if(stGame.combo >= 3) {
			CbkQue_render_string(pCbkQue,
				PRI__COMBO,
				&render,
				  X__COMBO__TITLE,
				  Y__COMBO__TITLE,
				"COMBO",
				2,
				1);
			if(stGame.combo <= 9) {
				x = X__COMBO__COUNT__1;
			} else if(stGame.combo <= 99) {
				x = X__COMBO__COUNT__2;
			} else {
				x = X__COMBO__COUNT__3;
			}
			CbkQue_render_printf(pCbkQue,
				PRI__COMBO,
				&render,
				  x,
				  Y__COMBO__COUNT,
				0,
				0,
				"%d", stGame.combo);
		}
	}
	//ITEM
	{
		int i, t, h, iSprNo;
		for(i = 0; i < 3; i++) {
			t = stGame.item_time[i];
			h = (t * H__ITEM + (ITEM_TIME_MAX - 1)) / ITEM_TIME_MAX;
			iSprNo = TBL_ItemSpr[i] + h;
			CbkQue_sprite_draw(pCbkQue,
				PRI__ITEM,
				  X__ITEM + W__ITEM * i,
				  Y__ITEM,
				iSprNo, 0);
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void game_draw_message() {
	//ゲーム開始前演出
	{
		int x, y, c;
		if(stGame.ready_cnt < READY_CNT_MAX) {
			CbkQue_render_rectangle_shadow(pCbkQue,
				PRI__MESSAGE_BACK,
				&render,
				  X__MESSAGE_BACK,
				  Y__MESSAGE_BACK,
				  W__MESSAGE_BACK,
				  H__MESSAGE_BACK,
				((stGame.ready_cnt <                  SEC(0.1)/*調整可*/) ||
				 (stGame.ready_cnt >= READY_CNT_MAX - SEC(0.1)/*調整可*/)) ? 1 : 2, 3);
			x = easeOutIn_i(DISP_X, -60, stGame.ready_cnt, READY_CNT_MAX);
			y = Y__MESSAGE;
			CbkQue_render_string_framed(pCbkQue,
				PRI__MESSAGE,
				&render,
				x,
				y,
				"GET READY...",
				0,
				0x330);
			c = 2 - (stGame.ready_cnt / SEC(0.1));
			if(c >= 0) {
				CbkQue_render_rectangle_shadow(pCbkQue,
					PRI__FADE,
					&render,
					0,
					0,
					DISP_X,
					DISP_Y,
					c, 3);
			}
			stGame.ready_cnt++;
		}
	}
	//ゲームオーバー演出
	{
		if(stGame.gameover_cnt >= GAMEOVER_CNT_MID) {
			int x, y, c;
			x = (DISP_X - (5 * 9 + 1)) / 2;
			y = Y__MESSAGE;
			CbkQue_render_string_framed(pCbkQue,
				PRI__MESSAGE,
				&render,
				x,
				y,
				"TIME'S UP",
				0,
				0x330);
			c = 0;
			if(stGame.gameover_cnt >= GAMEOVER_CNT_MAX - SEC(0.3)) { c = 1; }
			if(stGame.gameover_cnt >= GAMEOVER_CNT_MAX - SEC(0.2)) { c = 2; }
			if(stGame.gameover_cnt >= GAMEOVER_CNT_MAX - SEC(0.1)) { c = 3; }
			if(c) {
				CbkQue_render_rectangle_shadow(pCbkQue,
					PRI__FADE,
					&render,
					0,
					0,
					DISP_X,
					DISP_Y,
					c, 3);
			}
		}
	}
	//ゲームオーバーエフェクト
	{
		if((stGame.gameover_cnt >= GAMEOVER_CNT_MID) &&
		   (stGame.gameover_cnt <= GAMEOVER_CNT_MID + EFFECT_G_CNT_MAX)) {
			int c = (GAMEOVER_CNT_MID + EFFECT_G_CNT_MAX) - stGame.gameover_cnt;
			if(c < 3) {
				c = 2;
			} else if(c < 6) {
				c = 1;
			} else {
				c = 0;
			}
			EffAnm_RadLar(
				X__CELL + ((W__CELL * (FIELD_COLS - 1)) >> 1) + 11,	//x
				Y__CELL + ((H__CELL * (FIELD_ROWS - 1)) >> 1) + 11,	//y
				stGame.gameover_cnt - GAMEOVER_CNT_MID,			//t
				EFFECT_G_CNT_MAX,					//duration
	//			max(DISP_X,DISP_Y),					//screenSize
	/*小さめに調整した*/	max(DISP_X,DISP_Y)/3,					//screenSize
				0,							//seed
				fnRenderPolygon,					//fnRenderPolygon
				(void*)c);						//arg
			if(stGame.gameover_cnt == GAMEOVER_CNT_MID) {
				//ゲームオーバーエフェクト音
				{
					TapLogCh_Play(pTapSeq,
						TapLogCh_Se3,
						SND_SE_GAMEOVER,
						1,
						0,
						0);
				}
			}
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void game_draw_item_effect() {
	//FRAME
	{
		static const uint8_t TBL_FrameAnmPtn[]={0,0,0,1,1,1,2,2,2,3,3,3,2,2,2,1,1,1};
		int iSprNo;
		//緑アイテムが有効ならば…
		if(stGame.item_time[0]) {
			//フレームアニメーションを描画する。
			iSprNo = SPR_FRAME_B_0 + TBL_FrameAnmPtn[stGame.frame_anm_cnt];
			CbkQue_sprite_draw(pCbkQue,
				PRI__FRAME,
				  X__FRAME,
				  Y__FRAME,
				iSprNo, 0);
			//フレームアニメーションカウンタを進める。
			if(++stGame.frame_anm_cnt >= ARRAY_SIZE(TBL_FrameAnmPtn)) { stGame.frame_anm_cnt = 0; }
		//緑アイテムが無効ならば…
		} else {
			//フレームアニメーションカウンタが一番暗いパターンを指していなければ…
			if(TBL_FrameAnmPtn[stGame.frame_anm_cnt]) {
				//フレームアニメーションを描画する。
				iSprNo = SPR_FRAME_B_0 + TBL_FrameAnmPtn[stGame.frame_anm_cnt];
				CbkQue_sprite_draw(pCbkQue,
					PRI__FRAME,
					  X__FRAME,
					  Y__FRAME,
					iSprNo, 0);
				//フレームアニメーションカウンタを進める。
				if(++stGame.frame_anm_cnt >= ARRAY_SIZE(TBL_FrameAnmPtn)) { stGame.frame_anm_cnt = 0; }
			//フレームアニメーションカウンタが一番暗いパターンを指していたら…
			} else {
				//通常フレームを描画する。
				iSprNo = SPR_FRAME_A;
				CbkQue_sprite_draw(pCbkQue,
					PRI__FRAME,
					  X__FRAME,
					  Y__FRAME,
					iSprNo, 0);
				//フレームアニメーションカウンタをクリアする。
				stGame.frame_anm_cnt = 0;
			}
		}
	}
	//FIRE
	{
		int iFire;
		//赤アイテムが有効ならば…
		if(stGame.item_time[1]) {
			//炎の空きを検索する。
			for(iFire = 0; iFire < FIRE_MAX; iFire++) {
				ST_Fire* pFire = &stGame.TBL_Fire[iFire];
				//炎の空きが有れば…
				if(!pFire->stat) {
					//炎を生成する。
					pFire->stat = RND32_RANGE(seed, 1, 4 + 1);		//1=左側,2=左側(上下反転),3=右側,4=右側(上下反転)
					if(pFire->stat <= 2) {					//1=左側,2=左側(上下反転)
						pFire->x =      0 - RND32_RANGE(seed, 0, 16);
					} else {						//3=右側,4=右側(上下反転)
						pFire->x = DISP_X + RND32_RANGE(seed, 0, 16);
					}
					pFire->y = RND32_RANGE(seed, Y__FIRE__MIN, Y__FIRE__MAX);
					pFire->cnt = 0;
					break;	//ここまで
				}
			}
		}
		//炎を描画する。
		for(iFire = 0; iFire < FIRE_MAX; iFire++) {
			ST_Fire* pFire = &stGame.TBL_Fire[iFire];
			//出現中ならば…
			if(pFire->stat) {
				static const uint8_t TBL_Param[5]={0/*ダミー*/,0,DRW_REVY,DRW_REVX,DRW_REVX|DRW_REVY};	//1=左側,2=左側(上下反転),3=右側,4=右側(上下反転)
				int iSprNo = SPR_FIRE_0 + (pFire->cnt / FIRE_CNT_DIV);
				int iParam = TBL_Param[pFire->stat];
				CbkQue_sprite_draw(pCbkQue,
					PRI__FIRE,
					pFire->x,
					pFire->y,
					iSprNo, iParam);
				//移動する。
				if(now & 1) {	//少しゆっくり移動する事にした。調整可。
					if(pFire->stat <= 2) {
						pFire->x++;
					} else {
						pFire->x--;
					}
				}
				//アニメーションパターンの末尾を超えたら、この炎を消す。
				if(++pFire->cnt >= (16 * FIRE_CNT_DIV)) { pFire->stat = 0; }
			}
		}
	}
	//ICE
	{
		//青アイテムが有効ならば…
		if(stGame.item_time[2]) {
			//氷アニメーションカウンタを増やす。
			if(stGame.ice_anm_cnt < 16) { stGame.ice_anm_cnt++; }
		//青アイテムが無効ならば…
		} else {
			//氷アニメーションカウンタを減らす。
			if(stGame.ice_anm_cnt) { stGame.ice_anm_cnt--; }
		}
		//氷が出現中ならば…
		if(stGame.ice_anm_cnt) {
			//氷を描く。
			CbkQue_sprite_draw(pCbkQue,	//上
				PRI__ICE,
				DISP_X - 1,
				stGame.ice_anm_cnt,
				 SPR_ICE, DRW_REVX);		//見栄えのために上側と下側では左右も逆で描く事にした。上側は左右反転上下通常、下側は左右通常上下反転で描く。
			CbkQue_sprite_draw(pCbkQue,	//下
				PRI__ICE,
				0,
				DISP_Y - 1 - stGame.ice_anm_cnt,
				 SPR_ICE, DRW_REVY);
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void game_draw_cell() {
	//パネル、エフェクト
	{
		int col, row, x, y, iSprNo;
		for(row = 0; row < FIELD_ROWS; row++) {
			for(col = 0; col < FIELD_COLS; col++) {
				ST_Cell* pCell = &stGame.TBL_Cell[row][col];
				if(pCell->stat) {
					x = W__CELL * col;
					y = H__CELL * row;
					if(stGame.gameover_cnt) {
						x = X__CELL + lerp_i(x, (W__CELL * (FIELD_COLS - 1)) >> 1, stGame.gameover_cnt, GAMEOVER_CNT_MID);
						y = Y__CELL + lerp_i(y, (H__CELL * (FIELD_ROWS - 1)) >> 1, stGame.gameover_cnt, GAMEOVER_CNT_MID);
					} else {
						x = X__CELL + W__CELL * col;
						y = Y__CELL + H__CELL * row;
						if((pCell->stat == CELL_STAT_CHANGE) ||	//変化中
						   (pCell->stat == CELL_STAT_SELECT) ||	//選択状態
						   (pCell->stat == CELL_STAT_MISS  )) {	//ミス揺れ中
							CbkQue_sprite_draw(pCbkQue,
								PRI__SELECT,
								x,
								y,
								SPR_SELECT_0 + ((now >> 1) & 3), 0);
						}
						if(pCell->effect) {
							int tmp = pCell->effect_seed;
							switch(pCell->effect) {
							default:DIE();
							case EFFECT_S:
								{
									EffAnm_RadSma(
										x + 11,			//x
										y + 11,			//y
										pCell->effect_cnt,	//t
										EFFECT_S_CNT_MAX,	//duration
							//			max(DISP_X,DISP_Y),	//screenSize
							/*大きめに調整した*/	max(DISP_X,DISP_Y)*2,	//screenSize
										tmp,			//seed
										fnRenderCircle,		//fnRenderCircle
										&tmp);			//arg			複数の色で描くためのシードです。単色で良い場合はこれは不要です。
									if(++pCell->effect_cnt > EFFECT_S_CNT_MAX) { pCell->effect = 0; }
								}
								break;
							case EFFECT_M:
								{
									EffAnm_RadMed(
										x + 11,			//x
										y + 11,			//y
										pCell->effect_cnt,	//t
										EFFECT_M_CNT_MAX,	//duration
										max(DISP_X,DISP_Y),	//screenSize
										tmp,			//seed
										fnRenderPolygon,	//fnRenderPolygon
										NULL);			//arg
									if(++pCell->effect_cnt > EFFECT_M_CNT_MAX) { pCell->effect = 0; }
								}
								break;
							case EFFECT_L:
								{
									EffAnm_RadBo2(
										x + 11,			//x
										y + 11,			//y
										pCell->effect_cnt,	//t
										EFFECT_L_CNT_MAX,	//duration
										max(DISP_X,DISP_Y),	//screenSize
										tmp,			//seed
										fnRenderPolygon,	//fnRenderPolygon
										NULL);			//arg
									if(++pCell->effect_cnt > EFFECT_L_CNT_MAX) { pCell->effect = 0; }
								}
								break;
							}
						}
					}
					if(stGame.gameover_cnt <= GAMEOVER_CNT_MID) {
						iSprNo = 0;
						if(stGame.game_time < GAME_TIME_WARN) {
							static const uint8_t TBL_PanelAnmPtn[]={0,1,2,1,0,3,4,3};
							int tmp = col + row * FIELD_COLS;
							tmp = RND32_RANGE(tmp, 0, ARRAY_SIZE(TBL_PanelAnmPtn));
							tmp += now;
							tmp %= ARRAY_SIZE(TBL_PanelAnmPtn);
							iSprNo = TBL_PanelAnmPtn[tmp];
						}
						switch(pCell->stat) {
						default:DIE();
						case CELL_STAT_WIRE:	//ワイヤーフレーム出現中
							{
								iSprNo += TBL_WireSpr[pCell->cnt / WIRE_CNT_DIV];
								CbkQue_sprite_draw(pCbkQue,
									PRI__CELL,
									x,
									y,
									iSprNo, 0);
							}
							break;
						case CELL_STAT_APPEAR:	//出現中
						case CELL_STAT_CHANGE:	//変化中
							{
								iSprNo += TBL_PanelSmallSpr[pCell->panel_color][pCell->panel_number];
								CbkQue_sprite_draw(pCbkQue,
									PRI__CELL,
									x,
									y,
									iSprNo, 0);
							}
							break;
						case CELL_STAT_NORMAL:	//通常状態
							{
								iSprNo += TBL_PanelNormalSpr[pCell->panel_color][pCell->panel_number];
								CbkQue_sprite_draw(pCbkQue,
									PRI__CELL,
									x,
									y,
									iSprNo, 0);
							}
							break;
						case CELL_STAT_SELECT:	//選択状態
							{
								iSprNo += TBL_PanelSelectSpr[pCell->panel_color][pCell->panel_number];
								CbkQue_sprite_draw(pCbkQue,
									PRI__CELL,
									x,
									y,
									iSprNo, 0);
							}
							break;
						case CELL_STAT_MISS:	//ミス揺れ中
							{
								x += RND32_RANGE(seed, -2, 2 + 1);
								y += RND32_RANGE(seed, -2, 2 + 1);
								iSprNo += TBL_PanelSmallSpr[pCell->panel_color][pCell->panel_number];
								CbkQue_sprite_draw(pCbkQue,
									PRI__CELL,
									x,
									y,
									iSprNo, 0);
							}
							break;
						}
					}
				}
			}
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void game_draw_cursor() {
	int i, x, y, iSprNo;
	//カーソル
	{
		if((stGame.stat == GAME_STAT_SELECT1) ||
		   (stGame.stat == GAME_STAT_SELECT2)) {
			static const uint8_t TBL_CursorAnmPtn[]={0,0,1,1,2,2,1,1};
			x = X__CELL + (W__CELL * stGame.cursor_col);
			y = Y__CELL + (H__CELL * stGame.cursor_row);
			i = now % ARRAY_SIZE(TBL_CursorAnmPtn);
			iSprNo = SPR_CURSOR_0 + TBL_CursorAnmPtn[i];
			CbkQue_sprite_draw(pCbkQue,
				PRI__CURSOR,
				x,
				y,
				iSprNo, 0);
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void game_draw() {
	game_draw_ui();
	game_draw_message();
	game_draw_item_effect();
	game_draw_cell();
	game_draw_cursor();
}
//-----------------------------------------------------------------------------
void game_exec() {
	//コンボ
	{
		if( stGame.combo_time) { stGame.combo_time--; }
		if(!stGame.combo_time) { stGame.combo = 0; }
	}
	//アイテム
	{
		if(stGame.item_time[0]) { stGame.item_time[0]--; }
		if(stGame.item_time[1]) { stGame.item_time[1]--; }
		if(stGame.item_time[2]) { stGame.item_time[2]--; }
	}
	//パネル
	{
		if(stGame.stat != GAME_STAT_GAMEOVER) {
			int col, row;
			for(row = 0; row < FIELD_ROWS; row++) {
				for(col = 0; col < FIELD_COLS; col++) {
					ST_Cell* pCell = &stGame.TBL_Cell[row][col];
					if(pCell->stat) {
						switch(pCell->stat) {
						default:DIE();
						case CELL_STAT_WIRE:	//ワイヤーフレーム出現中
							{
								if(stGame.item_time[0]) { pCell->cnt = (WIRE_CNT_DIV * 7) - 1; }	//すでにワイヤー中のパネルがある時に緑アイテムが発動したら即ワイヤーを完了させる。(1フレーム遅れだが見た目上は問題無いだろう)
								if(++pCell->cnt >= (WIRE_CNT_DIV * 7)) {
									pCell->stat = CELL_STAT_APPEAR;
									pCell->panel_color = RND32_RANGE(seed, 0, 3);
									pCell->panel_number = 0;
									pCell->cnt = 0;
								}
							}
							break;
						case CELL_STAT_APPEAR:	//出現中
							{
								if(++pCell->cnt >= 4/*調整可*/) {
									pCell->stat = CELL_STAT_NORMAL;
									pCell->cnt = 0;
								}
							}
							break;
						case CELL_STAT_NORMAL:	//通常状態
							{
								/** no job **/
							}
							break;
						case CELL_STAT_SELECT:	//選択状態
							{
								/** no job **/
							}
							break;
						case CELL_STAT_CHANGE:	//変化中
							{
								if(++pCell->cnt >= 4/*調整可*/) {
									pCell->stat = CELL_STAT_NORMAL;
									pCell->cnt = 0;
								}
							}
							break;
						case CELL_STAT_MISS:	//ミス揺れ中
							{
								/** no job **/
							}
							break;
						}
					}
				}
			}
		}
	}
	//操作
	{
		switch(stGame.stat) {
		case GAME_STAT_SELECT1:		//一つ目選択中
		case GAME_STAT_SELECT2:		//二つ目選択中
			if(joy & TRG_LRUD) {
				if(joy & TRG_LF) {
					if(--stGame.cursor_col < 0) { stGame.cursor_col = FIELD_COLS - 1; }
				} else if(joy & TRG_RI) {
					if(++stGame.cursor_col > FIELD_COLS - 1) { stGame.cursor_col = 0; }
				}
				if(joy & TRG_UP) {
					if(--stGame.cursor_row < 0) { stGame.cursor_row = FIELD_ROWS - 1; }
				} else if(joy & TRG_DN) {
					if(++stGame.cursor_row > FIELD_ROWS - 1) { stGame.cursor_row = 0; }
				}
				//カーソル移動音
				{
					TapLogCh_Play(pTapSeq,
						TapLogCh_Se1,
						SND_SE_CURSOR,
						1,
						0,
						0);
				}
			}
			break;
		}
		switch(stGame.stat) {
		default:DIE();
		case GAME_STAT_READY:		//ゲーム開始前
			/** no job **/
			break;
		case GAME_STAT_SELECT1:		//一つ目選択中
			if(joy & TRG_A) {
				ST_Cell* pCell = &stGame.TBL_Cell[stGame.cursor_row][stGame.cursor_col];
				if((pCell->stat == CELL_STAT_NORMAL) ||
				   (pCell->stat == CELL_STAT_CHANGE)) {
					pCell->stat = CELL_STAT_SELECT;
					pCell->cnt = 0;
					stGame.stat = GAME_STAT_SELECT2;
					stGame.select_col = stGame.cursor_col;
					stGame.select_row = stGame.cursor_row;
					//一つ目選択音
					{
						PlayMusic(music_table[MUS_NOTE0]);
					}
				} else {
					//ペナルティ無しの短い警告音
					{
						TapLogCh_Play(pTapSeq,
							TapLogCh_Se2,
							SND_SE_ERROR,
							1,
							0,
							0);
					}
				}
			}
			break;
		case GAME_STAT_SELECT2:		//二つ目選択中
			if(joy & TRG_A) {
				ST_Cell* pCell1 = &stGame.TBL_Cell[stGame.cursor_row][stGame.cursor_col];
				ST_Cell* pCell2 = &stGame.TBL_Cell[stGame.select_row][stGame.select_col];
				if((pCell1->stat == CELL_STAT_NORMAL) ||
				   (pCell1->stat == CELL_STAT_CHANGE)) {
					if((pCell1->panel_color  == pCell2->panel_color ) &&
					   (pCell1->panel_number == pCell2->panel_number)) {
						stGame.combo++;
						stGame.combo_time = COMBO_TIME_MAX;
						{
							//本物の2fuseの得点システムがちょっとよくわからなかったので、真似せずに単純な得点システムにしました。
							int point = (pCell1->panel_color + stGame.combo);	//combo=1⇒[１]=1,[２]=2,[★]=3、combo=2⇒[１]=2,[２]=3,[★]=4、combo=3⇒[１]=3,[２]=4,[★]=5、…
							if(stGame.item_time[1]) { point *= 2; }		//本物の赤アイテムは(アイテムの表示とは裏腹に)2倍ではないようですが、当実装では判りやすく2倍にしました。
							stGame.score += (point * 10);
						}
						//二つ目選択音
						{
							static const uint16_t TBL_Note[]={
								MUS_NOTE1,	//1コンボ
								MUS_NOTE2,	//2コンボ
								MUS_NOTE3,	//3コンボ
								MUS_NOTE4,	//4コンボ
								MUS_NOTE5,	//5コンボ
								MUS_NOTE6,	//6コンボ
								MUS_NOTE7,	//7コンボ
								MUS_NOTE8,	//8コンボ
								MUS_NOTE9,	//9コンボ
								MUS_NOTE10,	//10コンボ以上
							};
							int i = stGame.combo - 1/*忘れないで!!*/;
							if(i >= ARRAY_SIZE(TBL_Note)) { i = ARRAY_SIZE(TBL_Note) - 1; }
							PlayMusic(music_table[TBL_Note[i]]);
						}
						if(pCell1->panel_number < 2) {	//数値
							pCell1->stat = CELL_STAT_CHANGE;
							pCell1->panel_number++;
							pCell1->cnt = 0;
							if(stGame.combo >= 3) {	//コンボエフェクトは3から。2では(内部的にはコンボだが)エフェクトは表示しない。
								pCell1->effect = EFFECT_M;
								pCell1->effect_cnt = 0;
								pCell1->effect_seed = RND32(seed);
								//コンボエフェクト音
								{
									TapLogCh_Play(pTapSeq,
										TapLogCh_Se3,
										SND_SE_COMBO,
										1,
										0,
										0);
								}
							}
						} else {	//星
							stGame.item_time[pCell1->panel_color] = ITEM_TIME_MAX;
							if(stGame.item_time[0]) {
								pCell1->stat = CELL_STAT_APPEAR;
								pCell1->panel_color = RND32_RANGE(seed, 0, 3);
								pCell1->panel_number = 0;
								pCell1->cnt = 0;
							} else {
								pCell1->stat = CELL_STAT_WIRE;
								pCell1->cnt = 0;
							}
							pCell1->effect = EFFECT_L;
							pCell1->effect_cnt = 0;
							pCell1->effect_seed = RND32(seed);
							//アイテム効果発動音
							{
								TapLogCh_Play(pTapSeq,
									TapLogCh_Se3,
									SND_SE_ITEM,
									1,
									0,
									0);
							}
						}
						if(stGame.item_time[0]) {
							pCell2->stat = CELL_STAT_APPEAR;
							pCell2->panel_color = RND32_RANGE(seed, 0, 3);
							pCell2->panel_number = 0;
							pCell2->cnt = 0;
						} else {
							pCell2->stat = CELL_STAT_WIRE;
							pCell2->cnt = 0;
						}
						pCell2->effect = EFFECT_S;
						pCell2->effect_cnt = 0;
						pCell2->effect_seed = RND32(seed);
						stGame.stat = GAME_STAT_SELECT1;
					} else {
						pCell1->stat = CELL_STAT_MISS;
						pCell1->cnt = 0;
						pCell2->stat = CELL_STAT_MISS;
						pCell2->cnt = 0;
						stGame.stat = GAME_STAT_MISS;
						stGame.miss_cnt = 0;
						//ペナルティ有りの長い警告音
						{
							TapLogCh_Play(pTapSeq,
								TapLogCh_Se2,
								SND_SE_MISS,
								1,
								0,
								0);
						}
					}
				} else {
					//ペナルティ無しの短い警告音
					{
						TapLogCh_Play(pTapSeq,
							TapLogCh_Se2,
							SND_SE_ERROR,
							1,
							0,
							0);
					}
				}
			} else if(joy & TRG_B) {
				ST_Cell* pCell = &stGame.TBL_Cell[stGame.select_row][stGame.select_col];
				pCell->stat = CELL_STAT_NORMAL;
				pCell->cnt = 0;
				stGame.stat = GAME_STAT_SELECT1;
				//選択解除音
				{
					TapLogCh_Play(pTapSeq,
						TapLogCh_Se2,
						SND_SE_UNSEL,
						1,
						0,
						0);
				}
			}
			break;
		case GAME_STAT_MISS:		//ミス揺れ中
			{
				if(++stGame.miss_cnt >= MISS_CNT_MAX) {
					ST_Cell* pCell1 = &stGame.TBL_Cell[stGame.cursor_row][stGame.cursor_col];
					ST_Cell* pCell2 = &stGame.TBL_Cell[stGame.select_row][stGame.select_col];
					pCell1->stat = CELL_STAT_NORMAL;
					pCell1->cnt = 0;
					pCell2->stat = CELL_STAT_NORMAL;
					pCell2->cnt = 0;
					stGame.stat = GAME_STAT_SELECT1;
				}
			}
			break;
		case GAME_STAT_GAMEOVER:	//ゲームオーバー
			{
				/** no job **/
			}
			break;
		}
	}
	//スコア
	{
		if(stGame.stat == GAME_STAT_GAMEOVER) {
			stGame.score_disp = stGame.score;
		} else {
			int diff;
			diff = stGame.score - stGame.score_disp;
			if(diff >= 10000) {
				stGame.score_disp += 1000;
			} else if(diff >= 1000) {
				stGame.score_disp += 100;
			} else if(diff >= 100) {
				stGame.score_disp += 10;
			} else if(diff >= 10) {
				stGame.score_disp += 5;
			} else if(diff >= 1) {
				stGame.score_disp++;
			}
		}
		if(stGame.score_disp > hi_score) {
			hi_score = stGame.score_disp;
		}
	}
	//警告音
	{
		if(stGame.stat != GAME_STAT_GAMEOVER) {
			if(stGame.game_time < SEC(10)) {
				stGame.game_warn++;
				if(stGame.game_warn >= ((stGame.game_time / SEC(0.5)) + SEC(0.5))) {
					stGame.game_warn = 0;
					TapLogCh_Play(pTapSeq,
						TapLogCh_Se4,
						SND_SE_WARN,
						1,
						0,
						0);
				}
			}
		}
	}
}
//-----------------------------------------------------------------------------
void game_init() {
	memset(&stGame, 0, sizeof stGame);
	stGame.game_time = GAME_TIME_MAX;
}
//-----------------------------------------------------------------------------
void game_run() {
	game_init();
	//ゲーム開始前演出
	{
		int i, col, row, att = 0;
		for(i = 0; i < SEC(0.34); i++) {
			schedule();
			game_exec();
			game_draw();
			pceWaveSetChAtt(music_wch, att++);
		}
		for(row = 0; row < FIELD_ROWS; row++) {
			for(col = 0; col < FIELD_COLS; col++) {
				schedule();
				game_exec();
				game_draw();
				pceWaveSetChAtt(music_wch, att++);
				{
					ST_Cell* pCell = &stGame.TBL_Cell[row][col];
					pCell->stat = CELL_STAT_APPEAR;
					pCell->panel_color = RND32_RANGE(seed, 0, 3);
					pCell->panel_number = 0;
					pCell->cnt = 0;
				}
				schedule();
				game_exec();
				game_draw();
				pceWaveSetChAtt(music_wch, att++);
			}
		}
		StopMusic();
		pceWaveSetChAtt(music_wch, 0);	//パネル音も音楽で鳴らすのでボリュームを戻しておく。
	}
	stGame.stat = GAME_STAT_SELECT1;
	//ゲーム中
	{
		while(stGame.game_time) {
			schedule();
			game_draw();
			game_exec();
			if(stGame.ready_cnt >= READY_CNT_MAX) {	//サービスとして、GET READY...が消えるまでは、操作は出来るが時間を減らさない。
				if(stGame.item_time[2]) {	//青アイテム発動中
					if(!(now % 3)) {
						stGame.game_time--;
					}
				} else {
					stGame.game_time--;
				}
			}
		}
	}
	//ゲームオーバー演出
	stGame.stat = GAME_STAT_GAMEOVER;
	stGame.item_time[0] = stGame.item_time[1] = stGame.item_time[2] = 0;
	{
		while(stGame.gameover_cnt < (GAMEOVER_CNT_MAX + SEC(0.5))) {
			schedule();
			game_draw();
			game_exec();
			if(stGame.gameover_cnt >= GAMEOVER_CNT_MAX) {
				if(joy & TRG_AB) { break; }
			}
			stGame.gameover_cnt++;
		}
	}
}
//*****************************************************************************
//	demo_run
//*****************************************************************************
void demo_run() {
	int i, fade_cnt = SEC(0.5), bStart = 0, iBlink = 0;
	//ゲーム中のBGMとSEを確実に止める。
	for(i = 0; i <= TapLogCh_Max; i++) { TapLogCh_Stop(pTapSeq, i); }
	StopMusic();
	pceWaveSetChAtt(music_wch, 0);
	//いきなりデモ画面になると見た目が悪かったので少しウェイトを入れた。
	delay(SEC(0.5));
	//デモBGM開始。
	PlayMusic(music_table[MUS_DEMO]);
	for(;;) {
		schedule();
		{
			int x, y;
			for(x = (( now / 2) + 0) & 15; x < DISP_X; x += 16) {
				CbkQue_render_line(pCbkQue,
					0,
					&render,
					x,
					0,
					x,
					DISP_Y,
					2);
			}
			for(y = ((-now / 2) + 8) & 15; y < DISP_Y; y += 16) {
				CbkQue_render_line(pCbkQue,
					0,
					&render,
					0,
					y,
					DISP_X,
					y,
					2);
			}
			for(x = ((-now / 4) + 4) & 15; x < DISP_X; x += 16) {
				CbkQue_render_line(pCbkQue,
					0,
					&render,
					x,
					0,
					x,
					DISP_Y,
					1);
			}
			for(y = (( now / 4) + 12) & 15; y < DISP_Y; y += 16) {
				CbkQue_render_line(pCbkQue,
					0,
					&render,
					0,
					y,
					DISP_X,
					y,
					1);
			}
		}
		//TITLE
		{
			CbkQue_sprite_draw(pCbkQue,
				PRI__TITLE,
				  X__TITLE,
				  Y__TITLE,
				 SPR_TITLE, 0);
			CbkQue_render_string_framed(pCbkQue,
				PRI__TITLE,
				&render,
				  X__TITLE+45,
				  Y__TITLE+1,
				"ﾓﾄｷ",
				2,
				0x330);
			CbkQue_render_string_framed(pCbkQue,
				PRI__TITLE,
				&render,
				  X__TITLE+45+6,
				  Y__TITLE+1-2,
				"ﾞ",
				2,
				0x330);
		}
		//HI
		{
			CbkQue_render_font_framed(pCbkQue,
				PRI__HI,
				&render,
				  X__HI__H-1	+27,
				  Y__HI-1,
				'H',
				2,
				0x330);
			CbkQue_render_font_framed(pCbkQue,
				PRI__HI,
				&render,
				  X__HI__I-1	+27,
				  Y__HI-1,
				'i',
				2,
				0x330);
			CbkQue_render_printf_framed(pCbkQue,
				PRI__HI,
				&render,
				  X__HI__SCORE-1	+27,
				  Y__HI-1,
				2,
				0x332,
				"%06d", hi_score);
			CbkQue_render_printf_framed(pCbkQue,
				PRI__HI,
				&render,
				  X__HI__SCORE-1	+27,
				  Y__HI-1,
				2,
				0x331,
				"%6d", hi_score);
		}
		//1P
		{
			CbkQue_render_printf_framed(pCbkQue,
				PRI__1P,
				&render,
				  X__1P__SCORE-1	+27,
				  Y__1P-1,
				0,
				0x332,
				"%06d", stGame.score);
			CbkQue_render_printf_framed(pCbkQue,
				PRI__1P,
				&render,
				  X__1P__SCORE-1	+27,
				  Y__1P-1,
				0,
				0x330,
				"%6d", stGame.score);
		}
		//PRESS TO START
		{
			int c = 3;
			if(!bStart) {
				if(!fade_cnt) {
					if((iBlink++ % SEC(1.0)) >= SEC(0.5)) { c = 0; }
				}
			} else {
				if(now & 2) { c = 0; }
			}
			if(c != 3) {
				CbkQue_render_string_framed(pCbkQue,
					PRI__1P,
					&render,
					(DISP_X-56)/2,
					77,
					"PRESS TO START",
					2,
					c|0x330);
			}
		}
		{
			int c = 0;
			if(!bStart) {
				if(fade_cnt) { fade_cnt--; }
			} else {
				if(bStart < SEC(1.0)) {
					bStart++;
				} else {
					if(fade_cnt < SEC(0.5)) { fade_cnt++; }
				}
			}
			c = fade_cnt / SEC(0.1);
			c = clamp(c, 0, 3);
			if(c) {
				CbkQue_render_rectangle_shadow(pCbkQue,
					PRI__FADE,
					&render,
					0,
					0,
					DISP_X,
					DISP_Y,
					c, 3);
			}
		}
		if(!bStart) {
			if(joy & TRG_AB) {
				TapLogCh_Play(pTapSeq,
					TapLogCh_Se4,
					SND_SE_WARN,
					1,
					0,
					0);
				bStart = 1;
				fade_cnt = 0;
			}
		} else {
			if(fade_cnt == SEC(0.5)) {
				break;
			}
		}
	}
}
//*****************************************************************************
//	app_main
//*****************************************************************************
int app_main(int argc, char* argv[]) {
//{{隠しコマンド。Bを押しながら起動すると電池表示を消す。デバッグ時に電池表示を消しやすいようにするためです。
	delay(1);
	if(joy & PAD_B) {
		pceWaveSetMasterAtt(0);
		pcePowerSetReport(PWR_RPTOFF);
	}
//}}隠しコマンド。Bを押しながら起動すると電池表示を消す。デバッグ時に電池表示を消しやすいようにするためです。
	for(;;) {
		SEH_use_shared;
		SEH_try_shared {
			demo_run();
		} SEH_catch(MAKEFOURCC('T','R','G','D')) {
			break;
		} SEH_end
		SEH_try_shared {
			game_run();
		} SEH_catch(MAKEFOURCC('T','R','G','D')) {
			/** no job **/
		} SEH_end
	}
	return 0;
}
