/* PLAYGAME.C
 * This file has the functions to play a game, including the functions to
 * play a game, manipulate the pack.
 */

#include	<proto/all.h>
#include	<stdlib.h>
#include	<math.h>
#include	<intuition/intuition.h>
#include	<string.h>
#include	"defns.h"
#include	"externs.h"

VOID	playgame(USHORT nplr)
{
	ULONG	pln=0,nc=0,win=0;
	USHORT	nextplr,startplr=1,i,j,hand=0,nhp;
	stdgad[5].Flags &= ~SELECTED; stdgad[6].Flags &= ~SELECTED;
	AddGList(wind,&stdgad[3],-1,5,NULL);
	RefreshGList(&stdgad[3],wind,NULL,5);
	initplrs();
	exgm=0;
	if(!mandeal)
	{ /*pack will be reinitialised in manualdeal()*/
		if(initpack[BEFOREGAME]) initialisepack();
		if(shufpack[BEFOREGAME]) shuffle();
	}
	while(!quit && !exgm && ++hand<=numhand)
	{
		for(pln=1,nhp=0;pln<=nplr;pln++)
		{
			if(plr[pln]->manctrl) nhp++;
			plr[pln]->turn=0;
			stci_d(out,plr[pln]->score);
			TXTOUT(270,plr[pln]->yc+SCTPY,1,0,out)
			stci_d(out,plr[pln]->wins);
			TXTOUT(270,plr[pln]->yc+NWTPY,1,0,out)
			for(i=1;i<=52;i++) plr[pln]->mem[i]=UNKNOWN;
		}
		win=0;
		nextplr=startplr;
		if(!mandeal)
		{/*pack will be reinitialised in manualdeal()*/
			if(initpack[BEFOREHAND]) initialisepack();
			if(shufpack[BEFOREHAND]) shuffle();
		}
		if(!mandeal || !manualdeal(nplr)) /*autodeal*/
		{
			for(nc=1;nc<=7;nc++)
				for(pln=1;pln<=nplr;pln++)
				{
					plr[pln]->hand[nc]=NEXTFDP;
					plr[pln]->mem[CARD2(plr[pln]->hand[nc])] = CURRPLRHOLDS;
				}
			plr[1]->nchld=plr[2]->nchld=plr[3]->nchld=7;
			for(pln=1;pln<=nplr;pln++) hsort(plr[pln]->hand,plr[pln]->nchld);
		}
		PUTFUP(NEXTFDP);
		DrawImage(wind->RPort,packim[0][CARD2(fup[fuptc])],FUPX,FUPY);
		DrawImage(wind->RPort,packim[0][0],FDPX,FDPY);
		for(pln=1;pln<=nplr;pln++) if(plr[pln]->viewcard) disphand(plr[pln]);
									else hidecards(plr[pln]);
		while(!quit && !exgm && !win)
		{
			plr[nextplr]->turn++;
			SETPTR(ptrs[nextplr])
			if((plr[nextplr]->level+10)<rand()%21) corrupt(plr[nextplr]); /*corrupt player memory*/
			if(plr[nextplr]->manctrl)
				manturn(plr[nextplr],plr[nextplr==nplr?1:nextplr+1],
				nplr==2?NULL:plr[nextplr==1?nplr:nextplr-1],nhp);
			else 
			{
				OffMenu(wind,0x0020); /*reorder off*/
				OffMenu(wind,0xf862); /*modmem off*/
				OffMenu(wind,0x0040); /*about off*/
				autoturn(plr[nextplr],plr[nextplr==nplr?1:nextplr+1],
				nplr==2?NULL:plr[nextplr==1?nplr:nextplr-1]);
				OnMenu(wind,0x0020); /*reorder on*/
				OnMenu(wind,0xf862); /*modmem on*/
				OnMenu(wind,0x0040); /*about on*/
			}
			win = plr[nextplr]->nchld ?0 :nextplr;
			nextplr = nextplr==nplr ?1 :nextplr+1;	/*another player's turn*/
		}
		SETPTR(ptrs[0])
		if(win) for(i=1;i<=nplr;i++) disphand(plr[i]);
		/*return cards to pack and update losers scores*/
		while(fuptc>0) PUTFDP(NEXTFUP);	/*return face up pack to main pack*/
			for(pln=1;pln<=nplr;pln++)
				while(plr[pln]->nchld) plr[pln]->score+=(PUTFDP(NEXTPLC(plr[pln]))).n;
		for(i=0;i<=5 && fupset[i]->ncard!=0;i++)
		{ /*return cards from face up sets*/
			for(j=1;j<=14;j++)
				if(!CDEQU(fupset[i]->cards[j],nulcard))
				{
					PUTFDP(fupset[i]->cards[j]);
					fupset[i]->cards[j]=nulcard;
				}
			fupset[i]->ncard=0;
		}
		if(win)
		{/*continuous mode will not display round winners*/
			plr[win]->wins++;
			if(!contplay) displaywin(plr[win],nplr,hand,numhand);
		}
		DrawImage(wind->RPort,packim[0][0],FUPX,FUPY);
		startplr = startplr==nplr ?1 :startplr+1;	/*next plr goes first in next game*/
		DrawImage(wind->RPort,&setrem,0,0); /*remove submitted sets*/
		sortdispset();	/*remove set gadgets*/
		for(pln=1;pln<=nplr;pln++)
		{
			DrawImage(wind->RPort,&handrem,0,plr[pln]->yc+HANDTOP);
			TXTOUT(270,plr[pln]->yc+HVTPY,1,0,"      ");
			TXTOUT(270,plr[pln]->yc+NWTPY,1,0,"      ");
			TXTOUT(270,plr[pln]->yc+SCTPY,1,0,"      ");
			TXTOUT(270,plr[pln]->yc+NWTPY+9,1,0,"      ");
		}
	}	
	displayresult(nplr); /*overall result still displayed in continuous mode*/
	RemoveGList(wind,&stdgad[3],5);
	RefreshGList(&stdgad[3],wind,NULL,5);
}

VOID	initplrs()
{	/*Initialise for start of game*/
	register	USHORT	p,c;
	for(p=1;p!=4;p++)
	{
		plr[p]->score = 0;
		for(c=1;c!=53;c++) plr[p]->mem[c]=UNKNOWN;
		for(c=0;c!=8;c++) plr[p]->hand[c]=nulcard;
		plr[p]->nchld = 0;
		plr[p]->wins = 0;
	}
	for(p=0;p!=6;p++)
	{
		fupset[p]->ncard=0;
		for(c=1;c!=15;c++) fupset[p]->cards[c]=nulcard;
	}
}


VOID	initialisepack()
{
	register	USHORT	s,n,t;
	for(s=DM;s<=SP;s++)
		for(n=ACE;n<=KING;n++)
		{
			fdp[t=s*13+n].s=s;
			fdp[t].n=n;
		}
	fdptc=52;fuptc=0;
}	

VOID	shuffle()
{
	register	USHORT	mid,nb,top,nt,nc,cnt; /*, nextbottom,,nexttop,destination pos*/
	register	long	dec;
	USHORT	pass=0;
	struct	card	tpack[53];	/*temporary pack*/
	mid = fdptc >> 1;	/*middle of face down pack*/
	top	= fdptc;
	cont = 0;
	TXTOUT(MESX,MESY,1,0,"Select Continue....")
	while(!cont && !quit && !exgm && ++pass<100)
	{
		nb=mid; nt=top; nc=0;
		while(nb && nt>mid)
		{
			dec = rand(); /*random number*/
			for(cnt=32;cnt && nb && nt>mid;cnt--)
				if(!((dec>>=1)&1)) tpack[++nc]=fdp[nb--]; /*card from lower half of pack*/
				else tpack[++nc]=fdp[nt--]; /*card from upper half of pack*/
		}
		
		if(!nb) while(nt>mid) tpack[++nc]=fdp[nt--]; /*copy rest of top half*/
		else while(nb) tpack[++nc]=fdp[nb--]; /*copy rest of bottom half*/
		/*shuffle back to fdp[]*/
		nb=mid; nt=top; nc=0;
		while(nb && nt>mid)
		{
			dec = rand();
			for(cnt=32;cnt && nb && nt>mid;cnt--)
				if((dec>>=1)&1) fdp[++nc]=tpack[nt--]; /*from top half*/
				else fdp[++nc]=tpack[nb--]; /*bottom half*/
		}
		if(!nb) while(nt>mid) fdp[++nc]=tpack[nt--]; /*copy top*/
		else while(nb) fdp[++nc]=tpack[nb--];
	}
}

USHORT	manualdeal(USHORT nplr)
{
	struct	IntuiMessage	*msg;
	struct	Gadget	*gaddr;
	register	USHORT	pln=1,i,pos;
	register	ULONG	class,gid;
	USHORT	fin=0,cardno=0,rval=0;
	initcardgads();
	initialisepack();
	SETPTR(ptrs[1])
	Request(&mdreq,wind);
	while(!fin)
	{
		Wait(1<<mp->mp_SigBit);
		while(msg=(struct IntuiMessage *)GetMsg(mp))
		{
			class=msg->Class;
			gaddr=(struct Gadget *)msg->IAddress;
			ReplyMsg((struct Message *)msg);
			if(class==GADGETUP)
			{
				gid=gaddr->GadgetID;
				if(gid>CARDG && gid<=CARDG+52)
				{
					if(cardgads[cardno=gid-CARDG].Flags&SELECTED)
					{
						if(plr[pln]->nchld<7)
						{ /*add card to hand*/
							plr[pln]->hand[++(plr[pln]->nchld)]=fdp[cardno];
							disphand(plr[pln]);
						}
						else
						{ /*deselect card*/
							pos=RemoveGadget(wind,&cardgads[cardno]);
							cardgads[cardno].Flags &= ~SELECTED;
							AddGList(wind,&cardgads[cardno],pos,1,&mdreq);
							if(cardno<=13) RefreshGList(&cardgads[1],wind,&mdreq,13);
							else if(cardno<=26) RefreshGList(&cardgads[14],wind,&mdreq,13);
							else if(cardno<=39) RefreshGList(&cardgads[27],wind,&mdreq,13);
							else RefreshGList(&cardgads[40],wind,&mdreq,13);
						}
					}
					else if(!(cardgads[cardno].Flags&SELECTED) && plr[pln]->nchld)
					{ /* remove card from hand*/
						for(i=1;i<=plr[pln]->nchld && !CDEQU(plr[pln]->hand[i],fdp[cardno]);i++);
						if(CDEQU(plr[pln]->hand[i],fdp[cardno]))
						{
							plr[pln]->hand[i]=nulcard;
							compacthand(plr[pln]->hand,&plr[pln]->nchld);
							REMHAND(plr[pln]);
							disphand(plr[pln]);
						}
					}
				}
				else if(gid==MDACCEPTG)
				{ /*disable gadgets and remove selected cards from pack*/
					pos=RemoveGList(wind,&cardgads[1],52);
					for(i=1;i<=plr[pln]->nchld;i++)
					{
						cardno=CARD2(plr[pln]->hand[i]);
						cardgads[cardno].Flags |= GADGDISABLED;
						fdp[cardno]=nulcard;
						plr[pln]->mem[cardno]=CURRPLRHOLDS;
					}
					AddGList(wind,&cardgads[1],pos,52,&mdreq);
					RefreshGList(&cardgads[1],wind,&mdreq,52);
					if(pln!=nplr)
					{ /*set new ptr in here*/
						pln++;
						SETPTR(ptrs[pln])
					}
					else
					{
						fin=rval=1;
						EndRequest(&mdreq,wind);
						compacthand(fdp,&fdptc);
						if(shufpack[BEFOREGAME] || shufpack[BEFOREHAND]) shuffle();
					}
				}
				else if(gid==MDAUTODG)
				{
					initialisepack();
					if(shufpack[BEFOREGAME] || shufpack[BEFOREHAND]) shuffle();
					plr[1]->nchld=plr[2]->nchld=plr[3]->nchld=0;
					fin=1;
					rval=0;
				}
			}
		}
	}
	SETPTR(ptrs[0])
	/*restore original pointer*/
	return rval;
}

struct	card	rvspk()
{	/*only use NEXTFDP from within another macro when it is known that 
	rvspk() cannot be called*/
	 
	struct	card	tc,c;
	USHORT	n;
	tc=fdp[1];	/*top card*/
	fdptc=0;
	while(fuptc)
	{
		c=PUTFDP(NEXTFUP);
		plr[1]->mem[n=CARD2(c)] &= ~GONEPAST;
		plr[2]->mem[n] &= ~GONEPAST;
		plr[3]->mem[n] &= ~GONEPAST;
	}
	shuffle();
	return tc;
}


VOID	displaywin(p,nplr,r,nr)
struct	playerinfo	*p;
USHORT	nplr,r,nr;
{
	struct	IntuiMessage	*msg;
	register	USHORT	fin=0;
	stci_d(intbuf[0],plr[1]->score);
	stci_d(intbuf[1],plr[2]->score);
	stci_d(intbuf[2],plr[3]->score);
	stci_d(intbuf[3],r);
	stci_d(intbuf[4],nr);
	rwtxt[4].IText = p->name;
	Request(&rwreq,wind);
	while(!fin)
	{
		Wait(1<<mp->mp_SigBit);
		while(msg=(struct IntuiMessage *)GetMsg(mp))
		{
			if(msg->Class == GADGETUP && ((struct Gadget *)(msg->IAddress))->GadgetID==PROCG) fin=1;
			ReplyMsg((struct Message *)msg);
		}
	}
}

VOID	displayresult(USHORT nplr)
{
	struct	IntuiMessage	*msg;
	register	USHORT	fin=0;
	struct	playerinfo	*p;
	if(plr[1]->score>plr[2]->score) p=plr[2]; else p=plr[1];
	if(nplr==3 && p->score>plr[3]->score) p=plr[3];
	switch(p->playernum)
	{
	case 1:	if(p->score==plr[2]->score || nplr==3 && p->score==plr[3]->score) gwreq.ReqText = &gwtxt[2]; /*draw*/
			else gwreq.ReqText = &gwtxt[0];
			break;
	case 2:	if(p->score==plr[1]->score || nplr==3 && p->score==plr[3]->score) gwreq.ReqText = &gwtxt[2]; /*draw*/
			else gwreq.ReqText = &gwtxt[0];
			break;
	case 3:	if(p->score==plr[1]->score || p->score==plr[2]->score) gwreq.ReqText = &gwtxt[2]; /*draw*/
			else gwreq.ReqText = &gwtxt[0];
			break;
	}
	stci_d(intbuf[0],plr[1]->score);
	stci_d(intbuf[1],plr[2]->score);
	stci_d(intbuf[2],plr[3]->score);
	stci_d(intbuf[5],plr[1]->wins);
	stci_d(intbuf[6],plr[2]->wins);
	stci_d(intbuf[7],plr[3]->wins);
	gwtxt[1].IText = p->name;
	Request(&gwreq,wind);
	while(!fin)
	{
		Wait(1<<mp->mp_SigBit);
		while(msg=(struct IntuiMessage *)GetMsg(mp))
		{
			if(msg->Class == GADGETUP && ((struct Gadget *)(msg->IAddress))->GadgetID==PROCG) fin=1;
			ReplyMsg((struct Message *)msg);
		}
	}
}

VOID	reorder(struct playerinfo *p)
{
	struct	IntuiMessage	*msg;
	struct	Gadget	*gaddr;
	register 	USHORT	n,pos,gpos;
	register	ULONG	class,gid;
	struct	card	t[8];
	OffMenu(wind,0x0020); /*reorder off*/
	OffMenu(wind,0xf862); /*modmem off*/
	OffMenu(wind,0x0040); /*about off*/
	gpos=RemoveGList(wind,&stdgad[3],5);
	handgads(p->nchld,p->yc); /*reset gadgets*/
	TXTOUT(MESX,MESY,1,0,"Select For Left to Right Display")
	for(n=p->nchld;n && !quit && !exgm;)
	{
		Wait(1 << mp->mp_SigBit);
		while(msg=(struct IntuiMessage *)GetMsg(mp))
		{
			class=msg->Class;
			gaddr=(struct Gadget *)(msg->IAddress);
			ReplyMsg((struct Message *)msg);
			if(class==GADGETUP && (gid=gaddr->GadgetID)>=HANDG1 && gid<=HANDG7)
			{
				t[n--]=p->hand[gid-HANDG1+1];
				pos=RemoveGadget(wind,gaddr);
				gaddr->Flags |= GADGDISABLED;
				AddGadget(wind,gaddr,pos);
			}
		}
	}
	if(!quit && !exgm)
	{
		for(n=1;n<=p->nchld;n++) p->hand[n]=t[n];
		handgads(p->nchld,p->yc); /*reset gadgets*/
		disphand(p);
		TXTOUT(MESX,MESY,1,0,"Reorder Complete                 ")
	}
	AddGList(wind,&stdgad[3],gpos,5,NULL);
	OnMenu(wind,0x0020); /*reorder on*/
	OnMenu(wind,0xf862); /*modmem on*/
	OnMenu(wind,0x0040); /*about on*/
}
			


VOID	modmem(struct playerinfo *p)
{
	struct	IntuiMessage	*msg;
	struct	Gadget	*gaddr;
	register	USHORT	i,fin=0;
	USHORT	m[53],pos,cardno=0,t;
	ULONG	class,gid;
	initcardgads();
	mmtxt[13].IText = p->name;
	for(i=0;i<=7;i++) mmgads[i].Flags &= ~SELECTED;
	for(i=1;i!=53;i++) m[i]=p->mem[i];
	Request(&mmreq,wind);
	while(!fin)
	{
		Wait(1<<mp->mp_SigBit);
		while(msg=(struct IntuiMessage *)GetMsg(mp))
		{
			gaddr=(struct Gadget *)msg->IAddress;
			class=msg->Class;
			ReplyMsg((struct Message *)msg);
			if(class==GADGETUP)
			{
				gid=gaddr->GadgetID;
				if(gid>CARDG && gid<=CARDG+52)
				{
					if(cardno && cardno!=(gid-CARDG))
					{/* deselect previous card*/
						cardgads[cardno].Flags &= ~SELECTED;
						if(cardno<=13) RefreshGList(&cardgads[1],wind,&mmreq,cardno+1);
						else if(cardno<=26) RefreshGList(&cardgads[14],wind,&mmreq,cardno-12);
						else if(cardno<=39) RefreshGList(&cardgads[27],wind,&mmreq,cardno-25);
						else RefreshGList(&cardgads[40],wind,&mmreq,cardno-38);
					}
					pos=RemoveGList(wind,&mmgads[0],8);
					RefreshGList(&mmgads[0],wind,&mmreq,8);
					t=m[cardno=gid-CARDG];
					if(cardgads[cardno].Flags&SELECTED)
					{
						for(i=0;i<=7;i++)
							if((t>>i)&1) mmgads[i].Flags |= SELECTED;
							else mmgads[i].Flags &= ~SELECTED;
					}
					else
					{
						for(i=0;i<=7;i++) mmgads[i].Flags &= ~SELECTED;
						cardno=0;	/*no card active*/
					}
					AddGList(wind,&mmgads[0],pos,8,&mmreq);
					RefreshGList(&mmgads[0],wind,&mmreq,8);
				}
				else if(gid==MMSETFG)
				{
					for(i=0;i<=7;i++)
						if(mmgads[i].Flags&SELECTED) m[cardno]|=(1<<i);
						else m[cardno]&=~(1<<i);
				}
				else if(gid==MMACCEPTG)
				{
					for(i=1;i<=52;i++) p->mem[i]=m[i];
					fin=1;
				}
				else if(gid==MMCANCELG) fin=1;
			}
		}
	}
}

VOID	corrupt(struct playerinfo *p)
{ /*corrupt memory of player p*/
	long	l;
	l=rand();
	if(l&256) p->mem[l%52] = l&255;
	if(l&512) p->mem[(l>>1)%52] |= l&(l>>10);
	if(l&1024) p->mem[(l>>5)%52] &= l&(l>>20);
}

VOID	initcardgads()
{ /*initialise card gadgets for manual deal and modify memory.*/
	register	USHORT	s,n,d;
	for(s=3;s!=-1;s--)
		for(n=13;n;n--)
		{
			d=s*13+n;	/*card number*/
			cardgads[d]=cardgads[0];	/*initialise*/
			cardgads[d].NextGadget = &cardgads[d+1];
			if(n==13)
			{
				cardgads[d].LeftEdge=210;
				cardgads[d].Width=64;
				cardgads[d].SpecialInfo = (APTR)&wide;
			}
			else
			{
				cardgads[d].LeftEdge=cardgads[d+1].LeftEdge - 17;
				cardgads[d].Width=17;
				cardgads[d].SpecialInfo = (APTR)&narrow;
			}
			cardgads[d].TopEdge=50*s+20;
			cardgads[d].GadgetRender=(APTR)packim[(n==13)?0:1][d];
			cardgads[d].GadgetID=d+CARDG;
			cardgads[d].Flags &= ~(SELECTED | GADGDISABLED);
		}
	cardgads[52].NextGadget=NULL;
}
