/* インベーダ */

#include "../api/api.h"
#include <stdio.h>
#include <string.h>

void dec2str(char *s, int i);
void putstr(int win, char *buf, int c, int x, int y, unsigned char *s);
void wait(int timer, char *kflag, int i);

/* キャラクタの絵 */
static unsigned char charset[16 * 8] = {
	/* インベーダ（1） */
	0x00, 0x00, 0x00, 0x43, 0x5f, 0x5f, 0x5f, 0x7f,
	0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x20, 0x3f, 0x00,
	/* インベーダ（2） */
	0x00, 0x0f, 0x7f, 0xff, 0xcf, 0xcf, 0xcf, 0xff,
	0xff, 0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0x00,
	/* インベーダ（3） */
	0x00, 0xf0, 0x7f, 0xff, 0xf3, 0xf3, 0xf3, 0xff,
	0xff, 0x07, 0xff, 0xff, 0x03, 0x03, 0x03, 0x00,
	/* インベーダ（4） */
	0x00, 0x00, 0x00, 0xc2, 0xfa, 0xfa, 0xfa, 0xfe,
	0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x04, 0xfc, 0x00,
	/* 自機（1） */
	0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x43, 0x47, 0x4f, 0x5f, 0x7f, 0x7f, 0x00,
	/* 自機（2） */
	0x18, 0x7e, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff,
	0xff, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0x00,
	/* 自機（3） */
	0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x80, 0xc2, 0xe2, 0xf2, 0xfa, 0xfe, 0xfe, 0x00,
	/* レーザ */
	0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00
};
/* インベーダ："abcd", 自機："efg", レーザ："h" */

void HariMain(void)
{
	char wbuf[336 * 261];							/* ウィンドウバッファ */
	int win, timer;								/* ウィンドウ・タイマ番号 */
	int fx, score, high, point;						/* プレイヤーの場所やスコア */
	int lx = 0, ly, lwait;							/* レーザの場所・待ち時間 */
	int ix, iy, iline, idir, mwait0, mwait;					/* インベーダの場所や移動方向、動くまでの待ち時間 */
	char istr[32 * 6];							/* インベーダの状態 */
	static char istr0[27] = " abcd abcd abcd abcd abcd ";			/* インベーダの列 */
	char kflag;								/* キーボードの状態 */
	int i, j;
	char s[12], *p;

	/* ウィンドウの準備 */
	win = api_wopen(wbuf, 336, 261, -1, "インベーダー");
	api_wbox(win, COLOR_000000, 6, 27, 329, 254);
	api_wrefresh(win, 6, 27, 330, 255);

	/* タイマの初期化 */
	timer = api_talloc();
	api_tinit(timer, 128);

	/* 最高スコアの初期化 */
	high = 0;
	putstr(win, wbuf, COLOR_FFFFFF, 22, 0, "HIGH:0000000");

restart:
	/* スコアとゲームの初期化 */
	score = 0;
	point = 1;
	putstr(win, wbuf, COLOR_FFFFFF, 4, 0, "SCORE:00000000");
	mwait0 = 20;
	fx = 18;
	putstr(win, wbuf, COLOR_00FFFF, fx, 13, "efg");
	wait(timer, &kflag, 100);

next_group:
	/* インベーダの初期化 */
	wait(timer,& kflag, 100);
	ix = 7;
	iy = 1;
	iline = 6;
	for(i = 0; i < 6; i++) {
		for(j = 0; j < 27; j++) {
			istr[i * 32 + j] = istr0[j];
		}
		putstr(win, wbuf, COLOR_00FF00, ix, iy + i, istr + i * 32);
	}
	kflag = 0;

	/* 非表示化と待ち時間の設定 */
	ly = 0;
	lwait = 0;
	mwait = mwait0;
	idir = 1;
	wait(timer, &kflag, 100);

	for(;;) {
		/* レーザの処理があるなら済ます */
		if(lwait != 0) {
			lwait--;
			kflag &= ~4;
		}
		wait(timer, &kflag, 4);

		/* 自機の処理 */
		if((kflag & 1) != 0 && fx > 0) {	/* 左 */
			fx--;
			putstr(win, wbuf, COLOR_00FFFF, fx, 13, "efg ");
			kflag &= ~1;
		}

		if((kflag & 2) != 0 && fx < 36) {	/* 右 */
			putstr(win, wbuf, COLOR_00FFFF, fx, 13, " efg");
			fx++;
			kflag &= ~2;
		}
		if((kflag & 4) != 0 && lwait == 0) {	/* レーザ */
			lwait = 15;
			lx = fx + 1;
			ly = 13;
		}

		/* インベーダの移動 */
		if(mwait != 0) {
			mwait--;
		} else {
			mwait = mwait0;
			if(ix + idir > 14 || ix + idir < 0) {
				if(iy+iline == 13) {	/* ゲームオーバー */
					break;
				}
				idir = - idir;
				putstr(win, wbuf, COLOR_000000, ix, iy, "                          ");
				iy++;
			} else {
				ix += idir;
			}
			for(i = 0; i < iline; i++) {
				putstr(win, wbuf, COLOR_00FF00, ix, iy + i, istr + i * 32);
			}
		}

		/* レーザ処理 */
		if(ly > 0) {
			if(ly < 13) {
				if(ix < lx && lx < ix + 25 && iy <= ly && ly < iy + iline) {
					putstr(win, wbuf, COLOR_00FF00, ix, ly, istr + (ly - iy) * 32);
				} else {
					putstr(win, wbuf, COLOR_000000, lx, ly, " ");
				}
			}
			ly--;
			if(ly > 0) {
				putstr(win, wbuf, COLOR_FFFF00, lx, ly, "h");
			} else {
				point -= 10;
				if(point < 1) {
					point = 1;
				}
			}
			if(ix < lx && lx < ix + 25 && iy <= ly && ly < iy + iline) {
				p = istr + (ly - iy) * 32 + (lx - ix);
				if(*p != ' ') {	/* 当たった */
					score += point;
					point++;
					dec2str(s, score);
					putstr(win, wbuf, COLOR_FFFFFF, 10, 0, s);
					if(high < score) {
						high = score;
						putstr(win, wbuf, COLOR_FFFFFF, 27, 0, s);
					}
					for(p--; *p != ' '; p--) { }
					for(i = 1; i < 5; i++) {
						p[i] = ' ';
					}
					putstr(win, wbuf, COLOR_00FF00, ix, ly, istr + (ly - iy) * 32);
					for(; iline > 0; iline--) {
						for(p = istr + (iline - 1) * 32; *p != 0; p++) {
							if(*p != ' ') {
								goto alive;
							}
						}
					}
					/* 全部倒した */
					mwait0 -= mwait0 / 3;
					goto next_group;
				alive:
					ly = 0;
				}
			}
		}
	}
	/* ゲームオーバー */
	putstr(win, wbuf, COLOR_FF0000, 15, 6, "GAME OVER");
	wait(timer, &kflag, 0);
	for(i = 1; i < 14; i++) {
		putstr(win, wbuf, COLOR_000000, 0, i, "                                       ");
	}
	goto restart;

	api_exit();
}

void dec2str(char *s, int i)
/* iを10進法でsに格納 */
{
	int j;

	for(j = 7; j >= 0; j--) {
		s[j] = '0' + i % 10;
		i /= 10;
	}
	s[8] = 0;
	return;
}

void putstr(int win, char *buf, int c, int x, int y, unsigned char *s)
{
	int ci, x0, i;
	char *p, *q, t[2];

	x = x * 8 + 8;
	y = y * 16 + 29;
	x0 = x;
	i = strlen(s);	/* sの文字数を数える */
	api_wbox(win, COLOR_000000, x, y, x + i * 8 - 1, y + 15);
	q = buf + y * 336;
	t[1] = 0;
	for(;;) {
		ci = *s;
		if(ci == 0) {
			break;
		}
		if(ci != ' ') {
			if('a' <= ci && ci <= 'h') {
				p = charset + 16 * (ci - 'a');
				q += x;
				for(i = 0;i < 16;i++) {
					if((p[i] & 0x80) != 0) { q[0] = c; }
					if((p[i] & 0x40) != 0) { q[1] = c; }
					if((p[i] & 0x20) != 0) { q[2] = c; }
					if((p[i] & 0x10) != 0) { q[3] = c; }
					if((p[i] & 0x08) != 0) { q[4] = c; }
					if((p[i] & 0x04) != 0) { q[5] = c; }
					if((p[i] & 0x02) != 0) { q[6] = c; }
					if((p[i] & 0x01) != 0) { q[7] = c; }
					q += 336;
				}
				q -= 336 * 16 + x;
			} else {
				t[0] = *s;
				api_wputs(win, c, x, y, t);
			}
		}
		s++;
		x += 8;
	}
	api_wrefresh(win, x0, y, x, y+16);
	return;
}

void wait(int timer, char *kflag, int i)
{
	int j;

	if(i > 0) {	/* 一定時間待つ */
		api_tset(timer, i);
		i = 128;
	} else {
		i = 0x0a;
	}
	for(;;) {
		j = api_getd(1);
		if(i == j) {
			break;
		}
		if(j == '4') {
			*kflag |= 1;
		} else if(j == '6') {
			*kflag |= 2;
		} else if(j == ' ') {
			*kflag |= 4;
		}
	}
	return;
}
