// Dungeoneer! (Allegro Speedhack 2005 Entry)
// By Michael Jensen
// mtj419@yahoo.com


//
// Jesus Speaking to his disciples before his betrayal:
//
// John 13:34 “I give you a new commandment—to love one another. Just as I have
//    loved you, you also are to love one another.
// John 13:35 Everyone will know by this that you are my disciples—if you have
//    love for one another.”
//
// N.E.T. BIBLE: http://bible.org/netbible
//


////////////////////////////////////////////////////////////////////////////////

#define __MAIN_CPP
#include "includes.h"

////////////////////////////////////////////////////////////////////////////////

// TIMERS:

void tmrLogic()
{
	tmcLogic++;
}
END_OF_FUNCTION(tmrLogic);

void tmrMeasurer()
{
	LPS=logics;
	FPS=frames;
	
	logics=0;
	frames=0;
}
END_OF_FUNCTION(tmrMeasurer);

////////////////////////////////////////////////////////////////////////////////

// MAIN FUNCTIONS:
	
char ctrl_string[10];

int main(void)
{
	srand (time(NULL));
	touch();
	// STARTUP
	int r=startup();
	if (r){return(r);}


	get_saves();
	if (start_engine())
	{
		return(1);	
	}
	main_menu();
	

	int max_fskip=9;
	tmcLogic=0;
	
	while (!quitting)
	{	
		if (key[KEY_F12])
		{quitting=true;}
		
		int fskip=0;
		while ((tmcLogic>0) && (fskip<9))
		{
			logic();

			logics++;		
			tmcLogic--;
			fskip++;
		}
		
		draw();	

		page_flip();
		frames++;
		
	}
	
	put_saves();
	if (saving)
	{
		quitting=true;
		save_world();
	}
	
	// SHUTDOWN
	shutdown();
	return(0);
};
END_OF_MAIN();

////////////////////////////////////////////////////////////////////////////////

int startup()
{
    allegro_init();

	joystick_installed=(!install_joystick(JOY_TYPE_AUTODETECT));
    load_config();
    
	int r=install_gfx();
	if (r){return(r);}
	
	install_keyboard();
	install_mouse();
	setup_timers();

	install_pmask();
	
	return(0);
}

////////////////////////////////////////////////////////////////////////////////

void shutdown()
{
	for (int i=0; i<512; i++)
	{
		if (tiles[i])
		{
			destroy_bitmap(tiles[i]);	
			tiles[i]=NULL;
		}
	}
	
	
	if (tileset)
	{
		destroy_bitmap(tileset);
		tileset=NULL;
	}
	
	if (apage){destroy_bitmap(apage); apage=NULL;}
	if (page1){destroy_bitmap(page1); apage=NULL;}
	if (page2){destroy_bitmap(page2); apage=NULL;}
	
	remove_keyboard();
	remove_mouse();
	allegro_exit();
}

////////////////////////////////////////////////////////////////////////////////

void setup_timers()
{
	install_timer();
	
	LOCK_FUNCTION(tmrLogic);
	LOCK_FUNCTION(tmrMeasurer);

	LOCK_VARIABLE(tmcLogic);
	LOCK_VARIABLE(logics);
	LOCK_VARIABLE(frames);
	LOCK_VARIABLE(LPS);	
	LOCK_VARIABLE(FPS);
	
	tmcLogic=logics=frames=LPS=FPS=0;
 	
	install_int_ex(tmrMeasurer, BPS_TO_TIMER(1));
	install_int_ex(tmrLogic, BPS_TO_TIMER(60));
}

////////////////////////////////////////////////////////////////////////////////

void logic()
{
	/*if (control(BTN_UP)){ ctrl_string[0]='U'; } else { ctrl_string[0]=32; }
	if (control(BTN_DN)){ ctrl_string[1]='D'; } else { ctrl_string[1]=32; }
	if (control(BTN_LF)){ ctrl_string[2]='L'; } else { ctrl_string[2]=32; }
	if (control(BTN_RT)){ ctrl_string[3]='R'; } else { ctrl_string[3]=32; }

	if (control(BTN_A)){ ctrl_string[4]='A'; } else { ctrl_string[4]=32; }
	if (control(BTN_B)){ ctrl_string[5]='B'; } else { ctrl_string[5]=32; }
	if (control(BTN_START)){ ctrl_string[6]='O'; } else { ctrl_string[6]=32; }
	if (control(BTN_SELECT)){ ctrl_string[7]='C'; } else { ctrl_string[7]=32; }*/
	
	
	if (display_menu)
	{
		menu_logic();
		return;
	}
	
	move_player();
	if (control(BTN_A))	//RUN
	{
		move_player();
	}
	
	if (control(BTN_B)) //SHOOT
	{
		//give_exp(10);
		if (player->shoot_cntr<1)
		{
			shoot_sword(player->team, (player->x+(dir_x[player->dir]*8))+8, (player->y+(dir_y[player->dir]*8))+8, player->dir);
			player->shoot_cntr=player->shoot_max;
		}
	}
	
	if (player->shoot_cntr>0)
	{player->shoot_cntr--;}
	
	do_objects();
	npcs_think();
	particle_logic();
	
	if (plr_blinky>0)
	{plr_blinky--;}
	
	if (player->hp<1)
	{
		current_menu=create_menu(1, "GAME OVER", -1, -1, 100, 30);
		display_menu=true;
	}
}

////////////////////////////////////////////////////////////////////////////////

void move_player()
{
	int ox, oy, od, oa;
	int wox, woy;
	wox=player->x;	//way old x
	woy=player->y;	//way old y
	
	ox=player->x; oy=player->y; od=player->dir; oa=player->needs_animate;
	if (control(BTN_LF)){walk_actor(player, A_LF);}
	if (control(BTN_RT)){walk_actor(player, A_RT);}
	if (collision(player)){player->x=ox;player->y=oy; player->dir=od; player->needs_animate=oa;}

	ox=player->x; oy=player->y; od=player->dir; oa=player->needs_animate;
	if (control(BTN_UP)){walk_actor(player, A_UP);}
	if (control(BTN_DN)){walk_actor(player, A_DN);}
	if (collision(player)){player->x=ox;player->y=oy; player->dir=od; player->needs_animate=oa;}
	
	if (player->needs_animate){animate_actor(player);}
	
	if (player->x==wox && player->y==woy)
	{
		if (control(BTN_LF)){player->dir=A_LF;}
		if (control(BTN_RT)){player->dir=A_RT;}
		if (control(BTN_UP)){player->dir=A_UP;}
		if (control(BTN_DN)){player->dir=A_DN;}
	}
}

////////////////////////////////////////////////////////////////////////////////

void draw()
{
	clear_to_color(apage, BLACK);


	if (DRAW_ALL)
	{
		saving=true;
		camera_x=(player->x+8)-160;
		camera_y=(player->y+8)-100;
	
		if (camera_x<0){camera_x=0;}
		if (camera_y<0){camera_y=0;}
		if (camera_x>3775){camera_x=3775;}
		if (camera_y>3995){camera_y=3995;}

		draw_map(camera_x, camera_y);

		
		for (int i=0; i<100; i++)
		{
			if (obj[i])
			{
				if (obj[i]->alive)
				{
					draw_object(camera_x, camera_y, obj[i]);
				}
			}	
		}
	
		if (plr_blinky<1)
		{
			draw_actor(camera_x, camera_y, player);
		}
		else
		{
			if ((plr_blinky/10) %2)	
			{
				draw_actor(camera_x, camera_y, player);
			}
		}
	
	
		for (int i=0; i<100; i++)
		{
			if (npc[i])
			{
				if (npc[i]->alive)
				{
					draw_actor(camera_x, camera_y, npc[i]);
				}
			}
		}
	
		draw_particles(camera_x, camera_y);

		draw_hud();
	}
	

	
	if (display_menu)
	{ draw_menu(current_menu); }
	
	if (control(BTN_SELECT))
	{
		textprintf_right_ex(apage, font, apage->w-10, 10, WHITE, -1, "FPS: %d", FPS);
		textprintf_right_ex(apage, font, apage->w-10, 20, WHITE, -1, "LPS: %d", LPS);
		textprintf_right_ex(apage, font, apage->w-10, 30, WHITE, -1, "DEBUG: %d", DEBUG);
	}
	
	//xtprintf_ex(apage, font, 10, 10, WHITE, -1, "%s", ctrl_string);
}

////////////////////////////////////////////////////////////////////////////////

void touch()
{
	//Dunno why but mingw complained about "undefined references" when I added
	//these lines it, it worked...
	
	//seems the vars just have to touched by main in someway...

	generate_332_palette(MASTER_PAL);
	tileset=NULL;
	for (int i=0; i<512; i++)
	{tiles[i]=NULL;}
		
	player=NULL;
	for (int i=0; i<100; i++)
	{npc[i]=NULL;}
	
	for (int i=0; i<100; i++)
	{obj[i]=NULL;}
	
	
	display_menu=false;
	current_menu=NULL;
	for (int i=0; i<50; i++)
	{menu_stack[i]=NULL;}
	menu_stack_depth=0;
	
	for (int i=0; i<20; i++)
	{
		map_archive[i][0][0]=0;
		map_visited[i]=false;
		for (int j=0; j<100; j++)
		{
			map_obj_arc[i][j]=NULL;
			map_npc_arc[i][j]=NULL;
		}
	}
	
	for (int i=0; i<10; i++)
	{
		ctrl_string[i]=0;
	}
	
	
	dir_x[A_UP]=0;	dir_y[A_UP]=-1;
	dir_x[A_DN]=0;	dir_y[A_DN]=1;
	dir_x[A_LF]=-1;	dir_y[A_LF]=0;
	dir_x[A_RT]=1;	dir_y[A_RT]=0;
	
	memset(parts, 0, sizeof(PARTICLE) *500);
	
	float pi128 = 3.14159265;
	pi128 /=128;
	
	for (int i=0; i<256; i++)
	{
		X_LOOK[i] = cos(((float)i) * pi128);
		Y_LOOK[i] = sin(((float)i) * pi128);
	}
	
}

////////////////////////////////////////////////////////////////////////////////

void load_config()
{
	set_config_file("config.ini");
	SCR_W = get_config_int("MAIN", "GFX_WIDTH", SCR_W);
	SCR_H = get_config_int("MAIN", "GFX_HEIGHT", SCR_H);
	DIS_W = get_config_int("MAIN", "DISPLAY_WIDTH", SCR_W);	//SCR_W/H here is not a mistake!
	DIS_H = get_config_int("MAIN", "DISPLAY_HEIGHT", SCR_H);
	VSYNC = get_config_int("MAIN", "VSYNC", true);

	sfx_volume=get_config_int("MAIN", "SFX_VOLUME", sfx_volume);
	midi_volume=get_config_int("MAIN", "MIDI_VOLUME", sfx_volume);
	
	key_map[0]=find_key((char*)get_config_string("MAIN", "KEY_A", "Z"));
	key_map[1]=find_key((char*)get_config_string("MAIN", "KEY_B", "X"));
	key_map[2]=find_key((char*)get_config_string("MAIN", "KEY_SELECT", "ESCAPE"));
	key_map[3]=find_key((char*)get_config_string("MAIN", "KEY_START", "ENTER"));
	
	if (joystick_installed)
	{
		joy_num=get_config_int("MAIN", "USE_JOYSTICK", 0) -1;
		
		joy_map[0]=get_config_int("MAIN", "JOY_A", 1) -1;
		joy_map[1]=get_config_int("MAIN", "JOY_B", 2) -1;
		joy_map[2]=get_config_int("MAIN", "JOY_SELECT", 3) -1;
		joy_map[3]=get_config_int("MAIN", "JOY_START", 4) -1;
		
	}

}

////////////////////////////////////////////////////////////////////////////////

int start_engine()
{
	for (int i=0; i<100; i++)
	{obj[i]=NULL; npc[i]=NULL;}
		
	
	build_player();
	if (!player)
	{
		char buffer[1000];
		sprintf(buffer, "NOT ENOUGH MEMORY TO BUILD PLAYER OBJECT!");
		allegro_message(buffer);
		allegro_exit();
		return(1);
	}
	
	load_map(0);
	return(0);
}

////////////////////////////////////////////////////////////////////////////////

void build_player()
{
	player=create_actor(0, 0, 30, 30, 37, 38, 39, 47, 48, 49, 57, 58, 59);
	player->x=128*16;
	player->y=25*16;

	plr_LEVEL=0;	//level
	plr_EXP=0;		//EXP
	plr_NEXT=300;	//EXP Needed to Level up

	player->state[10]=20;	//mp
	player->state[11]=20;	//max mp
}

////////////////////////////////////////////////////////////////////////////////

void build_town()
{	
	for (int i=0; i<256; i++)
	{
		for (int j=0; j<256; j++)
		{
			map[i][j]=2;
		}
	}
	
	for (int i=118; i<138; i++)
	{
		for (int j=10; j<30; j++)
		{
			map[i][j]=1;
		}
		
		for (int j=70; j<90; j++)
		{
			map[i][j]=1;
		}
	}
	
	create_house(1, 128*16, 17*16, "THE INN");
	
	//per graveyard -- find a randomized 5x5 area that's blank to put the tombstone
	//when player dies, if one can't be found, remove one at random and try again...
	//do this only once per player death -- but save positions to the graveyard file
	// max = 100 per file...
	
	for (int i=127; i<131; i++)
	{
		for (int j=30; j<70; j++)
		{
			map[i][j]=1;
		}	
	}
		
}

////////////////////////////////////////////////////////////////////////////////

void populate_dungeon_entrances()
{
	for (int i=118; i<127; i++)
	{
		for (int j=49; j<52; j++)
		{
			map[i][j]=1;
		}
	}
	
	for (int i=108; i<118; i++)
	{
		for (int j=45; j<56; j++)
		{
			map[i][j]=1;
		}
	}
	
	for (int i=0; i<100; i++)
	{
		if (obj[i])
		{
			if (obj[i]->alive)
			{
				if (obj[i]->type==2 || obj[i]->type==3)
				{
					if (obj[i]->id>0 && obj[i]->id<11)
					{
						obj[i]->alive=false;	
					}	
				}
			}
		}	
	}

	player->walk_speed=1;
	//deepest_dungeon_level=20;

	create_staircase(1, 111*16, 46*16, 1, A_DN);
	if (deepest_dungeon_level>=4){create_staircase(2, 111*16, 50*16, 4, A_DN);}
	if (deepest_dungeon_level>=7){create_staircase(3, 111*16, 54*16, 7, A_DN);}
	if (deepest_dungeon_level>=10){create_staircase(4, 115*16, 46*16, 10, A_DN);}
	if (deepest_dungeon_level>=13){create_staircase(5, 115*16, 50*16, 13, A_DN);}
	if (deepest_dungeon_level>=16){create_staircase(6, 115*16, 54*16, 16, A_DN);}
	if (deepest_dungeon_level>=19){create_staircase(7, 128*16, 80*16, 19, A_DN);}
	
}

////////////////////////////////////////////////////////////////////////////////

void draw_map(int cx, int cy)
{
	int tx=cx/16;
	int ty=cy/16;
	int ox=cx%16;
	int oy=cy%16;
	
	int px, py, c;
	py=ty;
	for (int j=-oy; j<apage->h; j+=16)
	{
		px=tx;
		for (int i=-ox; i<apage->w; i+=16)
		{	
			c=get_map(px, py);
			
			if (tiles[c])
			{
				masked_blit(tiles[c], apage, 0, 0, i, j, 16, 16);
			}

			px++;
		}
			
		py++;
	}	
}

////////////////////////////////////////////////////////////////////////////////

void set_map(int x, int y, int c)
{
	if ((x>=0 && x<256) && (y>=0 && y<256))
	{
		map[x][y]=c;
	}
}

////////////////////////////////////////////////////////////////////////////////

int get_map(int x, int y)
{
	if ((x>=0 && x<256) && (y>=0 && y<256))
	{
		return map[x][y];
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////

int object_collision_with_wall(OBJECT * o)
{
	if (!o){return(false);}

	int x1=o->x/16;
	int y1=o->y/16;
	int x2=(o->x+15)/16;
	int y2=(o->y+15)/16;
	

	int t=get_map(x1, y1);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x2, y1);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x1, y2);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x2, y2);
	if ((t>1) && (t != 30)){return true;}
	
	return(false);
}

////////////////////////////////////////////////////////////////////////////////

int collision(ACTOR * a)
{
	if (!a){return(false);}

	int x1=a->x/16;
	int y1=a->y/16;
	int x2=(a->x+15)/16;
	int y2=(a->y+15)/16;
	

	int t=get_map(x1, y1);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x2, y1);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x1, y2);
	if ((t>1) && (t != 30)){return true;}
	
	t=get_map(x2, y2);
	if ((t>1) && (t != 30)){return true;}
	
	for (int i=0; i<100; i++)
	{
		if (obj[i])
		{
			if ((obj[i]->alive) && (!obj[i]->passable))
			{
				if (actor_object_collision(a, obj[i]))
				{
					return(true);
				}
			}
		}
	}
	
	return (false);
}

////////////////////////////////////////////////////////////////////////////////

int actor_object_collision(ACTOR * a, OBJECT * o)
{
	if (!a){return(false);}
	if (!o){return(false);}
	
	if ((a->x+15)<(o->x)){return(false);}
	if ((a->x)>(o->x + o->w -1)){return(false);}
	if ((a->y+15)<(o->y)){return(false);}
	if ((a->y)>(o->y + o->h -1)){return(false);}
	
	if (o->mask)
	{
		if (a->mask)
		{
			return (check_pmask_collision(o->mask, a->mask, o->x, o->y, a->x, a->y));
		}
	}
	
	return(true);	
}

////////////////////////////////////////////////////////////////////////////////

void save_map(int lvl)
{
	if (lvl<0){lvl=0;}
	if (lvl>=20){lvl=19;}
	
	for (int i=0; i<256; i++)
	{
		for (int j=0; j<256; j++)
		{
			map_archive[lvl][i][j]=map[i][j];
		}
	}
	
		
	for (int j=0; j<100; j++)
	{
		map_obj_arc[lvl][j]=copy_obj(obj[j]);
		map_npc_arc[lvl][j]=copy_actor(npc[j]);
	}

	
	return;
}

////////////////////////////////////////////////////////////////////////////////

void load_map(int lvl)
{
	show_loading(0, 100);
	
	if (lvl<0){lvl=0;}
	if (lvl>=20){lvl=19;}
	
	if (dungeon_level!=lvl)
	{
		save_map(dungeon_level);
	
		for (int i=0; i<100; i++)
		{
			if (obj[i])
			{
				destroy_object(obj[i]);
				obj[i]=NULL;
			}
			if (npc[i])
			{
				destroy_actor(npc[i]);
				npc[i]=NULL;
			}
		}
	}
	
	
	if (!map_visited[lvl])
	{
		if (lvl==0)
		{
			build_town();
		}
		else
		{		
			//generate random dungeon level
			for (int i=0; i<256; i++)
			{
				for (int j=0; j<256; j++)
				{
					map[i][j]=0;		
				}
			}
			
			//debugging only!
			//create_staircase(1, 160, 160, 0, A_UP);
			generate_dungeon_level(lvl);		
		}
		
		map_visited[lvl]=true;
		save_map(lvl);	
	}
	else
	{
		for (int i=0; i<256; i++)
		{
			for (int j=0; j<256; j++)
			{
				map[i][j]=map_archive[lvl][i][j];
			}
		}		
	}
	
	if (dungeon_level != lvl)
	{
		for (int i=0; i<100; i++)
		{
			obj[i]=map_obj_arc[lvl][i];
			npc[i]=map_npc_arc[lvl][i];
		}
	}
	
	if (lvl==0)
	{
		populate_dungeon_entrances();
	}
		
	dungeon_level=lvl;
	
	if (deepest_dungeon_level<dungeon_level)
	{
		deepest_dungeon_level=dungeon_level;
	}
	
	spawn();
	return;
}

////////////////////////////////////////////////////////////////////////////////

void generate_dungeon_level(int lvl)
{
	show_loading(0, 5);
	
	int mw=30+(5*lvl);
	int mh=20+(5*lvl);
	if (mw<30){mw=30;}
	if (mh<20){mh=20;}
	if (mw>255){mw=255;}
	if (mh>255){mh=255;}
	
	int mx=128-(mw/2);
	int my=128-(mh/2);
	
	int map_ok=false;
	
	while (!map_ok)
	{
	show_loading(1, 5);
	
	for (int i=0; i<256; i++)
	{
		for (int j=0; j<256; j++)
		{
			set_map(i, j, 31);
		}
	}
	
	int box_num=5+(lvl*2);
	for (int i=0; i<box_num; i++)
	{	
		int btry=0;
		int ok=false;
		int x, y, w, h;
		
		while ((!ok) && (btry<20))
		{
			x=(rand()%mw);
			y=(rand()%mh);
			w=(rand()%15)+4;
			h=(rand()%15)+4;
			ok=true;
		
			for (int j=(x+mx)-1; j<=(x+mx+w); j++)
			{
				for (int k=(y+my)-1; k<=(y+my+h); k++)
				{
					if (get_map(j,k)==30){ok=false;}
				}
			}
			btry++;
		}
		
		if (ok)
		{
			for (int j=(x+mx); j<(x+mx+w); j++)
			{
				for (int k=(y+my); k<(y+my+h); k++)
				{
					set_map(j,k, 30);
				}
			}
		}
		
	}
	show_loading(2, 5);
	int n=lvl-(4+rand()%5);
	if (n<2){n=2;}
	int path_num=(10+(lvl*n));
	for (int i=0; i<path_num; i++)
	{
		int path_ok=false;
		int ok=false;
		int x, y, ox, oy, dir;
		while (!path_ok)
		{
			ok=false;
			while (!ok)
			{
				x=(rand()%mw); y=(rand()%mh);
				if (get_map(x+mx, y+my)==30)
				{
					ok=true;
				}	
			}

			dir=rand()%4;	
			while (get_map(x+mx, y+my)==30)
			{
				x+=dir_x[dir];
				y+=dir_y[dir];	
			}
			ox=x;
			oy=y;
		
			while (get_map(x+mx, y+my)==31)
			{
				x+=dir_x[dir];
				y+=dir_y[dir];	
			}
		
			if (get_map(x+mx, y+my)==30)
			{
				path_ok=true;		
			}
		}
		
		x=ox;
		y=oy;
		
		while (get_map(x+mx, y+my)==31)
		{
			set_map(x+mx, y+my, 30);
			set_map(x+mx+dir_y[dir], y+my+dir_x[dir], 30);
			x+=dir_x[dir];
			y+=dir_y[dir];
		}
	}
	
	show_loading(3, 5);
	
		BITMAP * bmap=create_bitmap(256, 256);
		int paint_x=-1;
		int paint_y=-1;
		if (map)
		{	
			for (int i=0; i<256; i++)
			{
				for (int j=0; j<256; j++)
				{
					putpixel(bmap, i,j, map[i][j]);
					if ((paint_x==-1) && (map[i][j]==30))
					{
						paint_x=i;
						paint_y=j;
					}
				}
			}
			
			if (paint_x !=-1)
			{
				floodfill(bmap, paint_x, paint_y, 0);
			}
			
			
			map_ok=true;
			for (int i=0; i<256; i++)
			{
				for (int j=0; j<256; j++)
				{
					if (getpixel(bmap, i, j) == 30)
					{
						map_ok=false;
						i=256;
						j=256;
					}
				}
			}
			
			destroy_bitmap(bmap);
		}
		else
		{
			map_ok=rand()%2;
		}
	
	} //good map
	
	show_loading(4, 5);

	//create staircases:
	int ok=false;
	int x,y;
	while (!ok)
	{
		x=(rand()%mw);
		y=(rand()%mh);
		
		if (get_map(x+mx,y+my)==30)
		{ok=true;}
	}
	
	create_staircase(1, (mx+x)*16, (my+y)*16, lvl-1, A_UP); //up
	
	if (lvl<19)
	{
		ok=false;
		x,y;
		while (!ok)
		{
			x=(rand()%mw);
			y=(rand()%mh);
		
			if (get_map(x+mx,y+my)==30)
			{ok=true;}
		}
	
		create_staircase(2, (mx+x)*16, (my+y)*16, lvl+1, A_DN); //down
	}
	
	//4, 7, 10, 13, 16, 19
	
	if ((lvl==4) || (lvl==7) || (lvl==10) || (lvl==13) || (lvl==16) || (lvl==19))
	{
		int ok=false;
		int x,y;
		while (!ok)
		{
			x=(rand()%mw);
			y=(rand()%mh);
		
			if (get_map(x+mx,y+my)==30)
			{ok=true;}
		}
	
		create_staircase(3, (mx+x)*16, (my+y)*16, 0, A_UP); //to town
	}
	
	int baddies=5+(rand()%10)+(lvl*3);
	for (int i=0; i<baddies; i++)
	{
		int s=-1;
		if (i>=(baddies*3/4))
		{
			s=create_goblin(0, 0, lvl*10, lvl/5);
		}
		else
		{
			s=create_skeleton(0, 0, lvl*10, lvl/5);
		}
		
		if (s>=0)
		{
			int ok=false;
			int x,y;
			while (!ok)
			{
				x=(rand()%mw);
				y=(rand()%mh);
		
				ok=true;
				if (get_map(x+mx,y+my)!=30)
				{ok=false;}
				if (get_map(x+mx+1,y+my)!=30)
				{ok=false;}
				if (get_map(x+mx,y+my+1)!=30)
				{ok=false;}
				if (get_map(x+mx+1,y+my+1)!=30)
				{ok=false;}
				
				npc[s]->x=((x+mx)*16)+8;
				npc[s]->y=((y+my)*16)+8;
				
				for (int j=0; j<100; j++)
				{
					if (obj[j])
					{
						if (obj[j]->alive)
						{
							if (actor_object_collision(npc[s], obj[j]))
							{
								ok=false;
							}
						}
					}
				}
			}
		}
	}
	
	int chains=(lvl+2)/5;
	if (chains>3){chains=3;}
	for (int i=0; i<chains; i++)
	{
		int ok=false;
		int x,y;
		while (!ok)
		{
			x=(rand()%mw);
			y=(rand()%mh);
			ok=true;
			for (int ox=-1; ox<2; ox++)
			{
				for (int oy=-1; oy<2; oy++)
				{
					if (get_map(x+mx,y+my)!=30)
					{ok=false;}
				}
			}
		}
		
		set_map(x+mx, y+my, 31);
		create_chain((mx+x)*16, (my+y)*16, 5+(rand()%5), 1, 2+(rand()%3));
	}
	
	show_loading(5, 5);
}

////////////////////////////////////////////////////////////////////////////////

void particle_logic()
{	
	for (int i=0; i<500; i++)
	{
		if (parts[i].life>0)
		{
			parts[i].life--;
			parts[i].x+=parts[i].vx;
			parts[i].y+=parts[i].vy;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////

void draw_particles(int cx, int cy)
{
	for (int i=0; i<500; i++)
	{
		if (parts[i].life>0)
		{
			int x=((int)parts[i].x)-cx;
			int y=((int)parts[i].y)-cy;
			
			putpixel(apage, x, y, parts[i].clr);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////

int find_particle()
{
	for (int i=0; i<500; i++)
	{
		if (parts[i].life<1)
		{
			return i;
		}
	}
	
	return -1;
}

////////////////////////////////////////////////////////////////////////////////

void explode(int x, int y, int size, int length, int c1, int c2, int c3, int c4)
{
	int c[4];
	c[0]=c1; c[1]=c2; c[2]=c3; c[3]=c4;	
	
	for (int i=0; i<40; i++)
	{
		int p=find_particle();
		if (p>=0)
		{
			parts[p].x=(x-4)+(rand()%8);
			parts[p].y=(y-4)+(rand()%8);
			parts[p].life=length;
			int dir=rand()%256;
			parts[p].vx=X_LOOK[dir]*size;
			parts[p].vy=Y_LOOK[dir]*size;
			parts[p].clr=c[rand()%4];
		}
	}
	
}

////////////////////////////////////////////////////////////////////////////////

void draw_hud(void)
{
	if (!player){return;}
	
	double life=((double)player->hp)/((double)player->max_hp);
	if (life<0){life=0;}
	int draw_life = (int)(life * 50);
	if (draw_life==50){draw_life=49;}
	
	rectfill(apage, 10, 10, 59, 19, DK_RED);
	rectfill(apage, 10, 10, 10+draw_life, 19, RED);
	rect(apage, 10, 10, 59, 19, WHITE);
	
	//no time to impliment anything special :-(
	//life=((double)player->state[10])/((double)player->state[11]);
	//if (life<0){life=0;}
	//draw_life = (int)(life * 50);
	//if (draw_life==50){draw_life=49;}

	//rectfill(apage, 10, 25, 59, 34, DK_BLUE);
	//rectfill(apage, 10, 25, (10+draw_life), 34, BLUE);
	//rect(apage, 10, 25, 59, 34, WHITE);
	
}

////////////////////////////////////////////////////////////////////////////////

void give_exp(int exp)
{
	plr_EXP += exp;

	if (plr_EXP >= plr_NEXT)	//LEVEL UP!
	{
		int old_level=plr_LEVEL;
		int old_next=plr_NEXT;
		int old_hp=player->max_hp;
		int old_mp=plr_max_mp;
		
		plr_LEVEL++;	//INC LVL
		plr_NEXT += (plr_NEXT*3/2);	//NEXT
		
		player->max_hp += (player->max_hp/5)+(rand()%(player->max_hp*3/10));
		plr_max_mp += (plr_max_mp/5)+(rand()%(plr_max_mp*3/10));
		
		player->hp = player->max_hp;
		plr_mp = plr_max_mp;
		
		current_menu=create_menu(3, "LEVEL UP!" , -1, -1, 200, 100);
		sprintf(current_menu->text[0], "LEVEL: %d -> %d", old_level, plr_LEVEL);
		sprintf(current_menu->text[1], "LIFE : %d -> %d", old_hp, player->max_hp);
		//sprintf(current_menu->text[2], "MPWR : %d -> %d", old_mp, plr_max_mp);
		sprintf(current_menu->text[2], "NEXT : %d -> %d", old_next, plr_NEXT);
		sprintf(current_menu->text[3], "EXP  : %d", plr_EXP);
		current_menu->text[4][0]=0;
		display_menu=true;
	}
}

////////////////////////////////////////////////////////////////////////////////

void main_menu()
{
	current_menu=create_menu(10, "DUNGEONEER", -1, -1, 220, 100);
	
	sprintf(current_menu->text[0], " NEW ");
	sprintf(current_menu->text[1], " LOAD ");
	sprintf(current_menu->text[2], " EXIT ");
	
	display_menu=true;
	
}

////////////////////////////////////////////////////////////////////////////////

void save_world()
{
	save_map(dungeon_level);
	
	show_loading(50,100);
	char filename[100];
	sprintf(filename, "%s.sav", player_name);
	
	PACKFILE * fp = pack_fopen(filename, F_WRITE_PACKED);
	if (fp)
	{
		pack_fwrite(player, sizeof(player), fp);
		pack_fwrite(&dungeon_level, 4, fp);
		pack_fwrite(&deepest_dungeon_level, 4, fp);
		
		pack_fwrite(map, 256*256*4, fp);
		pack_fwrite(map_archive, 256*256*4*20, fp);
		pack_fwrite(map_visited, 20*4, fp);
		
		pack_fclose(fp);	
	}
}

////////////////////////////////////////////////////////////////////////////////

void load_world()
{
	char filename[100];
	sprintf(filename, "%s.sav", player_name);
	
	PACKFILE * fp = pack_fopen(filename, F_READ_PACKED);
	if (fp)
	{
		pack_fread(player, sizeof(player), fp);
		pack_fread(&dungeon_level, 4, fp);
		pack_fread(&deepest_dungeon_level, 4, fp);
		
		pack_fread(map, 256*256*4, fp);
		pack_fread(map_archive, 256*256*4*20, fp);
		pack_fread(map_visited, 20*4, fp);
		
		pack_fclose(fp);
		
		for (int i=0; i<100; i++)
		{
			npc[i]=NULL;
			obj[i]=NULL;
			for (int l=0; l<20; l++)
			{map_obj_arc[l][i]=NULL; map_npc_arc[l][i]=NULL;}
		}
		
		
		ACTOR * a=player;
		a->bmp[A_DN][0]=tiles[57];
		a->bmp[A_DN][1]=tiles[58];
		a->bmp[A_DN][2]=tiles[59];
		a->bmp[A_UP][0]=tiles[47];
		a->bmp[A_UP][1]=tiles[48];
		a->bmp[A_UP][2]=tiles[49];
		a->bmp[A_RT][0]=tiles[37];
		a->bmp[A_RT][1]=tiles[38];
		a->bmp[A_RT][2]=tiles[39];
			
		a->bmp[A_LF][0]=create_bitmap(16,16);
		a->bmp[A_LF][1]=create_bitmap(16,16);
		a->bmp[A_LF][2]=create_bitmap(16,16);
			
		for (int k=0; k<3; k++)
		{
			if (a->bmp[A_LF][k])
			{
				clear_to_color(a->bmp[A_LF][k], makecol(255,0,255));
				draw_sprite_h_flip(a->bmp[A_LF][k], a->bmp[A_RT][k], 0, 0);
			}
		}
			
		if (a->bmp[A_DN][1])
		{a->mask = create_box_pmask(a->bmp[A_DN][1]);}
		else if (a->bmp[A_DN][0])
		{a->mask = create_box_pmask(a->bmp[A_DN][0]);}
		
		 spawn();
		 
			if (dungeon_level>0)
			{
				for (int i=0; i<100; i++)
				{
					if (obj[i])
					{
						if (obj[i]->type==3)
						{player->x=obj[i]->x*16; player->y=obj[i]->y*16;}
					}
				}
			}
	}
}

////////////////////////////////////////////////////////////////////////////////

void get_saves()
{
	save_num=0;	//loading doesn't work so why bother?
	return;
	
	PACKFILE * fp=pack_fopen("saves.all", F_READ_PACKED);
	if (fp)
	{
		pack_fread(&save_num, 4, fp);
		
		for (int i=0; i<save_num; i++)
		{
			pack_fread(saves[i], 50, fp);
		}
		pack_fclose(fp);	
	}
}

////////////////////////////////////////////////////////////////////////////////

void put_saves()
{
	PACKFILE * fp=pack_fopen("saves.all", F_WRITE_PACKED);
	if (fp)
	{
		pack_fwrite(&save_num, 4, fp);
		
		for (int i=0; i<save_num; i++)
		{
			pack_fwrite(saves[i], 50, fp);
		}
		pack_fclose(fp);	
	}	
}

////////////////////////////////////////////////////////////////////////////////

void spawn()
{
	for (int i=0; i<100; i++)
	{
		if (obj[i])
		{
			destroy_object(obj[i]);
			obj[i]=NULL;
		}
		
		if (npc[i])
		{
			destroy_actor(npc[i]);
			npc[i]=NULL;
		}
	}
	
	
	if (dungeon_level==0)
	{
		build_town();
		populate_dungeon_entrances();
	}
	else
	{
		int lvl=dungeon_level;
		int mw=30+(5*lvl);
		int mh=20+(5*lvl);
		if (mw<30){mw=30;}
		if (mh<20){mh=20;}
		if (mw>255){mw=255;}
		if (mh>255){mh=255;}
	
		int mx=128-(mw/2);
		int my=128-(mh/2);
		
		//create staircases:
		int ok=false;
		int x,y;
		while (!ok)
		{
			x=(rand()%mw);
			y=(rand()%mh);
		
			if (get_map(x+mx,y+my)==30)
			{ok=true;}
		}
		create_staircase(1, (mx+x)*16, (my+y)*16, lvl-1, A_UP); //up
		
	
		if (lvl<19)
		{
			ok=false;
			x,y;
			while (!ok)
			{
				x=(rand()%mw);
				y=(rand()%mh);
		
				if (get_map(x+mx,y+my)==30)
				{ok=true;}
			}
	
			create_staircase(2, (mx+x)*16, (my+y)*16, lvl+1, A_DN); //down
		}
		
		//4, 7, 10, 13, 16, 19
	
		if ((lvl==4) || (lvl==7) || (lvl==10) || (lvl==13) || (lvl==16) || (lvl==19))
		{
			int ok=false;
			int x,y;
			while (!ok)
			{
				x=(rand()%mw);
				y=(rand()%mh);
		
				if (get_map(x+mx,y+my)==30)
				{ok=true;}
			}
	
			create_staircase(3, (mx+x)*16, (my+y)*16, 0, A_UP); //to town
		}
		
		int baddies=5+(rand()%10)+(lvl*3);
		for (int i=0; i<baddies; i++)
		{
			int s=-1;
			if (i>=(baddies*3/4))
			{
				s=create_goblin(0, 0, lvl*10, lvl/5);
			}
			else
			{
				s=create_skeleton(0, 0, lvl*10, lvl/5);
			}
			
			if (s>=0)
			{
				int ok=false;
				int x,y;
				while (!ok)
				{
					x=(rand()%mw);
					y=(rand()%mh);
		
					ok=true;
					if (get_map(x+mx,y+my)!=30)
					{ok=false;}
					if (get_map(x+mx+1,y+my)!=30)
					{ok=false;}
					if (get_map(x+mx,y+my+1)!=30)
					{ok=false;}
					if (get_map(x+mx+1,y+my+1)!=30)
					{ok=false;}
				
					npc[s]->x=((x+mx)*16)+8;
					npc[s]->y=((y+my)*16)+8;
				
					for (int j=0; j<100; j++)
					{
						if (obj[j])
						{
							if (obj[j]->alive)
							{
								if (actor_object_collision(npc[s], obj[j]))
								{
									ok=false;
								}
							}
						}
					}
				}
			}
		}
	
		int chains=(lvl+2)/5;
		if (chains>3){chains=3;}
		for (int i=0; i<chains; i++)
		{
			int ok=false;
			int x,y;
			while (!ok)
			{
				x=(rand()%mw);
				y=(rand()%mh);
				ok=true;
				for (int ox=-1; ox<2; ox++)
				{
					for (int oy=-1; oy<2; oy++)
					{
						if (get_map(x+mx,y+my)!=30)
						{ok=false;}
					}
				}
			}
		
			set_map(x+mx, y+my, 31);
			create_chain((mx+x)*16, (my+y)*16, 5+(rand()%5), 1, 2+(rand()%3));
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
	
