/******************************************************************/
//	ＧＡＬＡＸＹ　ＦＯＲＭ
//		Ｉ／Ｏ合本 ＭＺ−８０活用研究 よりの移植
//	Copyright (C) 1980 by Hudson-soft S.Nakamoto
//    Ａｒｒａｎｅｄ  １９９７,９８  ｂｙ  ＡＩＧ−ＳＯＦＴ
/******************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>	// exit
#include <process.h>
#include <ctype.h>
#include <conio.h>
#include <jfctype.h>
#include <iocslib.h>
#include <audio.h>
#include "defines.h"
#include "doslibm.h"
#include "aigtext.h"
#include "amacro.h"
#include "ascreen.h"
#include "astdio.h"
#include "aenv.h"
#include "await.h"
#include "anum.h"
#include "abuff.h"
#include "font.h"
#include "akey.h"
#include "skey.h"

char prog[]="@@> GFORM.C : 1998/01/16 FRI : GAME : GALAXY_FORM";

// 97/10/18 初版
// 97/11/09 JOYSTICKボタン割り付け変更,一部キーによる動作がおかしかったので修正
//          画面モード強制変更(ファイラーからの直接起動対応)
// 98/01/16 効果音ウエイト微調整,ゲーム開始時にいきなり砲台が破壊されることがあるのを修正
//          PAUSE時にも'!'でexit出来るようにする
/******************************************************************/

#define	URIGHT	0x77	/* UFO右:＼ */
#define	ULEFT	0x76	/* UFO左:／ */
#define	UCENT1	'@'	/* UFO真ん中1 */
#define	UCENT2	'O'	/* UFO真ん中2 */
#define	MISSL	'\\'	/* UFOミサイル */

#define	IRIGHT	0xe7	/* 右向き隕石 */
#define	ILEFT	0xe8	/* 左向き隕石 */
#define	IHEAD	0x47	/* 隕石頭:● */

#define	HHEAD	0x41	/* 砲台頭 */
#define	HBODY	0x48	/* 砲台胴体:○ */

/* タイトル画面のみ */
#define	HOSHI	'.'	/* 星 */
#define	LBAR	'^'	/* ＼ : ロボット頭 */
#define	RBAR	'/'	/* ／ : ロボット頭 */
#define	LSAN	'<'	/* ebf4 : 左△ */
#define	RSAN	'>'	/* ebf3 : 右△ */
#define	LSIDE	'{'	/* ec61 : ロボット顔左 */
#define	RSIDE	'}'	/* ec43 : ロボット顔右 */
#define	LASHI	':'	/* ec73 : ロボット足左 */
#define	RASHI	';'	/* ec72 : ロボット足右 */
#define	TASHI	'+'	/* ┳ : ロボット足付け根 */
#define	LUBLK	'7'	/* ┏ */
#define	RUBLK	'9'	/* ┓ */
#define	LLBLK	'1'	/* ┗ */
#define	RLBLK	'3'	/* ┛ */
#define	VBAR	'|'	/* ┃ */
#define	HBAR	'-'	/* ━ */

#define	GATE	'~'	/* ec78 */
#define	LSHIP	'['	/* ec65 */
#define	RSHIP	']'	/* ec64 */
#define	BASE1	'%'	/* ec66 */

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

#define GWX	96		/* 実画面横幅 */
#define GWY	31		/* 実画面縦幅 */
#define WX	40		/* ゲーム画面横幅 */
#define WY	25		/* ゲーム画面縦幅 */
// ゲームキャラクターの表示は全て全角なので、画面幅はGWX/2
#define	CX	((GWX/2-WX)/2*2)	/* ゲーム画面を実画面の真ん中に持ってくる */
#define	CY	((GWY-WY)/2)		/* ゲーム画面を実画面の真ん中に持ってくる */

#define RECHIGH	"GFROM.HSC"	/* ハイ・スコア ファイル名 */

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

int	sco,hsc;	/* 得点 */
int	men;		// 仮の面（ゲーム自体に面の概念はない）
int	hono,hoe,ho,hol;	// 砲台関係
int	add,gnd;		// 砲台関係
int	ashi;		// ＵＦＯの動きの方向
int	dlay;		// ウエイト
int	ufo[8+1];	// ＵＦＯ座標
int	misl[6+1];	// ＵＦＯミサイル座標
int	insk[9+1];	// 隕石座標

// ADPCM用変数
unchar	*PCMappl,*PCMufo1,*PCMufo2,*PCMbang,*PCMhod,*PCMbufo;
int	 LENappl, LENufo1, LENufo2, LENbang, LENhod, LENbufo;

unchar	*exec;		/* PANIC 起動プログラム */

/******************************************************************/
// 仮想ＶＲＡＭ
/******************************************************************/

struct FNTBUF
f_ec67 = {16,16
	,{
	0xc0,0x00,0xf0,0x30,0xd8,0x30,0xcc,0x30,
	0xc6,0x30,0xc3,0x30,0xc1,0xb0,0xc0,0xff,
	0xc0,0xff,0xc1,0xb0,0xc3,0x30,0xc6,0x30,
	0xcc,0x30,0xd8,0x30,0xf0,0x30,0xc0,0x00}
},
f_ec68 = {16,16
	,{
	0x00,0x03,0x0c,0x0f,0x0c,0x1b,0x0c,0x33,
	0x0c,0x63,0x0c,0xc3,0x0d,0x83,0xff,0x03,
	0xff,0x03,0x0d,0x83,0x0c,0xc3,0x0c,0x63,
	0x0c,0x33,0x0c,0x1b,0x0c,0x0f,0x00,0x03}
},
f_ec5f = {16,16
	,{
	0x01,0x00,0x07,0xc0,0x0f,0xe0,0x1f,0xf0,
	0x3f,0xf8,0x7f,0xfc,0x7f,0xfe,0xff,0xff,
	0xff,0xff,0xff,0xff,0xfb,0xbe,0x73,0x9c,
	0x03,0x80,0x03,0x80,0x03,0x80,0x07,0xc0}
},
f_ec61 = {16,16
	,{
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff}
},
f_ec43 = {16,16
	,{
	0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,
	0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,
	0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,
	0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00}
},
f_ec73 = {16,16
	,{
	0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,
	0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,
	0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,
	0x00,0x03,0x00,0x03,0xff,0xff,0xff,0xff}
},
f_ec72 = {16,16
	,{
	0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,
	0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,
	0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,
	0xc0,0x00,0xc0,0x00,0xff,0xff,0xff,0xff}
},
f_ebf3 = {16,16
	,{
	0x80,0x00,0xc0,0x00,0xe0,0x00,0xf0,0x00,
	0xf8,0x00,0xfc,0x00,0xfe,0x00,0xff,0x00,
	0xff,0x80,0xff,0xc0,0xff,0xe0,0xff,0xf0,
	0xff,0xf8,0xff,0xfc,0xff,0xfe,0xff,0xff}
},
f_ebf4 = {16,16
	,{
	0x00,0x01,0x00,0x03,0x00,0x07,0x00,0x0f,
	0x00,0x1f,0x00,0x3f,0x00,0x7f,0x00,0xff,
	0x01,0xff,0x03,0xff,0x07,0xff,0x0f,0xff,
	0x1f,0xff,0x3f,0xff,0x7f,0xff,0xff,0xff}
},
f_ec78 = {16,16
	,{
	0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
},
f_ec65 = {16,16
	,{
	0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,
	0x01,0x80,0x01,0xc0,0x00,0xe0,0x00,0x7f,
	0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
},
f_ec64 = {16,16
	,{
	0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,
	0x01,0x80,0x03,0x00,0x07,0x00,0xfe,0x00,
	0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
},
f_ec66 = {16,16
	,{
	0x33,0x33,0x33,0x33,0xcc,0xcc,0xcc,0xcc,
	0x33,0x33,0x33,0x33,0xcc,0xcc,0xcc,0xcc,
	0x33,0x33,0x33,0x33,0xcc,0xcc,0xcc,0xcc,
	0x33,0x33,0x33,0x33,0xcc,0xcc,0xcc,0xcc}
};

static unchar tvram[WY][WX];

void printxy(int x,int y,unchar c)
/* 仮想画面と実画面変換を行う */
/* 内部処理キャラクターと表示キャラクターの変換を行う */
/* 仮想画面はWX桁、WY行で(0,0)〜(WX-1,WY-1)を表す */
{
_FNTBUF *font,font0;
int cc=0;
	/* 仮想テキストＶＲＡＭに出力 */
	tvram[y][x]=c;
	
	/* 座標変換 */
	/* 最下行は使えない */
	B_LOCATE(CX+x*2,CY+y);
	
	/* キャラクター変換 */
	switch (c) {
		// 半角全角変換
		default   :cc=hantozen(c); break;
		
		// 特殊変換
		case LBAR  :
		case URIGHT:cc=L'＼'; break;
		case RBAR  :
		case ULEFT :cc=L'／'; break;
		case UCENT1:
		case IHEAD :cc=L'●'; break;
		case UCENT2:
		case HBODY :cc=L'○'; break;
		//
		case TASHI :cc=L'┳'; break;
		case LUBLK :cc=L'┏'; break;
		case RUBLK :cc=L'┓'; break;
		case LLBLK :cc=L'┗'; break;
		case RLBLK :cc=L'┛'; break;
		case VBAR  :cc=L'┃'; break;
		case HBAR  :cc=L'━'; break;
		
		/* 外字変換 */
		case IRIGHT:font=&f_ec67; break;
		case ILEFT :font=&f_ec68; break;
		case HHEAD :font=&f_ec5f; break;
		//
		case LSIDE :font=&f_ec61; break;
		case RSIDE :font=&f_ec43; break;
		case LASHI :font=&f_ec73; break;
		case RASHI :font=&f_ec72; break;
		case LSAN  :font=&f_ebf4; break;
		case RSAN  :font=&f_ebf3; break;
		case GATE  :font=&f_ec78; break;
		case LSHIP :font=&f_ec65; break;
		case RSHIP :font=&f_ec64; break;
		case BASE1 :font=&f_ec66; break;
	}
	if (cc!=0) {
		// 普通文字
		B_PUTC(cc);
	} else {
		if (B_COLOR(-1)!=TWHITE) {
			// フォントが加工される時はバッファーにコピーしてから行う
			font0=*font; // 構造体のコピー
			font=&font0;
		}
		// 外字
		fontset(font);
		FONTPUT2(font);
	}
}

/******************************************************************/

void MZprint(int adrs,unchar c)
// MZ-80のアドレスのまま与えて表示する
{
int x,y;
	adrs-=0xd000;
	y=adrs/40;
	x=adrs-y*40;
	printxy(x,y,c);
}

/******************************************************************/

void printxys(int x,int y,unchar *s)
// 文字列表示
{
int i;
	for(i=0;i<strlen(s);i++) {
		printxy(x,y,s[i]);
		x++;
		if (x>=WX) {x=0; y++;}
		if (y>=WY) y=WY-1;
	}
}

/******************************************************************/

int scrn(int x,int y)
/* 仮想テキストＶＲＡＭを読み出す */
{
	return(tvram[y][x]);
}

/******************************************************************/

int MZscrn(int adrs)
// MZ-80のアドレスのまま与えて読み出す
{
int x,y;
	adrs-=0xd000;
	y=adrs/40;
	x=adrs-y*40;
	return(tvram[y][x]);
}

/******************************************************************/

void init_tvram()
/* 仮想テキストＶＲＡＭ初期化 */
{
int x,y;
	for (y=0;y<WY;y++) {
		for (x=0;x<WX;x++) tvram[y][x]=' ';
	}
}

/******************************************************************/

void pop_tvram()
/* 仮想テキストＶＲＡＭ表示 */
{
int x,y;
	for (y=0;y<WY;y++) {
		for (x=0;x<WX;x++) printxy(x,y,tvram[y][x]);
	}
}

/******************************************************************/

int center(int y,unchar *mes)
// メッセージをセンタリングで表示
{
int x;
	x=(GWX-strlen(mes))/2; /* センタリング */
	B_LOCATE(x,y+CY);
	B_PRINT(mes);
	return(x);
}

/******************************************************************/

void message(unchar *mes,int tim)
/* メッセージを指定回数点滅表示する */
{
int i,x;
	x=(GWX-strlen(mes))/2; /* センタリング */
	for (i=0;i<tim;i++) {
		B_LOCATE(x,GWY/2); B_PRINT(mes);
		vwait(20);
		B_LOCATE(x,GWY/2); eline();
		beep();
		vwait(20);
	}
}

/******************************************************************/

int LeftDisp(int y,unchar *mes,unchar ch,int mode)
// 文字列が本来センタリングで表示される場所に左から１文字づつ表示する
// ２文字単位専用
// mode=0:通常 , 1:ウエイトなし
{
unchar s[WX];
int i,x,len,ky;
	len=strlen(mes);
	x=(GWX-len)/2;
	for (i=2;i<=len;i+=2) {
		memcpy(s,mes,i);
		s[i]=0;
		B_LOCATE(x,y+CY);
		B_PRINT(s);
		if (!mode) {
			if (kbhit()) { // ここではPANICは効かない
				ky=getch();
				if (toupper(ky)==ch) return(1);
			}
			vwait(14);
		}
	}
	return(0);
}

/******************************************************************/

void WaitPCM()
{
	while (a_stat()); // 音が止まるまで待つ
}

void PCMplay(unchar *pcm,int len,int fq)
// ADPCMを鳴らす
{
	if (len>0) {
	    int f=0;
		if (fq<0) {f=1; fq=-fq;}
		a_play(pcm,len,fq,3,len);
		if (!f) WaitPCM(); // 音が止まるまで待つ
	}
}

/******************************************************************/

void ACLS()
{
	cls();
	init_tvram();	/* 仮想テキストＶＲＡＭクリア */
}

/******************************************************************/

void print_score(int sco)
{
	if (sco<0) {B_PUTC('-'); sco=-sco;} // マイナス得点
	print_decII(sco,'0',5);
	B_PUTC(' '); // - → +になった時の後ろ消しのため
}

/******************************************************************/

void hi_score()
/* ハイスコア表示 */
{
	B_LOCATE(CX+1,CY-1); B_PRINT("HI-SCORE:");
	print_score(hsc);
}

/******************************************************************/

void score(int point)
/* 得点加算＆表示 */
{
	sco+=point;
	B_LOCATE(CX+19,CY-1); B_PRINT("SCORE:");
	print_score(sco);
}

/******************************************************************/

void left()
// 残機数表示
{
	B_LOCATE(CX+38,CY-1); B_PRINT("LEFT:");
	print_decII(hono,'0',2);
}

/******************************************************************/

void title()
/* タイトル表示 */
{
int i;
	// 画面枠
	for (i=CY-3;i<=CY+WY;i++) { // 縦
		B_LOCATE(CX-2   ,i); B_PUTC(L'☆');
		B_LOCATE(CX+WX*2,i); B_PUTC(L'☆');
	}
	for (i=CX-2;i<=CX+WX*2;i+=2) { // 横
		B_LOCATE(i,CY-3 ); B_PUTC(L'☆');
		B_LOCATE(i,CY+WY); B_PUTC(L'☆');
	}
	#if 0
	for (i=CX;i<=CX+WX*2-2;i+=2) { // 横
		B_LOCATE(i,CY-1); B_PUTC(L'−');
	}
	#endif
	B_COLOR(TCYAN|HLIGHT|REVERSE);
	center(-2,"＊＊　ＧＡＬＡＸＹ ＦＯＲＭ − ＰＲＯ６８Ｋ　＊＊");
	B_COLOR(TWHITE);
	score(0);
	hi_score();
	left();
}

/******************************************************************/

int CalcHodai()
// ミサイルの初期位置を決定（まわりに隕石のいないところ）
{
#define	HY	21
#define	AX	5
int i,x=18;	// x>=AX
	do {
		for (i=-AX;i<=AX;i++) {
			if (scrn(x+i,HY)!=' ') break; // だめ
		}
		if (i>3) break; // 全てok
		x++;
		if (x>=40-AX) x=AX;
	} while (x!=18); // 一周するまで
	// キーボードバッファークリア
	while (kbhit()) getch();
	return(0xd000+HY*40+x);
}

void ResetHodai()
// 砲台セット(L7000)
{
int i;
	hoe=0;
	MZprint(ho   ,' ');
	MZprint(ho+40,' ');
	MZprint(ho+80,' ');
	score(0);
	if (insk[1]!=45) {
		if (insk[1]==66) return;
	}
	hol=ho=CalcHodai(); // ミサイルの初期位置を決定
	hoe=1;
	gnd=add=0;
	// 現在画面上にあるミサイルを消す
	for (i=1;i<=6;i++) {
		if (misl[i]!=0) MZprint(misl[i],' ');
		misl[i]=0;
	}
	WaitPCM();
}

/******************************************************************/

int CrashHodai()
// 砲台破壊(L6000)
{
	PCMplay(PCMbang,LENbang*2/3,-4); // 2/3の音でも十分（全部出すと長くて遅いから）
	B_COLOR(TYELLOW|HLIGHT);
	MZprint(ho   ,'*');
	MZprint(ho+40,'*');
	MZprint(ho+80,'*');
	B_COLOR(TWHITE);
	hono--;
	left();
	WaitPCM();
	if (hono==0) return(1); // 終わり
	ResetHodai();
	return(0); // 続く
}

/******************************************************************/

int MakeDisp0(int mode)
// タイトル画面
{
int i,x,y;
	ACLS();
	
	// 画面に星を撒く
	for (i=1;i<=99;i++) {
		x=rand2(WX);
		y=rand2(WY);
		printxy(x,y,HOSHI);
	}
	
	// タイトル表示
	title();
	
	// スタートボタン待ち
	center(4,"Ｐ Ｌ Ａ Ｙ");
	if (!mode) vwait(14);
	if (LeftDisp(6,"ＧＡＬＡＸＹ　ＦＯＲＭ",'S',mode)) return('S');
	if (!mode) vwait(14);
	if (LeftDisp(8,"ＰＵＳＨ　ＯＮＬＹ 'Ｓ' ＢＵＴＴＯＮ",'S',mode)) return('S');
	if (!mode) vwait(14);
	
	x=WX/2-2;
	printxys(x,15,"^<>/");
	printxys(x,16,"{@@}");
	printxys(x,17,"[++] 7-9");
	printxys(x,18,"(%%~ |S|");
	printxys(x,19," :;  1-3");
	center(13,"ＳＴＡＲＴ！");
	return(0);
}

int disp()
/* 画面初期化 */
{
int ky;
time_t ts,t;
	do {
		// 画面作成
		if (MakeDisp0(0)) return('S');
		
		PCMplay(PCMappl,LENappl,-4);
		
		time(&ts);
		do {
			ky=getky(1);
			if (ky=='S') break;
			time(&t);
		} while (t-ts<20); /* 20秒たったら自動的に再表示する */
		a_end(); // ADPCMを止める
	} while (ky!='S');
	return(ky);
}

/******************************************************************/

int getky(int mode)
/* キー入力：複数キーの設定を可能にするため */
/* でも基本操作はテンキーで行う */
/* 一応JOYSTICK対応だよ */
{
int c,js;
	// 使うキー
	// 456(=789,123)
	c=inkey2(1,0);
	if (c==0) { /* not key enter */
		/* JOY STICK input */
		js=JOYGET(0)&JOYGET(1);
		if ((js&BIT6)==0) c='5';
		if ((js&BIT5)==0) c='5'; // MISSL
		if ((js&BIT3)==0) c='6';
		if ((js&BIT2)==0) c='4';
	}
	switch (c) { /* カーソルキー対応コード変換 */
		case SKup:   /* up */
			c='5';
			break;
		case SKleft: /* left */
			c='4';
			break;
		case SKright: /* right */
			c='6';
			break;
	}
	c&=0xff;
	c=toupper(c);
	switch (c) {
		case '1':
		case '7':c='4'; break;
		case '2':
		case '8':
		case ' ':c='5';  break;
		case '9':
		case '3':c='6'; break;
		/* その他 */
		case CR:/* PANIC */
			curon();
			cls();
			spawnlp(P_WAIT,exec,exec,NULL,NULL);
			CRTMOD(16); // cwidth(96);
			if (mode) { // タイトル時
				MakeDisp0(1);
			} else { // ゲーム中
				cls();
				pop_tvram();
				title();
			}
		case ESC:/* PAUSE */
			curon();
			center(WY+2,"ＰＡＵＳＥ");
			if (KFLUSHGC()=='!') { // キー入力待ち
		    case '!':/* BREAK */
				curon();
				exit(1);
				/* なんとサブルーチンからexit()しているのだ。 */
				/* あまりお勧めは出来ない技だからみなさんはまねしないでね！ */
			}
			center(WY+2,"　　　　　");
			curoff();
			break;
	}
	return(c);
}

/******************************************************************/

int DispInseki(int i)
// 隕石表示
// ret : -1=goto L3000 , 1=GameOver , 0=Ok
{
int k,ad,j,c;
	k=(10-i)*80;
	if (insk[i]<=39) {
		ad=k+0xD050+insk[i];
		c=MZscrn(ad);
		if (c!=MISSL && c!=' ' && c!=IHEAD) {
			if (CrashHodai()) return(1);
			return(-1); // goto L3000;
		}
		MZprint(ad-2,' ');
		MZprint(ad-1,IRIGHT);
		MZprint(ad  ,IHEAD);
		for (j=0;j<=1;j++) MZprint(k+0xD078+j,' ');
	} else {
		ad=k+0xD0C6-insk[i];
		c=MZscrn(ad);
		if (c!=MISSL && c!=' ' && c!=IHEAD) {
			if (CrashHodai()) return(1);
			return(-1); // goto L3000;
		}
		MZprint(ad+2,' ');
		MZprint(ad+1,ILEFT);
		MZprint(ad  ,IHEAD);
		for (j=0;j<=1;j++) MZprint(k+0xD076+j,' ');
	}
	return(0);
}

/******************************************************************/

int GameMain()
// ret : 2=GiveUp , 1=GameOver , 0=Ok
{
int i,j,c,ufoc,dist;
	while (1) {
	    L3000:
		vwait(dlay);
		// UFO表示
		if (ashi) {
			ashi=0;
			PCMplay(PCMufo1,LENufo1,-3);
			for (i=1;i<=8;i++) {
				if (ufo[i]==0) continue; // このＵＦＯはすでに撃ち落とされている
				MZprint(ufo[i]-1,' ');
				MZprint(ufo[i]  ,URIGHT);
				MZprint(ufo[i]+1,UCENT1);
				MZprint(ufo[i]+2,ULEFT);
				MZprint(ufo[i]+3,' ');
			}
			WaitPCM();
		} else {
			ashi=1;
			PCMplay(PCMufo2,LENufo2,-3);
			for (i=1;i<=8;i++) {
				if (ufo[i]==0) continue; // このＵＦＯはすでに撃ち落とされている
				MZprint(ufo[i]-1,' ');
				MZprint(ufo[i]  ,ULEFT);
				MZprint(ufo[i]+1,UCENT2);
				MZprint(ufo[i]+2,URIGHT);
				MZprint(ufo[i]+3,' ');
			}
			WaitPCM();
		}
		// UFO 移動(L0065)
		for (i=1;i<=4;i++) {
			if (ufo[i]==0) continue; // このＵＦＯはすでに撃ち落とされている
			if (ufo[i]>=0xD048) {
				for (j=0;j<=3;j++) MZprint(ufo[i]+j,' ');
				ufo[i]=0xD028;
			}
			ufo[i]++;
		}
		for (i=5;i<=8;i++) {
			if (ufo[i]==0) continue; // このＵＦＯはすでに撃ち落とされている
			if (ufo[i]<=0xD078) {
				for (j=0;j<=3;j++) MZprint(ufo[i]+j,' ');
				ufo[i]=0xD099;
			}
			ufo[i]--;
		}
		// 隕石移動(L0082)
		for (i=1;i<=men;i++) {
			switch (DispInseki(i)) {
			    case  1: return(1);
			    case -1: goto L3000;
			}
			insk[i]++;
			if (insk[i]>79) insk[i]=2;
		}
		// ミサイルスタート(L0109)
		for (i=1;i<=8;i++) {
			if (ufo[i]==0) continue;
			if (rand2((9-men)*5)!=0) continue;
			for (j=1;j<=6;j++) {
				if (misl[j]==0) {
					misl[j]=ufo[i]+41;
					break; // １発出したら終わり
				}
			}
		}
		// ミサイル移動(L0119)
		for (i=1;i<=6;i++) {
			if (misl[i]==0) continue;
			MZprint(misl[i],' ');
			misl[i]+=40;
			c=MZscrn(misl[i]);
			if (c!=' ') {
				if (c==HHEAD||c==HBODY) {
					if (CrashHodai()) return(1);
					goto L3000;
				}
			} else {
				if (misl[i]<0xD3C0) {
					B_COLOR(TCYAN|HLIGHT);
					MZprint(misl[i],MISSL);
					B_COLOR(TWHITE);
					continue;
				}
				misl[i]=0;
			}
		}
		WaitPCM();
		//vwait(dlay);
		// 砲台移動(L0135)
		if (hoe==0) {
			ResetHodai();
			continue; // L3000
		}
		switch (getky(0)) {
			case '6': // 右
				ho++;
				break;
			case '4': // 左
				ho--;
				break;
			case '5': // 発射／上昇
				if (!gnd) { // 発射の時のみ音を出す
					PCMplay(PCMhod,LENhod,4);
					gnd=1;
				}
				add=1;
				break;
		}
		ho-=(add*40);
		if (add) {
			add=0;
		} else {
			if (gnd>0) add=rand2(2); // 発射された後はほっておいても上がる
		}
		for (i=0;i<=3;i++) MZprint(hol+i*40,' ');
		c=MZscrn(ho);
		MZprint(ho   ,HHEAD);
		MZprint(ho+40,HBODY);
		MZprint(ho+80,HBODY);
		hol=ho;
		if (ho<0xd028) {
			ResetHodai();
			continue; // 後はL3000へ
		}
		if (c==' ') continue; // L3000;
		
		// CRASH UFO(L0164)=砲台がＵＦＯ等に当たった
		if (c==MISSL||c==IRIGHT||c==ILEFT) {
			if (CrashHodai()) return(1);
			continue; // L3000;
		}
		sco+=((men-3)*50);
		if (c==IHEAD) {
			if (CrashHodai()) return(1);
			continue; // L3000;
		}
		if (c==UCENT1||c==UCENT2) {
			// 中心に当たった時はさらに倍の得点
			sco+=((men-3)*50);
		}
		dist=10000;
		ufoc=0;
		for (i=1;i<=8;i++) {
			if (ufo[i]==0) continue;
			ufoc++;
			if (ho>ufo[i]) {
				c=ho-ufo[i];
			} else {
				c=ufo[i]-ho;
			}
			if (c<dist) {
				dist=c;
				j=i;
			}
		}
		B_COLOR(TYELLOW|HLIGHT);
		for (i=0;i<=4;i++) MZprint(ufo[j]+i-1,'*');
		B_COLOR(TWHITE);
		PCMplay(PCMbufo,LENbufo,3);
		for (i=0;i<=4;i++) MZprint(ufo[j]+i-1,' ');
		ufo[j]=0;
		if (ufoc<=1) break;
		score(0);
		ResetHodai();
	} // L3000へ
	return(0);
}

/******************************************************************/

void main(int argc,unchar *argv[])
{
int ret,i;
FILE *fp;
	hsc=0; /* ハイスコアクリア */
	fp=fopen(RECHIGH,"rb");
	if (fp!=NULL) { /* hi-score read */
		fread(&hsc,sizeof(hsc),1,fp);
		fclose(fp);
	}
	
	/* 乱数初期化 */
	srand2();
	
	/* PANIC起動プログラム名 */
	exec=getenv2("PANIC","COMMAND.X");
	
	/* ADPCM読み込み */
	LENappl=fmalloc(&PCMappl,   "APL.PCM",0,0);
	LENufo1=fmalloc(&PCMufo1,  "RHIQ.PCM",0,0);
	LENufo2=fmalloc(&PCMufo2,   "HIQ.PCM",0,0);
	LENbang=fmalloc(&PCMbang, "BOMB1.PCM",0,0);
	LENhod =fmalloc(&PCMhod ,"SCRCH4.PCM",0,0);
	LENbufo=fmalloc(&PCMbufo, "WHIP1.PCM",0,0);
	
	// 画面関係
	CRTMOD(16); // cwidth(96);
	help_set(argv[0],0);
	
	do {
		/* 初期化 */
		curoff(); /* cursor off */
		disp();
		// 変数初期化
		sco=0;		/* 得点 */
		hono=6;		// 砲台残り数
		dlay=22;	// ウエイト
		men=3;
		do { /* ゲーム本体 */
			// 1面毎の初期化(L1500)
			hoe=1;
			if (++men>9) {
				ret=2; // ９面までで終了
				break;
			}
			if (dlay>10) dlay-=2;
			// 初期化(L1000)
			ACLS();
			title();
			for (i=1;i<=9;i++) {
				insk[i]=rand2(80);
				if (i<=men) DispInseki(i);
			}
			for (i=1;i<=5;i++) misl[i]=0;
			hol=ho=CalcHodai(); // ミサイルの初期位置を決定
			gnd=add=0;
			ashi=1;
			// UFOの初期位置
			for (i=1;i<=4;i++) {
				ufo[i  ]=i*8+0xD027;
				ufo[i+4]=i*8+0xD07A;
			}
			// ゲーム本体
			ret=GameMain();
		} while (!ret);
		
		/* ゲーム終了 */
		B_COLOR(TYELLOW|HLIGHT);
		message((ret==2? "★★ ＧＩＶＥ ＵＰ ★★":"☆☆ ＧＡＭＥ ＯＶＥＲ ☆☆"),4);
		B_COLOR(TWHITE);
		if (sco>hsc) {
			B_COLOR(TCYAN|REVERSE|HLIGHT);
			center(8,"＊＊　新記録！！　＊＊");
			PCMplay(PCMappl,LENappl,-4);
			B_COLOR(TWHITE);
			hsc=sco;
			hi_score();
		}
		center(10,"もう一度しますか？");
		curon();
		do {
			ret=KFLUSHGC(); // toupperマクロ化対応
			ret=toupper(ret);
		} while (ret!='Y' && ret!='N' && ret!=0x1b);
		a_stop(); // WaitPCM();
	} while (ret=='Y');
	
	crlf();
	fp=fopen(RECHIGH,"wb");
	if (fp!=NULL) { /* hi-score write */
		fwrite(&hsc,sizeof(hsc),1,fp);
		fclose(fp);
	}
	/* ADPCMバッファー開放 */
	if (PCMappl!=NULL) free(PCMappl);
	if (PCMufo1!=NULL) free(PCMufo1);
	if (PCMufo2!=NULL) free(PCMufo2);
	if (PCMbang!=NULL) free(PCMbang);
	if (PCMhod !=NULL) free(PCMhod );
	if (PCMbufo!=NULL) free(PCMbufo);
	exit(0);
}

/******************************************************************/
