/*
	    地盤隆起
	WonderSwanEdition
	  version.1.00
	programmed by F/T
*/


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

#include "jibanchr.h"


/* ************************************* 定数
*/

#define	SCN_OPEN		1
#define SCN_TITLE		2
#define SCN_HELP		3
#define	SCN_GAMESTART	4
#define SCN_GAME		5
#define SCN_GAMEEND		6



/* ************************************* グローバル変数
*/

int nScene;		/* ゲーム状態(シーン)				*/
int nCount;		/* シーン開始後カウンタ				*/
int	fExit;		/* ゲーム終了フラグ					*/

int nKey;		/* 押されているキー					*/
int nKeyBak;	/* 前回押されているキー				*/
int nNewKey;	/* 今回新たに押されたいるキー		*/

int nTime;		/* 開始経過時間						*/
int nScore;		/* スコア							*/
int nMaxImpact;	/* 最大衝撃							*/
int nMaxImpactDisp;
int nTopTime;		/* TOP開始経過時間						*/
int nTopScore;		/* TOPスコア							*/
int nTopMaxImpact;	/* TOP最大衝撃							*/


int	nMx,nMy;	/* プレイヤー座標 (4bit固定小数)	*/
int	nMxs,nMys;	/* プレイヤー移動速度(4bit固定小数)	*/
int nJP;		/* ジャンプ溜め						*/



int nGrHeight[32];	/* 地面地表高(4bit固定小数)	(T:0*16-B:144*16)		*/
int nGrGrow[32];	/* 地面隆起量 */
int fGrOver;		/* 地面が天に届いたフラグ */


/* ************************************* 関数プロトタイプ宣言
*/

void EnterScene( int nEnterScene );


/* ************************************* サブルーチン
*/


/* 画面モード初期化
*/
void InitScr()
{
	display_control( DCM_SCR1 | DCM_SCR2 | DCM_SPR  | DCM_SCR2_WIN_INSIDE );

	text_window_init(0,0, 18,18,512-18*18);
	lcd_set_segments( lcd_get_segments() | 0x04  );
}


/* PCG・スプライト定義
*/
void DefChr()
{
	int i;

	font_set_monodata( CHR_SPC , 1*1 , spc_bmp );

	font_set_monodata( CHR_MANF1 , 1*1 , &man_bmp1[0] );
	font_set_monodata( CHR_MANF2 , 1*1 , &man_bmp2[0] );
	font_set_monodata( CHR_MANR1 , 1*1 , man_bmp3 );
	font_set_monodata( CHR_MANR2 , 1*1 , man_bmp4 );
	font_set_monodata( CHR_GR1	 , 1*1 , gr_bmp1 );
	font_set_monodata( CHR_GR2	 , 1*1 , gr_bmp2 );

	for (i=0;i<25;i++) {
		font_set_monodata( CHR_GRS+i	 , 1*1 , &gr_bmps[i] );
		grs_chr[i]=CHR_GRS+i;
	}


	sprite_set_range( 0, 16 );
	for (i=0;i<16;i++) {
		sprite_set_char(i,0 | 4<<9);
	}
	


}


void ClrScr1()
{
	screen_fill_char( SCREEN1 , 0,0,32,32 , 0);
}

void ClrScr2()
{
	int i;
	for ( i=0; i<18; i++ )
		text_put_string(0,i,"                  ");
}


/* ゲーム初期化
*/
void InitGame()
{
	InitScr();
	DefChr();
	
	nKeyBak = key_press_check();
	fExit = 0;
	EnterScene( SCN_OPEN );

	/* ハイスコア初期化 */
	nTopTime = 0;
	nTopScore = 0;
	nTopMaxImpact =0;
}



/* ************************************* 地面描画
*/


void ResetGround(int nHeight )
{
	int i;
	for (i=0;i<32;i++ ) {
		nGrHeight[i] = nHeight;
		nGrGrow[i]=0;
	}
	fGrOver = 0;
}

void DrawGroundSub( int n )
{
	int y;
	int yg = nGrHeight[n];
	int *p;
	for ( y = 0; y<20; y++ ) {
		if ( y < yg ) {
			p= grs_chr;
		}else if ( y==yg ) {
			p= gr_chr1;
		}else{
			p= gr_chr2;
		}
		screen_set_char( SCREEN1 , n,y, 1,1, p );
	}
}

void DrawGroundSub2( int n )
{
	int yg = nGrHeight[n] >> 4;	/* 対象位置の地面高 */
	
	int y;					/* ループ */
	int ysh = yg/8;			/* 空部分のキャラクタ数 */
	int yofs = ysh*8-yg+8;	/* 地面高の8ドット協会からのオフセット */
	int *p;
	int *p1;				/* 地表キャラクタ定義 */
	int *p2;				/* 地表+1キャラクタ定義 */
	int *p3;				/* 地中キャラクタ定義 */
	

	p1 = &grs_chr[yofs];
	p2 = &grs_chr[yofs+8];
	p3 = &grs_chr[yofs+16];
	
	/* 空描画 */
	if ( ysh>0 ) screen_fill_char( SCREEN1 , n,0,1,ysh , 0);
	for ( y = ysh; y<20; y++ ) {
		if ( y==ysh ) {
			/* 地表 */
			p= p1;
		}else if ( y==ysh+1 ) {
			/* 地表+1 */
			p= p2;
		}else{
			/* 地中 */
			p= p3;
		}
		screen_set_char( SCREEN1 , n,y, 1,1, p );
	}
}

void DrawGround()
{
	int n;
	for (n=0;n<32;n++)
		DrawGroundSub2(n);
}

int GrowGround(int n,int nG)
{
	nGrHeight[n]+=nG;
	if ( nGrHeight[n]<0 ) { nGrHeight[n]=0; fGrOver=1; /* 天に届いた */ }
	DrawGroundSub2(n);
}


int SetGrowGround(int n,int nG)
{
	nGrGrow[n]+=nG;
}

void AllGrowGround()
{
	int i;
	for (i=0;i<32;i++ ) {
		if ( nGrGrow[i] != 0 ) {
			GrowGround(i,nGrGrow[i]);
			if ( nGrGrow[i]>0 ) nGrGrow[i]--; else nGrGrow[i]++;
		}
	}
}

int XtoN(int x)
{
	return x>=0 && x<32*8*16 ? x >> 4 >> 3 : -1;
}


int HitChkGround( int x, int y)
{
	int n = XtoN( x );
	return n>=0 ? y - nGrHeight[n] : 10000;
}




/* ************************************* プレイヤー移動
*/

void ActionMan()
{
	int nH;
	int nP;
	int nDown;

	nMy+=nMys;
	
	nH = HitChkGround(nMx,nMy);
	if ( nH>=0 ) {
		nDown = nMys*2;
/*		if ( nKey & KEY_X3 ) nDown*=2;
*/
		GrowGround( XtoN(nMx) , nDown  );
		nScore+=nDown >> 4;
		if ( nDown>nMaxImpact) { 
			nMaxImpact = nDown; 
			if ( nMaxImpact>=100 ) nMaxImpactDisp=250; 
		}
		
		nMy -= HitChkGround(nMx,nMy);
		nMys/=2;
	}else{
		nMys+= nKey & KEY_X3 ? 2 : 1;
	}

	if ( nKey & KEY_X2 && nMxs<16  ) nMxs+=2;
	if ( nKey & KEY_X4 && nMxs>-16 ) nMxs-=2;

	if ( HitChkGround(nMx+nMxs,nMy) > 64 ) nMxs=0;
	
	nMx+=nMxs;
	
	if ( nMxs>0 ) nMxs--;
	if ( nMxs<0 ) nMxs++;

	nH = HitChkGround(nMx,nMy);
	if ( nH>0 ) {
		nMy -= nH;
		nMys=0;
	}
	if ( nH == 0 ) {		
		if ( nKey & KEY_A || nNewKey & KEY_B ) {
			nMys = -30 - nJP/10;
			nJP=0;
		}
		if ( !(nKey & (KEY_X2 | KEY_X4)) ) nMxs/=2;
		if ( nKey & KEY_X3 && nJP<200 ) nJP++;
	}
	if ( (nKey & (KEY_X2 | KEY_X4)) || !(nKey & KEY_X3) ) nJP=0; 

	
	nP = CHR_MANF1;
	if ( nMys>2) nP=CHR_MANF2;
	if ( nMxs>2   || (nKey & KEY_X2) ) nP=CHR_MANR1 + ((nCount>>2) & 1 );
	if ( nMxs<-2  || (nKey & KEY_X4) ) nP=CHR_MANR1 + ((nCount>>2) & 1 ) | 0x8000;
		
	sprite_set_char_location( 0 , nP | (4 << 9 ) , (nMx >> 4) -4 - (nMx >> 7), (nMy >> 4) -8 );
	
	lcd_set_segments( 
		( lcd_get_segments() & 0xffc7 ) |
		0x04 |
		(nJP>10  ? 0x08  : 0) |
		(nJP>100 ? 0x010 : 0) |
		(nJP>190 ? 0x020 : 0) );
		
	screen_set_scroll( SCREEN1 , nMx >> 7 , 0 );

	
}



/* ************************************* シーン処理
*/

/* ***** オープニング
*/
void EnterScene_Open()
{
	ClrScr1();
	ClrScr2();
					/*   123456789012345678	*/
	text_put_string(0,0,"   JIBAN RYU-KI   ");
	text_put_string(0,1," since1987@MZ2000 ");
	text_put_string(0,3,"  progremmed by   ");
	text_put_string(0,4,"  FUKAYA Takashi  ");

}

void ExecScene_Open()
{
	int i,x,y;
	
	/* クレジット表示 */
	x=5*8;
	y=144-nCount;
	if ( y<50 ) y=50;
	screen2_set_window(x,y,18*8-2,nCount & 1 ? 5*8-2 : 0 );
	screen_set_scroll( SCREEN2,-x,-y);

	/* 背景表示 */
	for (i=0;i<2;i++ ) {
		x = rand() / 8 / 128;
		y = rand() / 8 / 128;
		screen_set_char( SCREEN1 ,x,y,1,1,i ? man_chrF2 : man_chrF1 );
		screen_set_scroll( SCREEN1,0,-nCount/2);
	}
	
	/* 次シーン移行判定 */
	if ( nNewKey & KEY_A || nCount>200 ) EnterScene( SCN_TITLE );
}


/* ***** タイトル
*/
void EnterScene_Title()
{
	int i,x,y;
	ClrScr1();
	ClrScr2();
	sys_wait(10);
	screen_set_scroll( SCREEN1,0,0);

	x=5*8;
	y=10;
	screen2_set_window(x,y,18*8-2,11*8-2 );
	screen_set_scroll( SCREEN2,-x,-y);
					/*   123456789012345678	*/
	text_put_string(0,1,"     地 盤 隆 起      ");
	text_put_string(0,3,"WonderSwanEdition ");
	text_put_string(0,4,"  version.1.00    ");
	text_put_string(0,5,"programmed by F/T ");

	text_put_string(0,8," PUSH [A] TO START");
	text_put_string(0,9," PUSH [B] TO HELP ");

	ResetGround(144 << 4);
	for (i=0;i<32;i++) SetGrowGround(i,-32);
	DrawGround();	
	DefChr();
}

void ExecScene_Title()
{
	if ( (nCount>400 || nCount<0) && rand()<2000 ) SetGrowGround( rand()/8/128 , (rand()-18000)/1000 );
	AllGrowGround();

	/* 次シーン移行判定 */
	if ( nNewKey & KEY_A ) EnterScene( SCN_GAMESTART );
	if ( nNewKey & KEY_B ) EnterScene( SCN_HELP );
}


/* ***** ヘルプ
*/
void EnterScene_Help()
{
	int i,x,y;
	ClrScr1();
	ClrScr2();
	sys_wait(10);
	screen_set_scroll( SCREEN1,0,0);

	x=5*8;
	y=0;
	screen2_set_window(x,y,18*8,18*8-2 );
	screen_set_scroll( SCREEN2,-x,-y);
					/*    123456789012345678	*/
					/*                      123456789012345678*/
	text_put_string(0, 0,"* 地 盤 隆 起 - 操作説明 *");
	text_put_string(0, 2,"　地殻変動のせいでしょうか。あなたの");
	text_put_string(0, 3,"土地が隆起しだしました。理不尽だと考");
	text_put_string(0, 4,"える前に目の前の問題をなんとかしてく");
	text_put_string(0, 5,"ださい。");
	text_put_string(0, 6,"　ジャンプして着地することで地面をへ");
	text_put_string(0, 7,"こませることができます。勢いに応じて");
	text_put_string(0, 8,"へこむ量がかわります。へこました量が");
	text_put_string(0, 9,"スコアに加算されます。");
	text_put_string(0,10,"　地面が天に届くか、へこましすぎて画");
	text_put_string(0,11,"面下まで落ちるとゲームオーバーです。");

	text_put_string(0,13,"[X2][X4]      左右移動");
	text_put_string(0,14,"[A][B]  ジャンプ(A:連続)");
	text_put_string(0,15,"[X3] 下降速度加速(ジャンプ中)");
	text_put_string(0,16,"[X3] 気合をためる(地上)");
	text_put_string(0,17,"     気合をためると高くジャンプ");

	ResetGround(0);
	for (i=0;i<32;i++) SetGrowGround(i,+60);
	DrawGround();
	DefChr();
}

void ExecScene_Help()
{
	if ( (nCount>400 || nCount<0) && rand()<2000 ) SetGrowGround( rand()/8/128 , (rand()-18000)/1000 );
	AllGrowGround();

	if ( nKey & KEY_X2 && nMxs<256  ) nMxs+=2;
	if ( nKey & KEY_X4 && nMxs>-256 ) nMxs-=2;
	nMx+=nMxs;
	screen_set_scroll( SCREEN1 , nMx >> 7 , 0 );


	/* 次シーン移行判定 */
	if ( nNewKey & (KEY_A | KEY_B) ) EnterScene( SCN_OPEN );
}


/* ***** ゲーム開始
*/
void EnterScene_GameStart()
{
	int i,x,y;
	ClrScr1();
	ClrScr2();
	screen_set_scroll( SCREEN1,0,0);

	x=5*8;
	y=5*8;
	screen2_set_window(x,y,18*8-2,1*8-2 );
	screen_set_scroll( SCREEN2,-x,-y);
					/*   123456789012345678	*/
	text_put_string(0,0,"    GAME START    ");

	ResetGround(144 << 4);
	for (i=0;i<32;i++) SetGrowGround(i,-24);
	DrawGround();	
	DefChr();
	
	/* 変数初期化 */
	nTime=0;
	nScore=0;
	nMaxImpact = 0;
	nMaxImpactDisp = 0;

	nMx = (16*8+4)*16;
	nMy = 14*8*16;
	nMxs=0;
	nMys=0;
	nJP = 0;

}

void ExecScene_GameStart()
{

	AllGrowGround();
	
	ActionMan();
	if ( nMys>4 ) nMys--;;

	/* 次シーン移行判定 */
	if ( nCount>50 ) EnterScene( SCN_GAME );
}





/* ***** ゲーム
*/
void EnterScene_Game()
{
	int x,y;

	x=10*8;
	y=0;
	screen2_set_window(x,y,18*8-2,1*8-2 );
	screen_set_scroll( SCREEN2,-x,-y);
					/*   123456789012345678	*/
	text_put_string(0,0,"                  ");

	nTime=0;
	nScore=0;
	
}

void ExecScene_Game()
{
	static char szBuf[256];
	
	/* 地面 */
	if ( nCount>400 && rand()<400 ) SetGrowGround( rand()/8/128 , -rand()/1000 );
	AllGrowGround();

	/* プレイヤー移動 */
	ActionMan();

	/* ゲームカウント */
	if ( nCount % 10 == 0 ) nTime ++;

	/* ステータス表示 */
	if ( 1 ) {
		sprintf( szBuf , "TM:%05d SC:%06d",nTime,nScore);
		if ( nMaxImpactDisp>0 ) {
			if (  nMaxImpactDisp<250 ) {
				if ( (nMaxImpactDisp >> 5) & 1 ) {
					sprintf( szBuf , "MaxImpact! *%05d*",nMaxImpact);
				}else{
					sprintf( szBuf , "                  ");
				}
			}
			nMaxImpactDisp--;
		}
		text_put_string(0,0,szBuf);
		screen2_set_window(10*8,0,18*8-2,1*8-2 );
	}else{
		screen2_set_window(10*8,0,18*8-2,0 );
	}

	/* 次シーン移行判定 = ゲームオーバー判定  */
	if ( fGrOver || nMy>18*8*16 ) EnterScene( SCN_GAMEEND );
}





/* ***** ゲーム終了
*/
void EnterScene_GameEnd()
{
	int x,y;

	ClrScr2();
	sys_wait(10);

	x=5*8;
	y=10;
	screen2_set_window(x,y,18*8-2,4*8 );
	screen_set_scroll( SCREEN2,-x,-y);


					/*   123456789012345678	*/
					/*                      123456789012345678*/
	text_put_string(0,1,"    GAME OVER     ");
	if ( fGrOver ) {
	text_put_string(0,2,"  地面が天に届いてしまいました  ");
	} else {
	text_put_string(0,2,"    地面を掘りすぎです     ");
	}

}


void ExecScene_GameEnd()
{
	int x,y,h;
	static char szBuf[256];

	x=5*8;
	y=10;
	h=4*8 + nCount/3;
	if ( h<11*8 ) {
		if ( nNewKey & KEY_A ) { nCount = 200; nNewKey=0; }
	} else {
		h=11*8;
	}
	screen2_set_window(x,y,18*8,h );
	screen_set_scroll( SCREEN2,-x,-y);

	if ( nCount < 4 || nCount == 300 ) {

		if ( nCount == 300 ) {
			if ( nScore>nTopScore ) nTopScore = nScore;
			if ( nTime>nTopTime ) nTopTime = nTime;
			if ( nMaxImpact>nTopMaxImpact ) nTopMaxImpact = nMaxImpact;
		}

						/*   123456789012345678	*/
						/*                      123456789012345678*/
	/*                    4 "       \you  \top "
	                      5 " SCORE:00000/00000"
	                      6 "  TIME:00000/00000"
	                      7 "IMPACT:00000/00000"
	*/

		text_put_string(0,4,"       ▼you  ▼top ");
		sprintf( szBuf , " SCORE:%05d/%05d",nScore,nTopScore);
		text_put_string(0,5,szBuf);
		sprintf( szBuf , "  TIME:%05d/%05d",nTime,nTopTime);
		text_put_string(0,6,szBuf);
		sprintf( szBuf , "IMPACT:%05d/%05d",nMaxImpact,nTopMaxImpact);
		text_put_string(0,7,szBuf);


						/*   123456789012345678	*/
		text_put_string(0,9,"PUSH [A] TO RETURN");
	}

	if ( nCount<0 || nCount>1000 ) {
		sprite_set_char_location( 0 , 0,0,0);
		nMx+=8;
		screen_set_scroll( SCREEN1 , nMx >> 7 , 0 );
	}


	/* 次シーン移行判定 */
	if ( nNewKey & KEY_A ) EnterScene( SCN_TITLE );
}





/* ************************************* シーン制御
*/

/* シーン実行
*/
void ExecScene()
{
	/* キー入力 */
	nKey = key_press_check();				/* 押されているキー */
	nNewKey = ( nKey ^ nKeyBak ) & nKey;	/* 新たに押されたキー */
	nKeyBak = nKey;
	
	if ( nNewKey & KEY_START ) fExit = 1;	/* スタートボタンで終了 */
	
	/* シーン分岐 */
	switch ( nScene ) {
		case SCN_OPEN:
			ExecScene_Open();
			break;
		
		case SCN_TITLE:
			ExecScene_Title();
			break;
		case SCN_HELP:
			ExecScene_Help();
			break;
		case SCN_GAMESTART:
			ExecScene_GameStart();
			break;
		case SCN_GAME:
			ExecScene_Game();
			break;
		case SCN_GAMEEND:
			ExecScene_GameEnd();
			break;
	};
	nCount++;

}


/* シーンチェンジ
*/
void EnterScene( int nEnterScene )
{
	switch ( nEnterScene ) {
		case SCN_OPEN:
			EnterScene_Open();
			break;
		case SCN_TITLE:
			EnterScene_Title();
			break;
		case SCN_HELP:
			EnterScene_Help();
			break;
		case SCN_GAMESTART:
			EnterScene_GameStart();
			break;
		case SCN_GAME:
			EnterScene_Game();
			break;
		case SCN_GAMEEND:
			EnterScene_GameEnd();
			break;
	};
	
	nScene = nEnterScene;
	nCount = 0;
	
}




/* ************************************* メインルーチン
*/


void main()
{
	InitGame();
	
	while ( !fExit ) {
		ExecScene();
		sys_wait(0);
	}
}
