/*===============================
	"prgame.c" : racing game
===============================*/
#include <fmcfrb.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "timekh.h"
#include "pad.h"
#include "prdef.h"

#define	startTM()	TIM_rdcalendar(&st)
#define waitTM(_t)	do{TIM_rdcalendar( &et );}while(getTime(st,et)<(_t))
#define readPad()	do{_outb(PADOUT,COM0);}while((_inb(PAD1IN)&COMIN)!=0);\
					pad=_inb(PAD1IN);

void putmap( int x0, int y0, int z0, vect ex, vect ey, vect ez );
void putcar( int x0, int y0, int z0, vect ex, vect ey, vect ez, int cpol );
int  mycar( short x, short y, short z, int ang, vect kv );
int  encar( short x, short y, short z, int ang, vect kv, int col, int pol );
void writeScreen();
void clearVram(int a, int b, int c);
int  onRoad( int x, int y, int n );

void putMess( char *str, int col );
void putLapTime( int lap, int time, int best );
void putRestTime( int rt );
void clearMess();
void putSpd( int spd );
void putlmap();
void setLap( short blp, short flp );
#define putExTime() putMess("Extended Time",14)

static TIM_CALEN	st,et;

#define	CPMAX	10
#define	PI		3.14159265
#define	MaxSpd	8000
#define	ACL		250
#define	BRK		300
//#define	GRP		40
#define	Ekt 	998
#define	ANG(_a)	_a = (_a+20000)%10000

extern poly   car[3];
extern rdata  *road;

#define slipFlag(col)	car[0].type=car[1].type= \
						(col)==0?C32K(31,0,0):C32K(0,31,0)
#define	onRdFlag(col)	car[2].type=((col)>=0?C32K(31,0,15):C32K(20,20,0))

extern int _time[20],_dn;
extern int *_cos,*_sin;
extern short rd;
extern short cpn, *cp;
extern short *exTime;

extern way *way1st,*waybst;
way *ecs[6]; //={waybst,waybst+1,waybst+2,way1st,way1st+1,way1st+2};
way cway,pw1st,pwbst;

extern int   maxtime,h;
extern int   lapFlag,wayFlag;

int pflap=0;

/*======================
	ベクトル加工関数
======================*/
vect ortholize( vect vec )
{
	int l ;
	l = sqrt(vec.x*vec.x+vec.y*vec.y+vec.z*vec.z)*10 ;
	if(l==0){
		vec.z = 1000 ;
		return vec;
	}
	vec.x = (int)vec.x*10000/l ;
	vec.y = (int)vec.y*10000/l ;
	vec.z = (int)vec.z*10000/l ;
	return vec ;
}
vect exProduct( vect v1, vect v2 )
{
	vect kv;
	int x1=v1.x,y1=v1.y,z1=v1.z ;
	int x2=v2.x,y2=v2.y,z2=v2.z ;

	kv.x = (y1*z2 - z1*y2)/1000 ;
	kv.y = (z1*x2 - x1*z2)/1000 ;
	kv.z = (x1*y2 - y1*x2)/1000 ;
	return kv ;
}
/*=========================
	game 3D test version
=========================*/
void	game( short sx,short sy,short sang )
{
	int  pol, i,j;
	int  x,y, spd=0,acl;
	int  ang,mang;
	int  ecx,ecy,ectime[6] = {-1,-1,-1,-1,-1,-1};
	int  sfc, grip, onRd=0, ekt;
	int  lapTime=30000, lap=0,messTime;
	int  restTime=6000;
	char lapflag[CPMAX]=0;
	char pad;
	int  mess=0,viewpt=1,viewtime=0;

	h = 150;	// [cm]
	int  height=road[0].pz,xx,yy;
	vect kv=road[0].k;
	vect zv;	zv.x=0,zv.y=0,zv.z=1000;
	int  brkf=0,bf,bs;

	vect	ex,ey,ez ;
	int		vx,vy,vz, cs,sn ;

	for(i=0; i<cpn; ++i) lapflag[i]=1 ;
	for(i=0; i<6; ++i )	ecs[i] = way1st+i ;
	pw1st.ltime=0 ;
	pwbst.ltime=0 ;

	x=sx*100, y=sy*100;
	ang=mang=sang*10;
	grip = (maxtime*MaxSpd*5/PI/1500)*MaxSpd; //*5000/Pi/150000/10
	ekt=100000;
	for(i=0; i<maxtime; ++i)
		ekt = ekt*Ekt/1000;
	ekt/=100;
	putlmap();
	putRestTime(-1) ;

	while(1)
	{
		startTM();
		readPad();

	//	エスケープ
		if(PUSH(PADRUN+PADSEL)) {
			j=0; break;
		}
	//	視点変更
		else if(PUSH(PADSEL)&&viewtime<=0)
		{
			viewpt++; viewpt%=3;
			viewtime=50 ;
		}
		if( viewtime>0 ) viewtime-=maxtime;

	//	スタート時のハンドル固定
		if( lap==0 ) pad = ~(PADA);

	//	ハンドルの操作
		if(PUSH(PADU))		i = 120000;
		else if(PUSH(PADD)) i = 30000;
		else				i = 60000;
		if(PUSH(PADL)) ang -= maxtime*spd*5000/PI/i/10;
		if(PUSH(PADR)) ang += maxtime*spd*5000/PI/i/10;
		ANG(ang);

	//	加速
		acl=0;
		brkf=0;
		if (PUSH(PADA)&&PUSH(PADB)) acl = ACL*2/3;
		else {
			if (PUSH(PADA))	acl = ACL;
			if (PUSH(PADB))	acl =-BRK,brkf=1;
		}

	//	コース位置確認
		onRd=onRoad( x,y,onRd );
		if( onRd<0 )			//	コース外
			acl -= BRK*2/3;
		onRdFlag(onRd);

	//	高度算出
		if( onRd>=0 ){
			xx = x-road[onRd].px[0]*100 ;
			yy = y-road[onRd].py[0]*100 ;
			kv = road[onRd].k ;
			//if ( 0!=kv.z) {
				height = road[onRd].pz-(kv.x*xx+kv.y*yy)/kv.z/10 ;
			//}
			//else {
			//	height = road[onRd].pz ;
			//	printf("onRd=[%d],(kz==0)\n",onRd);
			//}
		}else {
			kv.x = kv.y = 0 ;
			kv.z = 1000 ;
		}

		ANG(mang);
		bf = kv.x*_cos[mang/10]+kv.y*_sin[mang/10] ;
		bs =-kv.x*_sin[mang/10]+kv.y*_cos[mang/10] ;
	//	スリップの計算
		i = (ang-mang+25000)%10000-5000;	//進行方向とのずれ角
		//bs = bs ;	// bs=bs/1000/100*g*10000 ;
		sfc = spd*i - bs ; // sin(i)*v *10000-bs
		//	printf("bs=%d,sfc=%d,(bs&sfc)=%d\n",
		//			bs,spd*i,sfc);
		if (_abs(sfc)<=grip||spd==0) {
			mang = ang;
			j = 0;
		} else {
			if (sfc>0)
				mang += (grip-bs)/spd;
			else
				mang -= (grip+bs)/spd; 
			j = 1;
		}
		slipFlag(j);

//		i = (ang-mang+20000)%10000;
//		mang += acl*_sin[i/10]*100/spd/PI;
//		acl = acl*_cos[i/10]/100;
		ANG(mang);

		acl += bf/600 ; // 600->適当 (^^;
		spd = (	spd*ekt + MaxSpd*(1000-ekt)*acl/ACL )/1000;
		if (spd<0) spd=0 ;

	//	位置の更新
		x += spd*maxtime*_cos[mang/10]/1000;
		y += spd*maxtime*_sin[mang/10]/1000;

	//	ExtendTime 表示と Lap 処理
		if( onRd>=0 )
		{
		  for(i=0; i<cpn; ++i)
		  {
			if( onRd==cp[i] && lapflag[i]==0 )
			{
				lapflag[i] = 1;
				restTime += exTime[i]*100;
				if( mess!=0 ) clearMess();
				putExTime();
				mess = 2;
				messTime = 500;
				cway.stime[i] = lapTime ;
				break;
			}
		  }
		}
	//	メッセージの消去
		if( mess!=0 ){
			messTime -= maxtime;
			if( messTime<0 )
			{
				clearMess();
				mess=0;
			}
		}
	//	LapTime の表示等 ラップ更新時の処理
		if( onRd==0 )
		{
			for(i=0,j=1; i<cpn; ++i) j *= lapflag[i] ;
			if( j==1 ){
				if( lap==0 ) restTime=6000;
				else cway.ltime = lapTime;
				if( lap==1 )
				{
					memcpy( &pw1st, &cway, sizeof(way) );
					for(i=0; i<6; ++i )	ecs[i] = waybst+i;
				}
				if( lap!=0 )
				{
					for(i=0; i<6; i++) ectime[i]=maxtime*3*(6-i);
					if (pwbst.ltime>=lapTime||pwbst.ltime==0) {
						memcpy( &pwbst, &cway, sizeof(way) );
					}
				}
				putLapTime(lap,lapTime,pwbst.ltime);
				mess=1,	messTime=600 ;
				lap++,	lapTime=0 ;
				memset( lapflag, 0, cpn );
			}
		}

	//	走行データの記録
		if( lap!=0 && lapTime<10000 )
		{
			cway.pt[lapTime].x=x/100;
			cway.pt[lapTime].y=y/100;
			cway.pt[lapTime].z=height;
			cway.ang[lapTime]=ang/10;
		}
		lapTime += maxtime;

	//	RestTime の処理
		restTime -= maxtime;
		if( lap>0 && restTime<0 ){
			j = 1;
			break;
		}

	//	車ポリゴンのセット
		pol=mycar( x/100,y/100,height,ang/10,kv );
		for(j=0;j<6;j++)
		{
		  if( ecs[j]->ltime!=0 ){
			if( ectime[j]<0 ){
				ecx = x/100 + _cos[sang]*(6-j)/2;	// 5m*(6-j)
				ecy = y/100 + _sin[sang]*(6-j)/2;
				i = C32K((j+2)%2,((j+2)/2)%2,((j+2)/4)%2);
				pol = encar( ecx, ecy, height, sang, kv, i, pol );
				i = onRoad( ecx*100,ecy*100,rd-10 );
				if( i>=0 && i<10 ) ectime[j]=0;
			}else {
				if( ectime[j]<=ecs[j]->ltime ){
					i = C32K((j+2)%2,((j+2)/2)%2,((j+2)/4)%2);
					pol=encar( ecs[j]->pt[ectime[j]].x,
							   ecs[j]->pt[ectime[j]].y,
							   ecs[j]->pt[ectime[j]].z,
							   ecs[j]->ang[ectime[j]],
							   zv/*ecs[j]->kv[ectime[j]]*/, i, pol
							 );
					ectime[j]+=maxtime;
				}
			}
		  }
		}
	//	画面表示
		cs = cos(_PI*2*mang/10000)*1000;
		sn = sin(_PI*2*mang/10000)*1000;
		switch(viewpt){
		  case 1:
			ey.x = kv.z*cs /1000 ;
			ey.y = kv.z*sn /1000 ;
			ey.z = -(kv.x*cs + kv.y*sn) /1000 ;
			ey = ortholize( ey ) ;
			ez.x = (1000-sn)*kv.x /1000 ;
			ez.y = (1000-cs)*kv.y /1000 ;
			ez.z = kv.z ;
			ez = ortholize( ez ) ;
			ex = exProduct( ez,ey ) ; // 左手系
			vx = x/10-ey.x*h/400+ez.x*h/1000 ; // x,y[mm]
			vy = y/10-ey.y*h/400+ez.y*h/1000 ; // h,height[cm]
			vz = height-ey.z*h/400+ez.z*h/1000 ; // vx-z[cm]
			break ;
		  case 2:
			ey.x = kv.z*cs /1000 ;
			ey.y = kv.z*sn /1000 ;
			ey.z = -(kv.x*cs + kv.y*sn) /1000 ;
			ey = ortholize( ey ) ;
			ez.x = (1000-sn)*kv.x /1000 ;
			ez.y = (1000-cs)*kv.y /1000 ;
			ez.z = kv.z ;
			ez = ortholize( ez ) ;
			ex = exProduct( ez,ey ) ; // 左手系
			vx = x/10-ey.x*h/180+ez.x*h/500 ; // x,y[mm]
			vy = y/10-ey.y*h/180+ez.y*h/500 ; // h,height[cm]
			vz = height-ey.z*h/180+ez.z*h/500 ; // vx-z[cm]
			break ;
		  default :
			ey.x = cs*5000/5099 ;
			ey.y = sn*5000/5099 ;
			ey.z = -1000*1000/5099 ;
			ez.x = cs*1000/5099 ;
			ez.y = sn*1000/5099 ;
			ez.z = 1000*5000/5099 ;
			ex = exProduct( ez,ey ) ; // 左手系
			vx = x/10-ey.x*h/33+ez.x*h/100 ; // x,y[mm]
			vy = y/10-ey.y*h/33+ez.y*h/100 ; // h,height[cm]
			vz = height-ey.z*h/33+ez.z*h/100 ; // vx-z[cm]
			break ;
		}
#if debug>=2
	if(brkf){
		printf("ex[%d,%d,%d]ey[%d,%d,%d]ez[%d,%d,%d],kv[%d,%d,%d]\n",
			ex.x,ex.y,ex.z,
			ey.x,ey.y,ey.z,
			ez.x,ez.y,ez.z,
			kv.x,kv.y,kv.z );
	}
#endif
		clearVram( ex.z,ey.z,ez.z ) ;
		putmap( vx,vy,vz, ex,ey,ez ) ;
		putcar( vx,vy,vz, ex,ey,ez, pol ) ;
		putSpd( spd*280/MaxSpd ) ;
		if(lap>0) putRestTime(restTime/100) ;
		writeScreen() ;

	//	カウント終了処理
		TIM_rdcalendar( &et ) ;
		i=getTime(st,et) ;
		if (i<20) ++_time[i] ;
		++_dn ;
//		if (_dn>=300) break ;

		waitTM(maxtime) ;
	}
	if( mess!=0 ){
		clearMess();
		mess=0;
	}

	//	if( j==1 );	//	TimeOver
	//	if( j==0 );	//	Retire

	i = (pwbst.ltime==0 ? 30000:pwbst.ltime) ;
	j = (pw1st.ltime==0 ? 30000:pw1st.ltime) ;
	setLap( (short)i, (short)j );
	pflap = pw1st.ltime; // ?

	//	データの補間
	extern makeUpWay( way *wd, int st, int ed );
	for(i=0; i<pw1st.ltime; i+=maxtime)
		makeUpWay( &pw1st,i,i+maxtime );
	for(i=0; i<pwbst.ltime; i+=maxtime)
		makeUpWay( &pwbst,i,i+maxtime );

	//	way データの更新
	if (pw1st.ltime!=0&&(pw1st.ltime<=way1st[5].ltime||way1st[5].ltime==0))
	{
		for(i=0; i<6; ++i){
			if (pw1st.ltime<=way1st[i].ltime||way1st[i].ltime==0)
				break ;
		}
		_rmemcpy( &way1st[i+1], &way1st[i], sizeof(way)*(5-i) );
		_rmemcpy( &way1st[i], &pw1st, sizeof(way) );
		wayFlag = 1;
	}
	if (pwbst.ltime!=0&&(pwbst.ltime<=waybst[5].ltime||waybst[5].ltime==0))
	{
		for(i=0; i<6; ++i){
			if (pwbst.ltime<=waybst[i].ltime||waybst[i].ltime==0)
				break ;
		}
		_rmemcpy( &waybst[i+1], &waybst[i], sizeof(way)*(5-i) );
		_rmemcpy( &waybst[i], &pwbst, sizeof(way) );
		wayFlag = 1;
	}
}

