/*
 ==============================================================================
    [uniuni.c]    「うにうに」
                          (c)1995     Programed by Y.Gotoh(Gori)
 ==============================================================================
*/

/* include */
#include <stdio.h>
#include <stdlib.h>
#include <stdusr.h>
#include <ctype.h>
#include <dos.h>
#include <fmgraph.h>

/* define */
#define STAGE 8				/* 全ステージ数 */
#define X_MIN 8				/* ボールの移動範囲 */
#define Y_MIN 8				/*        〃        */
#define X_MAX 601			/*        〃        */
#define Y_MAX 300			/*        〃        */
#define BALL_R 15			/* ボール半径 */
#define BALL_HIT 22			/* ボール当たり判定 */
#define BALL_MAX 5			/* 一度に出てくるボールの数 */
#define MAX_SPEED 13		/* ボール最大速度 */
#define KEY_WAIT 50			/* キー入力待ち時間 */

/* 疑似関数 */
#define hitany()	stkclear();				\
					while( !kbhit() )		\
						;					\
					getch()

#define uni_putlife( n )	g_box( 620 , n*10 + 341 , 19 + (ball[n].hp / 16 ) , n * 10 + 349 );

#define value(a)	( !(a) ? 0 : ( (a)>0 ) ? 1 : -1 )

/* グローバル変数 */
void far *buf[BALL_MAX] ;
struct {
		int id , hp , x , y;
		int bx , by ;
		int speed_x , speed_y;
}ball[BALL_MAX];
unsigned short	BASNOTE[12*9] = {
/* C2 */	1174, 1108, 1046, 987, 932,  880, 830, 784, 740, 698,  659, 622,
/* C1 */	587,  554,  523,  494, 466,  440, 415, 392, 370, 349,  330, 311,
/* C  */	294,  277,  262,  247, 233,  220, 208, 196, 185, 175,  165, 156,
/* c  */	147,  139,  131,  123, 116,  110, 104, 98,  92,  87,   82,  78,
/* c1 */	73,   69,   65,   62,  58,   55,  52,  49,  46,  44,   41,  39,
/* c2 */	37,   35,   33,   31,  29,   27,  26,  24,  23,  22,   21,  19,
/* c3 */	18,   17,   16,   15,  15,   14,  13,  12,  12,  11,   10,  10,
/* c4 */	9,    9,    8,    8,   7,    7,   6,   6,   6,   5,    5,   5,
/* c5 */	5,    4,    4,    4,   4,    3,   3,   3,   3,   3,    3,   2
								} ;

/* 背景表示 */
void uni_bg( int num ){
	int i,j;
	
	for( i=0 ; i<2 ; i++){
		g_setdisplaypage( i );
		g_setdrawpage( abs(!i) );
		g_cls();
		
		/* 壁 */
		g_paintcolor( 15 );
		g_box( 0 , 0 , 639 , 340 );
		g_paintcolor( 0 );
		g_box( 8 , 8 , 631 , 332 );
		
		/* ライフウィンドウ */
		g_paintcolor( 14 );
		g_box( 0 , 332 , 639 , 399 );
		g_paintcolor(  0 );
		g_box( 8 , 340 , 631 , 391 );
		
		/* ライフバー表示 */
		for(j=0 ; j< num ; j++){
			g_paintcolor( ball[j].id*2+1 );
			g_box( 19 , j*10 + 341 , 619 , j * 10 + 349 );
			g_paintcolor( ball[j].id*2+2 );
			g_box( 19 , j*10 + 344 , 619 , j * 10 + 346 );
			
			g_put( buf[ ball[j].id ] , i , ball[j].x , ball[j].y ,
						 ball[j].x+31 , ball[j].y+31 , DRAW_OR );
		}
	}
}

/* タイトル */
void title( void ){
	int x , y , i ;
	char title_message[]="ウニウニ for FM";
	
	for( i=0 ; i<200 ; i++ ){
		x = rnd( 640 );
		y = rnd( 400 );
		
		g_put( buf[ rnd( BALL_MAX ) ] , 0 , x , y , x+31 , y+31 , DRAW_OR );
	}
	
	g_charcolor( 15 );
	g_string( 141 , 12*16 , 1 , (char far *)title_message );
	g_charcolor(14);
	g_string( 139 , 12*16 , 1 , (char far *)title_message );
	
	g_charheight(16);
	g_charcolor(15);
	g_string( 28*8 , 18*16 , 1 , (char far *)"Hit Any Key to Start!" );
	
	hitany();
	g_setdisplaypage( 1 );
	txt_cls();
	g_cls();
}

/* ウニ初期設定 */
int uni_init( int stage ){
	int i;
	static int chars[ STAGE ] = 		{	2, 2, 3, 3, 3, 4, 4, 2 };
	static int ids[STAGE][BALL_MAX-1]=	{	1, 0, 0, 0,
											3, 0, 0, 0,
											1, 3, 0, 0,
											2, 3, 0, 0,
											3, 3, 0, 0,
											1, 2, 3, 0,
											1, 1, 3, 0,
											4, 0, 0, 0
										};
	
	(stage+1 == STAGE) ? g_palette( 0 , 0 ,  0 ,   0 ):
						 g_palette( 0, 79 , 63 , 111 );
	/* 能力値設定 */
	for( i=1 ; i< BALL_MAX-1 ; i++ ){
		ball[i].id = ids[ stage ][i-1];
		ball[i].hp = 2400 ;
		ball[i].bx = ball[i].x = rnd(X_MAX - X_MIN ) + X_MIN ;
		ball[i].by = ball[i].y = rnd(Y_MAX - Y_MIN ) + Y_MIN ;
		ball[i].speed_x = 0;
		ball[i].speed_y = 0;
		uni_putlife( i );
	}
	uni_putlife(0);
	
	return( chars[ stage ] );
}

/* コンピューター思考ルーチン */
void uni_cpu( int i ){
	if(ball[i].hp)
	switch( ball[i].id ){
		case 1:	/* ひたすら追っ掛け型 */
			ball[i].speed_x+=value( ball[0].x - ball[i].x );
			ball[i].speed_y+=value( ball[0].y - ball[i].y );
			break;
		case 2:	/*  */
			ball[i].speed_x += value( ball[0].x - ball[i].x )
				* ( ( abs( ball[0].x - ball[i].x ) < 200 ) ? 0 : 1 );
			ball[i].speed_y += value( ball[0].y - ball[i].y )
				* ( ( abs( ball[0].y - ball[i].y ) < 200 ) ? 0 : 1 );
			
			if( ball[i].x-80 < X_MIN )	ball[i].speed_x+=2;
			else	if( ball[i].x+80 > X_MAX )	ball[i].speed_x-=2;
			if( ball[i].y-80 < Y_MIN )	ball[i].speed_y+=2;
			else	if( ball[i].y+80 > Y_MAX )	ball[i].speed_y-=2;
			break;
		case 3:	/* 突撃型 */
			ball[i].speed_x += value( ball[0].x - ball[i].x )
				* ( ( abs( ball[0].x - ball[i].x ) < 60 ) ? 2 : 1 );
			ball[i].speed_y += value( ball[0].y - ball[i].y )
				* ( ( abs( ball[0].y - ball[i].y ) < 60 ) ? 2 : 1 );
			break;
		case 4:	/* 最強？ */
			ball[i].speed_x+=value( ball[0].x - ball[i].x )
				* ( ( abs( ball[0].x - ball[i].x ) < 200 ) ? 1 : 2);
			ball[i].speed_y+=value( ball[0].y - ball[i].y )
				* ( ( abs( ball[0].y - ball[i].y ) < 200 ) ? 1 : 2);
			
			if( ball[i].x-60 < X_MIN )	ball[i].speed_x++;
			else	if( ball[i].x+60 > X_MAX )	ball[i].speed_x--;
			if( ball[i].y-60 < Y_MIN )	ball[i].speed_y++;
			else	if( ball[i].y+60 > Y_MAX )	ball[i].speed_y--;
			break;
	}
}

/* 各種チェック */
void uni_check( const int i , const int num ){
	int j;
	
	/* 速度制限 */
	if( ball[i].speed_x > MAX_SPEED )	ball[i].speed_x = MAX_SPEED;
	else	if( ball[i].speed_x < -MAX_SPEED )	ball[i].speed_x = -MAX_SPEED;
	
	if( ball[i].speed_y > MAX_SPEED )	ball[i].speed_y = MAX_SPEED;
	else	if( ball[i].speed_y < -MAX_SPEED )	ball[i].speed_y = -MAX_SPEED;
	
	/* 跳ね返り */
	if( ball[i].x > X_MAX ){
				ball[i].x = X_MAX - ( ball[i].x - X_MAX );
				ball[i].speed_x = -ball[i].speed_x;
				ball[i].hp -= ball[i].speed_x * ball[i].speed_x;
				_beep( 20 , BASNOTE[84]);
	}else	if( ball[i].x < X_MIN ){
				ball[i].x = X_MIN - ( ball[i].x - X_MIN );
				ball[i].speed_x = -ball[i].speed_x;
				ball[i].hp -= ball[i].speed_x * ball[i].speed_x;
				_beep( 20 , BASNOTE[84]);
	}
	if( ball[i].y >= Y_MAX ){
				ball[i].y = Y_MAX - ( ball[i].y - Y_MAX );
				ball[i].speed_y = -ball[i].speed_y;
				ball[i].hp -= ball[i].speed_y * ball[i].speed_y;
				_beep( 20 , BASNOTE[84]);
	}else	if( ball[i].y < Y_MIN ){
				ball[i].y = Y_MIN - ( ball[i].y - Y_MIN );
				ball[i].speed_y = -ball[i].speed_y;
				ball[i].hp -= ball[i].speed_y * ball[i].speed_y;
				_beep( 20 , BASNOTE[84]);
	}
	
	/* 当たり判定*/
	for( j = (i+1) ; j < num ; j++ ){
		if( abs(ball[j].x - ball[i].x) < BALL_HIT && 
			abs(ball[j].y - ball[i].y) < BALL_HIT ){
			
			/* 当たり音 */
			_beep( 20 , BASNOTE[83]);
			
			/* 跳ね返り */
			iswap( &ball[i].speed_x , &ball[j].speed_x );
			iswap( &ball[i].speed_y , &ball[j].speed_y );
			
			/* ダメージを受ける */
			ball[i].hp -= ( ball[i].speed_x * ball[i].speed_x 
							+ ball[i].speed_y * ball[i].speed_y );
			ball[j].hp -= ( ball[j].speed_x * ball[j].speed_x
							+ ball[j].speed_y * ball[j].speed_y );
			
			/* 接近戦を防ぐ */
			ball[i].speed_x +=5 ; ball[i].speed_y +=5;
			ball[j].speed_x +=5 ; ball[j].speed_y +=5;
		}
	}
}

/* プレイヤー */
int uni_player( int i ){
	register int j=0;
	char key = '\0';
	
	stkclear();
	/* キー入力待ち */
	for( ; j < KEY_WAIT ; j++ ){
		if( kbhit() ){
			key = getch();
			tolower( key );
			for( ; j < KEY_WAIT ; j++ ){	/* 押された後の時間稼ぎ */
				if( kbhit() )	getch();
			}
			break;
		}
	}
	
	switch( key ){
		case'\x1c':	/* → */
			ball[i].speed_x+=2;
			break;
		case'\x1d':	/* ← */
			ball[i].speed_x-=2;
			break;
		case'\x1e':	/* ↑ */
			ball[i].speed_y-=2;
			break;
		case'\x1f':	/* ↓ */
			ball[i].speed_y+=2;
			break;
		case'\x0d':
		case'\x0c':
		case'\x1b':
			return(1);
	}
	return(0);
}

/* ゲームメインプログラム */
int uni_gamemain( const int num ){
	register int i , flag = num;
	register int g_page = 0;	/* 書き込みページ */
	
	/* 書き込みページと表示ページを変えることで、チラつきを減少 */
	
	g_setdisplaypage(0);
	g_setdrawpage(0);
	g_paintcolor(0);
	
	while( flag > 1 && ball[0].hp ){
		flag = 0;
		g_setdrawpage( g_page );
		for( i = 0 ; i < num ; i++){
			/* 消す */
			g_circle( ball[i].bx + 15 , ball[i].by + 15 , BALL_R );
			
			if( ball[i].hp ){
				/* 思考ルーチンへ */
				if( !ball[i].id ){
					if( uni_player(i) )	return(1);
				}
				else	uni_cpu(i);
				
				/* 移動元座標保存 */
				ball[i].bx = ball[i].x;
				ball[i].by = ball[i].y;
				
				/* 移動 */
				ball[i].x+=ball[i].speed_x;
				ball[i].y+=ball[i].speed_y;
				
				/* 判定 */
				uni_check( i , num );
				
				if( ball[i].hp < 1 ){
					ball[i].hp = 0;		/* 死んだ奴の当たり判定を消す */
					ball[i].x = 999;
					ball[i].y = 999;
					uni_putlife( i );
					continue;
				}
				
				/* 表示 */
				g_put( buf[ ball[i].id ] , g_page , ball[i].x , ball[i].y ,
							 ball[i].x+31 , ball[i].y+31 , DRAW_OR );
			}
			flag += value( ball[i].hp );
			
			/* 残りライフ表示 */
			uni_putlife( i );
		}
		/* 全ての処理の終わった画面を表示 */
		g_setdisplaypage( g_page );
		
		/* 描画画面切替え */
		g_page = abs( !g_page );
	}
	
	/* 自分が死んだら1､敵全滅で0を返す */
	if( !ball[0].hp )	return(1);
	else return(0);
}

/* ゲームオーバー */
void uni_over( void ){
	g_cls();
	g_setdrawpage(0);
	g_setdisplaypage(0);
	
	stkclear();
	
	g_charcolor( 15 );
	g_charheight( 48 );
	g_string( 20*6 , 14*16 , 1 , (char far *)"ＧＡＭＥ　ＯＶＥＲ" );
	
	hitany();
}

/* エンディング */
void uni_end( void ){
}

/* ウニメイン */
void uni_main( void ){
	int i;
	int num;
	int check=0;
	
	/* プレイヤー能力設定 */
	ball[0].id = 0;
	ball[0].hp = 9600;
	
	for(i=0 ; i< STAGE ; i++){
		
		g_setdrawpage( 0 );
		g_setdisplaypage( 0 );
		
		/* 座標・速度の初期化 */
		ball[i].bx = (ball[0].x  = 319);
		ball[i].by = (ball[0].y  = 169);
		ball[0].speed_x = 0;
		ball[0].speed_y = 0;
		
		/* ステージ読み込み */
		num = uni_init( i );
		uni_bg( num );
		
		if( (check = uni_gamemain( num ) ) )	break;
		
		/*ステージクリアでライフ回復 */
		ball[0].hp += 2400;
		if( ball[0].hp > 9600 )	ball[0].hp = 9600;
	}
	
	if( check )	uni_over();
	else	uni_end();
}

/* キャラ定義 */
void char_init( void ){
	int i;
	
	for(i=0 ; i< BALL_MAX ; i++){
		buf[i] = farmalloc( 512 );
		if( buf[i] == NULL ){
			fputs( "not enough memory!\n" , stderr );
			exit(1);
		}
		
		g_bordermode( BORDER_NODRAW );
		
		g_paintmode( PAINT_FILL );
		
		g_paintcolor( i * 2 + 1 );
		g_circle( 15 , 15 , BALL_R );
		g_paintcolor( i * 2 + 2 );
		g_circle( 15 , 15 , 10 );
		
		g_get( buf[i] , 0 , 0 , 0 , 31 , 31 );
	}
}

/* パレット設定 */
void set_palette( void ){
	/*		   no	 r	   g	 b	*/
	g_palette(  0 ,  79 ,  63 , 111 );	/* バック */
	g_palette(  1 ,   0 ,  63 , 127 );	/* ウニ自機(淵) 青 */
	g_palette(  2 ,   0 ,  95 , 239 );	/* ウニ 〃 (芯)    */
	g_palette(  3 , 127 ,  63 ,   0 );	/* ウニ敵機(淵) 赤 */
	g_palette(  4 , 239 ,  95 ,   0 );	/* ウニ 〃 (芯)    */
	g_palette(  5 ,   0 , 127 ,   0 );	/* ウニ敵機(淵) 緑 */
	g_palette(  6 ,   0 , 239 ,   0 );	/* ウニ 〃 (芯)    */
	g_palette(  7 , 127 , 127 ,   0 );	/* ウニ敵機(淵) 黄 */
	g_palette(  8 , 239 , 239 ,   0 );	/* ウニ 〃 (芯)    */
	g_palette(  9 ,  31 ,  31 ,  31 );	/* ウニ敵機(淵) 黒 */
	g_palette( 10 ,  63 ,  63 ,  63 );	/* ウニ 〃 (芯)    */
	g_palette( 11 ,   0 ,   0 ,   0 );	
	g_palette( 12 ,   0 ,   0 ,   0 );	
	g_palette( 13 ,   0 ,   0 ,   0 );	
	g_palette( 14 , 159 , 239 , 159 );	/* ウインドウ枠 */
	g_palette( 15 , 239 , 127 , 239 );	/* 壁 */
}

/* メイン */
void main( void ){
	/* 初期設定 */
	txt_cls();
	txt_csloff();
	t_rand();
	g_init( NULL );
	g_setcompatiblemode( COMPATI_EXTD );	/* 16色モード */
	g_setdrawpage( 0 );
	
	/* 文字描画初期設定 */
	g_chardirection(0,-1,1,0);
	g_charhandling(1);
	g_charheight(48);
	g_charwidth(1,1);
	g_strdirection(0);
	
	g_setdisplaypage( 1 );					/* 初期設定中の画面を見せない */
	{
		set_palette();
		char_init();
		g_cls();
		g_paintcolor( 0 );
		g_box( 0, 0, 639 , 399 );
	}
	g_setdisplaypage( 0 );
	
	title();								/* タイトル */
	
	{
		/* モードセレクト */
		uni_main();							/* メインルーチン */
		/* 二人用 */
		
	}
	/* 終了 */
	g_cls();
	txt_cslon();
	g_cls();
}
