#include <math.h>
#include "game.h"

enum DrawMode drawMode;

void drawMap(int x,int y,struct Map *map)
{
	if(map->tile->type==MAP_ITEM || map->tile->type==MAP_DESK) {
		drawTile(x,y,&tileset[1],0);	// Carpet under the item.
	}
	drawTile(x,y,map->tile,map->frame);
}

struct Tile healthtile[2]={
	{"Health 1",768, 0,MAP_CHARACTER, TX(16),TY(1),20,20,0,0,1,1,1},	
	{"Health 2",769, 0,MAP_CHARACTER, TX(17),TY(1),20,20,0,0,1,1,1},	
};
struct Tile scoretile[2]={
	{"Score 1",768, 0,MAP_CHARACTER, TX(14),TY(1),20,20,0,0,1,1,1},	
	{"Score 2",769, 0,MAP_CHARACTER, TX(15),TY(1),20,20,0,0,1,1,1},	
};
struct Tile metertile[8]={
	{"Meter 1",1024, 0,MAP_CHARACTER, TX(13)+12,TY(1),6,8,0,0,1,1,1},	
	{"Meter 2",1025, 0,MAP_CHARACTER, TX(13)+8,TY(1),6,8,0,0,1,1,1},	
	{"Meter 3",1026, 0,MAP_CHARACTER, TX(13)+4,TY(1),6,8,0,0,1,1,1},	
	{"Meter 4",1027, 0,MAP_CHARACTER, TX(13)+0,TY(1),6,8,0,0,1,1,1},	
	{"Warning 1",1034, 0,MAP_CHARACTER, TX(13)+12,TY(1)+8,4,8,0,0,1,1,1},
	{"Warning 2",1035, 0,MAP_CHARACTER, TX(13)+8,TY(1)+8,4,8,0,0,1,1,1},	
	{"Warning 3",1036, 0,MAP_CHARACTER, TX(13)+4,TY(1)+8,4,8,0,0,1,1,1},	
	{"Warning 4",1037, 0,MAP_CHARACTER, TX(13)+0,TY(1)+8,4,8,0,0,1,1,1},	
};
struct Tile keytile[4]={
	{"Key 1",1024, 0,MAP_CHARACTER, TX(8),TY(5),11,5,0,0,1,1,1},	
	{"Key 2",1024, 0,MAP_CHARACTER, TX(8),TY(5)+5,11,5,0,0,1,1,1},	
	{"Key 3",1024, 0,MAP_CHARACTER, TX(8),TY(5)+10,11,5,0,0,1,1,1},	
	{"Key 4",1024, 0,MAP_CHARACTER, TX(8),TY(5)+15,11,5,0,0,1,1,1},	
};

struct Tile shadowtile[1]={
	{"Shadow",2048, 0,MAP_CHARACTER, TX(12),TY(3),20,20,0,0,1,1,1},	
};

struct Tile hudtile[2]={
	{"Level",4096, 0,MAP_CHARACTER, TX(13),TY(4),42,20,0,0,1,1,1},	
	{"Score",4097, 0,MAP_CHARACTER, TX(15),TY(4),42,20,0,0,1,1,1},	
};

void drawCharacter(struct Character *character)
{
	int frame=character->frame;
	
	if(character->tile->flags&TILEF_OSCILLATE && frame>=character->tile->frames) {
		frame=character->tile->frames*2-1-frame;
	}
	if(character->type==CHAR_PLAYER) {
		drawTile(character->x,character->y+3,shadowtile,0);
	}
		
	drawTile(character->x,character->y,character->tile,frame);
	
	int w,h;
	if(character->type==CHAR_NPC && game.player) {
		int dx,dy;
		dx=character->x-game.player->x;
		dy=character->y-game.player->y;
		if(dx<0) dx=-dx;
		if(dy<0) dy=-dy;
		if(dx*dx+dy*dy<8*TILE_WIDTH*TILE_HEIGHT) {
			extentMessage(&w,&h,FONT_SMALL,character->name);
			drawMessage(character->x+20-w/2-game.viewLeft,character->y-h-game.viewTop,FONT_SMALL,character->name);
		}
	}
	if(character->type==CHAR_PLAYER) {
		extentMessage(&w,&h,FONT_SMALLHIGHLIGHT,"Les");
		drawMessage(character->x+20-w/2-game.viewLeft,character->y-h-game.viewTop,FONT_SMALLHIGHLIGHT,"Les");
		if(game.newHealthTimer>0) {
			int i;
			for(i=0;i<16;i++) {
				int x=character->x+cosf(game.newHealthTimer/200.0f+i)*3*TILE_WIDTH*game.newHealthTimer/NEWHEALTHDUR;
				int y=character->y+sinf(game.newHealthTimer/200.0f+i)*3*TILE_HEIGHT*game.newHealthTimer/NEWHEALTHDUR;
				drawTile(x,y,healthtile+(i%2),0);
			}
		}
		if(game.newScoreTimer>0) {
			int i;
			for(i=0;i<16;i++) {
				int x=character->x+cosf(game.newScoreTimer/100.0f+i)*TILE_WIDTH*(NEWSCOREDUR+1-game.newScoreTimer)/NEWSCOREDUR;
				int y=character->y+sinf(game.newScoreTimer/100.0f+i)*TILE_HEIGHT*(NEWHEALTHDUR+1-game.newScoreTimer)/NEWSCOREDUR;
				drawTile(x,y,scoretile+(i%2),0);
			}
		}
	}
}

void drawPlayer(struct Character *player)
{
	drawCharacter(player);
}

void drawMessages()
{
	struct Message *m;
	char *buf;
//	unsigned long color=0xff0000ff;
	
	for(m=game.firstMessage;m!=NULL;m=m->next) {
		int y=m->y;
		while(y<0) y+=252;
		drawFilledRect(m->x,y,m->w,m->h,m->color);
//		drawFilledRect(m->x,y,m->w,m->h,color);
		
//		color<<=5;
//		color|=0xff000000;
//		if(color==0xff000000) color|=0xff;
		
		drawFilledRect(m->x,y,m->w,1,m->color^0xffffff);
		drawFilledRect(m->x,y,1,m->h,m->color^0xffffff);
		drawFilledRect(m->x+m->w-1,y,1,m->h,m->color^0xffffff);
		drawFilledRect(m->x,y+m->h-1,m->w,1,m->color^0xffffff);
		if(m->tile) {
			drawTile(+game.viewLeft+m->x+2,+game.viewTop+y+2,m->tile,m->frame);
		}
		buf=strdup(m->message);
		if(m->teletypePos>=0 && m->teletypePos<strlen(m->message))
			buf[m->teletypePos]=0;
		if(strlen(buf)>0) drawMessage(m->x+m->mx,y+m->my,FONT_MESSAGE,buf);
		free(buf);
	}
}


void drawMenu(const char **menu)
{	
	int i;
	int w,h,rw,rh;
	int x,y;
	int wpad=2,hpad=2;
	int animw;
	int animy;
	
	rw=0;
	rh=hpad;
	for(i=0;i<game.menuitemCount;i++) {
		extentMessage(&w,&h,FONT_BODYHIGHLIGHT,menu[i]);
		if(rw<w) rw=w;
		extentMessage(&w,&h,FONT_BODY,menu[i]);
		if(rw<w) rw=w;
		extentMessage(&w,&h,game.menuitem==i?FONT_BODYHIGHLIGHT:FONT_BODY,menu[i]);
		rh+=h+hpad;
	}
	rw+=wpad*3+20;
	x=(SCREEN_WIDTH-rw)/2;
	y=(SCREEN_HEIGHT-rh)/2;
	if(game.menuTimer>1000) animw=rw; else animw=rw*game.menuTimer/1000;
	drawFilledRect(x,y,animw,rh,0xc0ffffff);
	x+=wpad;
	y+=hpad;
	for(i=0;i<game.menuitemCount;i++) {
		int targetTime=i*500+750;
		
		if(game.menuitem==i) drawTile(game.viewLeft+x,game.viewTop+y,&playerTileDir[TRIGHT],0);
		extentMessage(&w,&h,game.menuitem==i?FONT_BODYHIGHLIGHT:FONT_BODY,menu[i]);
		if(game.menuTimer>targetTime) {
			animy=y;
		} else {
			animy=-y*10*(targetTime-game.menuTimer)/targetTime+y*(game.menuTimer)/targetTime;
		}
		drawMessage(x+wpad+20,animy,game.menuitem==i?FONT_BODYHIGHLIGHT:FONT_BODY,menu[i]);
		y+=h+hpad;
	}
}

void drawIntroMenu()
{
	static const char *menu[]={
		"Start Game",
		"Level Editor",
		"High Scores",
		"Extras",
		//"Credits",
		"How to Play",
		"Quit",
	};
	game.menuitemCount=sizeof(menu)/sizeof(char *);
	drawMenu(menu);
}

void drawExtrasMenu()
{
	static const char *menu[]={
		"Credits",
		"Play Intro",
		"Try Subway Preview",
		"Main Menu",
	};
	game.menuitemCount=sizeof(menu)/sizeof(char *);
	drawMenu(menu);
}

void drawEditorMenu()
{
	static const char *menu[]={
		"New Level",
		"Load Level",
		"Save Level",
		"Main Menu",
	};
	game.menuitemCount=sizeof(menu)/sizeof(char *);
	drawMenu(menu);
}

void drawPauseMenu()
{
	static const char *menu[]={
		"Continue",
		"How to play",
		"Main Menu",
	};
	
	game.menuitemCount=sizeof(menu)/sizeof(char *);
	drawMenu(menu);
}

void drawHighScore()
{
	int i;
	int w,h;
	int x,y;
	int mw,mh;
	int tab[5];
	char buf[16];
	int items=8;

	tab[0]=5;
	tab[1]=5;
	for(i=0;i<items;i++) {
		extentMessage(&mw,&mh,FONT_MESSAGE,game.highscore[i].name);
		if(tab[0]+mw+5>tab[1]) tab[1]=tab[0]+mw+5;
	}
	tab[2]=tab[1]+5;
	for(i=0;i<items;i++) {
		sprintf(buf,"%06d",game.highscore[i].score);
		extentMessage(&mw,&mh,FONT_MESSAGE,buf);
		if(tab[1]+mw+5>tab[2]) tab[2]=tab[1]+mw+5;
	}
	w=tab[2]+5;
	for(i=0;i<items;i++) {
		sprintf(buf,"%2d",game.highscore[i].level);
		extentMessage(&mw,&mh,FONT_MESSAGE,buf);
		if(tab[2]+mw+5>w) w=tab[2]+mw+5;
	}
	h=5+(mh+5)*items;
	x=(SCREEN_WIDTH-w)/2;
	y=(SCREEN_HEIGHT-h)/2;

	for(i=0;i<items;i++) {
		if(game.highscore[i].score>0) {
			drawFilledRect(x,y+i*(mh+5),w,mh+5,i%2?0xc0ffffff:0xc0c0ffff);
			drawFilledRect(x+tab[1]-2,y+i*(mh+5),1,mh+5,0xc0000000);
			drawFilledRect(x+tab[2]-2,y+i*(mh+5),1,mh+5,0xc0000000);
			drawMessage(x+tab[0],y+i*(mh+5),FONT_MESSAGE,game.highscore[i].name);
			sprintf(buf,"%06d",game.highscore[i].score);
			drawMessage(x+tab[1],y+i*(mh+5),FONT_MESSAGE,buf);
			sprintf(buf,"%2d",game.highscore[i].level);
			drawMessage(x+tab[2],y+i*(mh+5),FONT_MESSAGE,buf);
		}
	}
	drawFilledRect(x,y+i*(mh+5),w,mh+5,0xc0ffffff);
	drawMessage(x,y+i*(mh+5),FONT_SMALL,"Hit X to continue");
}

char *alphabet="ABCDEFGHIJKL"
				"MNOPQRSTUVWX"
				"YZ0123456789"
				" -.!?";

void drawNewHigh()
{
	int mw,mh,tw,th,cw,ch;
	int x,y;
	int t,l;
	int letter=0;
	int fade=(game.menuTimer/10)%0x80;
	if(fade>=0x40) fade=0x80-fade;
	int color=0xc000ffff+((0xbf+fade)<<16);
	int animw;
	
	extentMessage(&mw,&mh,FONT_MESSAGE,"High Score! Enter your name.");
	extentMessage(&tw,&th,FONT_BODY,"W");
	if(mw<tw*12) mw=tw*12;
	t=(272-4*th-mh*4)/2+mh*2;
	l=(480-mw)/2;
	animw=mw;
	if(game.menuTimer<500) {
		animw=mw*game.menuTimer/500;
	}
	drawFilledRect(l,t-2*mh,animw,mh*4+4*th,0xc0ffffff);
	drawMessage(l,t-mh*2,FONT_MESSAGE,"High Score! Enter your name.");
	char buf[32];
	strcpy(buf,game.name);
	strcat(buf,"________________"+strlen(game.name));
	drawMessage(l,t-mh,FONT_MESSAGE,buf);
	
	game.menuitemCount=strlen(alphabet);
	for(y=0;y<4;y++) {
		for(x=0;x<12;x++) {
			int xx,yy;
			char msg[2]=" ";			
			if(letter>=game.menuitemCount) continue;
			msg[0]=alphabet[letter];
			extentMessage(&cw,&ch,FONT_BODY,msg);
			xx=l+tw*x+(tw-cw)/2;
			yy=t+th*y;
			if(game.menuTimer<1500) {
				float theta=letter;
				xx=xx+cosf(theta)*(1500-game.menuTimer)/4.0f;
				yy=yy+sinf(theta)*(1500-game.menuTimer)/4.0f;
			}
			if(game.menuitem==letter) drawFilledRect(l+tw*x,t+th*y,tw,th,color);
			drawMessage(xx,yy,game.menuitem!=letter?FONT_BODY:FONT_BODYHIGHLIGHT,msg);
			letter++;
		}
	}
	
	drawMessage(l,t+4*th,FONT_SMALL,"Hit X to select letter, hit square to correct");
	drawMessage(l,t+4*th+mh,FONT_SMALL,"D-pad to choose level, triangle to accept");
}

void drawHud()
{
	char buf[256];
	int i;
	int t,l;
	int ipr=9;	// items per row
	int w=22,h=22;
	static int oscilate=0;
	int pulse=oscilate<32?oscilate*2:(63-oscilate)*2;
	pulse+=192;
	unsigned int pulsecolor=pulse+(pulse<<8)+(pulse<<16)+(0xff<<24);
	oscilate++;
	oscilate=oscilate&63;

	// Draw the heads up display
	switch(game.mode) {
	case GAME_EDITOR:	// Choose what to do
		drawEditorMenu();
		break;
	case GAME_EDITOR_PLACE:	// Choose active tile
		drawFilledRect(40,220,350,40,0x80ffffff);
		t=20;
		l=(game.player->x-game.viewLeft<240)?480-100:50;
		drawFilledRect(l-4,t-4,w*3+2*3,h*ipr+2*3,0xff400040);
		for(i=0;i<tilesetCount-1;i++) {
			drawTile(game.viewLeft+l+(i/ipr)*w,game.viewTop+t+(i%ipr)*h,&tileset[i+1],0);
		}
		drawFilledRect(l+(game.menuitem/ipr)*w-2,t+(game.menuitem%ipr)*h-2,w+2,h+2,pulsecolor);
		drawTile(game.viewLeft+l+(game.menuitem/ipr)*w,game.viewTop+t+(game.menuitem%ipr)*h,&tileset[game.menuitem+1],0);

		drawFilledRect(-game.viewLeft+(game.player->x/TILE_WIDTH)*TILE_WIDTH-1,  -game.viewTop+(game.player->y/TILE_HEIGHT)*TILE_HEIGHT-1,1,h+1,pulsecolor);
		drawFilledRect(-game.viewLeft+(game.player->x/TILE_WIDTH)*TILE_WIDTH-1,  -game.viewTop+(game.player->y/TILE_HEIGHT)*TILE_HEIGHT-1,w+1,1,pulsecolor);
		drawFilledRect(-game.viewLeft+(game.player->x/TILE_WIDTH)*TILE_WIDTH-1+w+1,-game.viewTop+(game.player->y/TILE_HEIGHT)*TILE_HEIGHT-1,1,h+1,pulsecolor);
		drawFilledRect(-game.viewLeft+(game.player->x/TILE_WIDTH)*TILE_WIDTH-1,  -game.viewTop+(game.player->y/TILE_HEIGHT)*TILE_HEIGHT-1+h+1,w+1,1,pulsecolor);
		//drawTile(game.player->x/TILE_WIDTH,game.player->y/TILE_HEIGHT,&tileset[game.menuitem+1],0);
		game.menuitemCount=tilesetCount-1;
		drawMessage(l,t-20,FONT_SMALL,tileset[game.menuitem+1].name);

		drawMessage(50,220,FONT_SMALL,"START to leave edit mode, X to place tile");
		drawMessage(50,240,FONT_SMALL,"L/R-trigger to select tile");
		break;
	case GAME_EDITOR_LOAD:
	case GAME_EDITOR_SAVE:
		drawFilledRect(25,25,350,220,0xc0ffffff);
		drawMessage(50,40,FONT_HEADLINE,game.mode==GAME_EDITOR_SAVE?"Save as":"Load from");
		sprintf(buf,"level%03d.lvl",game.level);
		drawMessage(50,80,FONT_HEADLINE,buf);
		drawMessage(50,150,FONT_SMALL,"Use up and down to select");
		drawMessage(50,170,FONT_SMALL,"the level number.");
		
		drawMessage(50,220,FONT_SMALL,game.mode==GAME_EDITOR_LOAD?"Select level to load with X":"O to quit, X to save");
		break;
	case GAME_HIGHSCORE:
		drawHighScore();
		break;
	case GAME_SUMMARY:
		if(game.firstMessage && game.firstMessage->teletypePos<strlen(game.firstMessage->message)) break;
		sprintf(buf,"Score: %d",game.score);
		if(game.menuitem==0) insertMessage(buf,0,0x80<<24,2000);
		sprintf(buf,"Level: %d",game.level);
		if(game.menuitem==1) insertMessage(buf,0,0x80<<24,2000);
		if(game.menuitem==2) {
			if(newhigh()) {
				insertMessage("New High Score!",0,0x80<<24,2000);
			} else {
				setMode(GAME_INTRO);	// Place holder page.
				break;
			}
		}
		if(game.menuitem==3) setMode(GAME_NEWHIGH);
		game.menuitem++;
		break;
	case GAME_NEWHIGH:
		drawNewHigh();
		break;
	case GAME_CREDITS:
		if(game.firstMessage && game.firstMessage->teletypePos<strlen(game.firstMessage->message)) break;
		if(game.menuitem==5) insertMessage("Testing: hardhat, PSPJunkie, Zion, Wally",0,0x80<<24,4000);
		if(game.menuitem==4) insertMessage("Testing: PSPdemon, legendpaul, Meyitzo, Whazilla",0,0x80<<24,4000);
		if(game.menuitem==3) insertMessage("Sounds: WernerVonBraun, meanrabbit.com",0,0x80<<24,4000);
		if(game.menuitem==2) insertMessage("Music: Aleksi 'Heatbeat' Eeben",0,0x80<<24,4000);
		if(game.menuitem==1) insertMessage("Art: birdman2078, hardhat, a_nub, Meyitzo, newcoleco",0,0x80<<24,4000);
		if(game.menuitem==0) insertMessage("Programming: hardhat, anub",0,0x80<<24,4000);
		if(game.menuitem>5) setMode(GAME_INTRO);	// Place holder page.
		game.menuitem++;
		break;
	case GAME_RULES:
	case GAME_RULESPAUSE:
		if(game.firstMessage && game.firstMessage->teletypePos<strlen(game.firstMessage->message)) break;
		if(game.menuitem==7) insertMessage("START to quit game",0,0x80<<24,4000);
		if(game.menuitem==6) insertMessage("SELECT for screenshot.",0,0x80<<24,4000);
		if(game.menuitem==5) insertMessage("Talk to people with square",0,0x80<<24,4000);
		if(game.menuitem==4) insertMessage("Open doors with triangle",0,0x80<<24,4000);
		if(game.menuitem==3) insertMessage("Clear dialog clutter with R-trigger.",0,0x80<<24,4000);
		if(game.menuitem==2) insertMessage("Search for items with X: desks, O: other",0,0x80<<24,4000);
		if(game.menuitem==1) insertMessage("You will stagger based upon how drunk you are.",0,0x80<<24,4000);
		if(game.menuitem==0) insertMessage("Navigate with the d-pad or analog stick.",0,0x80<<24,4000);
		if(game.menuitem>7 && game.firstMessage==0) setMode(game.mode==GAME_RULES?GAME_INTRO:GAME_PAUSE);	// Place holder page.
		game.menuitem++;
		break;
	case GAME_PAUSE:
		drawPauseMenu();
		break;
	case GAME_INTRO:
		drawIntroMenu();
		break;
	case GAME_EXTRAS:
		drawExtrasMenu();
		break;
	case GAME_PLAY:
		{
		int w,h;
		int i;
		int x,y;
		
		for(i=0;i<9;i++) {
			struct Map *map=nearbyPlayer(i,MAP_ITEM,&x,&y);
			if(map && map->tile->id!=3) {
				drawTile(x*TILE_WIDTH,y*TILE_HEIGHT-TILE_HEIGHT/2,buttontile+3,0);
			}
			map=nearbyPlayer(i,MAP_DESK,&x,&y);
			if(map) {
				drawTile(x*TILE_WIDTH,y*TILE_HEIGHT-TILE_HEIGHT/2,buttontile+0,0);
			}
			map=nearbyPlayer(i,MAP_DOOR,&x,&y);
			if(map) {
				drawTile(x*TILE_WIDTH,y*TILE_HEIGHT-TILE_HEIGHT/2,buttontile+2,0);
			}
			map=nearbyPlayer(i,MAP_EXIT,&x,&y);
			if(map) {
				drawTile(x*TILE_WIDTH,y*TILE_HEIGHT-TILE_HEIGHT/2,buttontile+2,0);
			}
		}
		struct Character *c=nearbyPlayerNPC();
		if(c) {
			drawTile(c->x,c->y-TILE_HEIGHT/2,buttontile+1,0);
		}
		
		// redraw the points infront of the key hints.
		for(c=game.player;c;c=c->next) {
			if(c->type==CHAR_POINTS) drawCharacter(c);
		}
		
		sprintf(buf,"%05d ",game.level);
		extentMessage(&w,&h,FONT_MESSAGE,buf);
		x=(SCREEN_WIDTH-2*w)/2;
		drawFilledRect(x-2,252-4,w*2+4,h*2+6,0x80ffffff);

		drawTile(x+game.viewLeft,246+game.viewTop,hudtile+0,0);
		drawTile(x+w+game.viewLeft,246+game.viewTop,hudtile+1,0);
		sprintf(buf," %2d",game.level);
		drawMessage(x,252,FONT_MESSAGE,buf);

		sprintf(buf,"%05d",game.score);
		drawMessage(x+w,252,FONT_MESSAGE,buf);
		
		for(i=0;i<game.keys && i<5;i++) {
			int x=5+12*i;
			int y=5;
			int id=(game.healthTimer/200)&3;
			
			drawTile(x+game.viewLeft,y+game.viewTop,keytile+id,0);
		}
		
		for(i=0;i<100;i+=4) {
			int x=474;
			int y=272/2+100-i*2;
			int id=0;
			if(game.health<i) id=3;
			else if(game.health>=i+8) id=0;
			else id=3-(game.health-i)/2;
			if(game.health>80 && (game.healthTimer&256)) id=3;
			if(i>80) id+=4;
			drawTile(x+game.viewLeft,y+game.viewTop,metertile+id,0);
		}
		
		}
		break;
	case GAME_QUIT:
		break;
	}
	
	drawMessages();
}

long titleNow,titleDur;
enum TitleStage { TS_SLIDEIN_NAME, TS_SLIDEIN_BY, TS_WAIT, TS_SLIDEOUT_BY, TS_SLIDEOUT_NAME, TS_INTRO, TS_DONE } titleStage;

void initTitle()
{
	titleNow=0;
	titleStage=TS_SLIDEIN_NAME;
	titleDur=1000;
}

int updateDrawTitle(long elapsed)
{
	if(titleStage==TS_DONE) return 1;
	int tw,th,bw,bh;
	int x,y;
	enum FontId byFont=FONT_BODY;
	
	if(elapsed>50) return;	// loading delay.
	titleNow+=elapsed;
	
	extentMessage(&tw,&th,FONT_HEADLINE,"Last Call");
	extentMessage(&bw,&bh,byFont,"By HardHat & team");
	
	//printf("stage: %d titlenow: %d elapsed: %d\n",titleStage,titleNow,elapsed);
	
	switch(titleStage) {
	case TS_SLIDEIN_NAME:
		if(titleNow<titleDur) {
			x=SCREEN_WIDTH/2+cosf(titleNow/(float)titleDur*M_PI*2)*200*(titleDur-titleNow)/(float)titleDur;
			y=20+sinf(titleNow/(float)titleDur*M_PI*2)*200*(titleDur-titleNow)/(float)titleDur;
			drawMessage(x,y,FONT_HEADLINE,"Last Call");
			break;
		}
		titleNow=0;
		titleStage=TS_SLIDEIN_BY;
		break;
	case TS_SLIDEIN_BY:
		drawMessage(SCREEN_WIDTH/2,20,FONT_HEADLINE,"Last Call");
		if(titleNow<titleDur) {
			x=SCREEN_WIDTH*2/3-bw/2+sinf(-titleNow/(float)titleDur*(float)M_PI*2)*200*(titleDur-titleNow)/(float)titleDur;
			y=252-bh+cosf(-titleNow/(float)titleDur*(float)M_PI*2)*200*(titleDur-titleNow)/(float)titleDur;
			drawMessage(x,y,byFont,"By HardHat & team");
			break;
		}
		titleNow=0;
		titleDur=500;
		titleStage=TS_WAIT;
		break;
	case TS_WAIT:
		drawMessage(SCREEN_WIDTH/2,20,FONT_HEADLINE,"Last Call");
		x=SCREEN_WIDTH*2/3-bw/2;
		y=252-bh;
		drawMessage(x,y,byFont,"By HardHat & team");
		if(titleNow<titleDur*3) {
			break;
		}
		titleDur=1000;
		titleNow=0;
		titleStage=TS_SLIDEOUT_BY;
		break;
	case TS_SLIDEOUT_BY:
		drawMessage(SCREEN_WIDTH/2,20,FONT_HEADLINE,"Last Call");
		if(titleNow<titleDur) {
			int i;
			char *msg="By HardHat & team";
			int len=strlen(msg);
			char buf[2]=" ";
			int pos=0;
			for(i=0;i<len;i++) {
				int pw,ph;
				x=pos+SCREEN_WIDTH*2/3-bw/2+cosf(i/(float)len*M_PI*2)*300*(titleNow)/(float)titleDur;
				y=272-bh+sinf(i/(float)len*M_PI*2)*300*(titleNow)/(float)titleDur;
				buf[0]=msg[i];
				drawMessage(x,y,byFont,buf);
				extentMessage(&pw,&ph,byFont,buf);
				pos+=pw;
			}
			break;
		}
		titleNow=0;
		titleStage=TS_SLIDEOUT_NAME;
		break;
	case TS_SLIDEOUT_NAME:
		if(titleNow<titleDur) {
			int i;
			char *msg="Last Call";
			int len=strlen(msg);
			char buf[2]=" ";
			int pos=0;
			for(i=0;i<len;i++) {
				int pw,ph;
				x=pos+SCREEN_WIDTH/2+cosf(i/(float)len*M_PI*2)*300*(titleNow)/(float)titleDur;
				y=20+sinf(i/(float)len*M_PI*2)*300*(titleNow)/(float)titleDur;
				buf[0]=msg[i];
				drawMessage(x,y,FONT_HEADLINE,buf);
				extentMessage(&pw,&ph,FONT_HEADLINE,buf);
				pos+=pw;
			}
			break;
		}
		titleNow=0;
		titleStage=TS_DONE;
		break;
	case TS_INTRO:
	case TS_DONE:
		break;
	}
	
	return 0;
}

struct IntroPile {
	struct IntroPilePos {
		char *fname;
		int x,y;
	} pilePos[8];
} introPile[]={
//	{"br-room.png",0,0,"boss60.png",80,50,"br-backchairs.png",0,0,"les60.png",196,67,
//		"br-table.png",0,0,"fredback100.png",280,100,"jerryback100.png",173,118,"br-frontchairs.png",0,0},
//	{"br2-notable.png",0,0,"boss80.png",67,87,"les80.png",354,160,"br2-table.png",0,0},
//	{"br3-notable.png",0,0,"boss80.png",77,63,"les80.png",286,85,"br3-table.png",0,0},
//	{"br4-backchair.png",0,0,"fred60.png",197,45,"jerry60.png",266,50,"br4-table.png",0,0,
//		"bossback120.png",379,117,"lesback120.png",66,160,"br4-frontchair.png",0,0},
//	{"br4-backchair.png",0,0,"fred60.png",197,45,"jerry60.png",266,40,"br4-table.png",0,0,
//		"bossback120.png",379,117,"lesback120.png",66,160,"br4-frontchair.png",0,0},
	{"br1full.png",0,0},
	{"br2full.png",0,0},
	{"br3full.png",0,0},
	{"br4full.png",0,0},
	{"br4full-a.png",0,0},
	{"later.png",0,0},
	{"cub-nochair.png",0,0,"jerry80.png",110,75,"les80.png",235,90,"cub-chair.png",0,0},
	{"cub2-nodesk.png",0,0,"jerry.png",250,-40,"les.png",-40,60,"lesdrink.png",47,176,"cub2-desk.png",0,0},
	{"later.png",0,0},
	{"cub-nochair.png",0,0,"cindyside80.png",90,84,"les80.png",235,90,"cub-chair.png",0,0},
	{"cub2-nodesk.png",0,0,"cindy.png",250,-40,"les.png",-40,60,"lesdrink.png",47,176,"cub2-desk.png",0,0},
	{"later.png",0,0},
	{"cub-nochair.png",0,0,"sandrineside80.png",90,84,"les80.png",235,90,"cub-chair.png",0,0},
	{"cub2-nodesk.png",0,0,"sandrine.png",250,-40,"les.png",-40,60,"lesdrink.png",47,176,"cub2-desk.png",0,0},
	{"later.png",0,0},
	{"cub-nochair.png",0,0,"fred80.png",110,75,"les80.png",235,90,"cub-chair.png",0,0},
	{"cub2-nodesk.png",0,0,"fred.png",250,-40,"les.png",-40,60,"lesdrink.png",47,176,"cub2-desk.png",0,0},
	{"friends.png",0,0,"cub3-chairwall.png",0,0,"les60.png",205,146,"cub3-desk_bubble.png",0,0},
	{"friends.png",0,0,"les60.png",283,90,"cub3-chairwall.png",0,0,"cub3-desk.png",0,0}
};

struct IntroDialog {
	int x,y;
	char *sound;
	char *dialog;
	int duration;
} introDialog[]={
	{288,33,"toughchoice11k16.wav","It was a |tough choice.", 3500 },
	{218,46,"agree11k16.wav","All of the department |heads agree.", 2500},
	{190,166,"newmanager11k16.wav", "Les, you are our |new manager of |special projects.", 4000},
	{202,128,"waytogo11k16.wav","Way to go Les!  |Yeah!",3200},
	{202,128,"jerry-a11k16.wav","This calls for a |celebration",2000},
	{175,100,NULL,"Later...",2500},
	{32,20,"jerry-b11k16.wav","Have one on me.  |You earned it!",2500},
	{0,0,NULL,NULL,2000},
	{175,100,NULL,"Two drinks |later...",2500},
	{32,30,"cindy-onetoo11k16.wav","I got you one too.",2000},
	{0,0,NULL,NULL,2000},
	{175,100,NULL,"Two more |drinks later...",2500},
	{32,20,"sandrine11k16.wav","Here's a drink |from me to you.",3000},
	{0,0,NULL,NULL,2000},
	{175,100,NULL,"Still more |drinks later...",2500},
	{32,20,"fred11k16.wav","Here you go buddy.  |Drink up.",4000},
	{0,0,NULL,NULL,2000},
	{78,30,"les11k16.wav","I'd love to stay |and party, with you |but really, I just |want to go home.",6000},
	{0,0,NULL,NULL,3000}
};

struct IntroMove {
	int shot;	// Which shot to change
	int layer;	// Which layer to affect.
	int sx,sy;	// Initial x,y
	int tx,ty;	// Target x,y
	int start;	// when to start it in that shot
	int duration;	// how long the motion takes
} introMove[]={
	{7,3,47,176,62,165,500,500},
	{10,3,47,177,62,166,500,600},
	{13,3,47,178,62,167,500,800},
	{16,3,47,179,62,168,500,1000},
	{17,2,208,153, 208,127, 500,1000},
	{17,2,208,127, 283,90, 1500,3500},
	{18,1,283,90, 480,64, 500,1500},
	{99}
};

struct IntroState {
	int shot;	// Which entry in pile/dialog
	int wavActive;
	int dlgPos;		// Which character we're on
	int dlgTimer;	// Countdown until next letter.
	int move;	// which introMove to look at next.
} istate;

void initIntro()
{
	titleStage=TS_INTRO;
	titleNow=0;
	istate.shot=0;
	istate.wavActive=0;
	istate.dlgPos=0;
	istate.dlgTimer=0;
	istate.move=0;
	freeCells();
	freeWavs();
}

void drawIntro()
{
	int x,y;
	int i;

	if(titleStage==TS_DONE) return;
	// Apply intro move, if appropriate.
	if(introMove[istate.move].shot<istate.shot) istate.move++;
	if(introMove[istate.move].shot==istate.shot && titleNow>introMove[istate.move].start+introMove[istate.move].duration) {
		int layer=introMove[istate.move].layer;
		
		introPile[istate.shot].pilePos[layer].x=introMove[istate.move].tx;
		introPile[istate.shot].pilePos[layer].y=introMove[istate.move].ty;
		
		istate.move++;
	}
	if(introMove[istate.move].shot==istate.shot && titleNow>introMove[istate.move].start) {
		int layer=introMove[istate.move].layer;		// Do Interpolation.
		float per,cent;
		
		per=(titleNow-introMove[istate.move].start)/(float)introMove[istate.move].duration;
		cent=1-per;
		
		introPile[istate.shot].pilePos[layer].x=cent*introMove[istate.move].sx+per*introMove[istate.move].tx;
		introPile[istate.shot].pilePos[layer].y=cent*introMove[istate.move].sy+per*introMove[istate.move].ty;
	}
	
	// draw the pile of sprites.
	for(i=0;i<8;i++) {
		struct IntroPilePos *p=&introPile[istate.shot].pilePos[i];
		char *f=p->fname;
		if(f) {
			drawCell(p->x,p->y,f);
		}
	}
	
	// Now draw the text.
	if(titleNow==0) {
		istate.dlgPos=0;
		istate.dlgTimer=0;
	}
	char *dlg=introDialog[istate.shot].dialog;
	if(dlg && istate.dlgTimer<0) {
		if(istate.dlgPos<=strlen(dlg)) istate.dlgPos++;
		istate.dlgTimer+=50;
	}
	if(dlg) {
		x=introDialog[istate.shot].x;
		y=introDialog[istate.shot].y;
		char buffer[256];
		strcpy(buffer,dlg);
		buffer[istate.dlgPos]=0;
		// Now interpret line breaks
		char *s=buffer;
		while( strchr(s,'|')) {
			int w,h;
			char *next=strchr(s,'|');
			next[0]=0;
			next++;
			drawMessage(x,y,FONT_MESSAGE,s);
			extentMessage(&w,&h,FONT_MESSAGE,s);
			y+=h;
			s=next;
		}
		drawMessage(x,y,FONT_MESSAGE,s);
	}
	// start any needed sounds
	if(titleNow==0) {
		if(introDialog[istate.shot].sound) {
			playWav(introDialog[istate.shot].sound);
			titleNow++;
		}
	}
}

int updateIntro(long elapsed) 
{
	if(titleStage==TS_DONE) return 1;
	titleNow+=elapsed;

	char *dlg=introDialog[istate.shot].dialog;
	if(dlg) {
		istate.dlgTimer-=elapsed;
	}
	// Update the state
	if(titleNow>introDialog[istate.shot].duration) {
		titleNow=0;
		freeCells();
		freeWavs();
		istate.shot++;
		if(istate.shot>=sizeof(introDialog)/sizeof(introDialog[0])) {
			titleStage=TS_DONE;
		}
	}
	
	return 0;
}
