/***  ゲーム本体  ***/

#include <stdlib.h>
#include <string.h>
#include <snd.h>

#include "graphic .h"
#include "calc    .h"
#include "spr_drv .h"
#include "sound   .h"
#include "str_sub .h"

#include "sblaster.h"

int		put_string_s( int x, int y, char *p, int fc, int bc );
int		CDDA_prog_play( int start, int rep ), CDDA_pause( void );

/***  エフェクト処理  ***/

void set_efxch( int n, int tp, int ajst )
{
	register DSPCH_S	*cp ;
	register EFFECT_S	*fxp ;

	if( 20 <= efxch_spc ) return ;

	cp = &chr[n], fxp = &effect_ch[ efxch_spc ];
	cp->efx_n = efxch_spc++ ;
	while( effect_ch[ efxch_spc ].swi && efxch_spc < efxch_c ) ++efxch_spc ;
	if( efxch_c <= efxch_spc ) efxch_c = efxch_spc ;

	fxp->cn = n ;
	fxp->pat = efx_idx[ fxp->type = tp ].pat ;
	fxp->x.all = cp->x.HL.H + efx_pos[ ajst + cp->ppos ].x << 16 ;
	fxp->y.all = cp->y.HL.H + efx_pos[ ajst + cp->ppos ].y << 16 ;
	fxp->ac = 0, fxp->swi = ON ;
}

/***  各移動処理  ***/

int move_shot( int bln )
{
	register D_QUAD		*php, *ehp ;
			 BULLET_S	*blp ;
			 int		x, y, xs, ys, enm, hit ;

	blp = &bullet[ bln ], x = ( blp->XH += 16 ), y = blp->YH ;
	php	= & pat_hit[ bltVar[bln].hit_tp ] ;

	for( enm = 1, hit = OFF ; enm <= show_max ; ++enm ){
		ehp = & pat_hit[ actCH[ enm ].hit_tp + chr[enm].ppos ] ;
		xs	= actCH[ enm ].XH - x, ys = actCH[ enm ].YH - y ;
		if( chr[enm].swi == ON
			&& ( php->x1 - ehp->x2 ) <= xs && xs <= ( php->x2 - ehp->x1 )
			&& ( php->y1 - ehp->y2 ) <= ys && ys <= ( php->y2 - ehp->y1 )
		){
			blp->swi = OFF ;
			actCH[ enm ].dmg_c = DMG_MAX, hit = ON, score += 100 ;
			put_string_s( 32+6*8, 0, set_num( score, 8 ), cvcl[7], cvcl[1] );
		}
	}
	return( hit );
}

void move_blaster( int bln )
{
	register D_QUAD	 	*php, *ehp	;
	register BULLET_S	*blp ;
			 DSPCH_S 	*cp ;
			 int		xs, ys ;

	blp = &bullet[bln];
	blp->x.all += SIN( bltVar[bln].ang )* 3 ;
	blp->y.all += COS( bltVar[bln].ang )* 3 ;
	blp->pal = GP( blp->pal == GP(0) ? 2 : 0 );

	cp	= &chr[PLYN] ;
	php	= &pat_hit[ bltVar[bln ].hit_tp ] ;
	ehp = &pat_hit[ actCH [PLYN].hit_tp + cp->ppos ] ;
	xs	= actCH[PLYN].XH - bullet[bln].XH ;
	ys	= actCH[PLYN].YH - bullet[bln].YH ;

	if( cp->swi == ON
		&& ( php->x1 - ehp->x2 ) < xs && xs < ( php->x2 - ehp->x1 )
		&& ( php->y1 - ehp->y2 ) < ys && ys < ( php->y2 - ehp->y1 )
	){
		actCH[ PLYN ].dmg_c = DMG_MAX, blp->swi = OFF ;
	}
}

int move_enemy( int n )
{
	register D_QUAD	 	*php, *ehp	;
	register DSPCH_S 	*cp, *ecp ;
			 ACTPR_S	*ap ;
			 BULLET_S	*blp ;
	int xs, ys ;

	cp  = &chr[n], ecp = &chr[ PLYN ], runCH = &actCH[n] ;

	if( cp->swi == OFF ) return 0 ;

	ap = &act_idx[ runCH->act_tp ];
	runCH->act_c = ( runCH->act_c + 1 )% ap->max ;
	cp->c = act_pat[ ap->ofs + runCH->act_c ].pat ;

	if( ( runCH->XH -= SPD ) <= -16 ) runCH->swi = cp->swi = OFF ;

	if( 0 < runCH->c ){ //  カウントが０になったら弾を出す。
		if( --runCH->c == 0 ){
			runCH->c = 1 ;
			if( blast_emp < EBLT_MAX ){
				blp = &bullet[ blast_emp ];
				blp->swi = ON ;
				blp->x.all = runCH->x.all - ( 12 * 0x10000 );
				blp->y.all = runCH->y.all ;
				bltVar[ blast_emp ].ang
					= delta_ang( actCH[0].XH-blp->XH, actCH[0].YH-blp->YH );
				runCH->c = ( 0 < --runCH->sc ? 86/SPD : 0 );
				do{
					++blast_emp ;
				}while( blast_emp < EBLT_MAX && bullet[blast_emp].swi == ON );
			}
		}
	}

	//  プレイヤーへの当たり判定チェック
	php = &pat_hit[ runCH->		hit_tp +  cp->ppos ] ;
	ehp = &pat_hit[ actCH[PLYN].hit_tp + ecp->ppos ] ;
	xs	= actCH[PLYN].XH - runCH->XH ;
	ys	= actCH[PLYN].YH - runCH->YH ;

	return ( ecp->swi == ON
		&&( php->x1 - ehp->x2 ) <= xs && xs <= ( php->x2 - ehp->x1 )
		&&( php->y1 - ehp->y2 ) <= ys && ys <= ( php->y2 - ehp->y1 ) );
}

/***  ダメージ・破壊処理  ***/

void damage( int n )
{
	switch( DMG_MAX - runCH->dmg_c-- ){
	case         0 :
		chr[n].swi = OFF, set_efxch( n, ( n == PLYN ), EFXPS );
		break ;
	case DMG_MAX-1 : runCH->swi = OFF ; break ;
	}
}

/***  キャラクター設定  ***/

void set_chpara( int n, int tp, int x, int y )
{
	register DSPCH_S	*cp ;
	register STKCH_S	*sp ;

	cp = &chr[n], sp = &actCH[n];
	sp->type = tp ;
	sp->hit_tp = chr_para[tp].htp ;
	sp->XH = x ;
	sp->YH = ( y == PR_RND )? ( rand() >> 8 )% 200 + 24 : y ;

	sp->c	= ( 0 < eshot_vol ) ? ( 16 + 50 + (3-eshot_vol)*43 )/ SPD : 0 ;
	sp->sc	= eshot_vol ;
	sp->act_tp = chr_para[tp].act_tp, sp->act_c = 0 ;

	cp->size	= 0x0202, cp->set_flag = 0 ;
 	cp->ajst	= cp->pat_ofs = 0 ;
	cp->pat		= chr_para[tp].pat ;
	cp->pal		= chr_para[tp].pal ;
	cp->ang_sft = 6, SETCP_ANG( 0 );
	cp->c		= act_pat[ act_idx[ sp->act_tp ].ofs ].pat ;
	cp->XH = FRAME_IX( sp->XH ), cp->YH = FRAME_IY( sp->YH );
	sp->swi	= cp->swi = ON ;
}

/***  ゲーム本体  ***/

void game_main( void )
{
	static struct MSG_SET { char str[20] ; int x, y, fc, bc } msg_set[] = {
		"GAME OVER"	, (320- 9*8)/2,116, GRB(31,31,10), GRB(0,0,0),
		"READY GO!!", (320-10*8)/2,116, GRB(31,31,10), GRB(0,0,0),
		"WARNNING!!", (320-10*8)/2,116, GRB( 0,31, 0), GRB(0,0,0),
	};

	register DSPCH_S	*cp ;
			 EFFECT_S	*fxp ;
			 EFXPR_S	*efp ;
			 BULLET_S	*blp ;
	struct	 MSG_SET	*msg_p ;
			 int		button, ct_trig, bs_trig, trig ;
			 int		n, i ;
			 int		shot_emp, enm_c, enm_left, level, slant, mvy ;
			 int		end_c, msg_c, msg_sw, wait_c ;
	unsigned long		time_c ;

	bs_trig = 0 ;
	show_max = 15, eblt_max = 48, pblt_max = 3 ;
	score = 0, level = -1, time_c = 0, enm_c = 20, end_c = -1 ;

	put_string_s(  32	 , 0, "SCORE", GRB(31,31,31), cvcl[1] );
	put_string_s(  32+8*6, 0, set_num( score, 8 ), GRB(31,31,31), cvcl[1] );
	put_string_s( 288-8*7, 0, "LEVEL00", GRB(31,31,31), cvcl[1] );

	map_reset( imgmap_wx - 1, imgmap_wy - 2 );

	//  キャラクター消去
	for( cp = &chr[ n = 0 ] ; n <= show_max ; n++, cp++ ){
		cp->swi	= actCH[n].swi = OFF ;
	}
	//  プレイヤー定義
	set_chpara( 0, TP_PLY, 64, 128 ), slant = 0 ;

	//  プレイヤー弾丸定義
	for( i = 0 ; i < pblt_max ; i++ ){
		blp = &bullet[i] ;
		blp->pal	= pattern_idx[ blp->pat	= BULPT ] & 255 ;
		blp->ajst	= 4 ;
		blp->dir	= blp->slant = blp->c = 0 ;
		blp->swi	= OFF ;
		bltVar[i].hit_tp = BULHT ;
	}

	//  敵弾丸定義
	for( i = 0 ; i < eblt_max ; i++ ){
		blp = &bullet[ ENM_BLT + i ];
		blp->pat	= BLAPT ;
		blp->pal	= GP(0);
		blp->ajst	=   4 ;
		blp->dir	= blp->slant = blp->c = 0 ;
		blp->swi	= OFF ;
		bltVar[ ENM_BLT + i ].hit_tp = BLAHT ;
	}
	bullet_vol = pblt_max + eblt_max ;

	//  ゲームスタート
	CDDA_prog_play( 2, 0 );
	chrs_entry( ON );
	bright_level( 0, 2, 0, BLV_ALL, 0 );
	msg_sw = 0, msg_c = MSGC_MAX, msg_p = &msg_set[1];
	wait_c = 0 ;

	do{
		//  レベル設定
		if( time_c / 400 != level && level < 21 ){
			++level ;
//			if( 0 < level && level % 4 == 0 ) wait_c = WAIT_MAX ;
			eshot_vol = lv_tbl[level].shot ;
			enemy_vol = lv_tbl[level].enemy ;
			put_string_s( 288-2*8, 0, set_num( level, -2 ), cvcl[7], cvcl[1] );
		}
		SND_joy_in_2( 0, &button );
		ct_trig = bs_trig, bs_trig = ~button>>4 & 15, trig = bs_trig&~ct_trig ;

		//  ショット処理
		for( n = 0, shot_emp = pblt_max ; n < pblt_max ; ++n ){
			if( bullet[n].swi == ON ) move_shot( n ); else shot_emp = n ;
		}
		//  プレイヤー機の操作
		runCH = &actCH[0];
		if( runCH->dmg_c != 0 ) damage( 0 );
		else{
			runCH->XH += (		 pad_plus[ button & 0xf ].x )* 2 ;
			runCH->YH += ( mvy = pad_plus[ button & 0xf ].y )* 2 ;
			     if( runCH->XH <  16 ) runCH->XH =  16 ;
			else if( 239 < runCH->XH ) runCH->XH = 239 ;
			     if( runCH->YH <  24 ) runCH->YH =  24 ;
			else if( 247 < runCH->YH ) runCH->YH = 247 ;

			//  プレイヤー機の傾き
			if( mvy == 0 && slant != 0 ) slant -= ( 0 < slant )? 1 : -1 ;
			else if( ABS( slant + mvy ) <= 11 ) slant += mvy ;
			chr[PLYN].c = ( slant <= 3 ? -slant/4 : slant/4+2 )<< 2 ;

			//  ショット発射
			if( (trig&2)!= 0 && shot_emp < pblt_max && chr[0].swi ){
				( blp = &bullet[ shot_emp ] )->swi = ON ;
				blp->XH = runCH->XH + 16, blp->YH = runCH->YH + 3 ;
				dim_voice( 0, 127, blp->XH - 128, 0 );
			}
		}
		//  エネミーブラスター処理
		for( n = ( blast_emp = EBLT_MAX )- 1 ; ENM_BLT <= n ; --n ){
			if( bullet[n].swi == ON ) move_blaster( n ); else blast_emp = n ;
		}
		//  エネミー処理
		for( n = 1, enm_left = enemy_vol ; n <= show_max ; n++ ){
			runCH = &actCH[n] ;
			if( runCH->swi == ON ){
					 if( runCH->dmg_c  != 0 ) damage( n );
				else if( move_enemy(n) != 0 ) actCH[PLYN].dmg_c = DMG_MAX ;
				--enm_left ;
			}
		}
		//  エネミー登場
		if( wait_c == 0 && msg_sw == 0 && 0 < enm_left ){
			if( ( enm_c -= ( 0 < enm_c )) == 0 ){
				for( n = 1 ; n <= show_max && actCH[n].swi == ON ; n++ );
				set_chpara( n, TP_ENM, 272, PR_RND );
				enm_c = ( ( 280/enemy_vol ) + ( rand()>> 8 & 15 ) - 8 )/ SPD ;
				--enm_left ;
			}
		}
		for( n = 0 ; n <= show_max ; n++ ){
			chr[n].XH = FRAME_IX( actCH[n].XH );
			chr[n].YH = FRAME_IY( actCH[n].YH );
		}
		//  エフェクト処理
		for( n = ( efxch_spc = efxch_c )- 1, efxch_c = 0 ; 0 <= n ; --n ){
			fxp = &effect_ch[ n ];
			if( ( fxp->swi & 1 ) != 0 ){
				efp = &efx_idx[ fxp->type ];
				if( fxp->ac == 2 && 0 <= efp->vc ){
					dim_voice( efp->vc, 127, fxp->XH - 128, 0 );
				}
				fxp->c	  = efx_pat[ efp->ofs + fxp->ac ].pat ;
				fxp->pal  = efx_pat[ efp->ofs + fxp->ac ].pal ;
				fxp->size = efx_pat[ efp->ofs + fxp->ac ].siz ;
				if( efp->max < ++fxp->ac ){
					fxp->swi &= ~1, chr[ fxp->cn ].efx_n = NO, fxp->cn = NO ;
				}
			}
				 if( fxp->swi == 0 ) efxch_spc = n ;
			else if( efxch_c  <= n ) efxch_c   = n + 1 ;
		}

		if( 0 < end_c ){ //  ゲームオーバー処理
			if( trig == 0 ) --end_c ; else end_c = 0 ;

		}else if( actCH[ PLYN ].swi == OFF ){
			if( msg_sw ){
				box_fill( msg_p->x, msg_p->y, msg_sw*8, 8,PSET,0x8000,0x8000 );
				msg_sw = 0 ;
			}
			CDDA_prog_play( 3, 1 );
			end_c = msg_c = 10 * 20, msg_p = &msg_set[0];

		}else if( 0 < wait_c ){ //  ワーニング処理
			switch( WAIT_MAX - ( wait_c-- ) ){
			case 20 : msg_c = MSGC_MAX, msg_p = &msg_set[2]; break ;
			}
//			put_string_s( 32, 232, set_num( wait_c, 3 ), cvcl[7], cvcl[1] );
		}else{
			++time_c ;
		}
		chrs_put( show_max, 1, 0 );

		if( 0 < msg_c ){ //  メッセージ
			if( msg_sw == 0 ){
				put_string_s(msg_p->x,msg_p->y,msg_p->str,msg_p->fc,msg_p->bc);
				msg_sw = strlen( msg_p->str );
			}else if( --msg_c == 0 ){
				box_fill( msg_p->x, msg_p->y, msg_sw*8, 8,PSET,0x8000,0x8000 );
				msg_sw = 0 ;
			}
		}else if( ( trig & 4 )!= 0 ){ //  ポーズ
			put_string_s( 140,116, "PAUSE", GRB(31,31,31), cvcl[1] );
			wait_Rtrg();
			box_fill( 140,116, 5*8, 8, PSET, 0x8000, 0x8000 );
		}
	}while( end_c != 0 );

	CDDA_pause();
}
