/*
	The Dungeons of Craci
	
	Copyright (c) 2010 Vlad Dumitru.

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
	
*/


/*INCLUDES*********************************************************************/

#include <curses.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>

/*DEFINES**********************************************************************/

#define max_x				100
#define max_y				100

#define scrn_max_x		40
#define scrn_max_y		20

#define cam_min_x			5
#define cam_max_x			35
#define cam_min_y			5
#define cam_max_y			15

#define magic				300

#define fov_rad			10

#define color_light		4
#define color_dark		8

#define inv_capacity		10

#define max_objects		100
#define max_entities		100
#define max_stairs		3

/*TYPEDEFS*********************************************************************/

typedef struct{
	int x, y;
	int hp;
	int atk, def;
	int type;
	char face, fgc;
	char name[32];
	int seen;
	}ent_;
	
typedef struct{
	int x, y;
	int type, taken;
	char face, fgc;
	char name[32];
	int id;
	int takeable, useable;
	}obj_;
	
typedef struct{
	char str[256];
	int time;
	}message_;

/*ENUMS************************************************************************/

enum direction {
	up,
	down,
	left,
	right,
	up_left,
	up_right,
	down_left,
	down_right,
	};

enum tile_types {
	t_nothing = 0,
	t_floor,
	t_wall,
	t_dopen,
	t_dclosed,
	t_dhidden,
	t_fountain,
	t_statue,
	t_rock,
	t_stairsdown,
	};
	
enum entity_types {
	e_player,
	e_dog_head,
	e_dog_body,
	e_dog_tail,
	e_zombie_head,
	e_headless_zombie,
	e_zombie_feet,
	e_bear_paws,
	e_bear_head,
	e_craci_boss,
	e_max
	};

enum object_types {
	o_medkit,
	o_gold,
	o_max
	};

/*MISC DECLARATIONS************************************************************/

int map[max_x+1][max_y+1];
int losmap[max_x+1][max_y+1];

ent_ ent[max_entities+1];
int entn=0;

obj_ obj[max_objects+1];
int objn=-1;
obj_ inv[max_objects+1];
int invn=-1;

int cam_x=0, cam_y=0;

message_ message [11];

int turns=0, msgt=0;

FILE *message_archive;

int depth=0;

/*PROTOTYPES*******************************************************************/

/* math stuff */
int sgn(int n);
int inrange(int x,int y);
int dist(int x1,int y1,int x2,int y2);
int inrange(int x,int y);
int is_near ( int x1, int y1, int x2, int y2 );

/* entity functions */
int ent_ex(int x, int y);
void melee_fight( int atk, int def );
void move_ent(int i, int d);
void ent_zombie_ai( int z );
void go_in_direction ( int e, int d );
void place_pc();
void place_ents ( int n );

/* object functions */
int obj_ex(int x, int y);
void place_objs ( int n );
void destroy_obj ( int n );
void take_obj ( int n );
void drop_obj ( int n );
void use_obj ( int n );

/* graphical stuff */
void setfgcol(int f);
void die();
void showmap();
void showinventory( int wait );
void drawbox ( int x1, int y1, int x2, int y2 );
void clear_area ( int x1, int y1, int x2, int y2 );

/* map stuff */
int gn(int x,int y,int r,int what);
void mkcoor(int x,int y,int xo,int yo,int len);
void mkmap();

/* misc */
void init_curses();
int los(int x1,int y1,int x2,int y2);
void wait_for_enter();

void push_message ( char * msg );
void clear_messages ();
void alert ( char * msg );

/*ACTUAL CODE******************************************************************/

void clear_area ( int x1, int y1, int x2, int y2 )
{
	int ix, iy;
	for ( ix = x1; ix <= x2; ix++ )
	{
		for ( iy = y1; iy <= y2; iy++ )
		{
			mvaddch ( iy, ix, ' ' );
		}
	}
}

void drawbox ( int x1, int y1, int x2, int y2 )
{
	int ix, iy;
	for ( ix = x1; ix <= x2; ix++ )
	{
		for ( iy = y1; iy <= y2; iy++ )
		{
			if ( ( ix == x1 ) || ( ix == x2 ) )
				mvaddch ( iy, ix, '|' );
			if ( ( iy == y1 ) || ( iy == y2 ) )
				mvaddch ( iy, ix, '-' );
		}
	}
	mvaddch ( y1, x1, '+' );
	mvaddch ( y1, x2, '+' );
	mvaddch ( y2, x1, '+' );
	mvaddch ( y2, x2, '+' );
}

void alert ( char * msg )
{
	int l = strlen ( msg );
	int p = (int) ((80-l)/2);
	
	move ( 12, p );
	setfgcol ( 1 );

	if ( l % 2 == 1 )
		strcat ( msg, " " );
	printw ( "%s", msg );

	drawbox ( p-1, 11, p+l, 13 );
	
	wait_for_enter();
}

void clear_messages ()
{
	int i;
	for ( i = 0; i <= 10; i++ )
	{
		strcpy ( message[i].str, "" );
		message[i].time = 0;
	}
}

void push_message ( char * msg )
{
	int i;
	for ( i = 1; i <= 9; i++ )
	{
		strcpy ( message[i].str, message[i+1].str );
		message[i].time = message[i+1].time;
	}
	strncpy ( message[10].str, msg, 256 );
	message[10].time = turns;
	
	fprintf ( message_archive, "[%i] %s\n", turns, message[10].str );
}

void wait_for_enter ()
{
	int c=0;
	c = getch();
	while ( c != '\n' ) { c = getch(); }
}

void die ()
{
	clear();
	
	mvprintw (	12, 11,
		"And so, another soul perishes in the Dungeons of Craci..." );
	mvprintw (	13, 11, "Press ENTER." );
	
	wait_for_enter();
	clear();
	
	endwin();
	exit(0);
}

void epic_win ()
{
	clear ();
	mvprintw ( 11, 1,
		"You killed Craci. It was really a pain in the village's arse." );
	mvprintw ( 12, 1,
		"Now the whole peasanthood awaits your victorious arrival.");
	mvprintw ( 14, 1, "Now go." );
	
	wait_for_enter ();
	clear ();
	endwin ();
	exit ( 0 );
}

void setfgcol ( int f )
{
	if( f == 1 )
		attrset ( A_STANDOUT );
	else
		attrset ( A_NORMAL );
}

int dist ( int x1, int y1, int x2, int y2 )
{
    return ( int )
    	sqrt ( ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 ) );
}

int inrange ( int x, int y )
{
    if ( x > 0 && y > 0 && x < max_x && y < max_y )
    	return 1;
    else
    	return 0;
}

int is_near ( int x1, int y1, int x2, int y2 )
{
	if ( dist ( x1, y1, x2, y2 ) < 2 ) return 1;
	return 0;
}

int los(int x1,int y1,int x2,int y2)
{
	int i=0,dx=0,dy=0,n=0,d=0,di1=0,di2=0,x=0,xi1=0,xi2=0,y=0,yi1=0,yi2=0,
	_s=1;dx=abs(x2-x1);dy=abs(y2-y1);if(dx>=dy){n=dx+1;d=(2*dy)-dx;di1=dy*2;
	di2=(dy-dx)*2;xi1=1;xi2=1;yi1=0;yi2=1;}else{n=dy+1;d=(2*dx)-dy;di1=dx*2;
	di2=(dx-dy)*2;xi1=0;xi2=1;yi1=1;yi2=1;}if(x1>x2){xi1=-xi1;xi2=-xi2;}
	if(y1>y2){yi1=-yi1;yi2=-yi2;}x=x1;y=y1;for(i=2;i<=n;i++){
	if(map[x][y]==t_wall||map[x][y]==t_dclosed||map[x][y]==t_dhidden)
	{_s=0;break;}if(d<0){d+=di1;x+=xi1;y+=yi1;}else{d+=di2;x+=xi2;y+=yi2;}}
	return _s;
}

void init_curses ()
{
	initscr();
	keypad( stdscr, TRUE ); 
	nodelay( stdscr, FALSE );
	cbreak();
	noecho();
	curs_set(0);
	leaveok(stdscr, 1);
}

int ent_ex ( int x, int y )
{
	int i;
	for ( i=0; i <= entn; i++ )
	{
		if ( ent[i].x == x && ent[i].y == y && ent[i].hp > 0 )
		{
			return i;
		}
	}
	return -1;
}

int obj_ex ( int x, int y )
{
	int i;
	for ( i=0; i <= objn; i++ )
	{
		if ( obj[i].x == x && obj[i].y == y && obj[i].taken == 0 )
		{
			return i;
		}
	}
	return -1;
}

int sgn ( int n )
{
	if ( n >= 0 )
		return 1;
	else
		return -1;
}

int gn ( int x, int y, int r, int what )
{
	int t=0,ix,iy;
	for( ix = x-r; ix <= x+r; ix++ )
	{
		for( iy = y-r; iy <= y+r; iy++ )
		{
	    	if( inrange ( ix, iy ) )
	    	{
				if ( map[ix][iy] == what )
				{
					t++;
				}
			}
			else
			{
				return -1;
			}
		}
    }
    return t;
}

void mkcoor ( int x, int y, int xo, int yo, int len )
{
	int cx=x,cy=y,cl=0;
	while ( cl <= len )
	{
		if ( inrange ( cx + xo, cy + yo ) )
		{
			cx += xo;
			cy += yo;
			map[cx][cy] = t_floor;
		}
		cl++;
    }
}

void mkmap ()
{
	int rx,ry,ix,iy,c;

    for( iy = 0; iy < max_y; iy++ )
    {
		for( ix = 0; ix < max_x; ix++ )
		{
			 map[ix][iy] = t_nothing;
		}
	}
	
	map[max_x/2][max_y/2] = t_floor;
	
	int i, t;
	
	for ( i = 0; i <= magic; i++ )
	{
		c=0;
		while(1)
		{
			rx = rand() % ( max_x - 2 ) + 1;
			ry = rand() % ( max_y - 2 ) + 1;
		
			t = gn ( rx, ry, 1, t_floor );
		
			if ( ( map[rx][ry] == t_floor ) && ( t <= 3 ) && ( t != -1  ) )
			{
				if ( rand()%2 == 0 )
					mkcoor ( rx, ry, sgn(rand()%2-1), 0, rand()%3+1 );
				else
					mkcoor ( rx, ry, 0, sgn(rand()%2-1), rand()%3+1 );
				
				break;
			}
			else
			{
				c++;
				if ( c >= 1000 )
					break;
			}
		}
	}

    for( iy = 0; iy < max_y; iy++ )
    {
		for( ix = 0; ix < max_x; ix++ )
		{
			if ( gn ( ix, iy, 1, t_floor ) > 0 && map[ix][iy] == t_nothing )
				map[ix][iy] = t_wall;
				
			if ( ix == 1 || iy == 1 || ix == max_x-1 || iy == max_y-1 )
				map[ix][iy] = t_wall;
			
			if ( map[ix][iy] == t_nothing )
				map[ix][iy] = t_wall;
			
		}
	}
	
	for ( i = 0; i <= magic; i++ )
	{
		rx = rand() % ( max_x - 2 ) + 1;
		ry = rand() % ( max_y - 2 ) + 1;
		
		if ( map[rx][ry] == t_floor )
		{
			t = rand()%3;
			if ( t == 0 ) map[rx][ry] = t_fountain;
			if ( t == 1 ) map[rx][ry] = t_statue;
			if ( t == 2 ) map[rx][ry] = t_rock;
		}
	}
	
	for ( i = 0; i <= max_stairs; i++ )
	{
		while ( map[rx][ry] != t_floor )
		{
			rx = rand() % ( max_x - 2 ) + 1;
			ry = rand() % ( max_y - 2 ) + 1;
		}
		
		if ( depth < 5 )
			map[rx][ry] = t_stairsdown;
	}
}

void showmap ()
{
	clear();

	int ix, iy, ax, ay, e;
	char t[256];
	
    for ( ix = ent[0].x - fov_rad; ix <= ent[0].x + fov_rad; ix++ )
    {
		for ( iy = ent[0].y - fov_rad; iy <= ent[0].y + fov_rad; iy++ )
		{
			if ( inrange ( ix, iy ) )
			{
				if ( losmap[ix][iy] == 2 ) losmap[ix][iy] = 1;
				if ( los ( ent[0].x, ent[0].y, ix, iy ) == 1 &&
					dist ( ent[0].x, ent[0].y, ix, iy ) < fov_rad )
					losmap[ix][iy] = 2;
			}
		}
    }
	
	for ( ix=0; ix<=scrn_max_x; ix++ )
	{
		for ( iy=0; iy<=scrn_max_y; iy++ )
		{
			ax = cam_x + ix;
			ay = cam_y + iy;
			
			move ( iy, ix );
			setfgcol ( 0 );
			
			if ( inrange ( ax, ay ) )
			{
				if ( losmap[ax][ay] != 0 )
				{
					if ( map[ax][ay] == t_floor   )
						addch ( ACS_BULLET );
					if ( map[ax][ay] == t_wall    )
						addch ( '#' );
					if ( map[ax][ay] == t_dopen   )
						addch ( '\'' );
					if ( map[ax][ay] == t_dclosed )
						addch ( '+' );
					if ( map[ax][ay] == t_stairsdown )
						addch ( '>' );
					if ( map[ax][ay] == t_fountain )
						addch ( '%' );
					if ( map[ax][ay] == t_statue )
						addch ( '!' );
					if ( map[ax][ay] == t_rock )
						addch ( '.' );
					if ( map[ax][ay] == t_nothing )
						addch ( '?' );
				}
				
				e = obj_ex ( ax, ay );

				if ( e != -1 && losmap[ax][ay] == 2 )
				{
					move ( iy, ix );
					setfgcol ( 1 );
					addch ( obj[e].face );
				}

				e = ent_ex ( ax, ay );
				
				if ( e != -1 && losmap[ax][ay] == 2 )
				{
					move ( iy, ix );
					setfgcol ( 1 );
					addch ( ent[e].face );
					ent[e].seen = 1;
				}
				
			}
		}
	}
	
	e = obj_ex ( ent[0].x, ent[0].y );
	
	if ( e != -1 )
	{
		snprintf ( t, 256, "You stand on %s. ", obj[e].name );
		push_message ( t );
	}
	
	if ( map[ent[0].x][ent[0].y] == t_fountain )
		push_message ( "You stand on a lovely fountain." );
	if ( map[ent[0].x][ent[0].y] == t_statue )
		push_message ( "You stand on a statue of Craci." );
	if ( map[ent[0].x][ent[0].y] == t_rock )
		push_message ( "You stand on a rock." );
	
	setfgcol ( 0 );
	mvprintw ( 21, 1, "The Dungeons of Craci. hp:%i atk:%i def:%i level:%i", ent[0].hp, ent[0].atk, ent[0].def, depth );
	
	for ( e = 0; e <= 10; e++ )
	{
		if ( message[e].time == turns )
			setfgcol ( 1 );
		else
			setfgcol ( 0 );
		mvprintw ( 9+e, 41, "%s", message[e].str );
	}
		
	setfgcol ( 0 );
	mvprintw ( 1, 41, "Inventory:" );
	
	for ( e = 0; e <= invn; e++ )
	{
		move ( 1, 52+e );
		setfgcol ( 1 );
		addch ( inv[e].face );
	}
	
	setfgcol ( 0 );
	drawbox ( 0, 0, 40, 20 );
	drawbox ( 40, 0, 79, 2 );
	drawbox ( 40, 9, 79, 20 );
	drawbox ( 40, 0, 79, 20 );
	
}

void showinventory ( int wait )
{
	int i;
	
	clear_area ( 41, 2, 78, 19 );
	
	for ( i = 0; i <= invn; i++ )
	{
		setfgcol ( 1 );
		mvprintw (	2+i, 41, "%c - (%c) %s",
					( 97 + i ), inv[i].face, inv[i].name );
	}
	if ( wait == 1 )
	{
		setfgcol ( 0 );
		mvprintw ( 3+invn, 41, "Press ENTER." );
		wait_for_enter();
	}
}

void melee_fight ( int atk, int def )
{
	
	char s1 [128];
	char s2 [128];
	
	int dmg;
	
	strcpy ( s1, "" );
	strcpy ( s2, "" );
	
	/* dmg = rand() % ( ent[atk].atk - ent[def].def + 1) ; */
	dmg = rand() % ( ent[atk].atk + 1 );
	
	if ( dmg > 0 )
	{
		if ( atk == 0 )
		{
			sprintf ( s1, "You hit the %s (%i dmg)!",
					  ent[def].name, dmg );
		}
		else
		{
			sprintf ( s1, "The %s hit you (%i dmg)!",
					  ent[atk].name, dmg );
		}
		
		ent[def].hp -= dmg;
		
		if ( ent[def].hp <= 0 )
		{
			if ( atk == 0 )
			{
				sprintf ( s2, "The %s dies! ", ent[def].name );
				if ( strcmp ( ent[def].name, "Craci" ) == 0 )
				{
					push_message ( "You just killed Craci." );
					showmap();
					wait_for_enter();
					epic_win();
				}
			}
			else
			{
				push_message ( "You die! Press ENTER." );
				showmap();
				wait_for_enter();
				die ();
			}
		}
	}
	else
	{
		if ( atk == 0 )
			sprintf ( s1, "You miss the %s! ", ent[def].name );
		else
			sprintf ( s1, "The %s misses! ", ent[atk].name );
	}
	
	if ( strcmp ( s1, "" ) != 0 ) push_message ( s1 );
	if ( strcmp ( s2, "" ) != 0 ) push_message ( s2 );
}

void move_ent ( int i, int d )
{
	int xo=0, yo=0;
	
	switch(d){
		case up:
			xo=0; yo=-1;
			break;
		case down:
			xo=0; yo=1;
			break;
		case left:
			xo=-1; yo=0;
			break;
		case right:
			xo=1; yo=0;
			break;
	}
	
	if  ( (	map[ent[i].x+xo][ent[i].y+yo] == t_floor 		||
			map[ent[i].x+xo][ent[i].y+yo] == t_dopen 		|| 
			map[ent[i].x+xo][ent[i].y+yo] == t_fountain 	||
			map[ent[i].x+xo][ent[i].y+yo] == t_statue 		|| 
			map[ent[i].x+xo][ent[i].y+yo] == t_rock			||
			map[ent[i].x+xo][ent[i].y+yo] == t_stairsdown	) &&
		  ( ent[i].x+xo >= 0 && ent[i].x+xo <= max_x &&
		    ent[i].y+yo >= 0 && ent[i].y+yo <= max_y ) &&
		    ent_ex ( ent[i].x+xo, ent[i].y+yo ) == -1 )
	{
		ent[i].x += xo;
		ent[i].y += yo;
		
		if ( i == 0 )
		{
			if ( ent[i].x-cam_x > cam_max_x ) cam_x++;
			if ( ent[i].x-cam_x < cam_min_x ) cam_x--;
			if ( ent[i].y-cam_y > cam_max_y ) cam_y++;
			if ( ent[i].y-cam_y < cam_min_y ) cam_y--;
		}
	}else{
		if ( i == 0 )
		{
			if ( map[ent[i].x+xo][ent[i].y+yo] == t_wall )
			{
				push_message ( "Yep, it's solid alright." );
			}
			if ( map[ent[i].x+xo][ent[i].y+yo] == t_dclosed )
			{
				push_message ( "You open the door." );
				
				map[ent[i].x+xo][ent[i].y+yo] = t_dopen;
			}
		}
	}
	
}

void ent_zombie_ai ( int z )
{

	if ( ent[z].hp > 0 )
	{
		if ( is_near( ent[0].x, ent[0].y, ent[z].x, ent[z].y ) == 0 )
		{
			if ( ent[z].x > ent[0].x ) move_ent ( z, left  );
			if ( ent[z].x < ent[0].x ) move_ent ( z, right );
			if ( ent[z].y > ent[0].y ) move_ent ( z, up    );
			if ( ent[z].y < ent[0].y ) move_ent ( z, down  );
		}
		else
		{
			melee_fight ( z, 0 );
		}
	}
}

void go_in_direction ( int e, int d )
{
	int xo=0, yo=0, xe;
	
	switch(d){
		case up:
			xo=0; yo=-1;
			break;
		case down:
			xo=0; yo=1;
			break;
		case left:
			xo=-1; yo=0;
			break;
		case right:
			xo=1; yo=0;
			break;
			
		case up_right:
			xo=1; yo=-1;
			break;
		case up_left:
			xo=-1; yo=-1;
			break;
		case down_right:
			xo=1; yo=1;
			break;
		case down_left:
			xo=-1; yo=1;
			break;
	}
	
	xe = ent_ex ( ent[e].x+xo, ent[e].y+yo );
	
	if ( xe == -1 )
		move_ent ( e, d );
	else
		melee_fight ( e, xe );
}

void place_pc ()
{
	int rx, ry;
	while (1)
	{
		rx = rand() % ( max_x - 2 ) + 1;
		ry = rand() % ( max_y - 2 ) + 1;
		
		if ( map[rx][ry] == t_floor )
		{
			ent[0].x = rx;
			ent[0].y = ry;
			
			cam_x = ent[0].x - ( ( cam_min_x + cam_max_x ) / 2 );
			cam_y = ent[0].y - ( ( cam_min_y + cam_max_y ) / 2 );
			
			break;
		}
	}
}

void place_ents ( int n )
{
	int rx, ry;
	int i, rt;
	
	for ( i = 1; i <= n+1; i++ )
	{
		entn++;
		rt = rand() % ( e_max - 1 );
		
		while (1)
		{
			rx = rand() % ( max_x - 2 ) + 1;
			ry = rand() % ( max_y - 2 ) + 1;
		
			if (	map[rx][ry] == t_floor &&
					ent_ex ( rx, ry ) == -1 &&
					obj_ex ( rx, ry ) == -1 )
			{
				ent[entn].x = rx;
				ent[entn].y = ry;
				break;
			}
		}
		
		switch ( rt )
		{
			case e_dog_head:
				ent[entn].hp   = 2;
				ent[entn].atk  = 1;
				ent[entn].def  = 1;
				ent[entn].face = 'd';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "dog head" , 32 );
				break;
			case e_dog_body:
				ent[entn].hp   = 5;
				ent[entn].atk  = 1;
				ent[entn].def  = 1;
				ent[entn].face = 'd';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "dog body" , 32 );
				break;
			case e_dog_tail:
				ent[entn].hp   = 1;
				ent[entn].atk  = 1;
				ent[entn].def  = 1;
				ent[entn].face = '~';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "dog tail" , 32 );
				break;
			case e_zombie_head:
				ent[entn].hp   = 2;
				ent[entn].atk  = 1;
				ent[entn].def  = 0;
				ent[entn].face = 'z';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "zombie head" , 32 );
				break;
			case e_headless_zombie:
				ent[entn].hp   = 3;
				ent[entn].atk  = 2;
				ent[entn].def  = 1;
				ent[entn].face = 'Z';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "headless zombie" , 32 );
				break;
			case e_zombie_feet:
				ent[entn].hp   = 3;
				ent[entn].atk  = 1;
				ent[entn].def  = 0;
				ent[entn].face = 'z';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "pair of zombie legs" , 32 );
				break;
			case e_bear_paws:
				ent[entn].hp   = 2;
				ent[entn].atk  = 1;
				ent[entn].def  = 1;
				ent[entn].face = 'b';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "pair of bear paws" , 32 );
				break;
			case e_bear_head:
				ent[entn].hp   = 1;
				ent[entn].atk  = 3;
				ent[entn].def  = 0;
				ent[entn].face = 'b';
				ent[entn].fgc  = 1;
				strncpy ( ent[entn].name, "bear head" , 32 );
				break;
		}
	}
	
	if ( depth == 5 )
	{
		entn++;
		while (1)
		{
			rx = rand() % ( max_x - 2 ) + 1;
			ry = rand() % ( max_y - 2 ) + 1;
		
			if (	map[rx][ry] == t_floor &&
					ent_ex ( rx, ry ) == -1 &&
					obj_ex ( rx, ry ) == -1 )
			{
				ent[entn].x = rx;
				ent[entn].y = ry;
				break;
			}
		}
		ent[entn].hp   = 15;
		ent[entn].atk  = 3;
		ent[entn].def  = 3;
		ent[entn].face = 'C';
		ent[entn].fgc  = 1;
		strncpy ( ent[entn].name, "Craci" , 32 );
	}
}

void place_objs ( int n )
{
	int rx, ry;
	int i, rt;
	
	for ( i = 1; i <= n+1; i++ )
	{
		objn++;
		rt = rand() % o_max;
		
		while (1)
		{
			rx = rand() % ( max_x - 2 ) + 1;
			ry = rand() % ( max_y - 2 ) + 1;
		
			if (	map[rx][ry] == t_floor	&&
					ent_ex ( rx, ry ) == -1 &&
					obj_ex ( rx, ry ) == -1 )
			{
				obj[objn].x = rx;
				obj[objn].y = ry;
				break;
			}
		}
		
		obj[objn].taken = 0;
		obj[objn].type = rt;
		obj[objn].id = objn;
		
		switch ( rt )
		{
			case o_gold:
				obj[objn].face = '$';
				obj[objn].fgc  = 12;
				strncpy ( obj[objn].name, "some gold" , 32 );
				obj[objn].takeable = 1;
				obj[objn].useable = 0;
				break;
			case o_medkit:
				obj[objn].face = '+';
				obj[objn].fgc = 2;
				strncpy ( obj[objn].name, "a medkit", 32 );
				obj[objn].takeable = 1;
				obj[objn].useable = 1;
				break;
		}
	}
}

void take_obj ( int n )
{
	char t[256];
	if ( n != -1 )
	{
		if ( obj[n].takeable == 1 )
		{
			if ( invn+1 < inv_capacity )
			{
				invn++;
				obj[n].taken = 1;
				inv[invn].id = n;
				inv[invn].type = obj[n].type;
				inv[invn].face = obj[n].face;
				inv[invn].fgc = obj[n].fgc;
				inv[invn].useable = obj[n].useable;
				strncpy ( inv[invn].name, obj[n].name, 32 );
				sprintf ( t, "You pick up %s. ", inv[invn].name );
			
				push_message ( t );
			}
			else
			{
				push_message ( "Your backpack is full!" );
			}
		}
		else
		{
			snprintf ( t, 256, "You fail to pick up %s!", obj[n].name );
			push_message ( t );
		}
	}
}

void destroy_obj ( int n )
{
	int i;
	for ( i = n; i <= invn; i++ )
	{
		inv[i].type = inv[i+1].type;
		inv[i].face = inv[i+1].face;
		inv[i].fgc = inv[i+1].fgc;
		inv[i].id = inv[i+1].id;
		strncpy ( inv[i].name, inv[i+1].name, 32 );
	}
	invn--;
}

void drop_obj ( int n )
{
	char t[256];
	int id;
	if ( obj_ex ( ent[0].x, ent[0].y ) == -1 )
	{
		id = inv[n].id;
		obj[id].taken = 0;
		obj[id].x = ent[0].x;
		obj[id].y = ent[0].y;
		obj[id].type = inv[n].type;
		
		snprintf ( t, 256, "You dropped %s. ", inv[n].name );
		push_message ( t );
		
		destroy_obj ( n );
	}
	else
	{
		push_message ( "There is already something here. " );
	}
}

void use_obj ( int n )
{
	char t[256];
	if ( inv[n].useable == 1 )
	{
		switch ( inv[n].type )
		{
			case o_medkit:
				ent[0].hp += 5;
				break;
			case o_gold:
				break;
		}
		snprintf ( t, 256, "You use %s.", inv[n].name );
		destroy_obj ( n );
		alert ( t );
	}
	else
	{
		snprintf ( t, 256, "You can't use %s!", inv[n].name );
		alert ( t );
	}
}

void level_init ( void )
{
	int ix, iy;
	
	for ( ix = 0; ix <= max_x; ix++ )
	{
		for ( iy = 0; iy <= max_y; iy++ )
		{
			map[ix][iy] = t_nothing;
			losmap[ix][iy] = 0;
		}
	}
	
	entn = 0; objn = -1;
	
	mkmap ();
	
	place_pc ();
		
	place_ents ( depth * 3 + 6 );
	place_objs ( depth * 3 + 2 );
}

void init_game ( void )
{
	int a=0, d=0, k=0;

	clear ();
	
	setfgcol ( 1 );
	mvprintw ( 1, 1, "The Dungeons of Craci." );
	
	setfgcol ( 0 );
	mvprintw ( 3, 1, "You are the ordinary peasant. But after years of having" );
	mvprintw ( 4, 1, "to stand powerless in front of Craci, the evil pair of " );
	mvprintw ( 5, 1, "legs who terrifies people by saluting them when they " );
	mvprintw ( 6, 1, "expect less, you decide it's time to bring Craci's terror " );
	mvprintw ( 7, 1, "to an end. So you dive in the local dungeon he dwells in... ");
	
	setfgcol ( 1 );
	mvprintw ( 9, 1, "Press ENTER." );
	wait_for_enter ();
	
	while ( 1 )
	{
		a = rand() % 6 + 2;
		d = rand() % 6 + 2;
				
		if ( a+d < 10 ) break;
	}
			
	
	clear ();
	setfgcol ( 1 );
	mvprintw ( 1, 1, "Character Creation!" );
	
	setfgcol ( 0 );
	mvprintw ( 2, 1, "Press R to roll or A to accept." );
	mvprintw ( 4, 1, "Stats:" );
		
	mvprintw ( 6, 5, " Attack: %i", a );
	mvprintw ( 7, 5, "Defense: %i", d );

	while ( 1 )
	{
		if ( k == 'R' )
		{
			while ( 1 )
			{
				a = rand() % 6 + 2;
				d = rand() % 6 + 2;
				
				if ( a+d < 10 ) break;
			}
			
			clear ();
			setfgcol ( 1 );
			mvprintw ( 1, 1, "Character Creation!" );
	
			setfgcol ( 0 );
			mvprintw ( 2, 1, "Press R to roll or A to accept." );
			mvprintw ( 4, 1, "Stats:" );
		
			mvprintw ( 6, 5, " Attack: %i", a );
			mvprintw ( 7, 5, "Defense: %i", d );
		}
			
		if ( k == 'A' )
			break;
		
		k = getch();
	}
	
	ent[0].hp   = 10;
	ent[0].type = 0;
	ent[0].face = '@';
	ent[0].fgc  = 15;
	
	ent[0].atk = a;
	ent[0].def = d;
}

int main ( int argc, char *argv[] )
{
	int k=0, kp=0, i;
	int kk=0;

	init_curses();
	srand(time(0));
	
	message_archive = fopen ("./archive.log","w");
	fprintf (	message_archive,
				"Vlad's Tiny Dungeon Crawler Message Log\n" );
	
	level_init();
	init_game ();
	
	showmap();
	
	while( k != 'q' )
	{
	
		if (	k == KEY_UP		||
				k == 'k'		||
				k == '8' ){ go_in_direction ( 0, up    ); kp = 1; }
		if ( 	k == KEY_DOWN 	||
				k == 'j'		||
				k == '2' ){ go_in_direction ( 0, down  ); kp = 1; }
		if (	k == KEY_LEFT	||
				k == 'h'		||
				k == '4' ){ go_in_direction ( 0, left  ); kp = 1; }
		if (	k == KEY_RIGHT	||
				k == 'l'		||
				k == '6' ){ go_in_direction ( 0, right ); kp = 1; }
		
		if ( k == 55 ){ go_in_direction ( 0, up_left    ); kp = 1; }
		if ( k == 57 ){ go_in_direction ( 0, up_right   ); kp = 1; }
		if ( k == 49 ){ go_in_direction ( 0, down_left  ); kp = 1; }
		if ( k == 51 ){ go_in_direction ( 0, down_right ); kp = 1; }
		
		if ( k == ',' ) 
		{
			take_obj ( obj_ex ( ent[0].x, ent[0].y ) );
			kp = 1;
		}
		
		if ( k == 'i' ) showinventory( 1 );
		
		if ( k == 'd' )
		{
			showinventory ( 0 );
			setfgcol ( 0 );
			mvprintw ( 3 + invn, 41, "What object do you want to drop?" );
temp_:
			kk = getch();
			if ( kk >= 'a' && kk <= 'z' )
			{
				if ( invn >= ( kk - 'a' ) )
				{
					drop_obj ( kk - 'a' );
				}
				else
					goto temp_;
			}
			else if ( kk == ' ' )
				;		/* only permit as an exit. */
			else
				goto temp_;
				
			kp = 1;
		}
		
		if ( k == 'u' )
		{
			showinventory ( 0 );
			setfgcol ( 0 );
			mvprintw ( 3 + invn, 41, "What object do you want to use?" );
temp2_:
			kk = getch();
			if ( kk >= 'a' && kk <= 'z' )
			{
				if ( invn >= ( kk - 'a' ) )
				{
					use_obj ( kk - 'a' );
				}
				else
					goto temp2_;
			}
			else if ( kk == ' ' )
				;		/* only permit as an exit. */
			else
				goto temp_;
				
			kp = 1;
		}
		
		if ( k == '>' )
		{
			if ( map[ent[0].x][ent[0].y] == t_stairsdown )
			{
				push_message ( "You descend..." );
				push_message ( "The evil of Craci closes the trapdoor!" );
				depth++;
				ent[0].hp+=2;
				
				if ( depth % 3 == 0 )
				{
					ent[0].atk++;
					ent[0].def++;
					push_message ( "You also feel more powerful!" );
				}
				
				level_init ();
			}
			else
				push_message ( "Descend what? Dust?" );
		}
		
		if ( kp == 1 )
		{
			
			for ( i=1; i <= entn ; i++ )
			{
				if ( ent[i].seen == 1 )
					ent_zombie_ai ( i );
			}
			
			showmap();
			turns++;
		}
		
		k = getch();
		
	}
	
	fclose ( message_archive );
	
	endwin();
	
	return 0;
}

/*EOF.*************************************************************************/


