#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <pspgu.h>
#include <pspgum.h>
#include <pspkernel.h>

#include "main.h"
#include "ase.h"
#include "particle.h"
#include "map.h"
#include "duel.h"

//enum PlaceType { PT_WHITE,PT_YELLOW,PT_BLUE,PT_BLACK,PT_RED,PT_GREEN, PT_CHEST,PT_CRATE,PT_GRASS,PT_TREE,PT_ARIDTREE } type;
const char *placeTypeName[]={ "White","Yellow","Blue","Black","Red","Green","Chest","Crate","Grass","Tree","Arid Tree" };
int placeItemId[12]={11,12,13,14,15,16, 17,18, 4,2,3};
int grassItemId[7]={4,5,6,7,8,9,10};
int aridTreeItemId[2]={3,1};
int treeItemId[3]={0,1,2};

const char *shardName[]={"Diamond Shard","Yellow Onyx Shard","Blue Garnet Shard","Black Obsidian Shard","Ruby Shard","Green Emerald Shard" };
const char *itemName[]={"Mini Healing Potion","Healing Potion","Mega Healing Potion",
	"Strengthing Potion","Defensive Potion",
	"Mini Action Point Salve","Action Point Salve","Mega Action Point Salve",
	"Mini Pill of Experience","Pill of Experience",	"Mega Pill of Experience",
	};
extern int shardId;
extern int potionId;
extern int salveId;
extern int pillId;
	
Image *brickTexture[6];
const char *textureName[]={"models/brick/doctor.png","models/brick/yellow.png","models/brick/pirate.png",
"models/brick/comm.png","models/brick/red.png","models/brick/green.png"};

struct PlaceAvatar {
	int itemId;
	float rotateY;
};

#define PF_VISIBLE 1	// Is it available yet?
#define PF_VISITED 2	// Is it new?
#define PF_MASTERED 4	// Has it been beaten/opened/smashed
#define PF_TOURNAMENT 8	// Is there a tournament available there?
struct Place {
	float x,y,z;
	enum PlaceType type;
	int level;	// what level of creatures you'll find there.
	int flags;
	enum PlaceType keyRequired;	// only used by chests
	char contains[48];		// what's in it.
	struct PlaceAvatar avatar;
	float dist;	// how far away from the the welcome centre this is, for sorting purposes.
};

extern struct AnimMotion animMotion[];
extern const char *animMotionName[];

#define DIR_UP 1
#define DIR_RIGHT 2
#define DIR_DOWN 4
#define DIR_LEFT 8

struct MapActor {
	char name[128];	// name of the creature
	struct AnimMesh *mesh;
	float x,y,z;
	int direction;	// force applied by the dpad to dx/dy
	float dx,dy;	// direction/speed
	float ax,ay;	// analog stick direction/speed
	float angle;

	// animation part
	enum MotionType motion;	// automatically goes back to STANDING when done an animation
	enum MotionType oldMotion;	// just for debugging output.
	int lastFrame;
	int frame;
	int frameElapsed;	
};

struct Map {
	// important
	struct Place *place;
	int placeCount;
	int scale;
	int activePlace;
	MapState state;

	// transient
	struct MapActor actor;
	float cameraAngle;
	int angleDir;
	
	short *elev;
	int width;
	int height;
	struct Terrain *terrain;
	struct ParticleEmitter *particleEmitter;
	struct ParticleEmitter particleSelectedEmitter;
	int popupMenu;
};
struct ParticleDescription mapDesc={45000,20,1000,0.5f,{0,25,0},1.0f,{0,-20,0},0.0f/*6.28f*/,40,5,0,0.0f,0xffffff80,0xffe0e0e0};
struct ParticleDescription mapSelectedDesc={45000,20,500,0.5f,{0,50,0},1.0f,{0,-20,0},0.0f/*6.28f*/,40,10,7,0.0f,0xffff0080,0xff8000ff};
struct ParticleDescription mapChestDesc={45000,40,500,0.5f,{0,50,0},1.0f,{0,-20,0},0.0f/*6.28f*/,40,5,5,0.0f,0xff008080,0xff00ffff};
struct ParticleDescription mapCrateDesc={45000,40,500,0.5f,{0,50,0},1.0f,{0,-20,0},0.0f/*6.28f*/,40,5,3,0.0f,0xff406080,0xff007fcf};

int cmpPlace(const void *one,const void *two)
{
	struct Place *a=(struct Place *)one,*b=(struct Place *)two;
	//printf("comparing %08x to %08x\n",(int)a,(int)b);
	//printf("a->level=%d, b->level=%d\n",a->level,b->level);

	if(a->type>PT_CRATE && b->type<=PT_CRATE) return 1;
	if(a->type<=PT_CRATE && b->type>PT_CRATE) return -1;

	if(a->level<b->level && a->level!=0) return -1;
	if(a->level>b->level && b->level!=0) return 1;

	//printf("a->type=%d, b->type=%d\n",a->type,b->type);
	if(a->type<b->type) return -1;	
	if(a->type>b->type) return 1;

	//printf("a->type=%f, b->type=%f\n",a->dist,b->dist);
	// same type.
	return a->dist==b->dist?0:a->dist<b->dist?-1:1;
}

struct Map *initMap()
{
	struct Map *map=(struct Map *)calloc(sizeof(struct Map),1);
	
	map->terrain=terrainDetailInit("data/mapelev.png","data/mapcolor.png");
	// init the map locations.
	Image *loc=loadPng("data/maptraining.png");
	Image *elev=loadPng("data/mapelev.png");
	if(!loc) return 0;
	int cx=0,cy=0;
	int x,y;
	int count=0;
	int decCount=0;
	for(y=0;y<loc->imageHeight;y++) {
		for(x=0;x<loc->imageWidth;x++) {
			Color c=loc->data[x+y*loc->textureWidth];
			if(c==0xff808080) continue;
			if(c==0xff008000 || c==0xff800080) decCount++;
			count++;
			if(c==0xffffffff) {
				cx=x;
				cy=y;
			}
		}
	}
	map->placeCount=count;
	if(count<1) return 0;
	map->place=(struct Place *)calloc(sizeof(struct Place),count);
	// now we know how many locations there are, and also where on the island we start.
	map->height=loc->imageHeight;
	map->width=loc->imageWidth;
	map->scale=32;
	map->elev=(short *)malloc(map->width*map->height*sizeof(short));
	count=0;
	for(y=0;y<loc->imageHeight;y++) {
		for(x=0;x<loc->imageWidth;x++) {
			Color c=loc->data[x+y*loc->textureWidth];
			int height=elev->data[x+elev->textureWidth*y]&255;
			height=(height-128);
			map->elev[x+map->width*y]=height;
			if(c==0xff808080) continue;
			map->place[count].flags=0;	// new, invisible, no tournament yet.
			map->place[count].level=0;	// last step.
			enum PlaceType type=PT_WHITE;
			switch(c) {
			case 0xffffffff: type=PT_WHITE; break;
			case 0xff0000ff: type=PT_RED; break;
			case 0xff00ff00: type=PT_GREEN; break;
			case 0xff00ffff: type=PT_YELLOW; break;
			case 0xffff0000: type=PT_BLUE; break;
			case 0xff000000: type=PT_BLACK; break;

			case 0xff008080: type=PT_CRATE; break;
			case 0xff800000: type=PT_CHEST; break;
			case 0xff000080: type=PT_ARIDTREE; break;
			case 0xff008000: type=PT_TREE; break;
			case 0xff800080: type=PT_GRASS; break;
			}
			
			//printf("color %08x mapped to place type %d (\"%s\")\n",c,type,placeTypeName[type]);
			
			map->place[count].type=type;
			if(map->place[count].type>PT_WHITE) map->place[count].flags|=PF_VISIBLE|PF_VISITED;
			if(map->place[count].type==PT_WHITE) map->place[count].flags|=PF_VISIBLE;
			if(map->place[count].type==PT_BLACK) map->place[count].flags|=PF_TOURNAMENT;
			map->place[count].contains[0]=0;
			map->place[count].x=(x-loc->imageWidth/2)*map->scale;
			map->place[count].z=(y-loc->imageHeight/2)*map->scale*2;
			map->place[count].y=height;
			map->place[count].dist=sqrtf((x-(float)cx)*(x-cx)+(y-(float)cy)*(y-cy));
			map->place[count].avatar.itemId=placeItemId[type];
			map->place[count].avatar.rotateY=0;
			const char *contains="";
			if((rand()>>16)&1) {
				contains=shardName[(rand()>>16)%6];
			} else {
				contains=itemName[(rand()>>16)%11];
			}
			strcpy(map->place[count].contains,contains);
			if(type==PT_GRASS) {
				static int grassId=0;
				map->place[count].avatar.itemId=grassItemId[(grassId++)%6];
				map->place[count].avatar.rotateY=rand()/(float)RAND_MAX*GU_PI*2;
			} else if(type==PT_TREE) {
				static int treeId=0;
				map->place[count].avatar.itemId=treeItemId[(treeId++)%3];
				map->place[count].avatar.rotateY=rand()/(float)RAND_MAX*GU_PI*2;
			} else if(type==PT_ARIDTREE) {
				static int aridTreeId=0;
				map->place[count].avatar.itemId=aridTreeItemId[(aridTreeId++)%2];
				map->place[count].avatar.rotateY=rand()/(float)RAND_MAX*GU_PI*2;
			}
			count++;
		}
	}
	if(elev) freeImage(elev);
	if(loc) freeImage(loc);
	printf("Sorting place %08x, with %d items\n",(int)map->place,map->placeCount);
	qsort(map->place,map->placeCount,sizeof(struct Place),cmpPlace);
	int typeLevel[]={1,3,5,7,9,11,0,0,0,0,0,0,0,0,0,0};
	int i;
	for(i=0;i<map->placeCount;i++) {
		map->place[i].level=typeLevel[map->place[i].type];
		if(map->place[i].type<=PT_GREEN) typeLevel[map->place[i].type]+=12;
	}
	qsort(map->place,map->placeCount,sizeof(struct Place),cmpPlace);
#if 0
	FILE *out=fopen("log","w");
	for(i=0;i<map->placeCount;i++) {
		fprintf(out,"place %d: type='%s', level=%d\n",i,placeTypeName[map->place[i].type],map->place[i].level);
	}
	fclose(out);
#endif
	map->place[0].flags|=PF_VISIBLE;
	strcpy(map->actor.name,"Hero");
	map->actor.x=map->place[0].x;
	map->actor.y=map->place[0].y;
	map->actor.z=map->place[0].z;
	map->state=MS_ACTIVE;
	if(!map->particleEmitter) {
		map->particleEmitter=(struct ParticleEmitter *)calloc(sizeof(struct ParticleEmitter),map->placeCount);
	}
	//sceKernelDelayThread(100000000);
	return map;
}

float getMapElev(struct Map *map,float x,float y)
{
	if(!map) return 0;
	int gridx=floor(x/map->scale),gridy=floor(y/(map->scale*2));
	float fx=x/map->scale-gridx,fy=y/(map->scale*2)-gridy;
	gridx+=map->width/2;
	gridy+=map->height/2;
	if(gridx<0 || gridx+1>=map->width) gridx=0;
	if(gridy<0 || gridy+1>=map->height) gridy=0;
	int a=map->elev[gridx+map->width*gridy],
		b=map->elev[gridx+1+map->width*gridy],
		c=map->elev[gridx+map->width*(gridy+1)],
		d=map->elev[gridx+1+map->width*(gridy+1)];
	float rc=d*fx*fy+c*(1-fx)*fy+b*fx*(1-fy)+a*(1-fx)*(1-fy);
	//printf("Elev for %f,%f is %f;",x,y,rc);
	//printf(" %d,%d==%d (%f,%f)\n",gridx,gridy,map->elev[gridx+map->width*gridy],fx,fy);
	return rc;
}

void freeMap(struct Map *map)
{
	if(!map) return;
	if(map->terrain) freeTerrain(map->terrain);
	map->terrain=0;
	if(map->place) free(map->place);
	map->place=0;
	if(map->elev) free(map->elev);
	map->elev=0;
	free(map);
}

//#define DEBUG_ANGLE 1
#ifdef DEBUG_ANGLE
float debugDX,debugDY,debugTX,debugTY, debugNewAngle;
#endif

void updateMapActor(struct Map *map,struct MapActor *actor,int elapsed)
{
	actor->dx=actor->dx*0.80+0.20*((actor->direction&DIR_LEFT?-100:0)+(actor->direction&DIR_RIGHT?100:0));
	actor->dy=actor->dy*0.80+0.20*((actor->direction&DIR_UP?-100:0)+(actor->direction&DIR_DOWN?100:0));
	if(actor->dx>-0.05f && actor->dx<0.05f) actor->dx=0;	// round tiny values.
	if(actor->dy>-0.05f && actor->dy<0.05f) actor->dy=0;	// round tiny values.
	float dx=(actor->dx+actor->ax)*elapsed/1000.0f;
	float dy=(actor->dy+actor->ay)*elapsed/1000.0f;
	float tx=dx*cosf(map->cameraAngle)+dy*sinf(map->cameraAngle);
	float ty=-dx*sinf(map->cameraAngle)+dy*cosf(map->cameraAngle);
#ifdef DEBUG_ANGLE
	debugDX=dx;
	debugDY=dy;
	debugTX=tx;
	debugTY=ty;
#endif

	float y=getMapElev(map,actor->x+tx,actor->z+ty);
	if(y>-128) {		// no walking on water! should really slide I guess.
		actor->x+=tx;
		actor->z+=ty;
		actor->y=y;
	}
	if(dx!=0 || dy!=0) {
		float newAngle;
		newAngle=atan2f(-(float)tx,-(float)ty);		
#ifdef DEBUG_ANGLE
		debugNewAngle=newAngle;
#endif
		if(newAngle<actor->angle-GU_PI) newAngle+=2*GU_PI;
		if(newAngle>actor->angle+GU_PI) newAngle-=2*GU_PI;
		actor->angle=actor->angle*0.95f+newAngle*0.05f;
		if(newAngle<map->cameraAngle-GU_PI) newAngle+=2*GU_PI;
		if(newAngle>map->cameraAngle+GU_PI) newAngle-=2*GU_PI;
		map->cameraAngle=map->cameraAngle*0.99f+newAngle*0.01f;
		// renormalize actor->angle and map->cameraAngle:
		if(actor->angle<-GU_PI) actor->angle+=2*GU_PI;
		if(actor->angle>GU_PI) actor->angle-=2*GU_PI;
		if(map->cameraAngle<-GU_PI) map->cameraAngle+=2*GU_PI;
		if(map->cameraAngle>GU_PI) map->cameraAngle-=2*GU_PI;
	}

	if(actor->oldMotion!=actor->motion) {
		//printf("character %s now doing motion %s.\n",actor->name,animMotionName[actor->motion]);
		actor->oldMotion=actor->motion;
	}

	actor->frameElapsed+=elapsed;
	if(dx==0 && dy==0) {
		if(actor->motion!=MT_STAND) {
			actor->motion=MT_STAND;
			actor->frameElapsed=0;
		}
	} else {
		if(actor->motion!=MT_RUN) {
			actor->motion=MT_RUN;
			actor->frameElapsed=0;
		}
	}
	
	// animate the MD2
	struct AnimMotion *motion=animMotion+actor->motion;
	int frameMod=actor->frameElapsed/(1000/motion->fps);
	frameMod+=motion->startFrame;
	if(frameMod>motion->endFrame) {
		frameMod=motion->startFrame;
		actor->frameElapsed=0;
		// we always cycle whatever motion.
	}
	if(frameMod!=actor->frame) {
		actor->lastFrame=actor->frame;
		actor->frame=frameMod;
	}
}

void drawMapActor(struct MapActor *actor)
{
	if(!actor) return;
	if(!actor->mesh) {
		actor->mesh=md2Load("models/brick/tris.md2");
		if(actor->mesh && !actor->mesh->texture) {
			actor->mesh->texture=loadPng("models/brick/blue.png");
		}
		if(actor->mesh && actor->mesh->texture) swizzleFast(actor->mesh->texture);
	}
	if(!actor->mesh) return;
	int frameDuration=1000/animMotion[actor->motion].fps;
	float fraction=(actor->frameElapsed%frameDuration)/(float)frameDuration;
	int frame,nextFrame;
	frame=actor->lastFrame;
	nextFrame=actor->frame;

	struct AnimMesh *mesh=actor->mesh;
	struct Vertex3DTP *buffer=(struct Vertex3DTP *)sceGuGetMemory( sizeof(struct Vertex3DTP)*mesh->polyCount*3);
	//if(model->cullBackface==0) buffer=(struct Vertex3DTP *)(0x0fffffff&(int)buffer);
	buffer=(struct Vertex3DTP *)((int)buffer&~0x40000000);
	float minMax[6];
	md2GetFractionalFrame(mesh,frame,nextFrame,fraction,buffer,minMax);
	sceKernelDcacheWritebackAll();

	Image *source=mesh->texture;
	if(source) {
		sceGuTexMode(GU_PSM_8888, 0, 0, source->isSwizzled);
		sceGuTexImage(0, source->textureWidth, source->textureHeight, source->textureWidth, source->data);
	}
	sceGumMatrixMode(GU_MODEL);
	sceGumPushMatrix();
	/* int i;
	for(i=0;i<6;i++) printf("minMax[%d]=%.2f; ",i,minMax[i]);
	printf("\n"); */
	ScePspFVector3 trans={actor->x,actor->y+20,actor->z};
	sceGumTranslate(&trans);
	sceGumRotateY(actor->angle);
	sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_TRANSFORM_3D,mesh->polyCount*3,0,buffer);
	sceGumPopMatrix();
	//printf("acting\n");
}

MapState updateMap(struct Map *map,int elapsed)
{
	updateMapActor(map,&map->actor,elapsed);
	if(map->angleDir<0) map->cameraAngle-=elapsed*GU_PI/4/1000.0f*2;
	if(map->cameraAngle>GU_PI) map->cameraAngle-=2*GU_PI;
	if(map->angleDir>0) map->cameraAngle+=elapsed*GU_PI/4/1000.0f*2;
	if(map->cameraAngle<-GU_PI) map->cameraAngle+=2*GU_PI;
	
	int i;
	float ax=map->actor.x,ay=map->actor.y,az=map->actor.z;
	// places
	float nearestDist2=0;
	int oldActivePlace=map->activePlace;
	for(i=0;i<map->placeCount;i++) {
		struct Place *p=map->place+i;
		if(!(p->flags&PF_VISIBLE)) continue;
		if(p->type>=PT_GRASS) continue;
		float dx=ax-p->x,dy=ay-p->y,dz=az-p->z;
		float dist2=dx*dx+dy*dy+dz*dz;
		if((nearestDist2==0 || dist2<nearestDist2) && p->type<=PT_CRATE) { //PT_GREEN) {
			map->activePlace=i;
			nearestDist2=dist2;
		}
		if(dist2<512*512) {
			// nearby one
			if(!(p->flags&PF_VISITED)) {
				if(!map->particleEmitter[i].desc) newParticle(map->particleEmitter+i,&mapDesc,0,0,0);
				updateParticles(map->particleEmitter+i,elapsed);
			} else {
				if(map->particleEmitter[i].desc) {
					newParticle(map->particleEmitter+i,0,0,0,0);	// clear it out.
				}
			}
		}
	}
	if(map->place[map->activePlace].type!=map->place[oldActivePlace].type) {
		struct ParticleDescription *desc=&mapSelectedDesc;
		if(map->place[map->activePlace].type==PT_CHEST) desc=&mapChestDesc;
		if(map->place[map->activePlace].type==PT_CRATE) desc=&mapCrateDesc;
		newParticle(&map->particleSelectedEmitter,desc,0,0,0);
	}
	if(updateParticles(&map->particleSelectedEmitter,elapsed)) {
		struct ParticleDescription *desc=&mapSelectedDesc;
		if(map->place[map->activePlace].type==PT_CHEST) desc=&mapChestDesc;
		if(map->place[map->activePlace].type==PT_CRATE) desc=&mapCrateDesc;
		newParticle(&map->particleSelectedEmitter,desc,0,0,0);
	}
	/* if(map->coin) {
		if(updateAseSceneMax(map->coin,elapsed,map->coin->lastFrame))
			resetAseScene(map->coin,map->coin->firstFrame);
	} */
	if(map->state==MS_ACTIVE) {
		struct MapActor *a=&map->actor;
		float x=a->x+sinf(map->cameraAngle)*200;
		float z=a->z+cosf(map->cameraAngle)*200;
		float y=getMapElev(map,x,z)+90;
		if(y<a->y+150) y=a->y+90;
		cameraSetFromTo(x,y,z,a->x,a->y,a->z);
		//printf("setting camera to go from %f,%f,%f to %f,%f,%f\n",x,y,z,a->x,a->y,a->z);
	}
	return map->state;
}

extern const char *tournamentName[6];

void drawMap(struct Map *map)
{
	sceGuEnable(GU_LIGHTING);

	sceGuEnable(GU_BLEND);
	sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);

	sceGuFrontFace(GU_CCW);
	drawTerrain(map->terrain);

	sceGumMatrixMode(GU_MODEL);
	sceGumLoadIdentity();

	sceGuDisable(GU_LIGHTING);
	sceGuEnable(GU_DEPTH_TEST);
	sceGuFrontFace(GU_CW);
	sceGuEnable(GU_TEXTURE_2D);
	drawMapActor(&map->actor);	
	sceGuEnable(GU_LIGHTING);
	int i;
	int available=0;
	int tournamentCentre=0;
	int masteredTournament=0;
	int activePlaceClose=0;
	int crateTotal=0;
	int crateOpen=0;
	int chestTotal=0;
	int chestOpen=0;
	float ax=map->actor.x,ay=map->actor.y,az=map->actor.z;
	sceGuAlphaFunc(GU_GREATER,3,0xff);
	sceGuEnable(GU_ALPHA_TEST);
	for(i=0;i<map->placeCount;i++) {
		struct Place *p=map->place+i;
		if(!(p->flags&PF_VISIBLE)) continue;
		if(p->flags&PF_TOURNAMENT) tournamentCentre++;
		if(p->flags&PF_MASTERED && p->flags&PF_TOURNAMENT) masteredTournament++;
		if(p->type==PT_CHEST) {
			chestTotal++;
			if(p->flags&PF_MASTERED) chestOpen++;
		}
		if(p->type==PT_CRATE) {
			crateTotal++;
			if(p->flags&PF_MASTERED) crateOpen++;
		}
		available++;
		float dx=ax-p->x,dy=ay-p->y,dz=az-p->z;
		float dist2=dx*dx+dy*dy+dz*dz;
		if(dist2<512*512) {
			// draw this one
			sceGumPushMatrix();
			ScePspFVector3 trans={p->x,p->y,p->z};
			sceGumTranslate(&trans);
			sceGumPushMatrix();
			if(p->avatar.rotateY!=0) sceGumRotateY(p->avatar.rotateY);
			/* if(map->coin) {
				Image *oldCoin=map->coin->material->image;
				if(map->coinTexture[p->type+(p->flags&PF_MASTERED?6:0)])
					map->coin->material->image=map->coinTexture[p->type+(p->flags&PF_MASTERED?6:0)];
				drawAseScene(map->coin);
				map->coin->material->image=oldCoin;
			} */
			int item=p->avatar.itemId;
			setItemPos(item,0,0,0);
			renderItem(item);
			sceGumPopMatrix();
			
			if(!(p->flags&PF_VISITED)) {
				if(map->particleEmitter) {
					drawParticles(map->particleEmitter+i);
				}
			}
			if(i==map->activePlace && dist2<96*96) {
				if(map->particleSelectedEmitter.desc) {
					drawParticles(&map->particleSelectedEmitter);
				} else {
					newParticle(&map->particleSelectedEmitter,&mapSelectedDesc,0,0,0);
				}
				activePlaceClose=1;
			}
			sceGumPopMatrix();
		}
	}
	sceGuDisable(GU_ALPHA_TEST);
	sceGuEnable(GU_DEPTH_TEST);
	sceGuDisable(GU_LIGHTING);


	//int w=intraFontMeasureText(iFont[FONT_BODY],"Welcome to Sushi Island");
	//intraFontPrint(iFont[FONT_BODY],240-w/2,25,"Welcome to Sushi Island");

	char buf[256];
	//sprintf(buf,"Centers: %d/%d",available,6);
	//intraFontPrint(iFont[FONT_BODY],5,205,buf);
	sprintf(buf,"Crates and Chests: %d, %d/%d",crateTotal,chestOpen,chestTotal);
	intraFontPrint(iFont[FONT_BODY],5,220,buf);
	sprintf(buf,"Tournaments: %d/%d",map->place[PT_BLACK].level/12+1,6);
	intraFontPrint(iFont[FONT_BODY],5,235,buf);
	// Draw stuff about the currently selected destination.
	if(map->activePlace>=0 && map->activePlace<6 && activePlaceClose) {
		struct Place *p=map->place+map->activePlace;
		sprintf(buf,"Training Center %s",placeTypeName[map->activePlace]);
		intraFontPrint(iFont[FONT_BODY],300,190,buf);
		sprintf(buf,"Mastered: %s",p->flags&PF_MASTERED?"yes":"no");
		intraFontPrint(iFont[FONT_BODY],300,205,buf);
		sprintf(buf,"%s",p->flags&PF_TOURNAMENT?tournamentName[(p->level/12)%6]:"");
		intraFontPrint(iFont[FONT_BODY],300,220,buf);
		sprintf(buf,"Type: %s (%d)",placeTypeName[p->type],p->type);
		intraFontPrint(iFont[FONT_BODY],300,235,buf);
		sprintf(buf,"Level: %d",p->level);
		intraFontPrint(iFont[FONT_BODY],300,250,buf);
	} else {
		struct Place *p=map->place+map->activePlace;
		sprintf(buf,"Type: %s (%d)",placeTypeName[p->type],p->type);
		intraFontPrint(iFont[FONT_BODY],300,235,buf);
		sprintf(buf,"Location: %d",map->activePlace);
		intraFontPrint(iFont[FONT_BODY],300,250,buf);
	}
	if(map->popupMenu) {
		sprintf(buf,"Use arrow keys to choose which training centre to visit.");
	} else {
		char *opt="";
		if(activePlaceClose && map->activePlace<6) opt="X to enter battle. ";
		else if(activePlaceClose && map->activePlace>6) opt="X to open. ";
		sprintf(buf,"Press O to Save. %s[] to teleport",opt);
	}
	intraFontPrint(iFont[FONT_SMALL],15,260,buf);
#ifdef DEBUG_ANGLE
	sprintf(buf,"dx,dy: %.2f,%.2f TX,TY: %.2f,%.2f",map->actor.dx,map->actor.dy,debugTX,debugTY);
	intraFontPrint(iFont[FONT_SMALL],15,45,buf);
	sprintf(buf,"camera %.3f; actor %.3f; newAngle %.3f",map->cameraAngle,map->actor.angle,debugNewAngle);
	intraFontPrint(iFont[FONT_SMALL],15,60,buf);
	sprintf(buf,"DX,DY: %.2f,%.2f",debugDX,debugDY);
	intraFontPrint(iFont[FONT_SMALL],15,75,buf);
#endif
}

PlaceType getActivePlace(struct Map *map)
{
	return map->place[map->activePlace].type;
}

int getActiveLevel(struct Map *map)
{
	return map->place[map->activePlace].level;
}

int mapCompletePercent(struct Map *map)
{
	int per=0,cent=73;	// difficult of the last green level
	int i;
	for(i=0;i<6;i++) {
		if(per<map->place[i].level && map->place[i].flags&PF_MASTERED) per=map->place[i].level;
	}
	if(per>cent) per=cent;
	return 100*per/cent;
}

int applyDuelResult(struct Map *map,DuelState ds)
{
	int result=0;
	map->place[map->activePlace].flags|=PF_VISITED;
	struct Place *curr=map->place+map->activePlace;
	struct Place *old=0;
	if(map->activePlace>0)
		old=map->place+map->activePlace-1;
	else if(map->activePlace==0 && curr->level>1) 
		old=map->place+5;
	if(old && ds==DS_WIN) {
		old->level=curr->level+10;
		old->flags&=~PF_MASTERED;
	}

	if(map->activePlace+1<map->placeCount && map->place[map->activePlace].type<=PT_GREEN) {
		map->place[map->activePlace+1].flags|=PF_VISIBLE;
	}
	if(ds==DS_WIN) {
		if((map->place[map->activePlace].flags&PF_MASTERED)==0 && map->place[map->activePlace].type==PT_BLACK) {
			printf("*** Black tournament victory.  Time for spoils.\n");
			// we rebuild all crates!
			int i;
			for(i=0;i<map->placeCount;i++) {
				if(map->place[i].type==PT_CRATE) {
					map->place[i].flags|=PF_VISIBLE;	// more chocolately goodness!
					map->place[i].flags&=~PF_MASTERED;	// more chocolately goodness!
				}
			}
			result=1;
		}
		map->place[map->activePlace].flags|=PF_MASTERED;
		if(map->activePlace+1<map->placeCount && (map->place[map->activePlace].flags&PF_MASTERED)) {
			map->place[(map->activePlace+1)%6].flags&=~PF_VISITED;
		}
	}
	map->state=MS_ACTIVE;
	return result;
}

int mapSaveSize(struct Map *map)
{
	return sizeof(struct Place)*map->placeCount+sizeof(int)*3;
}

void saveMapBuffer(struct Map *map,char *buffer,int size)
{
	memcpy(buffer,&map->activePlace,sizeof(int));
	buffer+=4;
	memcpy(buffer,&map->scale,sizeof(int));
	buffer+=4;
	memcpy(buffer,&map->placeCount,sizeof(int));
	buffer+=4;
	memcpy(buffer,map->place,sizeof(struct Place)*map->placeCount);
}

void loadMapBuffer(struct Map *map,char *buffer,int size)
{
	if(((int *)buffer)[2]*sizeof(struct Place)+sizeof(int)*3!=size) {
		printf("Buffer is corrupt\n");
		return;
	}
	memcpy(&map->activePlace,buffer,sizeof(int));
	buffer+=4;
	// now, teleport the actor to the new active place.
	struct Place *p=map->place+map->activePlace;
	map->actor.x=p->x-32;
	map->actor.y=p->y;
	map->actor.z=p->z;
	
	memcpy(&map->scale,buffer,sizeof(int));
	buffer+=4;
	memcpy(&map->placeCount,buffer,sizeof(int));
	buffer+=4;
	free(map->place);
	map->place=(struct Place *)malloc(sizeof(struct Place)*map->placeCount);
	memcpy(map->place,buffer,sizeof(struct Place)*map->placeCount);
}

int handleMapDown(struct Map *map,Buttons button)
{
	if(map->popupMenu) {
		map->actor.direction=0;
		return 0;
	}
	switch(button) {
	case BT_LTRIGGER:
		map->angleDir=1;
		break;
	case BT_RTRIGGER:
		map->angleDir=-1;
		break;
	case BT_UP:
		map->actor.direction|=DIR_UP;
		//printf("Going up.\n");
		break;
	case BT_DOWN:
		map->actor.direction|=DIR_DOWN;
		//printf("Going down.\n");
		break;
	case BT_LEFT:
		map->actor.direction|=DIR_LEFT;
		//printf("Going left.\n");
		break;
	case BT_RIGHT:
		map->actor.direction|=DIR_RIGHT;
		//printf("Going right.\n");
		break;
	case BT_SQUARE:
		map->popupMenu=1;
		break;
	default:
		break;
	}
	return 0;	
}

extern int chestId;

int handleMap(struct Map *map,enum Buttons button) 
{
	if(map->popupMenu) {
		map->actor.direction=0;
		switch(button) {
		case BT_SQUARE:
			map->popupMenu=0;
			return 0;
		case BT_LEFT:
		case BT_UP:
			map->activePlace--;
			if(map->activePlace>5) map->activePlace=0;		// we where near a chest or something.
			if(map->activePlace<0) map->activePlace=5;
			break;
		case BT_RIGHT:
		case BT_DOWN:
			map->activePlace++;
			if(map->activePlace>5) map->activePlace=0;
			break;
		default:
			return 0;
		}
		// now, teleport the actor to the new active place.
		struct Place *p=map->place+map->activePlace;
		map->actor.x=p->x-32;
		map->actor.y=p->y;
		map->actor.z=p->z;
		return 0;
	}
	struct Place *p=map->place+map->activePlace;
	float x=map->actor.x-p->x;
	float y=map->actor.y-p->y;
	float z=map->actor.z-p->z;
	float dist2;
	switch(button) {
	case BT_LTRIGGER:
		if(map->angleDir>0) map->angleDir=0;
		break;
	case BT_RTRIGGER:
		if(map->angleDir<0) map->angleDir=0;
		break;
	case BT_CROSS:
	case BT_START:
		dist2=x*x+y*y+z*z;
		if(dist2<96*96) {
			playSfx(SFX_MENUSELECT);
			struct Place *p=map->place+map->activePlace;
			if(map->activePlace<6) {
				setTimeLeft(0);
				map->state=map->place[map->activePlace].flags&PF_TOURNAMENT?MS_DUEL:MS_TOURNAMENT;
				printf("map state now %d\n",map->state);
				//map->activePlace=0;
			} else if(p->type==PT_CHEST) {
				if(!(p->flags&PF_MASTERED)) {
					p->flags|=PF_MASTERED;
					p->avatar.itemId=chestId+2;	// mark it as opened.
					char buf[256];
					//printf("contains: %s\n",p->contains);
					sprintf(buf,"Found %s in chest!",p->contains);
					addMessage(buf,1000);
					addInventory(p->contains);
					playSfx(SFX_POWERUP);
				}
			} else if(p->type==PT_CRATE) {
				p->flags&=~PF_VISIBLE;
				char buf[256];
				//printf("contains: %s\n",p->contains);
				sprintf(buf,"Found %s in crate!",p->contains);
				addMessage(buf,1000);
				addInventory(p->contains);
				playSfx(SFX_POWERUP);
			}
		}
		break;
	case BT_UP:
		map->actor.direction&=~DIR_UP;
		break;
	case BT_DOWN:
		map->actor.direction&=~DIR_DOWN;
		break;
	case BT_LEFT:
		map->actor.direction&=~DIR_LEFT;
		break;
	case BT_RIGHT:
		map->actor.direction&=~DIR_RIGHT;
		break;
	default:
		break;
	}
	return 0;
}

int analogMap(struct Map *map,int lx,int ly)
{
	//printf("Analog %d,%d\n",lx,ly);
	static int oldx,oldy;
	// make analog stick less noisy.
	if(oldx>=80 && oldx<=175 && lx>=80 && lx<=175 &&
	oldy>=80 && oldy<=175 && ly>=80 && ly<=175) 
		return 0;
	oldx=lx;
	oldy=ly;
	// put in range from -100 to +100.
	map->actor.ax=lx-100;
	if(map->actor.ax>=0 && map->actor.ax<=56) map->actor.ax=0;
	else if(map->actor.ax>56) map->actor.ax-=56;
	map->actor.ay=ly-100;
	if(map->actor.ay>=0 && map->actor.ay<=56) map->actor.ay=0;
	else if(map->actor.ay>56) map->actor.ay-=56;
	// make the deadzone bigger
	if(map->actor.ax>-20 && map->actor.ax<20) map->actor.ax=0;
	if(map->actor.ay>-20 && map->actor.ay<20) map->actor.ay=0;
	return 0;
}
