/*
 *	game.c
 *
 *	ぱづりもどき
 *
 *	* Wed Jan 18 21:03:09 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"
/****************************************************************************
 *	
 ****************************************************************************/
#define FIELD_COLS		6			//列数
#define FIELD_ROWS		(8+1)			//最大行数		(+1)の行は引き潮無しで一列埋まった状態でタイムアウトで置かされた場合のみ必要になる。
/*--------------------------------------------------------------------------*/
#define LOW_WARN_PUT_CNT	5			//何回魚を置くと引き潮警告が発生するか?		1以上で調整可
#define LOW_TIDE_PUT_CNT	(LOW_WARN_PUT_CNT+2)	//何回魚を置くと引き潮が発生するか?		(LOW_WARN_PUT_CNT+1)以上で調整可
/*--------------------------------------------------------------------------*/
#define CELL_TYPE_FISH1		1			//魚1
#define CELL_TYPE_FISH2		2			//魚2
#define CELL_TYPE_FISH3		3			//魚3
#define CELL_TYPE_FISH4		4			//魚4
#define CELL_TYPE_FISH5		5			//魚5
#define CELL_TYPE_FISH6		6			//魚6
#define CELL_TYPE_BUBBLE	7			//泡
/*--------------------------------------------------------------------------*/
#define BOAT_STAT_READY		0			//開始前		矢印無し…実機では開始前も矢印有りだが、無しの方が自然だろう。
#define BOAT_STAT_NORMAL	1			//通常中		矢印有り	移動可,交換可,置き可
#define BOAT_STAT_CHANGE	3			//交換中		矢印有り	移動可,交換可(モーションキャンセル),置き可(モーションキャンセル)
#define BOAT_STAT_PUT		4			//置き中		矢印無し…実機では置き中も矢印有りだが、無しの方が自然だろう。釣り中も同様です。
#define BOAT_STAT_GET		5			//釣り中		矢印無し
#define BOAT_STAT_NEXT		6			//次の魚		矢印有り	移動可,交換可(モーションキャンセル),置き可(モーションキャンセル)
#define BOAT_STAT_WIN		7			//勝利中		矢印無し
#define BOAT_STAT_LOSE		8			//敗北中		矢印無し
/*--------------------------------------------------------------------------*/
#define FISHOBJ_STAT_FLASH 	0			//フラッシュ中
#define FISHOBJ_STAT_VERT	1			//上昇中
#define FISHOBJ_STAT_HORZ	2			//水平移動中
#define FISHOBJ_STAT_WAIT	3			//水面下待機中
#define FISHOBJ_STAT_JUMP	4			//ジャンプ中
/*--------------------------------------------------------------------------*/
//#define MAX_FISHOBJ		(FIELD_COLS*FIELD_ROWS)	//┬理論上はこうだが…
//#define MAX_BUBBLEOBJ		(FIELD_COLS*FIELD_ROWS)	//┘
#define MAX_FISHOBJ		32			//┬これぐらいで充分だろう。演出だけなので超過したら生成しなくても動作上は問題無いから。
#define MAX_BUBBLEOBJ		16			//┘
#define MAX_SCOREOBJ		3			//1でも大丈夫だと思うが、一応余裕を見て3にした。
/*--------------------------------------------------------------------------*/
#define CHANGE_TIME		5			//交換時間		調整可
#define PUT_TIME		5			//置き時間		調整可
#define NEXT_TIME		CHANGE_TIME		//次の魚の出現時間	調整可。必ずしもCHANGE_TIMEと同じでなくても構いません。
#define HURRY_TIME		SEC(5.0)		//急ぎ開始時間		調整可
#define FORCE_TIME		(HURRY_TIME+SEC(3.0))	//強制置き発動時間
/*--------------------------------------------------------------------------*/
#define FISHOBJ_FLASH_TIME	SEC(0.3)		//魚釣り上げオブジェクトフラッシュ時間		調整可
#define FISHOBJ_MOVE_TIME	2			//魚釣り上げオブジェクト移動時間		調整可
#define FISHOBJ_JUMP_TIME	SEC(1.0)		//魚釣り上げオブジェクトジャンプ時間		調整可
#define FISHOBJ_JUMP_TIME_CUT	SEC(0.75)		//魚釣り上げオブジェクトジャンプ切り上げ時間	調整可
#define FISHOBJ_JUMP_HEIGHT	32			//魚釣り上げオブジェクトジャンプ高さ
/*--------------------------------------------------------------------------*/
#define INFO_STAT_SEAL		1			//アザラシ出現要求
#define INFO_STAT_SHARK		2			//サメ出現要求
/*--------------------------------------------------------------------------*/
#define SEAL_STAT_HIDE		0			//非表示中
#define SEAL_STAT_APPEAR	1			//出現中
#define SEAL_STAT_SHOW		2			//表示中
#define SEAL_STAT_DISAPPEAR	3			//消去中
/*--------------------------------------------------------------------------*/
#define SEAL_APPEAR_TIME	SEC(1.0)		//アザラシ出現時間		調整可
#define SEAL_DISAPPEAR_TIME	SEC(1.0)		//アザラシ退場時間		調整可
/*--------------------------------------------------------------------------*/
#define SHARK_STAT_HIDE		0			//非表示中
#define SHARK_STAT_APPEAR	1			//出現中
#define SHARK_STAT_SHOW		2			//表示中
#define SHARK_STAT_DISAPPEAR	3			//消去中
/*--------------------------------------------------------------------------*/
#define SHARK_APPEAR_TIME	SEC(1.0)		//アザラシ出現時間		調整可
#define SHARK_DISAPPEAR_TIME	SEC(1.0)		//アザラシ退場時間		調整可
#define SCOREOBJ_TIME		SEC(1.0)		//スコアオブジェクト表示時間	調整可
/****************************************************************************
 *	
 ****************************************************************************/
typedef struct _ST_Cell {
	uint8_t			type;			//0,又は,CELL_TYPE_*
} ST_Cell;
/*--------------------------------------------------------------------------*/
typedef struct _ST_FishObj {
	uint8_t			type;			//0,又は,CELL_TYPE_FISH*
	uint8_t			stat;			//FISHOBJ_STAT_*
	uint8_t			col, row;
	uint8_t			anm;
} ST_FishObj;
/*--------------------------------------------------------------------------*/
typedef struct _ST_BubbleObj {
	uint8_t			type;			//0,又は,CELL_TYPE_BUBBLE
//不要	uint8_t			stat;			//BUBBLEOBJ_STAT_*
	uint8_t			col, row;
	uint8_t			anm;
} ST_BubbleObj;
/*--------------------------------------------------------------------------*/
typedef struct _ST_ScoreObj {
	uint16_t		score;			//0以外ならばアクティブ
	uint8_t			col;			//表示列	スコアを加算した瞬間で猫の方の列をセットする。
	uint8_t			anm;
} ST_ScoreObj;
/*--------------------------------------------------------------------------*/
typedef struct _ST_Game {
	uint32_t		score;			//スコア
	uint8_t			stage;			//ステージ
	//
	uint8_t			lowCnt;			//引き潮発生回数[行]		ステージ開始時は0。((FIELD_ROWS-1)-lowCnt)の行に魚,又は,泡が存在していたら、ゲームオーバーになる。
	uint8_t			lowOfs;			//引き潮オフセット[ピクセル]	通常時は0。引き潮が発生したら、lowOfsが1→(H__CELL+H__CELL__GAP-1)に変化して行き、lowOfsが0に戻ると同時にlowCntを+1する。
	uint8_t			putCnt;			//魚を置いた回数[回]		引き潮が発生したら0に戻す。
	uint8_t			hurryCnt;		//急ぎカウンタ			魚を置いたら0に戻す。
	//
	uint8_t			boatStat;		//ボートの状態			BOAT_STAT_*
	uint8_t			boatCol;		//ボートの位置			0〜FIELD_COLS-1		人が乗っている位置で示す。従って、左向きの場合は0〜FIELD_COLS-2の範囲，右向きの場合は1〜FIELD_COLS-1の範囲である。
	uint8_t			boatDir;		//ボートの向き			0=左向き,1=右向き
	uint8_t			boatOfs;		//ボートの移動オフセット	ボートが移動する時は、即boatColを変化させて、boatOfsを(H__CELL+H__CELL__GAP-1)→0に変化させて行く。lowCnt,lowOfsとは変化順序が違う事に注意しろ。	向きを変えた瞬間は、即boatColとboatDirを変化させて、即boatOfs=0にする。移動アニメーションは行わない。
	uint8_t			boatAnm;		//汎用アニメーションカウンタ	置き,交換,次の魚,逃げ等のアニメーションカウンタとして使用する。
	uint8_t			TBL_Next[2];		//[0]=持っている魚,[1]=次の魚	CELL_TYPE_FISH*
	//
	uint8_t			infoStat;		//情報表示の状態		INFO_STAT_*		アザラシとサメの選択を行う。アザラシとサメはこれを見て行動する。両方が一時的に同時に出現する事も有り得る。(本物でも同じ)
	//
	uint8_t			sealStat;		//アザラシの状態		SEAL_STAT_*
	uint8_t			sealAnm;		//汎用アニメーションカウンタ
	//
	uint8_t			sharkStat;		//サメの状態			SHARK_STAT_*
	uint8_t			sharkAnm;		//汎用アニメーションカウンタ
	//
	uint8_t			cellAnm;		//汎用アニメーションカウンタ	浮上のアニメーションカウンタとして使用する。
	ST_Cell			TBL_Cell[FIELD_ROWS][FIELD_COLS];	//セル配列
	//
	ST_FishObj		TBL_FishObj[MAX_FISHOBJ];	//魚釣り上げオブジェクト
	ST_BubbleObj		TBL_BubbleObj[MAX_BUBBLEOBJ];	//泡消滅オブジェクト
	ST_ScoreObj		TBL_ScoreObj[MAX_SCOREOBJ];	//スコアオブジェクト
} ST_Game;
ST_Game stGame;		//唯一のインスタンス
/****************************************************************************
 *	
 ****************************************************************************/
//この列に置かれている、セルの数を求める。
int get_height(int col) {
	int row;
	for(row = 0; row < FIELD_ROWS; row++) {
		ST_Cell* pCell = &stGame.TBL_Cell[row][col];
		if(!pCell->type) { break; }
	}
	return row;
}
/*--------------------------------------------------------------------------*/
//一つの列に対して、一杯かの判定を行う。				Aボタンが押された時の置き開始判定に利用します。
int is_full_col(int col) {
	return get_height(col) >= ((FIELD_ROWS - 1) - stGame.lowCnt);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//フィールド全体に対して、一杯になっている列が有るかの判定を行う。	警告表示(サメ)の表示開始判定に利用します。
int is_full() {
	int col;
	for(col = 0; col < FIELD_COLS; col++) {
		if(is_full_col(col)) { return 1; }
	}
	return 0;
}
/*--------------------------------------------------------------------------*/
//ゲームオーバー判定
int is_gameover() {
	int col;
	for(col = 0; col < FIELD_COLS; col++) {
		if(get_height(col) >= (FIELD_ROWS - stGame.lowCnt)) { return 1; }
	}
	return 0;
}
/*--------------------------------------------------------------------------*/
//クリア判定
int is_clear() {
	int col;
	for(col = 0; col < FIELD_COLS; col++) {
		if(get_height(col)) { return 0; }
	}
	return 1;
}
/****************************************************************************
 *	
 ****************************************************************************/
//次の魚を抽選する。
int get_next() {
	int col, row, i, sum, val, cnt[6/*0=CELL_TYPE_FISH1〜5=CELL_TYPE_FISH6*/];
	//フィールド上に有る各魚の数を数える。
	memset(cnt, 0, sizeof cnt);
	sum = 0;
	for(row = 0; row < FIELD_ROWS; row++) {
		for(col = 0; col < FIELD_COLS; col++) {
			ST_Cell* pCell = &stGame.TBL_Cell[row][col];
			if(pCell->type) {
				switch(pCell->type) {
				default:DIE();
				case CELL_TYPE_FISH1:
				case CELL_TYPE_FISH2:
				case CELL_TYPE_FISH3:
				case CELL_TYPE_FISH4:
				case CELL_TYPE_FISH5:
				case CELL_TYPE_FISH6:
					i = pCell->type - CELL_TYPE_FISH1;
					if((unsigned)i >= ARRAY_SIZE(cnt)) { DIE(); }
				    #if 0
					//□魚の数に応じて重みを付ける場合	実際に試してみた所これでは簡単過ぎるので…
					cnt[i]++;
					sum++;
				    #else
					//□存在する魚は均等に抽選する場合	こっちの方が良いと思います。
					if(!cnt[i]) {
						cnt[i]++;
						sum++;
					}
				    #endif
					break;
				case CELL_TYPE_BUBBLE:
					/** no job **/
					break;
				}
			}
		}
	}
	if(!sum) { DIE(); }	//フィールドに魚が一匹も居ない状態で、当関数を呼び出してはいけない。
	//次の魚を抽選する。
	val = RND32_RANGE(seed, 0, sum);
	for(i = 0; i < ARRAY_SIZE(cnt); i++) {
		val -= cnt[i];
		if(val < 0) { return CELL_TYPE_FISH1 + i; }
	}
	DIE();	//バグ
}
/****************************************************************************
 *	
 ****************************************************************************/
//魚釣り上げオブジェクトを生成する。
void gen_fish_obj(int col, int row, int type) {
	int i;
	for(i = 0; i < MAX_FISHOBJ; i++) {
		ST_FishObj* pFishObj = &stGame.TBL_FishObj[i];
		if(!pFishObj->type) {
			memset(pFishObj, 0, sizeof(ST_FishObj));
			pFishObj->type = type;
			pFishObj->col = col;
			pFishObj->row = row;
			return;	//ここまで
		}
	}
}
/*--------------------------------------------------------------------------*/
//泡消滅オブジェクトを生成する。
void gen_bubble_obj(int col, int row) {
	int i;
	for(i = 0; i < MAX_BUBBLEOBJ; i++) {
		ST_BubbleObj* pBubbleObj = &stGame.TBL_BubbleObj[i];
		if(!pBubbleObj->type) {
			memset(pBubbleObj, 0, sizeof(ST_BubbleObj));
			pBubbleObj->type = CELL_TYPE_BUBBLE;
			pBubbleObj->col = col;
			pBubbleObj->row = row;
			return;	//ここまで
		}
	}
}
/*--------------------------------------------------------------------------*/
//スコアオブジェクトを生成する。
void gen_score_obj(int col, int score) {
	int i;
	for(i = 0; i < MAX_SCOREOBJ; i++) {
		ST_ScoreObj* pScoreObj = &stGame.TBL_ScoreObj[i];
		if(!pScoreObj->score) {
			memset(pScoreObj, 0, sizeof(ST_ScoreObj));
			pScoreObj->score = score;
			pScoreObj->col = col;
			return;	//ここまで
		}
	}
}
/****************************************************************************
 *	
 ****************************************************************************/
//魚釣り上げオブジェクトが残っているかを調べる。
int is_fish_obj_exists() {
	int i;
	for(i = 0; i < MAX_FISHOBJ; i++) {
		ST_FishObj* pFishObj = &stGame.TBL_FishObj[i];
	   #if 0
		if(pFishObj->type) { return 1; }
	   #else
		if(pFishObj->type && !((pFishObj->stat == FISHOBJ_STAT_JUMP) && pFishObj->anm >= (FISHOBJ_JUMP_TIME / 2/*調整可*/))) { return 1; }	//テンポを良くするために、ジャンプが峠を越えたら次の処理のアニメーションと進行進行させるようにした。
	   #endif
	}
	return 0;
}
/****************************************************************************
 *	
 ****************************************************************************/
static int do_erase_fish_subr(int col, int row, int type, unsigned char mark[FIELD_ROWS][FIELD_COLS]) {	//スタック消費量は約32以下。最大に再帰しても(6*9)*32で2KB以下で済むはず。
	int cnt = 0;
	//フィールド内で、この位置が走査済みでなければ…
	if(((unsigned)col < FIELD_COLS) && ((unsigned)row < FIELD_ROWS) && !mark[row][col]) {
		ST_Cell* pCell = &stGame.TBL_Cell[row][col];
		//同じ種類ならば…
		if(pCell->type == type) {
			//揃い数を増やす。
			cnt++;
			//この位置が走査済みである事をマークする。
			mark[row][col] = 1;
			//隣のセルに再帰する。
			cnt += do_erase_fish_subr(col - 1, row, type, mark);
			cnt += do_erase_fish_subr(col + 1, row, type, mark);
			cnt += do_erase_fish_subr(col, row - 1, type, mark);
			cnt += do_erase_fish_subr(col, row + 1, type, mark);
		}
	}
	return cnt;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int do_erase_fish() {
	int i, col, nFish = 0;
	//最上段の各セルについて…	魚釣り上げオブジェクトがボートに近い列から生成されるように、ボートに近い列から順に処理する。
	static_assert(FIELD_COLS == 6, "FIELD_COLSが想定値と異なります。");
	static const char TBL_ColOfs[] = {0,-1,1,-2,2,-3,3,-4,4,-5,5};
	for(i = 0; i < ARRAY_SIZE(TBL_ColOfs); i++) {
		col = stGame.boatCol + TBL_ColOfs[i];
		if((unsigned)col < FIELD_COLS) {
			ST_Cell* pCell = &stGame.TBL_Cell[0/*row*/][col];
			//魚ならば…
			if((pCell->type >= CELL_TYPE_FISH1) &&
			   (pCell->type <= CELL_TYPE_FISH6)) {
				//隣接数が3以上ならば…
				unsigned char mark[FIELD_ROWS][FIELD_COLS];
				memset(mark, 0, sizeof mark);
				if(do_erase_fish_subr(col, 0/*row*/, pCell->type, mark) >= 3) {
					int col2, row2;
					//マークされた各位置について…
					for(row2 = 0; row2 < FIELD_ROWS; row2++) {
						for(col2 = 0; col2 < FIELD_COLS; col2++) {
							if(mark[row2][col2]) {
								ST_Cell* pCell2 = &stGame.TBL_Cell[row2][col2];
								//魚釣り上げオブジェクトを生成する。
								gen_fish_obj(col2, row2, pCell2->type);
								//セルを空にする。
								memset(pCell2, 0, sizeof(ST_Cell));
								//魚釣り上げ数を加算する。
								nFish++;
							}
						}
					}
				}
			}
		}
	}
	//魚釣り上げ数を返す。
	return nFish;
}
/*--------------------------------------------------------------------------*/
int do_erase_bubble() {
	int col, nBubble = 0;
	//最上段の各セルについて…
	for(col = 0; col < FIELD_COLS; col++) {
		ST_Cell* pCell = &stGame.TBL_Cell[0/*row*/][col];
		//泡ならば…
		if(pCell->type == CELL_TYPE_BUBBLE) {
			//泡消滅オブジェクトを生成する。
			gen_bubble_obj(col, 0/*row*/);
			//セルを空にする。
			memset(pCell, 0, sizeof(ST_Cell));
			//泡消滅数を加算する。
			nBubble++;
		}
	}
	//泡消滅数を返す。
	return nBubble;
}
/****************************************************************************
 *	
 ****************************************************************************/
int do_raise(int testOnly) {
	int col, row, bRaise = 0;
	for(row = 1/*二段目から*/; row < FIELD_ROWS; row++) {
		for(col = 0; col < FIELD_COLS; col++) {
			ST_Cell* pCell1 = &stGame.TBL_Cell[row][col];
			//このセルが空でなければ…
			if(pCell1->type) {
				ST_Cell* pCell2 = &stGame.TBL_Cell[row - 1][col];
				//上のセルが空ならば…
				if(!pCell2->type) {
					//テストのみならばここまで。
					if(testOnly) { return 1; }
					//このセルを上へ浮上する。
					*pCell2 = *pCell1;
					memset(pCell1, 0, sizeof(ST_Cell));
					//浮上が発生した事をマークする。
					bRaise = 1;
				}
			}
		}
	}
	//浮上が発生したら1，浮上が発生しなければ0を返す。
	return bRaise;
}
/****************************************************************************
 *	
 ****************************************************************************/
//引き潮の状態を参照して、現在のフィールドオフセットを求める。
int field_offset() {
	return (H__CELL + H__CELL__GAP) * stGame.lowCnt + stGame.lowOfs;
}
/****************************************************************************
 *	
 ****************************************************************************/
void field_exec() {
	int i;
	//魚釣り上げオブジェクト
	for(i = 0; i < MAX_FISHOBJ; i++) {
		ST_FishObj* pFishObj = &stGame.TBL_FishObj[i];
		if(pFishObj->type) {
			switch(pFishObj->stat) {
			default:DIE();
			case FISHOBJ_STAT_FLASH:	//フラッシュ中
				if(++pFishObj->anm >= FISHOBJ_FLASH_TIME) {
					pFishObj->stat = FISHOBJ_STAT_VERT;
					pFishObj->anm = 0;
				}
				break;
			case FISHOBJ_STAT_VERT:		//上昇中
				if(pFishObj->row) {
					pFishObj->anm += 4/*調整可*/;
					if(pFishObj->anm >= (H__CELL + H__CELL__GAP)) {
						pFishObj->anm = 0;
						if(!--pFishObj->row) {
							pFishObj->stat = FISHOBJ_STAT_HORZ;
							pFishObj->anm = 0;
						}
					}
					break;	//ここまで
				} else {
					pFishObj->stat = FISHOBJ_STAT_HORZ;
					pFishObj->anm = 0;
				}
				/* FALLTHRU */
			case FISHOBJ_STAT_HORZ:		//水平移動中
				if(pFishObj->col != stGame.boatCol) {
					pFishObj->anm += 4/*調整可*/;
					if(pFishObj->anm >= (W__CELL + W__CELL__GAP)) {
						pFishObj->anm = 0;
						if(pFishObj->col < stGame.boatCol) {
							pFishObj->col++;
						} else {
							pFishObj->col--;
						}
						if(pFishObj->col == stGame.boatCol) {
							pFishObj->stat = FISHOBJ_STAT_WAIT;
							pFishObj->anm = 0;
						}
					}
					break;	//ここまで
				} else {
					pFishObj->stat = FISHOBJ_STAT_WAIT;
					pFishObj->anm = 0;
				}
				/* FALLTHRU */
			case FISHOBJ_STAT_WAIT:		//水面下待機中
				if(i) {
					ST_FishObj* pFishObj2 = &stGame.TBL_FishObj[i - 1];
					if(!pFishObj2->type ||
					  ((pFishObj2->stat == FISHOBJ_STAT_JUMP) && (pFishObj2->anm >= 2/*調整可*/))) {
						pFishObj->stat = FISHOBJ_STAT_JUMP;
						pFishObj->anm = 0;
					}
					break;	//ここまで
				} else {
					pFishObj->stat = FISHOBJ_STAT_JUMP;
					pFishObj->anm = 0;
				}
				/* FALLTHRU */
			case FISHOBJ_STAT_JUMP:		//ジャンプ中
				if(!i && !pFishObj->anm) {	//'一匹目'が'ジャンプ開始する時'に効果音を鳴らす。
					sound_play(0, sound_table[SND_SE_WAV3], 1);
				}
				if(++pFishObj->anm >= FISHOBJ_JUMP_TIME_CUT) {
					memset(pFishObj, 0, sizeof(ST_FishObj));
				}
				break;
			}
		}
	}
	//泡消滅オブジェクト
	for(i = 0; i < MAX_BUBBLEOBJ; i++) {
		ST_BubbleObj* pBubbleObj = &stGame.TBL_BubbleObj[i];
		if(pBubbleObj->type) {
			if(++pBubbleObj->anm >= (3 * 2/*調整可*/)) {
				memset(pBubbleObj, 0, sizeof(ST_BubbleObj));
			}
		}
	}
	//スコアオブジェクト
	for(i = 0; i < MAX_SCOREOBJ; i++) {
		ST_ScoreObj* pScoreObj = &stGame.TBL_ScoreObj[i];
		if(pScoreObj->score) {
			if(++pScoreObj->anm >= SCOREOBJ_TIME) {
				memset(pScoreObj, 0, sizeof(ST_ScoreObj));
			}
		}
	}
	//アザラシ
	{
		switch(stGame.sealStat) {
		default:DIE();
		case SEAL_STAT_HIDE:		//非表示中
			if(stGame.infoStat == INFO_STAT_SEAL) {
				stGame.sealStat = SEAL_STAT_APPEAR;
				stGame.sealAnm = 0;
				sound_play(2, sound_table[SND_SE_WARN], 1);
			}
			break;
		case SEAL_STAT_APPEAR:		//出現中
			if(++stGame.sealAnm >= SEAL_APPEAR_TIME) {
				stGame.sealStat = SEAL_STAT_SHOW;
				stGame.sealAnm = 0;
			}
			break;
		case SEAL_STAT_SHOW:		//表示中
			if(stGame.infoStat != INFO_STAT_SEAL) {
				stGame.sealStat = SEAL_STAT_DISAPPEAR;
				stGame.sealAnm = 0;
			}
			break;
		case SEAL_STAT_DISAPPEAR:	//消去中
			if(++stGame.sealAnm >= SEAL_DISAPPEAR_TIME) {
				stGame.sealStat = SEAL_STAT_HIDE;
				stGame.sealAnm = 0;
			}
			break;
		}
	}
	//サメ
	{
		switch(stGame.sharkStat) {
		default:DIE();
		case SHARK_STAT_HIDE:		//非表示中
			if(stGame.infoStat == INFO_STAT_SHARK) {
				stGame.sharkStat = SHARK_STAT_APPEAR;
				stGame.sharkAnm = 0;
				sound_play(2, sound_table[SND_SE_ELEC], 1);
			}
			break;
		case SHARK_STAT_APPEAR:		//出現中
			if(++stGame.sharkAnm >= SHARK_APPEAR_TIME) {
				stGame.sharkStat = SHARK_STAT_SHOW;
				stGame.sharkAnm = 0;
			}
			break;
		case SHARK_STAT_SHOW:		//表示中
			if(stGame.infoStat != INFO_STAT_SHARK) {
				stGame.sharkStat = SHARK_STAT_DISAPPEAR;
				stGame.sharkAnm = 0;
			}
			break;
		case SHARK_STAT_DISAPPEAR:	//消去中
			if(++stGame.sharkAnm >= SHARK_DISAPPEAR_TIME) {
				stGame.sharkStat = SHARK_STAT_HIDE;
				stGame.sharkAnm = 0;
			}
			break;
		}
	}
}
/****************************************************************************
 *	
 ****************************************************************************/
//フィールドを描く。
void field_draw() {
	int iPri, x, y, w, h, iSprNo, iParam;
	//背景
	{
		iPri	= PRI__BG;
		//空背景
		x	= 0;
		y	= 0;				//←┐
		w	= DISP_X;			//　├クリッピング範囲は、画面上端から、海背景の上端まで。
		h	= Y__BG__SEA + field_offset();	//←┘
		iSprNo	= SPR_SKY;
		iParam	= DRW_NOMAL;
		CbkQue_render_set_draw_area(pCbkQue, iPri, &render, x, y, x + w, y + h);
		CbkQue_sprite_draw(pCbkQue, iPri, 0, 0, iSprNo, iParam);	//描画は画面全体に対して行う。
		//海背景
		x	= 0;
		y	= Y__BG__SEA + field_offset();	//←┐
		w	= DISP_X;			//　├クリッピング範囲は、海背景の上端から、画面下端まで。
		h	= DISP_Y - y;			//←┘
		iSprNo	= SPR_SEA;
		iParam	= DRW_NOMAL;
		CbkQue_render_set_draw_area(pCbkQue, iPri, &render, x, y, x + w, y + h);
		CbkQue_sprite_draw(pCbkQue, iPri, 0, 0, iSprNo, iParam);	//描画は画面全体に対して行う。
		//クリッピングを解除する。
		CbkQue_render_set_draw_area_full(pCbkQue, iPri, &render);	//忘れないで!!
	}
	//波
	{
		iPri	= PRI__WAVE;
		x	= ((now / 4/*調整可*/) % W__WAVE) - W__WAVE;
		y	= Y__WAVE + field_offset();
		{
			int ofs = ((now / 8/*調整可*/) % 4) - 1;	//┐
			if(ofs == 2) { ofs = 0; }			//├波は全体を縦に揺らす事にした。波スプライトは上下1ピクセルずらしても背景の切れ目は見えません。それを利用して波の上下アニメーションを行います。
			y += ofs;					//┘
		}
		iSprNo	= SPR_WAVE;
		iParam	= DRW_NOMAL;
		do {
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			x += W__WAVE;
		} while(x < DISP_X);
	}
	//セル
	{
		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->type) {
					iPri	= PRI__CELL;	//この下の処理で優先度を調整する可能性が有るので、毎回ここで基本優先度を代入しておく。
					x	= X__CELL + ((W__CELL + W__CELL__GAP) * col);
					y	= Y__CELL + ((H__CELL + H__CELL__GAP) * row) + field_offset();
					if(pCell->type == CELL_TYPE_BUBBLE) {
						iSprNo	= SPR_BUBBLE1 + ((((now / 2/*調整可*/) + col) / 2/*調整可*/) % 3);
						//                                            ~~~~~~~~~~~~~~~~~~~~~	//泡は列ごとに少しづつアニメーションをずらす事にした。必須ではない。
					} else {
						iSprNo	= SPR_FISH1 + (pCell->type - CELL_TYPE_FISH1);
					}
					iParam	= DRW_NOMAL;
					//置き中の列ならば…
					if((stGame.boatStat == BOAT_STAT_PUT) && (col == stGame.boatCol)) {
						int ofs = (((Y__CELL - Y__NEXT1) * stGame.boatAnm) / PUT_TIME) - ((Y__CELL - Y__NEXT1) - (H__CELL + H__CELL__GAP));	//┬押し下げ量が上端セルにめり込んだ量
						//        │←─────────置き下げ量────────→│   │←──────上端セルより上の高さ ────→│	//┘
						if(ofs > 0) { y += ofs; }
					//敗北中ならば…
					} else if(stGame.boatStat == BOAT_STAT_LOSE) {
						if(pCell->type == CELL_TYPE_BUBBLE) {
							int i = stGame.boatAnm / 2/*調整可*/;
							if(i > 2) { continue; }	//描画無し
							iSprNo = SPR_BURST1 + i;
							iPri++;	//逃げて行く魚よりも目立たないので手前に描画する事にした。
						} else {
							double speed;	//逃げる速度
							switch(pCell->type) {
							default:DIE();
							case CELL_TYPE_FISH1: speed = 2.5; break;	//タイ
							case CELL_TYPE_FISH2: speed = 1.3; break;	//フグ
							case CELL_TYPE_FISH3: speed = 1.0; break;	//タコ
							case CELL_TYPE_FISH4: speed = 2.2; break;	//イカ
							case CELL_TYPE_FISH5: speed = 1.9; break;	//ハゼ
							case CELL_TYPE_FISH6: speed = 1.6; break;	//ヒラメ
							}
							if(col <= ((FIELD_COLS - 1) / 2)) {	//左半分
								x -= speed * stGame.boatAnm;
							} else {				//右半分
								iParam	= DRW_REVX;
								x += (W__CELL - 1);
								x += speed * stGame.boatAnm;
							}
						}
					//浮上中ならば…
					} else if(row && !stGame.TBL_Cell[row - 1][col].type && !is_fish_obj_exists()) {
						y -= stGame.cellAnm;
					//それ以外ならば…
					} else {
						int ofs = ((((now / 4/*調整可*/) + col) / 2/*調整可*/) % 4) - 1;	//┐
						if(ofs == 2) { ofs = 0; }						//├泡も魚も列ごとに少しずつ縦位置をずらす事にした。必須ではない。尚、揃った後の魚の描画には影響させない。(揃った後の魚は、セルとしての描画ではなく、オブジェクトとして描画するから)
						y += ofs;								//┘
					}
					CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				}
			}
		}
	}
	//電気
	if(stGame.boatStat != BOAT_STAT_WIN) {	//クリア後は電気は表示しない。
		iPri	= PRI__ELEC;
		//クリッピングを設定する。
		x	= X__FIELD;
		y	= Y__FIELD;
		w	= W__FIELD;
		h	= H__FIELD;
		CbkQue_render_set_draw_area(pCbkQue, iPri, &render, x, y, x + w, y + h);
		//電気を描く。
		x	= ((now * 1/*調整可*/) % W__ELEC) + (X__FIELD - W__ELEC);
		y	= Y__ELEC;	//'Y__ELEC+field_offset()'ではない事に注意せよ。
		iSprNo	= SPR_ELEC;
		iParam	= DRW_NOMAL;
		do {
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			x += W__ELEC;
		} while(x < (X__FIELD + W__FIELD));
		//クリッピングを解除する。
		CbkQue_render_set_draw_area_full(pCbkQue, iPri, &render);	//忘れないで!!
	}
	//フィールド枠
	{
		int c = 3;	//←非警告中は黒、↓警告中は点滅。
		if(!is_clear() && !is_gameover() && is_full()) { c = UPDOWN_MOTION(now,4); }
		iPri	= PRI__FIELD;
		x	= X__FIELD;
		y	= Y__FIELD;
		w	= W__FIELD;
		h	= H__FIELD;
		CbkQue_render_rectangle_fill(pCbkQue, iPri, &render, x,     y, -W__FIELD__BORDER, h, c);	//左側
		CbkQue_render_rectangle_fill(pCbkQue, iPri, &render, x + w, y,  W__FIELD__BORDER, h, c);	//右側
	}
	//魚釣り上げオブジェクト
	{
		int i;
		for(i = 0; i < MAX_FISHOBJ; i++) {
			ST_FishObj* pFishObj = &stGame.TBL_FishObj[i];
			if(pFishObj->type) {
				iPri	= PRI__FISHOBJ;
				x	= X__CELL + ((W__CELL + W__CELL__GAP) * pFishObj->col);
				y	= Y__CELL + ((H__CELL + H__CELL__GAP) * pFishObj->row) + field_offset();
				iSprNo	= SPR_FISH1 + (pFishObj->type - CELL_TYPE_FISH1);
				iParam	= DRW_NOMAL;
				switch(pFishObj->stat) {
				default:DIE();
				case FISHOBJ_STAT_FLASH:	//フラッシュ中
					if(!(pFishObj->anm & 1)) {
						iSprNo += (SPR_FLASH1 - SPR_FISH1);
					}
					break;
				case FISHOBJ_STAT_VERT:		//上昇中
					y -= pFishObj->anm;
					break;
				case FISHOBJ_STAT_HORZ:		//水平移動中
					if(pFishObj->col < stGame.boatCol) {
						iParam	= DRW_REVX;
						x += (W__CELL - 1);
						x += pFishObj->anm;
					} else {
						x -= pFishObj->anm;
					}
					break;
				case FISHOBJ_STAT_WAIT:		//水面下待機中
					if(stGame.boatDir == 0) {
						iParam	= DRW_REVX;
						x += (W__CELL - 1);
					}
					break;
				case FISHOBJ_STAT_JUMP:		//ジャンプ中
					{
						int tx, dx;
						if(stGame.boatDir == 0) {
							tx = X__BOAT + ((W__CELL + W__CELL__GAP) * (stGame.boatCol + 1));
						} else {
							tx = X__BOAT + ((W__CELL + W__CELL__GAP) * (stGame.boatCol - 1));
						}
						dx = tx - x;
						x += (    (  dx * pFishObj->anm) / FISHOBJ_JUMP_TIME_CUT                       );
						y -= (sin((M_PI * pFishObj->anm) / FISHOBJ_JUMP_TIME    ) * FISHOBJ_JUMP_HEIGHT);
						if(stGame.boatDir == 0) {
							iParam	= DRW_REVX;
							x += (W__CELL - 1);
						}
						if(pFishObj->anm >= (FISHOBJ_JUMP_TIME / 2)) {
							iPri = PRI__BOAT - 1;	//峠を越えたらボートの後ろの優先度にする。
						}
					}
					break;
				}
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			}
		}
	}
	//泡消滅オブジェクト
	{
		int i;
		for(i = 0; i < MAX_BUBBLEOBJ; i++) {
			ST_BubbleObj* pBubbleObj = &stGame.TBL_BubbleObj[i];
			if(pBubbleObj->type) {
				iPri	= PRI__BUBBLEOBJ;
				x	= X__CELL + ((W__CELL + W__CELL__GAP) * pBubbleObj->col);
				y	= Y__CELL + ((H__CELL + H__CELL__GAP) * pBubbleObj->row) + field_offset();
				{
					int j = pBubbleObj->anm / 2/*調整可*/;
					if(j > 2) { j = 2; }
					iSprNo	= iSprNo = SPR_BURST1 + j;
				}
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			}
		}
	}
	//スコアオブジェクト
	{
		int i;
		for(i = 0; i < MAX_SCOREOBJ; i++) {
			ST_ScoreObj* pScoreObj = &stGame.TBL_ScoreObj[i];
			if(pScoreObj->score) {
				iPri	= PRI__SCOREOBJ;
				x	= X__CELL + ((W__CELL + W__CELL__GAP) * pScoreObj->col) + ((W__CELL + W__CELL__GAP) / 2);
				y	= Y__NEXT2 + field_offset() - min((int)pScoreObj->anm, (int)7/*調整可*/);
				{
					int xy = render_printf_framed(
						&null_render,
						0,
						0,
						3,
						0,
						"%d",
						pScoreObj->score);
					w = LOHALF(xy);
					h = HIHALF(xy);
					x -= w / 2;
					CbkQue_render_printf_framed(pCbkQue,
						PRI__SCOREOBJ,
						&render,
						x,
						y,
						3,
						0x330,
						"%d",
						pScoreObj->score);
				}
			}
		}
	}
	//アザラシ
	{
		switch(stGame.sealStat) {
		default:DIE();
		case SEAL_STAT_HIDE:		//非表示中
			/** no job **/
			break;
		case SEAL_STAT_APPEAR:		//出現中
		case SEAL_STAT_SHOW:		//表示中
		case SEAL_STAT_DISAPPEAR:	//消去中
			iPri	= PRI__INFO;
			x	= X__INFO__LEFT;
			y	= Y__INFO__LEFT;
			switch(stGame.sealStat) {
			case SEAL_STAT_APPEAR:		//出現中
				{
					double t = (double)stGame.sealAnm * (double)(M_PI_2 / SEAL_APPEAR_TIME);	//0→π/2
					y = (y + 70/*調整可*/) - (sin(t) * 70/*調整可*/);
				}
				break;
			case SEAL_STAT_SHOW:		//表示中
				{
					int ofs = ((now / 4/*調整可*/) % 4) - 1;
					if(ofs == 2) { ofs = 0; }					//├泡も魚も列ごとに少しずつ縦位置をずらす事にした。必須ではない。尚、揃った後の描画には影響させない。
					y += ofs;							//┘
				}
				break;
			case SEAL_STAT_DISAPPEAR:	//消去中
				{
					double t = (double)(SEAL_DISAPPEAR_TIME - stGame.sealAnm) * (double)(M_PI_2 / SEAL_DISAPPEAR_TIME);	//π/2→0
					y = (y + 70/*調整可*/) - (sin(t) * 70/*調整可*/);
				}
				break;
			}
			iSprNo	= SPR_SEAL1 + ((now / 2/*調整可*/) % 4);
			iParam	= DRW_NOMAL;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			x	= X__INFO__RIGHT;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			break;
		}
	}
	//サメ
	{
		switch(stGame.sharkStat) {
		default:DIE();
		case SHARK_STAT_HIDE:		//非表示中
			/** no job **/
			break;
		case SHARK_STAT_APPEAR:		//出現中
		case SHARK_STAT_SHOW:		//表示中
		case SHARK_STAT_DISAPPEAR:	//消去中
			iPri	= PRI__INFO;
			x	= X__INFO__LEFT;
			y	= Y__INFO__LEFT;
			switch(stGame.sharkStat) {
			case SHARK_STAT_APPEAR:		//出現中
				{
					double t = (double)stGame.sharkAnm * (double)(M_PI_2 / SHARK_APPEAR_TIME);	//0→π/2
					y = (y - 70/*調整可*/) + (sin(t) * 70/*調整可*/);
				}
				break;
			case SHARK_STAT_SHOW:		//表示中
				{
					int ofs = ((now / 4/*調整可*/) % 4) - 1;
					if(ofs == 2) { ofs = 0; }					//├泡も魚も列ごとに少しずつ縦位置をずらす事にした。必須ではない。尚、揃った後の描画には影響させない。
					y += ofs;							//┘
				}
				break;
			case SHARK_STAT_DISAPPEAR:	//消去中
				{
					double t = (double)(SHARK_DISAPPEAR_TIME - stGame.sharkAnm) * (double)(M_PI_2 / SHARK_DISAPPEAR_TIME);	//π/2→0
						y = (y - 70/*調整可*/) + (sin(t) * 70/*調整可*/);
				}
				break;
			}
			iSprNo	= SPR_SHARK1;
			iParam	= DRW_NOMAL;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			x	= X__INFO__RIGHT;
			iSprNo	= SPR_SHARK2;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			break;
		}
	}
	//スコア
	{
		CbkQue_render_printf_framed(pCbkQue,
			SHRT_MAX,
			&render,
			0,
			0,
			3,
			0x330,
			"1P\n%06d",
			stGame.score);
		CbkQue_render_printf_framed(pCbkQue,
			SHRT_MAX,
			&render,
			0,
			DISP_Y-11,
			3,
			0x330,
			"HI\n%06d",
			hiscore);
	}
}
/****************************************************************************
 *	boat_exec()
 ****************************************************************************/
void boat_exec() {
	//開始前
	{
		//開始前の処理
		if(stGame.boatStat == BOAT_STAT_READY) {
			/** no job **/
		}
	}
	//移動
	{
	    //{{クリア表示中のホームポジションへ移動
		if((stGame.boatStat == BOAT_STAT_WIN) && !stGame.boatOfs) {
			if(stGame.boatCol < 2) { goto L_WIN_RI; }
			if(stGame.boatCol > 3) { goto L_WIN_LF; }
			if((stGame.boatCol == 2) && (stGame.boatDir == 1)) { goto L_WIN_RI; }
			if((stGame.boatCol == 3) && (stGame.boatDir == 0)) { goto L_WIN_LF; }
		}
	    //}}クリア表示中のホームポジションへ移動
		switch(stGame.boatStat) {
		default:DIE();
		case BOAT_STAT_READY:	//開始前
		case BOAT_STAT_PUT:	//置き中
		case BOAT_STAT_GET:	//釣り中
		case BOAT_STAT_WIN:	//勝利中
		case BOAT_STAT_LOSE:	//敗北中
			/** no job **/
			break;
		case BOAT_STAT_NORMAL:	//通常中
		case BOAT_STAT_CHANGE:	//交換中
		case BOAT_STAT_NEXT:	//次の魚
			//左移動開始判定
			if(joy & TRG_LF) {
				if(stGame.boatCol > 0) {
					sound_play(0, sound_table[SND_SE_MOVE], 1);
L_WIN_LF://{{クリア表示中のホームポジションへ移動}}
					stGame.boatCol--;
					if(stGame.boatDir == 0) {
						stGame.boatOfs = H__CELL + H__CELL__GAP;	//この後の移動中の処理ですぐに減少する。
					} else {
						stGame.boatDir = 0;
						stGame.boatOfs = 0;
					}
				}
			}
			//右移動開始判定
			if(joy & TRG_RI) {
				if(stGame.boatCol < FIELD_COLS - 1) {
					sound_play(0, sound_table[SND_SE_MOVE], 1);
L_WIN_RI://{{クリア表示中のホームポジションへ移動}}
					stGame.boatCol++;
					if(stGame.boatDir == 1) {
						stGame.boatOfs = H__CELL + H__CELL__GAP;	//この後の移動中の処理ですぐに減少する。
					} else {
						stGame.boatDir = 1;
						stGame.boatOfs = 0;
					}
				}
			}
			break;
		}
		//移動中の処理
		if(stGame.boatOfs) {
			stGame.boatOfs -= 2/*調整可*/;
		}
	}
	//交換
	{
		switch(stGame.boatStat) {
		default:DIE();
		case BOAT_STAT_READY:	//開始前
		case BOAT_STAT_PUT:	//置き中
		case BOAT_STAT_GET:	//釣り中
		case BOAT_STAT_WIN:	//勝利中
		case BOAT_STAT_LOSE:	//敗北中
			/** no job **/
			break;
		case BOAT_STAT_NORMAL:	//通常中
		case BOAT_STAT_CHANGE:	//交換中
		case BOAT_STAT_NEXT:	//次の魚
			if(joy & TRG_B) {
				stGame.boatStat = BOAT_STAT_CHANGE;
				stGame.boatAnm = 0;	//この後の交換中の処理ですぐに増加する。
				XCHG(stGame.TBL_Next[0], stGame.TBL_Next[1]);
			}
			break;
		}
		//交換中の処理
		if(stGame.boatStat == BOAT_STAT_CHANGE) {
			if(!stGame.boatAnm) {
				sound_play(0, sound_table[SND_SE_CHANGE], 1);
			}
			if(++stGame.boatAnm >= CHANGE_TIME) {
				stGame.boatStat = BOAT_STAT_NORMAL;
				stGame.boatAnm = 0;
			}
		}
	}
	//置き
	{
		switch(stGame.boatStat) {
		default:DIE();
		case BOAT_STAT_READY:	//開始前
		case BOAT_STAT_PUT:	//置き中
		case BOAT_STAT_GET:	//釣り中
		case BOAT_STAT_WIN:	//勝利中
		case BOAT_STAT_LOSE:	//敗北中
			/** no job **/
			break;
		case BOAT_STAT_NORMAL:	//通常中
		case BOAT_STAT_CHANGE:	//交換中
		case BOAT_STAT_NEXT:	//次の魚
			//強制置き判定
			if(++stGame.hurryCnt >= FORCE_TIME) {	//強制置き判定を置き開始判定よりも先に判定する事に注意せよ。もし逆にすると一杯の列でAボタンを連打する事でhurryCntのカウントアップを回避して延命出来てしまう。
				stGame.boatStat = BOAT_STAT_PUT;
				stGame.boatAnm = 0;	//この後の交換中の処理ですぐに増加する。
				stGame.hurryCnt = 0;
			//置き開始判定
			} else if(joy & TRG_A) {
				//この列が一杯でなければ…
				if(!is_full_col(stGame.boatCol)) {
					stGame.boatStat = BOAT_STAT_PUT;
					stGame.boatAnm = 0;	//この後の交換中の処理ですぐに増加する。
					stGame.hurryCnt = 0;
				} else {
					sound_play(0, sound_table[SND_SE_ERR], 1);
				}
			}
			break;
		}
		//置き中の処理
		if(stGame.boatStat == BOAT_STAT_PUT) {
			if(stGame.boatAnm < PUT_TIME) {
				stGame.boatAnm++;
				if(stGame.boatAnm == (PUT_TIME / 2)) {
					sound_play(0, sound_table[SND_SE_WAV1], 1);
				}
			}
		}
	}
	//釣り
	{
		//釣り中の処理
		if(stGame.boatStat == BOAT_STAT_GET) {
			/** no job **/
		}
	}
	//次の魚
	{
		//次の魚の処理
		if(stGame.boatStat == BOAT_STAT_NEXT) {
			if(++stGame.boatAnm >= NEXT_TIME) {
				//※TODO:
				stGame.boatStat = BOAT_STAT_NORMAL;
				stGame.boatAnm = 0;
			}
		}
	}
	//逃げ
	{
		//敗北中の処理
		if(stGame.boatStat == BOAT_STAT_LOSE) {
			stGame.boatAnm++;	//魚が逃げるアニメーションのために必要
		}
	}

}
/****************************************************************************
 *	
 ****************************************************************************/
//ボートを描く。
void boat_draw() {
	int iPri, x, y, iSprNo, iParam;
	//ボート
	{
		iPri	= PRI__BOAT;
		x	= X__BOAT + ((W__CELL + W__CELL__GAP) * stGame.boatCol);
		y	= Y__BOAT + field_offset();
		if(stGame.boatDir == 0) {	//左向き
			x += stGame.boatOfs;
			iParam	= DRW_NOMAL;
		} else {			//右向き
			x += W__BOAT - 1;		//┬DRW_REVXによるスプライト座標の調整を相殺するため
			x -= W__CELL + W__CELL__GAP;	//┘
			x -= stGame.boatOfs;
			iParam	= DRW_REVX;
		}
		switch(stGame.boatStat) {
		default:DIE();
		case BOAT_STAT_READY:	//開始前
		case BOAT_STAT_NORMAL:	//通常中
		case BOAT_STAT_CHANGE:	//交換中
		case BOAT_STAT_PUT:	//置き中
		case BOAT_STAT_GET:	//釣り中
		case BOAT_STAT_NEXT:	//次の魚
			iSprNo	= SPR_BOAT;
			break;
		case BOAT_STAT_WIN:	//勝利中
			iSprNo	= SPR_BOAT_WIN1 + ((now / 4) % 4);
			break;
		case BOAT_STAT_LOSE:	//敗北中
			iSprNo	= SPR_BOAT_LOSE1 + ((now / 4) % 2);
			break;
		}
		CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
	}
	//矢印
	{
		switch(stGame.boatStat) {
		default:DIE();
		case BOAT_STAT_READY:	//開始前
		case BOAT_STAT_PUT:	//置き中
		case BOAT_STAT_GET:	//釣り中
		case BOAT_STAT_WIN:	//勝利中
		case BOAT_STAT_LOSE:	//敗北中
			/** no job **/
			break;
		case BOAT_STAT_NORMAL:	//通常中
		case BOAT_STAT_CHANGE:	//交換中
		case BOAT_STAT_NEXT:	//次の魚
			{
				iPri	= PRI__ARROW;
				x	= X__ARROW + ((W__CELL + W__CELL__GAP) * stGame.boatCol);
				y	= Y__ARROW + field_offset();
				if(stGame.boatDir == 0) {	//左向き
					x += stGame.boatOfs;
				} else {			//右向き
					x -= stGame.boatOfs;
				}
				iSprNo	= SPR_ARROW1 + ((now / 4) % 3);
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			}
			break;
		}
	}
	{
		int x1, x2, y1, y2, x3, y3, iSprNo1, iSprNo2;
		x1	= X__NEXT1 + ((W__CELL + W__CELL__GAP) * stGame.boatCol);	//┬持っている魚の基本位置
		y1	= Y__NEXT1 + field_offset();					//┘
		x2	= X__NEXT2 + ((W__CELL + W__CELL__GAP) * stGame.boatCol);	//┬次の魚の基本位置
		y2	= Y__NEXT2 + field_offset();					//┘
		x3	= X__CELL + ((W__CELL + W__CELL__GAP) * stGame.boatCol);	//┬ボートの直下のセルの位置
		y3	= Y__CELL + field_offset();					//┘
		if(stGame.boatDir == 0) {	//左向き
			x1 += stGame.boatOfs;
			x2 += stGame.boatOfs;
			x2 += (W__CELL + W__CELL__GAP);
			x3 += stGame.boatOfs;
		} else {			//右向き
			x1 -= stGame.boatOfs;
			x2 -= stGame.boatOfs;
			x2 -= (W__CELL + W__CELL__GAP);
			x3 -= stGame.boatOfs;
		}
		iSprNo1	= SPR_FISH1 + (stGame.TBL_Next[0] - CELL_TYPE_FISH1);		//持っている魚のスプライト
		iSprNo2	= SPR_FISH1 + (stGame.TBL_Next[1] - CELL_TYPE_FISH1);		//次の魚のスプライト
		//持っている魚
		{
			iPri	= PRI__NEXT1;
			switch(stGame.boatStat) {
			default:DIE();
			case BOAT_STAT_GET:	//釣り中
			case BOAT_STAT_WIN:	//勝利中
			case BOAT_STAT_LOSE:	//敗北中
				/** no job **/
				break;
			case BOAT_STAT_READY:	//開始前
			case BOAT_STAT_NORMAL:	//通常中
				x	= x1;
				y	= y1;
				if(stGame.hurryCnt >= HURRY_TIME) {	//急ぎ中ならば持っている魚を上下に揺らす。
					int ofs = ((now / 2/*調整可*/) % 4) - 1;
					if(ofs == 2) { ofs = 0; }
					y += ofs;
				}
				iSprNo	= iSprNo1;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			case BOAT_STAT_PUT:	//置き中
				x	= ((x3 * stGame.boatAnm) + (x1 * (PUT_TIME - stGame.boatAnm))) / PUT_TIME;	//┬持っている魚の基本位置から、ボートの直下のセルの位置へ
				y	= ((y3 * stGame.boatAnm) + (y1 * (PUT_TIME - stGame.boatAnm))) / PUT_TIME;	//┘
				iSprNo	= iSprNo1;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			case BOAT_STAT_CHANGE:	//交換中
				x	= ((x1 * stGame.boatAnm) + (x2 * (CHANGE_TIME - stGame.boatAnm))) / CHANGE_TIME;	//┬次の魚の基本位置から、持っている魚の基本位置へ
				y	= ((y1 * stGame.boatAnm) + (y2 * (CHANGE_TIME - stGame.boatAnm))) / CHANGE_TIME;	//┘
				iSprNo	= iSprNo1;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			case BOAT_STAT_NEXT:	//次の魚
				x	= ((x1 * stGame.boatAnm) + (x2 * (NEXT_TIME - stGame.boatAnm))) / NEXT_TIME;	//┬次の魚の基本位置から、持っている魚の基本位置へ
				y	= ((y1 * stGame.boatAnm) + (y2 * (NEXT_TIME - stGame.boatAnm))) / NEXT_TIME;	//┘
				iSprNo	= iSprNo1;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			}
		}
		//次の魚
		{
			iPri	= PRI__NEXT1;
			switch(stGame.boatStat) {
			default:DIE();
			case BOAT_STAT_WIN:	//勝利中
			case BOAT_STAT_LOSE:	//敗北中
				/** no job **/
				break;
			case BOAT_STAT_READY:	//開始前
			case BOAT_STAT_NORMAL:	//通常中
			case BOAT_STAT_PUT:	//置き中
			case BOAT_STAT_NEXT:	//次の魚
			case BOAT_STAT_GET:	//釣り中
				x	= x2;
				y	= y2;
				iSprNo	= iSprNo2;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			case BOAT_STAT_CHANGE:	//交換中
				x	= ((x2 * stGame.boatAnm) + (x1 * (CHANGE_TIME - stGame.boatAnm))) / CHANGE_TIME;	//┬持っている魚の基本位置から、次の魚の基本位置へ
				y	= ((y2 * stGame.boatAnm) + (y1 * (CHANGE_TIME - stGame.boatAnm))) / CHANGE_TIME;	//┘
				iSprNo	= iSprNo1;
				iParam	= DRW_NOMAL;
				CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
				break;
			}
		}
	}
	//急ぎ
	if(stGame.hurryCnt >= HURRY_TIME) {	//急ぎ中ならば持っている魚を上下に揺らす。
		iPri	= PRI__HURRY;
		x	= X__HURRY + ((W__CELL + W__CELL__GAP) * stGame.boatCol);
		y	= Y__HURRY + field_offset();
		if(stGame.boatDir == 0) {	//左向き
			x += stGame.boatOfs;
		} else {			//右向き
			x -= stGame.boatOfs;
			x += 19/*見た目で調整した*/;
		}
		iParam	= DRW_NOMAL;
		if(stGame.hurryCnt >= (FORCE_TIME - SEC(1.0))) {
			if(stGame.hurryCnt == (FORCE_TIME - SEC(1.0))) {
				sound_play(1, sound_table[SND_SE_HURRY], 1);
			}
			iSprNo	= SPR_HURRY1;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
		} else if(stGame.hurryCnt >= (FORCE_TIME - SEC(2.0))) {
			if(stGame.hurryCnt == (FORCE_TIME - SEC(2.0))) {
				sound_play(1, sound_table[SND_SE_HURRY], 1);
			}
			iSprNo	= SPR_HURRY2;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
		} else if(stGame.hurryCnt >= (FORCE_TIME - SEC(3.0))) {
			if(stGame.hurryCnt == (FORCE_TIME - SEC(3.0))) {
				sound_play(1, sound_table[SND_SE_HURRY], 1);
			}
			iSprNo	= SPR_HURRY3;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
		} else {
			/** no job **/
		}
	}
}
/****************************************************************************
 *	
 ****************************************************************************/
//1ステージの処理
int stage_run() {
	int col, row;
	//初期化
	{
		//初期配置
		{
			const int level = stGame.stage/*1〜*/ - 1;
			int initLow, initHeight, fishLimit;
			initLow = level / 5/*調整可*/;
			if(initLow > FIELD_ROWS - 4) { initLow = FIELD_ROWS - 4; }
			initHeight = 3 + (level / 3/*調整可*/);
			if(initHeight > (FIELD_ROWS - initLow) - 2) { initHeight = (FIELD_ROWS - initLow) - 2; }
			fishLimit  = CELL_TYPE_FISH4 + (level / 2/*調整可*/);
			if(fishLimit > CELL_TYPE_FISH6) { fishLimit = CELL_TYPE_FISH6; }
			stGame.lowCnt = initLow;
			for(;;) {
				int bPut = 0;
				for(row = 0; row < initHeight; row++) {
					for(col = 0; col < FIELD_COLS; col++) {
						ST_Cell* pCell = &stGame.TBL_Cell[row][col];
						if(!pCell->type) {
							if(!row) {
								pCell->type = RND32_RANGE(seed, CELL_TYPE_FISH1, fishLimit + 1);
							} else {
								pCell->type = RND32_RANGE(seed, CELL_TYPE_FISH1, fishLimit + 2);
								if(pCell->type > fishLimit) { pCell->type = CELL_TYPE_BUBBLE; }
							}
							bPut = 1;
						}
					}
				}
				if(!bPut) { break; }
				for(;;) {
					if(!do_erase_fish()) { break; }
					for(;;) {
						while(do_raise(0/*testOnly*/)) { /** no job **/ }
						if(!do_erase_bubble()) { break; }
					}
				}
				memset(stGame.TBL_FishObj, 0, sizeof stGame.TBL_FishObj);
				memset(stGame.TBL_BubbleObj, 0, sizeof stGame.TBL_BubbleObj);
			}
		}
		//初期位置
		stGame.boatStat = BOAT_STAT_READY;
		stGame.boatCol = (FIELD_COLS - 1) / 2;
		stGame.TBL_Next[0] = get_next();
		stGame.TBL_Next[1] = get_next();
	}
	//サイレン
	sound_play(0, sound_table[SND_SE_SILEN], 1);
	//ステージスタート
	{
		int i;
		for(i = 0; i < SEC(1.9); i++) {
			schedule();
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
			{
				int x, y, w, h;
				x = render_printf_framed(
					&null_render,
					0,
					0,
					1,
					0,
					(i < SEC(1.0)) ? "STAGE %d" : "START!",
					stGame.stage);
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) - h;
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					1,
					0x230,
					(i < SEC(1.0)) ? "STAGE %d" : "START!",
					stGame.stage);
			}
		}
		{
			stGame.boatStat = BOAT_STAT_NORMAL;
			stGame.boatAnm = 0;
		}
	}
	{
		//メインBGM開始
		extern /*const*/ unsigned char MAIN_BGM[];
		PlayMusic(MAIN_BGM);
	}
	//ゲームループ
	for(;;) {
		//アザラシ・サメ出現判定。
		if(stGame.putCnt >= LOW_WARN_PUT_CNT) {	//アザラシの方が優先度が高いです。
			stGame.infoStat = INFO_STAT_SEAL;
		} else if(is_full()) {
			stGame.infoStat = INFO_STAT_SHARK;
		} else {
			stGame.infoStat = 0;
		}
		//魚を置くまでのループ
		for(;;) {
			schedule();
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
			if((stGame.boatStat == BOAT_STAT_PUT) && (stGame.boatAnm >= PUT_TIME)) { break; }
		}
		//魚を置く処理
		{
			//この列を一段下へシフトする。
			col = stGame.boatCol;
			for(row = FIELD_ROWS - 1; row > 0; row--) {
				stGame.TBL_Cell[row][col] = stGame.TBL_Cell[row - 1][col];
			}
			//魚を置く。
			stGame.TBL_Cell[row][col].type = stGame.TBL_Next[0];
			//ゲームオーバ判定	もしいま置いた魚で三匹揃ったとしても、揃った魚が消滅する前にゲームオーバーになる仕様です。
			if(is_gameover()) { goto L_GAMEOVER; }
		    #if 0 //{{不要:置いた魚によって危険と揃い浮上が同時に発生する時は警告は無い方が判り易いから。
			//アザラシ・サメ出現判定。
			if(stGame.putCnt >= LOW_WARN_PUT_CNT) {	//アザラシの方が優先度が高いです。
				stGame.infoStat = INFO_STAT_SEAL;
			} else if(is_full()) {
				stGame.infoStat = INFO_STAT_SHARK;
			} else {
				stGame.infoStat = 0;
			}
		    #endif//}}不要:置いた魚によって危険と揃い浮上が同時に発生する時は警告は無い方が判り易いから。
		}
		//置きが完了したら、釣りの発生有無に関係無く、釣り状態にしておく事にした。
		//そうしないと、ボートの描画による置いた魚と、フィールド上の置いた魚が二重に描画されてしまう。
		//普段は一瞬なので気付かないが、引き潮中は時間が長いので、前者(揺れない)と後者(揺れる)が重なって見える事に気付いてしまう。
		{
			stGame.boatStat = BOAT_STAT_GET;
			stGame.boatAnm = 0;
		}
		{
			int nChain = 0;	//連鎖数
			for(;;) {
				//魚釣り上げ処理
				int nFish = do_erase_fish();
				if(!nFish) { break; }	//魚釣り上げ無しならば抜ける。
				//魚揃い効果音
				sound_play(0, sound_table[SND_SE_ERASE], 1);
				//魚釣り上げオブジェクトのアニメーションが完了するまでのループ
				for(;;) {
					schedule();
					if(joy & TRG_SELECT) { goto L_EXIT; }
					field_draw();
					boat_draw();
					field_exec();
					boat_exec();
					if(!is_fish_obj_exists()) { break; }	//魚釣り上げオブジェクトが居なくなったら抜ける。尚、泡消滅オブジェクトは残っていても構わない。
				}
				//スコア処理
				{
					//スコアを決定する。
					int add = (nFish - 3) * 20;	//3匹=0,4匹=20,5匹=40,...
					switch(nChain) {
					case 0:  add +=  1000; break;
					case 1:  add +=  2000; break;
					case 2:  add +=  5000; break;
					case 3:  add +=  7000; break;
					case 4:  add += 10000; break;
					case 5:  add += 15000; break;
					default: add += 30000 + (20000 * nChain - 6); break;
					}
					//スコアを加算する。
					stGame.score += add;
					sound_play(3, sound_table[SND_SE_SCORE], 1);
					if(stGame.score > hiscore) { hiscore = stGame.score; }
					//スコアオブジェクトを生成する。
					gen_score_obj(
						(stGame.boatDir == 0) ? (stGame.boatCol + 1) : (stGame.boatCol - 1),	//猫の列
						add);
					//次回のために連鎖数を増やす。
					nChain++;
				}
				//セル浮上処理
				for(;;) {
					//セル浮上のアニメーションが完了するまでのループ
					if(do_raise(1/*testOnly*/)) {
						for(;;) {
							//浮上アニメーション
							for(stGame.cellAnm = 0; stGame.cellAnm < (H__CELL + H__CELL__GAP); stGame.cellAnm += 4/*調整可*/) {	//(H__CELL+H__CELL__GAP)=8なので、4ピクセル×2フレームで浮上する。
								schedule();
								if(joy & TRG_SELECT) { goto L_EXIT; }
								field_draw();
								field_exec();
								boat_exec();
								boat_draw();
							}
							//浮上処理
							if(!do_raise(0/*testOnly*/)) { break; }	//浮上するセルが無くなったら抜ける。
							//アザラシ・サメ出現判定。
							if(stGame.putCnt >= LOW_WARN_PUT_CNT) {	//アザラシの方が優先度が高いです。
								stGame.infoStat = INFO_STAT_SEAL;
							} else if(is_full()) {
								stGame.infoStat = INFO_STAT_SHARK;
							} else {
								stGame.infoStat = 0;
							}
						}
						stGame.cellAnm = 0;	//忘れないで!!cellAnmを0に戻しておかないと、次にdo_erase_fish()でセルを空にした瞬間に、下のセルの表示位置が変わってしまう。
					}
					//泡消滅処理
					if(!do_erase_bubble()) { break; }	//泡消滅無しならば抜ける。
					//セル浮上へ戻って繰り返す。泡消滅オブジェクトのアニメーションは他の演出と並行して進行出来るので、残っていて構わない。
				}
			}
		}
		//クリア判定
		if(is_clear()) { goto L_CLEAR; }
		//引き潮処理
		{
			//既に干潮でなければ…
			if(stGame.lowCnt < FIELD_ROWS) {
				//魚を置いた回数を増やす。
				stGame.putCnt++;
				//引き潮発生判定
				if(stGame.putCnt >= LOW_TIDE_PUT_CNT) {
					stGame.putCnt = 0;
					//引き潮効果音
					sound_play(1, sound_table[SND_SE_WAV2], 1);
					//引き潮アニメーション
					for(stGame.lowOfs = 0; stGame.lowOfs < (H__CELL + H__CELL__GAP); stGame.lowOfs += 2/*調整可*/) {
						schedule();
						if(joy & TRG_SELECT) { goto L_EXIT; }
						field_draw();
						field_exec();
						boat_exec();
						boat_draw();
					}
					stGame.lowOfs = 0;
					stGame.lowCnt++;
					//ゲームオーバー判定
					if(is_gameover()) { goto L_GAMEOVER; }
					//アザラシ・サメ出現判定。
					if(stGame.putCnt >= LOW_WARN_PUT_CNT) {	//アザラシの方が優先度が高いです。
						stGame.infoStat = INFO_STAT_SEAL;
					} else if(is_full()) {
						stGame.infoStat = INFO_STAT_SHARK;
					} else {
						stGame.infoStat = 0;
					}
				}
			}
		}
		//次の魚
		{
			stGame.boatStat = BOAT_STAT_NEXT;
			stGame.boatAnm = 0;
			stGame.TBL_Next[0] = stGame.TBL_Next[1];
			stGame.TBL_Next[1] = get_next();
		}
	}
L_CLEAR:
	{
		//ステージクリアジングル再生
		extern /*const*/ unsigned char CLER_JGL[];
		PlayMusic(CLER_JGL);
	}
	{
		int i, bonus = 0;
		//勝利
		{
			stGame.boatStat = BOAT_STAT_WIN;
			stGame.boatAnm = 0;
			stGame.infoStat = 0;	//決着後は、アザラシもサメも退場させる。
		}
		for(i = 0; i < SEC(0.5); i++) {
			schedule();
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
		}
		for(i = 0; i < SEC(1.0); i++) {
			schedule();
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
			{
				int x, y, w, h;
				x = render_printf_framed(
					&null_render,
					0,
					0,
					1,
					0,
					"STAGE\nCLEAR");
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) - (h / 2);
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					1,
					0x230,
					"STAGE\nCLEAR");
			}
		}
		for(;;) {
			for(i = 0; i < SEC(0.25); i++) {
				schedule();
				if(joy & TRG_SELECT) { goto L_EXIT; }
				field_draw();
				boat_draw();
				field_exec();
				boat_exec();
				{
					int x, y, w, h;
					x = render_printf_framed(
						&null_render,
						0,
						0,
						1,
						0,
						"BONUS\n%5d",
						bonus);
					w = LOHALF(x);
					h = HIHALF(x);
					x = (DISP_X / 2) - (w / 2);
					y = (DISP_Y / 2) - (h / 2);
					CbkQue_render_printf_framed(pCbkQue,
						SHRT_MAX,
						&render,
						x,
						y,
						1,
						0x230,
						"BONUS\n%5d",
						bonus);
				}
			}
			if(stGame.lowCnt >= FIELD_ROWS - 1) { break; }
			//引き潮効果音
			sound_play(1, sound_table[SND_SE_WAV2], 1);
			//引き潮アニメーション
			for(stGame.lowOfs = 0; stGame.lowOfs < (H__CELL + H__CELL__GAP); stGame.lowOfs += 2/*調整可*/) {
				schedule();
				if(joy & TRG_SELECT) { goto L_EXIT; }
				field_draw();
				field_exec();
				boat_exec();
				boat_draw();
				{
					int x, y, w, h;
					x = render_printf_framed(
						&null_render,
						0,
						0,
						1,
						0,
						"BONUS\n%5d",
						bonus);
					w = LOHALF(x);
					h = HIHALF(x);
					x = (DISP_X / 2) - (w / 2);
					y = (DISP_Y / 2) - (h / 2);
					CbkQue_render_printf_framed(pCbkQue,
						SHRT_MAX,
						&render,
						x,
						y,
						1,
						0x230,
						"BONUS\n%5d",
						bonus);
				}
			}
			stGame.lowOfs = 0;
			stGame.lowCnt++;
			//ボーナス加算
			bonus += ((bonus < 2000) ? 500 : 1000);		//0→500→1000→1500→2000→3000→4000→5000→…	本物は3000→5000→8000だが、まあ良いだろう。
		}
		//スコア加算
		while(bonus) {
			schedule();
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			field_exec();
			boat_exec();
			boat_draw();
			{
				int x, y, w, h;
				x = render_printf_framed(
					&null_render,
					0,
					0,
					1,
					0,
					"BONUS\n%5d",
					bonus);
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) - (h / 2);
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					1,
					0x230,
					"BONUS\n%5d",
					bonus);
			}
			{
				int add = min(bonus, 300);
				stGame.score += add;
				bonus        -= add;
				sound_play(3, sound_table[SND_SE_SCORE], 1);
				if(stGame.score > hiscore) { hiscore = stGame.score; }
			}
		}
		//少し待つ
		for(i = 0; (i < SEC(2.0)) || !MusicCheck(); i++) {
			schedule();	//   ~~~~~~~~~~~~~2秒経過しても、まだステージクリアジングルが鳴っていたら待つ。
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
			{
				int x, y, w, h;
				x = render_printf_framed(
					&null_render,
					0,
					0,
					1,
					0,
					"BONUS\n%5d",
					bonus);
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) - (h / 2);
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					1,
					0x230,
					"BONUS\n%5d",
					bonus);
			}
			if(joy & TRG_AB) { break; }	//キャンセル可
		}
		return 1;
	}
L_GAMEOVER:
	{
		//ゲームオーバージングル再生
		extern /*const*/ unsigned char OVER_JGL[];
		PlayMusic(OVER_JGL);
	}
	{
		int i;
		//敗北
		{
			stGame.boatStat = BOAT_STAT_LOSE;
			stGame.boatAnm = 0;
			stGame.infoStat = 0;	//決着後は、アザラシもサメも退場させる。
		}
		for(i = 0; (i < SEC(5.0)) || !MusicCheck(); i++) {
			schedule();	//   ~~~~~~~~~~~~~5秒経過しても、まだゲームオーバージングルが鳴っていたら待つ。
			if(joy & TRG_SELECT) { goto L_EXIT; }
			field_draw();
			boat_draw();
			field_exec();
			boat_exec();
			if(i > SEC(1.0)) {
				int x, y, w, h;
				x = render_printf_framed(
					&null_render,
					0,
					0,
					1,
					0,
					"GAME OVER");
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) - h;
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					1,
					0x230,
					"GAME OVER");
			}
			if((i >= SEC(2.0)) && (joy & TRG_AB)) { break; }	//2秒後からキャンセル可
		}
		return 0;
	}
L_EXIT:
	return 0;
}
/****************************************************************************
 *	
 ****************************************************************************/
//1ゲームの処理
void game_run() {
	memset(&stGame, 0, sizeof stGame);
	stGame.stage = stage_sel;
	while(stage_run()) {
		int score = stGame.score;	//退避
		int stage = stGame.stage;	//退避
		memset(&stGame, 0, sizeof stGame);
		stGame.score = score;		//復帰
		stGame.stage = stage + 1;	//復帰
		StopMusic();
#ifdef  USE_TRUESPEECH
		TrueSpeech_StopAll();
#endif//USE_TRUESPEECH
	}
	StopMusic();
#ifdef  USE_TRUESPEECH
	TrueSpeech_StopAll();
#endif//USE_TRUESPEECH
}
/****************************************************************************
 *	
 ****************************************************************************/
//タイトルの処理
void demo_run() {
	int iPri, x, y, w, h, iSprNo, iParam, start = 0;
	for(;;) {
		schedule();
		if(joy & TRG_SELECT) { exit(EXIT_SUCCESS); }
		//背景
		iPri	= 0;
		x	= 0;
		y	= 0;
		iSprNo	= SPR_SKY;
		iParam	= DRW_NOMAL;
		CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
		//タイトル
		{
			iPri	= 0;
			x	= DISP_X / 2;
			y	= (DISP_Y / 2) - 8;
			iSprNo	= SPR_TITLE;
			iParam	= DRW_NOMAL;
			CbkQue_sprite_draw(pCbkQue, iPri, x, y, iSprNo, iParam);
			//
			x	+= 31;
			y	+= 10;
			CbkQue_render_printf_framed(pCbkQue,
				iPri,
				&render,
				x,
				y,
				3,
				0x130,
				"ﾓﾄﾞｷ");
		}
		//スコア
		{
			CbkQue_render_printf_framed(pCbkQue,
				SHRT_MAX,
				&render,
				0,
				0,
				3,
				0x330,
				"1P\n%06d",
				stGame.score);
		}
		{
			x = render_printf_framed(
				&null_render,
				0,
				0,
				3,
				0,
				"HI\n%06d",
				hiscore);
			w = LOHALF(x);
			h = HIHALF(x);
			x = (DISP_X / 2) - (w / 2);
			y = 0;
			CbkQue_render_printf_framed(pCbkQue,
				SHRT_MAX,
				&render,
				x,
				y,
				3,
				0x330,
				"HI\n%06d",
				hiscore);
		}
		//ステージセレクト
		{
			if((stage_sel >  1) && (joy & TRG_LF)) {
				stage_sel--;
				sound_play(0, sound_table[SND_SE_BTN], 1);
			}
			if((stage_sel < 99) && (joy & TRG_RI)) {
				stage_sel++;
				sound_play(0, sound_table[SND_SE_BTN], 1);
			}
			x = render_printf_framed(
				&null_render,
				0,
				0,
				3,
				0,
				"%c STAGE SELECT %02d %c",
				(stage_sel >  1) ? '<' : ' ',
				stage_sel,
				(stage_sel < 99) ? '>' : ' ');
			w = LOHALF(x);
			h = HIHALF(x);
			x = (DISP_X / 2) - (w / 2);
			y = (DISP_Y / 2) + 18;
			CbkQue_render_printf_framed(pCbkQue,
				SHRT_MAX,
				&render,
				x,
				y,
				3,
				0x330,
				"%c STAGE SELECT %02d %c",
				(stage_sel >  1) ? '<' : ' ',
				stage_sel,
				(stage_sel < 99) ? '>' : ' ');
		}
		//プロンプト
		{
			if((!start && ((now % SEC(1.0)) < SEC(0.5))) ||	//押す前
			   (start & 1)) {				//押した後
				x = render_printf_framed(
					&null_render,
					0,
					0,
					3,
					0,
					"PRESS A BUTTON TO START");
				w = LOHALF(x);
				h = HIHALF(x);
				x = (DISP_X / 2) - (w / 2);
				y = (DISP_Y / 2) + 31;
				CbkQue_render_printf_framed(pCbkQue,
					SHRT_MAX,
					&render,
					x,
					y,
					3,
					0x330,
					"PRESS A BUTTON TO START");
			}
			if(!start) {
				if(joy & TRG_AB) {
					start = 1;
					sound_play(0, sound_table[SND_SE_ERASE], 1);
				}
			} else {
				if(++start >= SEC(1.0)) {
					break;
				}
			}
		}
	}
}
/****************************************************************************
 *	app_main
 ****************************************************************************/
int app_main(int argc, char* argv[]) {
//{{隠しコマンド。Bを押しながら起動すると電池表示を消す。デバッグ時に電池表示を消しやすいようにするためです。
	delay(1);
	if(joy & PAD_B) {
		pceWaveSetMasterAtt(0);
		pcePowerSetReport(PWR_RPTOFF);
	}
//}}隠しコマンド。Bを押しながら起動すると電池表示を消す。デバッグ時に電池表示を消しやすいようにするためです。
	stage_sel = 1;
	for(;;) {
		demo_run();
		game_run();
	}
	return 0;
}



//	■なんだこれ!?
//	http://www.collectorvision.com/old/game-puzzli.htm

