// Biedny Pies Antoni 3 for Atari 8-bit by mgr_inz_rafal

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <conio.h>
#include <unistd.h>
#include <peekpoke.h>
#include <string.h>		// For memset()
#include <atari.h>
#include <joystick.h> 
#include <errno.h>

#include "atari_defs.h"

#define RANDOM_LEVEL	20
#define CUSTOM_LEVEL	21

#define MAX_SX			22
#define MAX_SY			22

#define COST_DIRECT		5	// because: 1 * 10 = 10;				10 / 2 = 5;
#define COST_SLANT		7	// because: 1,41 (sqrt(2)) * 10 = 14;	14 / 2 = 7;

#define	NUM_LEVELS		10

#define	FT_ROAD			0
#define	FT_GRASS		1
#define	FT_HILL			2
#define	FT_MOUNTAIN		3
#define	FT_WATER		4
#define FT_WALL			5
#define FT_COW			6
#define FT_START		9
#define FT_END			8
#define FT__LAST__		10

#define SPRITE0_POS		176
#define SPRITE1_POS		148
#define SPRITE2_POS		144

#define COLOR_RIGHT		COLOR_LIGHTGREEN
#define COLOR_MIDDLE	COLOR_YELLOW
#define COLOR_CURSOR	COLOR_VIOLET

#define STATUS_WAITFORPLAYER	0
#define STATUS_CALCULATING		1
#define STATUS_PLAYERWIN		2
#define STATUS_PLAYERLOST		3
#define STATUS_CONFIRMATION		4
#define STATUS_FUTILE			5
#define STATUS_BEEN_HERE		6
#define STATUS_EDITING			7
#define STATUS_FIRE				8
#define STATUS_VALIDATING		9

#define MAP_SIZE_AVERAGE ((l.dim_x + l.dim_y) >> 1)

#define GM_EPISODE				0
#define GM_RANDOM				1
#define GM_EDITOR				2
unsigned char game_mode;

FILE* handle;

unsigned char drawing_offset_x;
unsigned char drawing_offset_y;
unsigned char current_level;

extern char font_base;

extern 		char joy_driver;
unsigned	char joy;
unsigned	char act_on_fire = 1;

unsigned	int	screenmem;

signed 		char caret;
signed 		char below_messagebox[38*3];
unsigned	char filename_buffer[20] = {'D', ':'};

#define pmg_memory		0x2800
#define pmg_memory_ptr	((unsigned char *) (pmg_memory ) )
#define pmg_memory_P0	((unsigned char *) (pmg_memory + 0x200) )
#define pmg_memory_P1	((unsigned char *) (pmg_memory + 0x280) )
#define pmg_memory_P2	((unsigned char *) (pmg_memory + 0x300) )
#define pmg_memory_P3	((unsigned char *) (pmg_memory + 0x380) )

unsigned char current_status 	= STATUS_WAITFORPLAYER;
unsigned char total_score;

#define DIFF_EASY	0
#define DIFF_HARD	1
unsigned char difficulty = DIFF_EASY;

signed char cursor_x_relative	= 0;
signed char cursor_y_relative	= 0;
unsigned char cursor_color		= 0;

unsigned int scores[2] = {0, 0};
unsigned int scoretmp;

signed char custom_width = 10;
signed char custom_height = 16;
signed char current_char = FT_GRASS;

#define	LEVEL_0_SIZE_X	10
#define LEVEL_0_SIZE_Y	6
#define	LEVEL_1_SIZE_X	8
#define LEVEL_1_SIZE_Y	8
#define	LEVEL_2_SIZE_X	10
#define LEVEL_2_SIZE_Y	8
#define	LEVEL_3_SIZE_X	22
#define LEVEL_3_SIZE_Y	6
#define	LEVEL_4_SIZE_X	10
#define LEVEL_4_SIZE_Y	18
#define	LEVEL_5_SIZE_X	8
#define LEVEL_5_SIZE_Y	8
#define	LEVEL_6_SIZE_X	16
#define LEVEL_6_SIZE_Y	10
#define	LEVEL_7_SIZE_X	4
#define LEVEL_7_SIZE_Y	14
#define	LEVEL_8_SIZE_X	12
#define LEVEL_8_SIZE_Y	16
#define	LEVEL_9_SIZE_X	22
#define LEVEL_9_SIZE_Y	22
unsigned char level_0_data[LEVEL_0_SIZE_Y][LEVEL_0_SIZE_X] = {
	{ 9, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
	{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
	{ 1, 2, 2, 2, 2, 1, 0, 0, 0, 0 },
	{ 2, 3, 3, 2, 2, 2, 0, 1, 1, 0 },
	{ 3, 3, 3, 3, 2, 2, 0, 1, 1, 8 },
};

unsigned char level_1_data[LEVEL_1_SIZE_Y][LEVEL_1_SIZE_X] = {
	{ 9, 1, 1, 1, 1, 1, 3, 3 },
	{ 6, 1, 1, 1, 6, 3, 6, 3 },
	{ 1, 1, 6, 1, 1, 2, 2, 2 },
	{ 1, 1, 1, 1, 6, 6, 2, 2 },
	{ 1, 1, 6, 6, 2, 2, 1, 1 },
	{ 1, 1, 1, 2, 2, 2, 1, 1 },
	{ 6, 1, 1, 1, 2, 2, 6, 1 },
	{ 1, 1, 1, 1, 1, 3, 1, 8 },
};

unsigned char level_2_data[LEVEL_2_SIZE_Y][LEVEL_2_SIZE_X] = {
	{ 1, 1, 2, 2, 2, 2, 1, 1, 1, 1 },
	{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ 1, 0, 1, 1, 1, 2, 2, 2, 0, 1 },
	{ 1, 0, 1, 1, 2, 2, 3, 2, 0, 6 },
	{ 2, 0, 2, 0, 0, 9, 4, 4, 0, 6 },
	{ 2, 6, 1, 4, 4, 4, 4, 4, 0, 1 },
	{ 2, 2, 1, 4, 4, 4, 4, 4, 0, 1 },
	{ 3, 3, 2, 1, 1, 1, 8, 1, 0, 1 }
};

unsigned char level_3_data[LEVEL_3_SIZE_Y][LEVEL_3_SIZE_X] = {
	{ 8, 5, 1, 1, 1, 1, 1, 2, 1, 2, 3, 2, 2, 1, 1, 2, 1, 2, 1, 1, 1, 4 },
	{ 1, 5, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 2, 1, 2, 2, 3, 4, 1 },
	{ 1, 5, 4, 9, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1 },
	{ 1, 5, 4, 4, 4, 5, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 6, 1, 1 },
	{ 1, 5, 5, 5, 5, 5, 1, 6, 1, 1, 1, 6, 1, 1, 6, 1, 6, 1, 6, 1, 1, 1 },
	{ 1, 1, 1, 1, 1, 1, 1, 6, 1, 6, 1, 1, 1, 1, 0, 1, 6, 1, 1, 1, 1, 1 }
};

unsigned char level_4_data[LEVEL_4_SIZE_Y][LEVEL_4_SIZE_X] = {
	{ 9, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
	{ 5, 5, 1, 1, 1, 1, 1, 0, 0, 1 },
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
	{ 1, 1, 1, 1, 4, 4, 1, 1, 1, 1 },
	{ 1, 1, 4, 4, 4, 4, 4, 1, 1, 1 },
	{ 1, 1, 4, 4, 4, 4, 4, 4, 1, 1 },
	{ 1, 4, 4, 4, 4, 4, 4, 4, 1, 1 },
	{ 1, 4, 4, 4, 4, 4, 4, 4, 1, 1 },
	{ 1, 4, 4, 4, 4, 4, 4, 4, 4, 1 },
	{ 1, 1, 1, 4, 4, 4, 4, 4, 4, 1 },
	{ 1, 1, 1, 4, 4, 4, 4, 4, 4, 1 },
	{ 1, 1, 1, 1, 1, 4, 4, 4, 1, 2 },
	{ 2, 1, 1, 1, 1, 1, 1, 1, 1, 2 },
	{ 2, 2, 1, 1, 1, 1, 2, 2, 2, 6 },
	{ 3, 3, 2, 1, 1, 2, 2, 2, 6, 8 },
	{ 3, 3, 2, 1, 2, 3, 3, 3, 1, 0 },
	{ 3, 3, 2, 1, 1, 2, 2, 2, 1, 0 },
	{ 3, 3, 3, 2, 1, 0, 0, 0, 0, 0 }
};

unsigned char level_5_data[LEVEL_5_SIZE_Y][LEVEL_5_SIZE_X] = {
	{ 1, 3, 3, 3, 2, 1, 1, 1 },
	{ 2, 3, 3, 3, 3, 2, 1, 1 },
	{ 1, 2, 8, 3, 3, 2, 2, 6 },
	{ 2, 3, 3, 3, 3, 3, 2, 6 },
	{ 0, 2, 3, 3, 2, 2, 2, 1 },
	{ 0, 2, 2, 2, 1, 1, 4, 4 },
	{ 0, 1, 1, 1, 4, 4, 4, 4 },
	{ 1, 1, 1, 4, 4, 4, 4, 9 }
};

unsigned char level_6_data[LEVEL_6_SIZE_Y][LEVEL_6_SIZE_X] = {
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
	{ 1, 4, 4, 4, 1, 1, 1, 5, 5, 1, 1, 2, 2, 2, 1, 1 },
	{ 4, 4, 4, 4, 1, 4, 1, 5, 5, 3, 3, 2, 1, 1, 1, 1 },
	{ 1, 1, 1, 1, 4, 4, 4, 5, 5, 0, 1, 1, 1, 1, 2, 2 },
	{ 2, 2, 1, 1, 4, 4, 4, 5, 5, 0, 1, 2, 1, 1, 2, 2 },
	{ 2, 2, 2, 2, 1, 4, 4, 5, 5, 0, 2, 2, 2, 1, 1, 1 },
	{ 1, 1, 1, 1, 1, 1, 4, 5, 5, 0, 2, 2, 2, 1, 1, 1 },
	{ 1, 1, 1, 2, 3, 1, 4, 5, 1, 0, 3, 3, 1, 1, 1, 1 },
	{ 1, 1, 9, 3, 3, 2, 4, 1, 5, 0, 1, 3, 2, 2, 1, 1 },
	{ 1, 1, 1, 1, 2, 2, 4, 5, 5, 0, 0, 0, 2, 2, 1, 8 }
};

unsigned char level_7_data[LEVEL_7_SIZE_Y][LEVEL_7_SIZE_X] = {
	{ 9, 1, 6, 1 },
	{ 1, 1, 1, 1 },
	{ 1, 6, 2, 2 },
	{ 2, 2, 2, 6 },
	{ 2, 2, 2, 4 },
	{ 1, 4, 4, 1 },
	{ 1, 6, 6, 1 },
	{ 6, 1, 1, 1 },
	{ 0, 2, 3, 3 },
	{ 0, 2, 2, 2 },
	{ 3, 3, 3, 1 },
	{ 0, 1, 1, 1 },
	{ 0, 0, 2, 2 },
	{ 8, 0, 2, 3 },
};

unsigned char level_8_data[LEVEL_8_SIZE_Y][LEVEL_8_SIZE_X] = {
	{ 1, 1, 9, 1, 1, 1, 6, 1, 1, 1, 1, 1 },
	{ 1, 1, 1, 2, 1, 1, 1, 1, 6, 1, 1, 1 },
	{ 1, 2, 2, 3, 2, 1, 1, 1, 1, 1, 1, 4 },
	{ 2, 3, 3, 3, 3, 2, 2, 1, 1, 4, 4, 4 },
	{ 2, 3, 3, 3, 3, 3, 2, 1, 4, 4, 4, 4 },
	{ 2, 3, 3, 3, 3, 3, 2, 1, 4, 4, 4, 4 },
	{ 2, 2, 3, 3, 2, 2, 2, 1, 1, 4, 4, 4 },
	{ 0, 2, 3, 2, 2, 2, 1, 1, 1, 1, 1, 4 },
	{ 0, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1 },
	{ 0, 0, 1, 1, 1, 1, 1, 2, 3, 3, 2, 1 },
	{ 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 2, 1 },
	{ 0, 2, 3, 2, 2, 2, 3, 3, 2, 2, 1, 1 },
	{ 0, 2, 3, 3, 3, 3, 3, 3, 2, 1, 1, 1 },
	{ 0, 2, 2, 3, 3, 3, 3, 2, 2, 1, 1, 1 },
	{ 0, 6, 2, 2, 2, 2, 2, 1, 1, 6, 1, 1 },
	{ 1, 0, 0, 1, 1, 1, 8, 1, 1, 1, 1, 1 }
};

unsigned char level_9_data[LEVEL_9_SIZE_Y][LEVEL_9_SIZE_X] = {
	{ 1, 1, 1, 1, 5, 8, 5, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{ 1, 5, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
	{ 1, 5, 1, 1, 1, 1, 5, 1, 5, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1},
	{ 1, 5, 1, 1, 5, 1, 5, 1, 5, 1, 5, 1, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1},
	{ 1, 5, 5, 1, 5, 1, 5, 1, 5, 5, 5, 5, 5, 1, 5, 5, 1, 5, 1, 1, 5, 1},
	{ 1, 5, 1, 1, 5, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 5, 1, 1, 5, 1},
	{ 1, 5, 5, 5, 5, 1, 5, 5, 1, 5, 5, 5, 5, 5, 1, 5, 1, 5, 1, 1, 5, 1},
	{ 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 1, 5, 1, 5, 1, 1, 5, 1},
	{ 1, 1, 1, 1, 1, 5, 1, 1, 1, 5, 5, 5, 1, 5, 1, 5, 1, 5, 1, 1, 5, 1},
	{ 1, 5, 1, 5, 5, 5, 1, 1, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 1, 1, 1},
	{ 1, 5, 1, 5, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 1, 5, 1, 5, 1, 1, 1, 1},
	{ 1, 5, 1, 5, 1, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1},
	{ 1, 5, 1, 5, 1, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1},
	{ 1, 5, 1, 5, 1, 5, 5, 5, 1, 1, 5, 5, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1},
	{ 1, 5, 1, 5, 1, 1, 1, 5, 5, 1, 1, 5, 5, 5, 5, 1, 5, 1, 1, 5, 1, 5},
	{ 1, 5, 1, 1, 1, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1},
	{ 1, 5, 1, 5, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 1, 1, 5, 1, 1, 5, 1, 1},
	{ 1, 5, 1, 5, 1, 5, 5, 5, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 5, 1},
	{ 1, 5, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1},
	{ 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 1, 1, 5, 1, 1},
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 1, 1},
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 9, 1, 1, 1, 1, 1, 1, 1}
};

// Lake templates for random level generation
#define	LAKE_TEMPLATE_COUNT		3
signed char lake_template_0[5*4] =
	{ 100, 004, 004, 100, 100, 
	  004, 004, 004, 004, 100,
	  004, 004, 004, 004, 004,
	  100, 100, 100, 004, 004 };
signed char lake_template_1[5*5] =
	{ 100, 004, 004, 004, 004,
	  004, 004, 100, 100, 004,
	  004, 100, 100, 004, 004,
	  004, 004, 100, 004, 004,
	  100, 004, 004, 100, 100 };
signed char lake_template_2[2*3] =
	{ 004, 004,
	  004, 004,
	  004, 100};
typedef struct object_template
{
	signed char sx;
	signed char sy;
	signed char* templ;
} Object_template;
Object_template	lakes[] = 
{
	{5, 4, lake_template_0},
	{5, 5, lake_template_1},
	{2, 3, lake_template_2}
};

// Mountain templates for random level generation
#define	MOUNTAIN_TEMPLATE_COUNT		3
signed char mountain_template_0[8*8] =
	{ 100, 100, 100, 100, 100, 002, 002, 100,
	  100, 002, 002, 002, 002, 002, 002, 002,
	  002, 002, 002, 003, 100, 002, 002, 002,
	  002, 002, 003, 003, 003, 003, 002, 100,
	  002, 003, 003, 003, 003, 003, 002, 100,
	  002, 003, 003, 003, 003, 003, 002, 100,
	  100, 002, 003, 003, 003, 002, 002, 100,
	  100, 100, 002, 002, 002, 002, 002, 100 };
signed char mountain_template_1[10*4] =
	{ 100, 002, 002, 100, 003, 003, 002, 100, 100, 100,
	  002, 003, 003, 003, 100, 003, 003, 002, 100, 100,
	  100, 002, 003, 003, 002, 100, 003, 003, 002, 100,
	  100, 100, 002, 002, 100, 100, 002, 003, 003, 100 };
signed char mountain_template_2[4*10] =
	{ 002, 002, 002, 100,
	  002, 003, 002, 100,
	  002, 003, 002, 100,
	  100, 003, 100, 100,
	  002, 100, 100, 002,
	  002, 003, 003, 002,
	  002, 002, 003, 100,
	  002, 002, 003, 002,
	  100, 002, 003, 002,
	  100, 100, 002, 100};
Object_template	mountains[] = 
{
	{8, 8, mountain_template_0},
	{10, 4, mountain_template_1},
	{4, 10, mountain_template_2}
};

unsigned char penalties[] = 
{
	1,
	2,
	7,
	11,
	19,
	0,
	0
};

typedef struct field
{
	unsigned char	type_DEBUG;

	unsigned char	penalty;
	void*			pred;
	unsigned int	dist;
	unsigned char	visited;
	signed char		myx;
	signed char		myy;
	signed char		stepped_on;
} Field;

typedef struct level
{
	signed char		dim_x;
	signed char		dim_y;
	Field f[MAX_SY][MAX_SX];
} Level;
Level l;
typedef struct episode
{
	unsigned char size_x;
	unsigned char size_y;
	void* level_data;
} Episode;
Episode e[NUM_LEVELS] =	{
		{LEVEL_0_SIZE_X, LEVEL_0_SIZE_Y, level_0_data},
		{LEVEL_1_SIZE_X, LEVEL_1_SIZE_Y, level_1_data},
		{LEVEL_2_SIZE_X, LEVEL_2_SIZE_Y, level_2_data},
		{LEVEL_3_SIZE_X, LEVEL_3_SIZE_Y, level_3_data},
		{LEVEL_4_SIZE_X, LEVEL_4_SIZE_Y, level_4_data},
		{LEVEL_5_SIZE_X, LEVEL_5_SIZE_Y, level_5_data},
		{LEVEL_6_SIZE_X, LEVEL_6_SIZE_Y, level_6_data},
		{LEVEL_7_SIZE_X, LEVEL_7_SIZE_Y, level_7_data},
		{LEVEL_8_SIZE_X, LEVEL_8_SIZE_Y, level_8_data},
		{LEVEL_9_SIZE_X, LEVEL_9_SIZE_Y, level_9_data}
	};

// Starting position
unsigned char sx;
unsigned char sy;

// Ending position
unsigned char ex;
unsigned char ey;

// Cursor position
unsigned char cx;
unsigned char cy;
unsigned char csx;	// Sprite
unsigned char csy;

signed char my_random(signed char max)
{
	return PEEK(0xD20A) % max;
}

void synchro(int rep)
{
	unsigned char vcount;
	while(rep--)
	{
		for(;;)
		{
			vcount = PEEK(VCOUNT);
			if(vcount == 0x90)
			{
				break;
			}
		}
	}
}

void setftype(unsigned char x, unsigned char y, unsigned char type, signed char setcursor)
{
	l.f[y][x].type_DEBUG = type;
	switch(type)
	{
	case FT_START:
		if(setcursor)
		{
			cx = x;
			cy = y;
		}
		sx = x;
		sy = y;
		l.f[y][x].penalty = 0;
		break;
	case FT_END:
		ex = x;
		ey = y;
		l.f[y][x].penalty = 0;
		break;
	default:
		l.f[y][x].penalty = penalties[type];
		break;
	}
}

unsigned char type2char(unsigned char t, unsigned char offset)
{
	switch(t)
	{
	case FT_GRASS:
		return 2 + offset;
	case FT_ROAD:
		return 1 + offset;
	case FT_HILL:
		return 3 + offset;
	case FT_MOUNTAIN:
		return 4 + offset;
	case FT_WATER:
		return 5 + offset;
	case FT_WALL:
		return 6 + offset;
	case FT_COW:
		return 7 + offset;
	case FT_START:
		return 22 + offset;
	case FT_END:
		return 21 + offset;
	default:
		return 0;
	}
}

void clearplayfield()
{
	unsigned int i = 0;
	for(; i < 24; ++i)
	{
		gotoxy(0, i);
		printf("                        ");
	}
}

void wait_for_fire()
{
	unsigned char keypress;
	POKE(CH, 0xFF);
	for(;;)
	{
		joy = joy_read(JOY_1);
		if(JOY_BTN_FIRE(joy))
		{
			break;
		}
		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			break;
		}
	}
	
	// Prevent accidental click
	synchro(15);
}

void printannouncement()
{
	char tmp[15];
	clearplayfield();
	gotoxy(5, 10);
	printf("Entering level");
	switch(game_mode)
	{
		case GM_RANDOM:
			if(current_level == RANDOM_LEVEL)
			{
				gotoxy(7, 12);
				printf("%c RANDOM %c", 23, 23);
			}
			else
			{
				sprintf(tmp, "- %s -", filename_buffer + 2);
				gotoxy(12 - (strlen(tmp) >> 1), 12);
				printf(tmp);
			}
			break;
		case GM_EPISODE:
			gotoxy(9, 12);
			printf("- %02d -", current_level + 1);
			break;
		case GM_EDITOR:
			break;
	}
	
	wait_for_fire();
	clearplayfield();
}

void printfield()
{
	unsigned char y, x;
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			// Draw the playfield itself
			gotoxy(x + drawing_offset_x, y + drawing_offset_y);
			if((x == sx) && (y == sy))
			{
				printf("%c", 22);
			}
			else if((x == ex) && (y == ey))
			{
				printf("%c", 21);
			}
			else
			{
				printf("%c", type2char(l.f[y][x].type_DEBUG, 0));
			}

			// Draw top and bottom borders
			if(0 == y)
			{
				gotoxy(x + drawing_offset_x, y - 1 + drawing_offset_y);
				printf("'");
				gotoxy(x + drawing_offset_x, y + drawing_offset_y + l.dim_y);
				printf("'");
			}
		}

		// Draw left and right borders
		gotoxy(drawing_offset_x - 1, y + drawing_offset_y);
		printf("^");
		gotoxy(drawing_offset_x + l.dim_x, y + drawing_offset_y);
		printf("^");
	}

	// Draw corners
	gotoxy(drawing_offset_x - 1, drawing_offset_y - 1);
	printf("%%");
	gotoxy(drawing_offset_x - 1, drawing_offset_y + l.dim_y);
	printf("$");
	gotoxy(drawing_offset_x + l.dim_x, drawing_offset_y - 1);
	printf(";");
	gotoxy(drawing_offset_x + l.dim_x, drawing_offset_y + l.dim_y);
	printf("\"");
}

void init_positions()
{
	unsigned char y, x;
	Field* fld;
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			fld = &l.f[y][x];
			fld->pred = 0;
			fld->visited = 0;
			fld->myx = x;
			fld->myy = y;
			if((x == sx) && (y == sy))
			{
				fld->stepped_on = 1;
			}
			else
			{
				fld->stepped_on = 0;
			}
			if((x == sx) && (y == sy))
			{
				fld->dist = 0;
			}
			else
			{
				fld->dist = 65535U;	// Max 16-bit for Atari
			}
		}
	}
}

void printstatus()
{
	gotoxy(28, 10);
	switch(current_status)
	{
	case STATUS_CALCULATING:
		printf("Thinking ");
		break;
	case STATUS_PLAYERLOST:
		printf("You lost!");
		break;
	case STATUS_PLAYERWIN:
		printf("Good job!");
		break;
	case STATUS_WAITFORPLAYER:
		printf("Your turn");
		break;
	case STATUS_CONFIRMATION:
		printf("You sure?");
		break;
	case STATUS_FUTILE:
		printf("Futile...");
		break;
	case STATUS_BEEN_HERE:
		printf("Been here");
		break;
	case STATUS_EDITING:
		printf("Construct");
		break;
	case STATUS_FIRE:
		printf("> FIRE! <");
		break;
	case STATUS_VALIDATING:
		printf("Verifying");
		break;
	default:
		printf("KURWA !!!");
	}
}

void printdivider()
{
	unsigned char i = 0;
	for(; i < 24; ++i)
	{
		gotoxy(24, i);
		printf("%c", '&');
	}
}

void printstatusborder()
{
	gotoxy(26, 9);
	printf("%%''''''''''';");
	gotoxy(26, 10);
	printf("^           ^");
	gotoxy(26, 11);
	printf("$'''''''''''\"");
}

void printlegend_common()
{
	unsigned char i = 0;
	gotoxy(26, 13);
	printf("%c Antoni", 22);
	gotoxy(26, 14);
	printf("%c Die Wurst", 21);
	if(DIFF_HARD == difficulty)
	{
		for(i = 5; i < 7; ++i)
		{
			gotoxy(26, 16 + i);
			printf("%c    BLOCK", type2char(i, 0));
		}
	}
}

void printlegend()
{
	unsigned char i = 0;
	switch(game_mode)
	{
		case GM_EPISODE:
		case GM_RANDOM:
			printlegend_common();
			switch(difficulty)
			{
				case DIFF_HARD:
					for(i = 0; i < 5; ++i)
					{
						gotoxy(26, 16 + i);
						printf("%c %4d /%4d", type2char(i, 0), penalties[i] * COST_DIRECT, penalties[i] * COST_SLANT);
					}
					break;
				case DIFF_EASY:
					gotoxy(26, 16);
					printf("%c Road   [%2u]", type2char(FT_ROAD, 0), penalties[0] * COST_DIRECT);
					gotoxy(26, 17);
					printf("%c Grass  [%2u]", type2char(FT_GRASS, 0), penalties[1] * COST_DIRECT);
					gotoxy(26, 18);
					printf("%c Hill   [%2u]", type2char(FT_HILL, 0), penalties[2] * COST_DIRECT);
					gotoxy(26, 19);
					printf("%c Mount  [%2u]", type2char(FT_MOUNTAIN, 0), penalties[3] * COST_DIRECT);
					gotoxy(26, 20);
					printf("%c Swamp  [%2u]", type2char(FT_WATER, 0), penalties[4] * COST_DIRECT);
					gotoxy(26, 21);
					printf("%c Wall[BLOCK]", type2char(FT_WALL, 0), penalties[4] * COST_DIRECT);
					gotoxy(26, 22);
					printf("%c Cow [BLOCK]", type2char(FT_COW, 0), penalties[4] * COST_DIRECT);
					break;
			}
			break;
		case GM_EDITOR:
			gotoxy(26, 15);
			printf("%c%c%c Load", ' '+128, 'L'+128, ' ' + 128);
			gotoxy(26, 16);
			printf("%c%c%c Save", ' '+128, 'S'+128, ' ' + 128);
			gotoxy(26, 17);
			printf("%c%c%c Resolve", ' '+128, 'R'+128, ' ' + 128);
			gotoxy(26, 18);
			printf("%c%c%c Difficulty", ' '+128, 'D'+128, ' ' + 128);
			break;
	}
}

void show_difficulty(unsigned char x, unsigned char y)
{
	gotoxy(x, y);
	printf("%s", difficulty == DIFF_EASY ? "EASY" : "HARD");
}

void print_editor_difficulty()
{
	show_difficulty(31, 2);
}

void printscoresborder()
{
	switch(game_mode)
	{
		case GM_EPISODE:
		case GM_RANDOM:
			gotoxy(26, 1);
			printf("@%c  %%''''''';", 13);
			gotoxy(26, 2);
			printf("%c%c  ^       ^", 14, 15);
			gotoxy(26, 3);
			printf("%c%c  $'''''''\"", 16, 17);
			break;
		case GM_EDITOR:
			gotoxy(28, 1);
			printf("%%'''''''';");
			gotoxy(28, 2);
			printf("^        ^");
			gotoxy(28, 3);
			printf("$''''''''\"");
			print_editor_difficulty();
			break;
	}

	gotoxy(26, 5);
	printf("%c%c  %%''''''';", 18, 19);
	gotoxy(26, 6);
	printf("%c%c  ^       ^", 20, 26);
	gotoxy(26, 7);
	printf("q_  $'''''''\"");
}

void printborder()
{
	printdivider();
	printscoresborder();
	printstatusborder();
	printlegend();
}

void inverse_at(unsigned char x, unsigned char y)
{
	POKE(screenmem + y * 40 + x, PEEK(screenmem + y * 40 + x) ^ 0x80);
}

void* find_minimum_dist()
{
	unsigned char xx = 0xFF, yy, x, y;
	unsigned int mindist = 65535U;
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			if((!l.f[y][x].visited) && (l.f[y][x].dist < mindist))
			{
				xx = x;
				yy = y;
				mindist = l.f[y][x].dist;
			}
		}
	}

	if(xx == 0xFF)
	{
		// No solution possible
		return 0;
	}
		
	// Uncomment to track the pathfinding process
	//  --------------
	//	gotoxy();
	//	printf("@");
	//  --------------
	
	return &l.f[yy][xx];
}

signed char newx, newy;

signed char off_y[] = {+0, +0, -1, +1, +1, +1, -1, -1};
signed char off_x[] = {+1, -1, +0, +0, +1, -1, +1, -1};
signed char	costs_easy[] = {
	COST_DIRECT, COST_DIRECT, COST_DIRECT, COST_DIRECT,
	COST_DIRECT, COST_DIRECT, COST_DIRECT, COST_DIRECT};
signed char	costs_hard[] = {
	COST_DIRECT, COST_DIRECT, COST_DIRECT, COST_DIRECT,
	COST_SLANT, COST_SLANT, COST_SLANT, COST_SLANT};
signed char* costs = costs_easy;

unsigned char in_range(signed char x, signed char y)
{
	return	
			((FT_WALL == l.f[y][x].type_DEBUG) || (FT_COW == l.f[y][x].type_DEBUG))
		?
			0
		:
			(x >= 0) && 
			(x < l.dim_x) && 
			(y >= 0) && 
			(y < l.dim_y);
}
	
unsigned char calculate()
{
	Field* fld = find_minimum_dist();
	unsigned char ind;
	
	if(!fld)
	{
		// No solution possible
		return 2;
	}
	
	for(
		ind = 0;
		ind < ((difficulty == DIFF_EASY) ? 4 : 8);
		++ind)
	{
		newx = fld->myx + off_x[ind];
		newy = fld->myy + off_y[ind];
		if(in_range(newx, newy))
		{
			Field* tmpfld = &l.f[newy][newx];
			if(0 == tmpfld->visited)
			{
				unsigned int newdist = fld->dist + costs[ind] * tmpfld->penalty;
				if(newdist < tmpfld->dist)
				{
					tmpfld->dist = newdist;
					tmpfld->pred = fld;
					if((newx == ex) && (newy == ey))
					{
						// At destination
						return 1;
					}
				}
			}
		}
	}

	fld->visited = 1;
	
	// Not at destination
	return 0;
}

void init_drawing_offset()
{
	drawing_offset_x = (24 - l.dim_x) >> 1;
	drawing_offset_y = (24 - l.dim_y) >> 1;
}

void fill_with_grass()
{
	unsigned char i, j;
	for(i = 0; i < l.dim_y; ++i)
	{
		for(j = 0; j < l.dim_x; ++j)
		{
			setftype(j, i, FT_GRASS, 1);
		}
	}
}

void setup_level()
{
	unsigned char i, j;
	switch(game_mode)
	{
		case GM_EPISODE:
			l.dim_x = e[current_level].size_x;
			l.dim_y = e[current_level].size_y;
			
			for(i = 0; i < l.dim_y; ++i)
			{
				for(j = 0; j < l.dim_x; ++j)
				{
					setftype(j, i, ((unsigned char *)(e[current_level].level_data))[i * l.dim_x + j], 1);
				}
			}
			break;
		case GM_RANDOM:
			l.dim_x = (my_random(10) + 2) << 1; // [6, 22]
			l.dim_y = (my_random(10) + 2) << 1; // [ditto]
			fill_with_grass();
			break;
		case GM_EDITOR:
			l.dim_x = custom_width;
			l.dim_y = custom_height;
			fill_with_grass();
			break;
	}
	
	init_drawing_offset();
}

void carve_AI()
{
	unsigned char i;
	for(i = 36; i < 36+4*3; ++i)
	{
		pmg_memory_P1[i] = 159;
	}	
}

void setup_pmg()
{
	unsigned char i;

	POKE(SDMCTL,
		DMACTL_ENABLE_PLAYER_DMA |
		DMACTL_ENABLE_MISSLE_DMA |
		DMACTL_NORMAL_PLAYFIELD |
		DMACTL_DMA_FETCH_INSTRUCTION); 
	POKE(PMBASE, pmg_memory / 256);

	memset(pmg_memory_ptr, 0, 1024);
	
	POKE(GRACTL, PMG_PLAYERS | PMG_MISSILES); 
	POKE(GPRIOR, 1); 

	// Set the sprite colors
	POKE(PCOLR0, COLOR_RIGHT);
	POKE(PCOLR1, COLOR_RIGHT);
	POKE(PCOLR2, COLOR_MIDDLE);
	POKE(PCOLR3, COLOR_CURSOR);
	POKE(SIZEP0, SIZEP_QUAD); 
	POKE(SIZEP1, SIZEP_QUAD); 
	POKE(SIZEP2, SIZEP_QUAD); 
	POKE(SIZEP3, SIZEP_SINGLE); 
	POKE(HPOSP0, SPRITE0_POS);
	POKE(HPOSP1, SPRITE1_POS);
	POKE(HPOSP2, SPRITE2_POS);

	for(i = 16; i < 112; ++i)
	{
		pmg_memory_P0[i] = 255;
		pmg_memory_P1[i] = 255;
		pmg_memory_P2[i] = 128;
	}

	if((GM_EPISODE == game_mode) || (GM_RANDOM == game_mode))
	{
		// Sprite "holes" for icons
		for(i = 20; i < 20+4*3; ++i)
		{
			pmg_memory_P1[i] = 159;
		}
		carve_AI();
		
		// Sprite "holes" for the legend
		for(i = 68; i < 76; ++i)
		{
			pmg_memory_P1[i] = 191;
		}	
		for(i = 80; i < 80+4*7; ++i)
		{
			pmg_memory_P1[i] = 191;
		}	
	}
	else
	{
		carve_AI();
	}
}

void setup_game_colors()
{
	_setcolor_low(2, COLOR_WHITE);
	_setcolor_low(1, COLOR_BLACK);
}

void setup_menu_colors()
{
	_setcolor_low(3, COLOR_LIGHTRED);
	_setcolor_low(2, COLOR_BLACK);
	_setcolor_low(1, COLOR_LIGHTGREEN);
	_setcolor_low(0, COLOR_VIOLET);
}

void paintcursor(unsigned int value)
{
	unsigned char i;
	unsigned char y = (cy + drawing_offset_y + 4) << 2;	
	POKE(HPOSP3, (cx + drawing_offset_x + 12) << 2);
	
	for(i = y; i < y + 4; ++i)
	{
		pmg_memory_P3[i] = value;
	}	
}

void clearcursor()
{
	paintcursor(0);
}

void drawcursor()
{
	paintcursor(240);
}

// Physical cursor movement
void execute_cursor_move_left()
{
	unsigned char i;
	for(i = 0; i < 4; ++i)
	{
		--csx;
		POKE(HPOSP3, csx);
		synchro(3);
	}	
}
void execute_cursor_move_right()
{
	unsigned char i;
	for(i = 0; i < 4; ++i)
	{
		++csx;
		POKE(HPOSP3, csx);
		synchro(3);
	}	
}
void execute_cursor_move_up()
{
	unsigned char i;
	for(i = 1; i < 5; ++i)
	{
		pmg_memory_P3[csy - i] = 240;
		pmg_memory_P3[csy - i + 4] = 0;
		synchro(3);
	}
	csy -= 4;
}
void execute_cursor_move_down()
{
	unsigned char i;
	for(i = 0; i < 4; ++i)
	{
		pmg_memory_P3[csy + i + 4] = 240;
		pmg_memory_P3[csy + i] = 0;
		synchro(3);
	}
	csy += 4;
}

// Editor cursor movement
void editor_move_cursor_left()
{
	if(cx > 0)
	{
		execute_cursor_move_left();
		--cx;
	}
}
void editor_move_cursor_up()
{
	if(cy > 0)
	{
		execute_cursor_move_up();
		--cy;
	}
}
void editor_move_cursor_down()
{
	if(cy < l.dim_y - 1)
	{
		execute_cursor_move_down();
		++cy;
	}
}
void editor_move_cursor_right()
{
	if(cx < l.dim_x - 1)
	{
		execute_cursor_move_right();
		++cx;
	}
}


// Game cursor movement
signed char move_cursor_left()
{
	if(cursor_x_relative > -1)
	{
		--cursor_x_relative;
		execute_cursor_move_left();
		return 1;
	}
	return 0;
}
signed char move_cursor_leftup()
{
	unsigned char i;
	if((cursor_x_relative > -1) && (cursor_y_relative > -1))
	{
		--cursor_x_relative;
		--cursor_y_relative;
		for(i = 1; i < 5; ++i)
		{
			--csx;
			POKE(HPOSP3, csx);
			pmg_memory_P3[csy - i] = 240;
			pmg_memory_P3[csy - i + 4] = 0;
			synchro(3);
		}	
		csy -= 4;
		return 1;
	}
	return 0;
}
signed char move_cursor_rightup()
{
	unsigned char i;
	if((cursor_x_relative < 1) && (cursor_y_relative > -1))
	{
		++cursor_x_relative;
		--cursor_y_relative;
		for(i = 1; i < 5; ++i)
		{
			++csx;
			POKE(HPOSP3, csx);
			pmg_memory_P3[csy - i] = 240;
			pmg_memory_P3[csy - i + 4] = 0;
			synchro(3);
		}	
		csy -= 4;
		return 1;
	}
	return 0;
}
signed char move_cursor_rightdown()
{
	unsigned char i;
	if((cursor_x_relative < 1) && (cursor_y_relative < 1))
	{
		++cursor_x_relative;
		++cursor_y_relative;
		for(i = 0; i < 4; ++i)
		{
			++csx;
			POKE(HPOSP3, csx);
			pmg_memory_P3[csy + i + 4] = 240;
			pmg_memory_P3[csy + i] = 0;
			synchro(3);
		}	
		csy += 4;
		return 1;
	}
	return 0;
}
signed char move_cursor_leftdown()
{
	unsigned char i;
	if((cursor_x_relative > -1) && (cursor_y_relative < 1))
	{
		--cursor_x_relative;
		++cursor_y_relative;
		for(i = 0; i < 4; ++i)
		{
			--csx;
			POKE(HPOSP3, csx);
			pmg_memory_P3[csy + i + 4] = 240;
			pmg_memory_P3[csy + i] = 0;
			synchro(3);
		}	
		csy += 4;
		return 1;
	}
	return 0;
}
signed char move_cursor_right()
{
	if(cursor_x_relative < 1)
	{
		++cursor_x_relative;
		execute_cursor_move_right();
		return 1;
	}
	return 0;
}
signed char move_cursor_up()
{
	if(cursor_y_relative > -1)
	{
		--cursor_y_relative;
		execute_cursor_move_up();
		return 1;
	}
	return 0;
}	
signed char move_cursor_down()
{
	if(cursor_y_relative < 1)
	{
		++cursor_y_relative;
		execute_cursor_move_down();
		return 1;
	}
	return 0;
}

void printscoretmp()
{
	gotoxy(32, 2);
	printf("%5u%c", scoretmp + scores[0], 63 + 128);
}

void printscores()
{
	unsigned int i = game_mode == GM_EDITOR ? 1 : 0;
	for(; i < 2; ++i)
	{
		gotoxy(32, 2 + (i << 2));
		printf("%5u ", scores[i]);
	}
}

void quick_print_status(unsigned char status_to_print)
{
	unsigned char tmp = current_status;
	current_status = status_to_print;
	printstatus();
	current_status = tmp;
}

void handle_cursor()
{
	joy = joy_read(JOY_1);
	if(joy)
	{
		if(JOY_BTN_LEFT(joy))
		{
			move_cursor_left();
		}
		if(JOY_BTN_RIGHT(joy))
		{
			move_cursor_right();
		}
		if(JOY_BTN_UP(joy))
		{
			move_cursor_up();
		}
		if(JOY_BTN_DOWN(joy))
		{
			move_cursor_down();
		}
		
		if((0 != cursor_x_relative) || (0 != cursor_y_relative))
		{
			if(in_range(cx + cursor_x_relative, cy + cursor_y_relative))
			{
				if(l.f[cy + cursor_y_relative][cx + cursor_x_relative].stepped_on)
				{
					quick_print_status(STATUS_BEEN_HERE);
				}
				else if((DIFF_EASY == difficulty) && (0 != cursor_x_relative) && (0 != cursor_y_relative))
				{
					// No going slant on EASY level
					quick_print_status(STATUS_FUTILE);
				}
				else
				{
					unsigned char distance_penalty = costs[0];
					if((0 != cursor_x_relative) && (0 != cursor_y_relative))
					{
						distance_penalty = costs[4];
					}
					scoretmp = l.f[cy + cursor_y_relative][cx + cursor_x_relative].penalty * distance_penalty;
					printscoretmp();
					quick_print_status(STATUS_CONFIRMATION);
				}
			}
			else
			{
				quick_print_status(STATUS_FUTILE);
			}
			
			synchro(3);
		}
		else
		{
			quick_print_status(STATUS_BEEN_HERE);
			printscores();
		}
	}
}

unsigned char do_move()
{
	unsigned char step_on = l.f[cy][cx].type_DEBUG;
	l.f[cy][cx].stepped_on = 1;
	gotoxy(cx + drawing_offset_x, cy + drawing_offset_y);
	printf("%c", type2char(step_on, 7));
	cursor_x_relative = cursor_y_relative = 0;
	scores[0] += scoretmp;
	scoretmp = 0;
	printstatus();
	printscores();
	return step_on == FT_END;
}

unsigned char handle_movement()
{
	unsigned char at_destination = 0;
	joy = joy_read(JOY_1);
	if(JOY_BTN_FIRE(joy) && act_on_fire)
	{
		if((difficulty == DIFF_EASY) && (cursor_x_relative != 0) && (cursor_y_relative != 0))
		{
		}
		else
		{
			cx += cursor_x_relative;
			cy += cursor_y_relative;
			if(in_range(cx, cy) && !l.f[cy][cx].stepped_on)
			{
				at_destination = do_move();
			}
			else
			{
				cx -= cursor_x_relative;
				cy -= cursor_y_relative;
			}
			act_on_fire = 0;
		}
	}
	if(!(JOY_BTN_FIRE(joy)))
	{
		act_on_fire = 1;
	}
	return at_destination;
}
	
signed char playerloop()
{
	unsigned char keypress;
	POKE(CH, 0xFF);
	for(;;)
	{
		POKE(ATRACT, 0);
		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			keypress = cgetc();
			if(keypress == 27)
			{
				// ESC
				return 1;
			}
		}
		POKE(CH, 0xFF);

		handle_cursor();
		if(handle_movement())
		{
			current_status = STATUS_CALCULATING;
			printstatus();
			break;
		}
	}
	return 0;
}

unsigned char px, py, tmpx, tmpy;
unsigned char xxx[22*22];
unsigned char yyy[22*22];
unsigned char index;
void present_calculated_path_advance()
{
	xxx[index] = px;
	yyy[index] = py;
	++index;
}

void present_calculated_path()
{
	index = 0;
	px = ex; py = ey;
	present_calculated_path_advance();
	while(l.f[py][px].pred)
	{
		Field* fld = ((Field*)l.f[py][px].pred);
		px = fld->myx;
		py = fld->myy;
		present_calculated_path_advance();
	}
	printf("\n");
	while(index)
	{
		unsigned char distance_penalty = costs[0];
		px = xxx[index];
		py = yyy[index];
		index--;
		if((px != xxx[index]) && (py != yyy[index]))
		{
			distance_penalty = costs[4];
		}
		gotoxy(xxx[index] + drawing_offset_x, yyy[index] + drawing_offset_y);
		printf("%c", type2char(l.f[yyy[index]][xxx[index]].type_DEBUG, 128));
		scores[1] += l.f[yyy[index]][xxx[index]].penalty * distance_penalty;
		printscores();
		synchro(40);
	}
}

void enable_message_box()
{
	// Carve the sprites
	signed char i, j, k;
	for(i = 56; i < 56+4*3; ++i)
	{
		pmg_memory_P0[i] = 1;
		pmg_memory_P1[i] = 0;
		pmg_memory_P2[i] = 0;
	}	
	
	// Hide cursor if under the messagebox
	if((cy + drawing_offset_y > 9) && (cy + drawing_offset_y < 13))
	{
		POKE(HPOSP3, 0);
	}
	
	// Remember the screen below the messagebox
	k = 0;
	for(i = 10; i < 13; ++i)
	{
		for(j = 1; j < 39; ++j)
		{
			below_messagebox[k++] = PEEK(screenmem + i * 40 + j);
		}
	}
	
	// Draw border
	gotoxy(1, 10);
	printf("%s", "%'''''''''''''''''''''''''''''''''''';");
	gotoxy(1, 11);
	printf("%s", "^                                    ^");
	gotoxy(1, 12);
	printf("%s", "$''''''''''''''''''''''''''''''''''''\"");
}

void disable_message_box()
{
	// Restore the screen below messagebox
	signed char i, j, k;
	k = 0;
	for(i = 10; i < 13; ++i)
	{
		for(j = 1; j < 39; ++j)
		{
			POKE(screenmem + i * 40 + j, below_messagebox[k++]);
		}
	}
	
	// Restore cursor position
	POKE(HPOSP3, (cx + drawing_offset_x + 12) << 2);
	
	// Restore the sprite carving
	for(i = 56; i < 56+4*3; ++i)
	{
		pmg_memory_P0[i] = 255;
		pmg_memory_P1[i] = 255;
		pmg_memory_P2[i] = 128;
	}	
}

void show_messagebox(const char* message, signed char keep_visible)
{
	enable_message_box();
	gotoxy(3, 11);
	printf("%s\n", message);
	if(!keep_visible)
	{
		wait_for_fire();
		disable_message_box();
	}
}

signed char ai_loop()
{
	unsigned char keypress;
	unsigned char calculation_result; 
	for(;;)
	{
		POKE(ATRACT, 0);
		calculation_result = calculate();
		if(1 == calculation_result)
		{
			// Destination found
			break;
		}
		else if(2 == calculation_result)
		{
			// Unreachable destination
			show_messagebox("Map has no solution", 0);
			return 1;
		}
		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			keypress = cgetc();
			if(keypress == 27)
			{
				// ESC
				return 1;
			}
		}
		POKE(CH, 0xFF);
	}
	present_calculated_path();
	current_status = GM_EPISODE == game_mode || GM_RANDOM == game_mode
		? scores[0] > scores[1] ? STATUS_PLAYERLOST : STATUS_PLAYERWIN
		: STATUS_FIRE;
	if(STATUS_PLAYERWIN == current_status)
	{
		++total_score;
	}
			
	printstatus();
	return 0;
}

void prepare_next_level()
{
	++current_level;
	current_status = STATUS_WAITFORPLAYER;
	clearcursor();
	scores[0] = scores[1] = 0;
}

signed char manhattan_distance(signed char x1, signed char y1, signed char x2, signed char y2)
{
	signed char tmp;
	if(x1 > x2)
	{
		tmp = x1;
		x1 = x2;
		x2 = tmp;
	}
	if(y1 > y2)
	{
		tmp = y1;
		y1 = y2;
		y2 = tmp;
	}
	return (y2 - y1) + (x2 - x1);
}

void generate_starting_points()
{
	for(;;)
	{
		sx = my_random(l.dim_x);
		sy = my_random(l.dim_y);

		// Try to find the other end far enough
		// but only 255 times (to prevent deadlock)
		for(index = 0; index < 255; ++index)
		{
			ex = my_random(l.dim_x);
			ey = my_random(l.dim_y);
			if(manhattan_distance(sx, sy, ex, ey) > MAP_SIZE_AVERAGE)
			{
				break;
			}
		}
		if((sx != ex) && (sy != ey))
		{
			// Prevent starting position being
			// equal to finish position.
			// Quite rare, but still probable on very
			// small maps.
			break;
		}
	}
	
	setftype(sx, sy, FT_START, 1);
	setftype(ex, ey, FT_END, 1);
}

void place_object(signed char x, signed char y, signed char templ, Object_template* obj)
{
	signed char yy, xx;
	for(yy = 0; yy < obj[templ].sy; ++yy)
	{
		for(xx = 0; xx < obj[templ].sx; ++xx)
		{
			if(
				(obj[templ].templ[yy * obj[templ].sx + xx] != 100) &&
				(l.f[yy + y][xx + x] != FT_START) &&
				(l.f[yy + y][xx + x] != FT_END) &&
				(xx + x < l.dim_x) &&
				(yy + y < l.dim_y))
			{
				setftype(xx + x, yy + y, obj[templ].templ[yy * obj[templ].sx + xx], 1);
			}
		}
	}
}

void generate_objects(signed char num, Object_template* objs)
{
	signed char i;
	for(i = 0; i < num; ++i)
	{
		place_object(
			my_random(l.dim_x),
			my_random(l.dim_y),
			my_random(LAKE_TEMPLATE_COUNT), objs);
	}
}

void generate_road()
{
	signed char i;
	signed char rx, ry;
	unsigned char dir;
	signed char length;

	rx = my_random(l.dim_x);
	ry = my_random(l.dim_y);
	dir = my_random(4);
	length = my_random(MAP_SIZE_AVERAGE);

	for(;;)
	{
		for(i = 0; i < length; ++i)
		{
			if((l.f[ry][rx] != FT_END) && (l.f[ry][rx] != FT_START))
			{
				setftype(rx, ry, FT_ROAD, 1);
			}
			switch(dir)
			{
			case 0:
				++rx;
				break;
			case 1:
				--rx;
				break;
			case 2:
				++ry;
				break;
			case 3:
				--ry;
				break;
			}
			if((rx < 0) || (rx >= l.dim_x) || (ry < 0) || (ry >= l.dim_y))
			{
				return;
			}
		}
		dir = my_random(4);
		length = my_random(MAP_SIZE_AVERAGE);
	}
}

void generate_cows()
{
	// There should be no more cows that the lesser dimension
	// of the map minus 1. For example for 16x10 map the maximum
	// number of cows is 9 so they never block the path entirely.
	// I am too lazy to allow more cows 
	// and add additional validation for this :)
	//
	// Well, still it could happen that cows completely
	// surrounds either DOG or WURST, but... fuck that :)
	signed char minsize = (l.dim_x < l.dim_y ? l.dim_x : l.dim_y) - 1;
	signed char i = 0;
	signed char cowx, cowy;
	for(; i < minsize; ++i)
	{
		cowx = my_random(l.dim_x);
		cowy = my_random(l.dim_y);
		if((l.f[cowy][cowx] != FT_END) && (l.f[cowy][cowx] != FT_START))
		{
			setftype(cowx, cowy, FT_COW, 1);
		}
	}
}

void generate_random_level()
{
	signed char i = 0;
	setup_level();
	generate_starting_points();
	for(; i < 4; ++i)
	{
		generate_road();
	}
	generate_objects(MAP_SIZE_AVERAGE, lakes);
	generate_objects(MAP_SIZE_AVERAGE >> 1, mountains);
	generate_cows();
	
	// Some more roads on top of everything
	generate_road();
	if(MAP_SIZE_AVERAGE > 10)
	{
		generate_road();
	}
}

signed char t0 = 0, t1, t2;

void setup_menu_displaylist()
{
	unsigned int DL = PEEK(560) + 256 * PEEK(561);
	t0 = PEEK(DL + 8); POKE(DL + 8, 7);
	t1 = PEEK(DL + 9); POKE(DL + 9, 7);
	t2 = PEEK(DL + 27); POKE(DL + 27, 65);
}

void restore_displaylist()
{
	// Needed because it seems that _graphics(0)
	// is not always restoring it properly
	unsigned int DL = PEEK(560) + 256 * PEEK(561);
	if(t0)
	{
		POKE(DL + 8, t0);
		POKE(DL + 9, t1);
		POKE(DL + 27, t2);
	}
}

void prepare_graphics_mode()
{
	restore_displaylist();
	printf("%c", 125);
	POKE(CHBAS,((unsigned int) &font_base)/256);
}

void game_editor_common_setup()
{
	prepare_graphics_mode();
	setup_pmg();
	setup_game_colors();
	screenmem = (PEEK(89) << 8) + PEEK(88);
//	gotoxy(0, 0); printf("%u", screenmem);
//	for(;;);
}

void calculate_cursor_sprite_position()
{
	csx = (cx + drawing_offset_x + 12) << 2;
	csy = (cy + drawing_offset_y + 4) << 2;
}

// Will also set the start and end point locations
signed char validate_field()
{
	unsigned char y, x;
	unsigned char cstart = 0, cend = 0;
	unsigned char tmp_status = current_status;
	current_status = STATUS_VALIDATING;
	printstatus();
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			if(FT_START == l.f[y][x].type_DEBUG)
			{
				sx = x; sy = y;
				++cstart;
			}
			else if(FT_END == l.f[y][x].type_DEBUG)
			{
				ex = x; ey = y;
				++cend;
			}
		}
	}	
	current_status = tmp_status;
	printstatus();
	return (1 == cstart) && (1 == cend);
}

signed char savemap()
{
	signed int tmp;
	unsigned char y, x;
	handle = fopen(filename_buffer, "wb");
	if(NULL == handle)
	{
		return errno;
	}
	
	// Write dimensions
	tmp = fwrite(&custom_width, sizeof(signed char), 1, handle);
	if(1 != tmp)
	{
		fclose(handle);
		return errno;
	}
	tmp = fwrite(&custom_height, sizeof(signed char), 1, handle);
	if(1 != tmp)
	{
		fclose(handle);
		return errno;
	}

	// Write array
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			tmp = fwrite(&l.f[y][x].type_DEBUG, sizeof(unsigned char), 1, handle);
			if(1 != tmp)
			{
				fclose(handle);
				return errno;
			}
		}
	}	
	
	fclose(handle);
	
	return 0;
}

signed char loadmap(signed char setcursor)
{
	signed int tmp;
	unsigned char tmp1;
	unsigned char y, x;
	sx = ex = sy = ey = 0xFF;
	handle = fopen(filename_buffer, "rb");
	if(NULL == handle)
	{
		return errno;
	}
	
	// Read dimensions
	tmp = fread(&custom_width, sizeof(signed char), 1, handle);
	if(1 != tmp)
	{
		fclose(handle);
		return errno;
	}
	tmp = fread(&custom_height, sizeof(signed char), 1, handle);
	if(1 != tmp)
	{
		fclose(handle);
		return errno;
	}
	
	l.dim_x = custom_width;
	l.dim_y = custom_height;

	// Read array
	for(y = 0; y < l.dim_y; ++y)
	{
		for(x = 0; x < l.dim_x; ++x)
		{
			tmp = fread(&tmp1, sizeof(unsigned char), 1, handle);
			if(1 != tmp)
			{
				fclose(handle);
				return errno;
			}
			setftype(x, y, tmp1, setcursor);
		}
	}	
	
	fclose(handle);
	
	return 0;
}

void paintcaret()
{
	gotoxy(caret, 11);
	printf("%c\n", '#');
}

unsigned char validate_keypress(unsigned char keypress)
{
	if((keypress >= 97) && (keypress <= 122))
	{
		// Translate Lowercase to Uppercase
		return keypress - 32;
	}
	if(((keypress >= 65) && (keypress <= 90))
		|| (keypress == 126)
		|| (keypress == 155)
		|| (keypress == 27))
	{
		// For ESC, Backspace, Return and Uppercase simply do nothing
		return keypress;
	}
	
	// Key not accepted
	return 255;
}

signed char readfilename()
{
	unsigned char keypress, i, finished = 0;
	caret = 14;
	enable_message_box();
	gotoxy(3, 11);
	printf("File name: ");
	
	POKE(CH, 0xFF);
	while(!finished)
	{
		paintcaret();

		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			keypress = validate_keypress(cgetc());
			if(keypress != 255)
			{
				switch(keypress)
				{
				case 27: // ESC
					disable_message_box();
					return 0;
				case 126: // Backspace
					if(caret > 14)
					{
						gotoxy(caret, 11);
						printf("%c", ' ');
						--caret;
						paintcaret();
					}
					break;
				case 155: // Return
					if(caret > 14)
					{
						i = 2;
						for(;;)
						{
							keypress = PEEK(screenmem + 11 * 40 + 14 + (i - 2));
							if(3 == keypress)
							{
								filename_buffer[i++] = '.';
								filename_buffer[i++] = 'D';
								filename_buffer[i++] = 'O';
								filename_buffer[i++] = 'G';
								filename_buffer[i] = '\0';
								finished = 1;
								break;
							}
							filename_buffer[i++] = keypress + 32;
						}
					}
					break;
				default:
					if(caret < 22)
					{
						gotoxy(caret, 11);
						printf("%c\n", keypress);
						++caret;
						paintcaret();
					}
					break;
				}
			}
		}
	}
	
	disable_message_box();
	return 1;
}

void perform_game_save()
{
	signed char current_tmp;
	if(readfilename())
	{
		current_tmp = savemap();
		if(0 == current_tmp)
		{
			show_messagebox("Map saved correctly", 0);
		}
		else
		{
			show_messagebox(strerror(current_tmp), 0);
		}
	}
}

void perform_game_load()
{
	signed char current_tmp;
	if(readfilename())
	{
		current_tmp = loadmap(0);
		if(0 == current_tmp)
		{
			show_messagebox("Map loaded correctly", 0);
			clearcursor();
			init_drawing_offset();
			cx = cy = 1;
			calculate_cursor_sprite_position();
			init_positions();
			clearplayfield();
			printfield();
			drawcursor();
			init_drawing_offset();
		}
		else
		{
			show_messagebox(strerror(current_tmp), 0);
		}
	}
}

void perform_resolve()
{
	scores[1] = 0;
	printscores();
	if(validate_field())
	{
		init_positions();
		current_status = STATUS_CALCULATING;
		printstatus();
		if(1 == ai_loop())
		{
			current_status = STATUS_EDITING;
			printstatus();
			return;
		}
		wait_for_fire();
		printfield();
		current_status = STATUS_EDITING;
		printstatus();
	}
	else
	{
		show_messagebox("One dog and one wurst needed", 0);
	}
	return;
}

void switch_difficulty()
{
	difficulty ^= 1;
	costs = (difficulty == DIFF_EASY) ? costs_easy : costs_hard;
}

signed char editor_loop()
{
	signed char current_tmp;
	unsigned char keypress;
	for(;;)
	{
		POKE(ATRACT, 0);
		joy = joy_read(JOY_1);

		if(JOY_BTN_LEFT(joy))
		{
			editor_move_cursor_left();
			synchro(7);
		}
		if(JOY_BTN_RIGHT(joy))
		{
			editor_move_cursor_right();
			synchro(7);
		}
		if(JOY_BTN_UP(joy))
		{
			editor_move_cursor_up();
			synchro(7);
		}
		if(JOY_BTN_DOWN(joy))
		{
			editor_move_cursor_down();
			synchro(7);
		}
		
		if(JOY_BTN_FIRE(joy))
		{
			current_tmp = l.f[cy][cx].type_DEBUG;
			if(current_tmp == current_char)
			{
				++current_char;
				if(7 == current_char)
				{
					// For some reason there is no type 7. Why did I do that?
					++current_char;
				}
				if(FT__LAST__ == current_char)
				{
					current_char = FT_ROAD;
				}
			}
			setftype(cx, cy, current_char, 1);
			gotoxy(cx + drawing_offset_x, cy + drawing_offset_y);
			printf("%c", type2char(current_char, 0));
			synchro(20);
		}

		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
//			gotoxy(0, 0); printf("%3u", keypress);
			switch(keypress)
			{
			case 40:	// 'r' - resolve
			case 104:	// 'R'
				perform_resolve();
				break;
			case 62:	// 's' - save
			case 126:	// 'S'
				perform_game_save();
				break;
			case 0:		// 'l' - load
			case 64:	// 'L'
				perform_game_load();
				break;
			case 58:	// 'd' - difficulty
			case 122:	// 'D'
				switch_difficulty();
				print_editor_difficulty();
				break;
			case 28:	// 'ESC'
				return -1;
			}
			POKE(CH, 0xFF);
		}
	}
	
	return 0;
}

void enable_editing_status()
{
	current_status = STATUS_EDITING;
	printstatus();
}

void do_editor()
{
	POKE(CH, 0xFF);
	setup_level();
	game_editor_common_setup();
	sx = sy = ex = ey = 100;		// Start and end not defined
	printdivider();
	printstatusborder();
	printscoresborder();
	printlegend();
	enable_editing_status();

	// Prepare cursor in default position
	cx = cy = 1;
	calculate_cursor_sprite_position();
	drawcursor();
	printfield();
	editor_loop();
}

void final()
{
	char buffer[36];
	sprintf(buffer, "GAME OVER! Your final score: %2u/10", total_score);
	show_messagebox(buffer, 0);
}

void do_game(signed char basic_validation)
{
	int approaching_next_level;
	
	total_score = 0;
	scores[0] = scores[1] = 0;
	cursor_x_relative = 0;
	cursor_y_relative = 0;
	
	game_editor_common_setup();
	
	for(;;)
	{
		approaching_next_level = 0;
		if(GM_EPISODE == game_mode)
		{
			setup_level();
		}
		init_positions();

		printborder();
		printannouncement();
		printscores();
		printstatus();
		printfield();
		
		calculate_cursor_sprite_position();
		drawcursor();
		
		if((basic_validation) && (!validate_field()))
		{
			
			show_messagebox("One dog and one wurst needed", 0);
			return;
		}

		while(!approaching_next_level)
		{
			switch(current_status)
			{
			case STATUS_CALCULATING:
				if(1 == ai_loop())
				{
					return;
				}
				break;
			case STATUS_PLAYERLOST:
			case STATUS_PLAYERWIN:
				if(GM_RANDOM == game_mode)
				{
					wait_for_fire();
					return;
				}
				else
				{
					if((NUM_LEVELS - 1) == current_level)
					{
						final();
						return;
					}
					else
					{
						prepare_next_level();
						approaching_next_level = 1;
					}
				}
				wait_for_fire();
				break;
			case STATUS_WAITFORPLAYER:
				if(playerloop())
				{
					return;
				}
				break;
			}
		}
	}
	wait_for_fire();
}

void hide_sprites()
{
	POKE(HPOSP0, 0);
	POKE(HPOSP1, 0);
	POKE(HPOSP2, 0);
	POKE(HPOSP3, 0);
	POKE(HPOSM0, 0);
	POKE(HPOSM1, 0);
	POKE(HPOSM2, 0);
	POKE(HPOSM3, 0);
}

signed char do_menu(signed char redraw)
{
	unsigned char keypress;

	// Prevent cursor from reappearing when
	// messageboxes are used in menu
	cx = 244;
	drawing_offset_x = 0;
	
	if(redraw)
	{
		prepare_graphics_mode();
		setup_menu_displaylist();
		setup_menu_colors();
		screenmem = (PEEK(89) << 8) + PEEK(88);
		hide_sprites();
		gotoxy(4, 3);	printf("biedny  pies");
		gotoxy(26, 3);	printf("%c%c%c%c%c%c 3", 'a'+128, 'n'+128, 't'+128, 'o'+128, 'n'+128, 'i'+128);
		gotoxy(9, 8);	printf("%s\n", "1]            New game");
		gotoxy(9, 9);	printf("%s\n", "2]          Random map");
		gotoxy(9, 10);	printf("%s\n", "3]          Custom map");
		gotoxy(9, 11);	printf("%s\n", "4]        Level editor");
		gotoxy(9, 12);	printf("%s\n", "5]    Difficulty: ");
		gotoxy(9, 13);	printf("%s\n", "6]        Instructions");
		gotoxy(9, 14);	printf("%s\n", "ESC]     Return to DOS");
		gotoxy(7, 18);	printf("mgr in%c. Rafa(  -  03.2015", 96);
		show_difficulty(27, 12);
		gotoxy(35, 20); printf("v1.0");
		for(keypress = 6; keypress < 34; ++keypress)
		{
			inverse_at(keypress, 18);
			synchro(1);
		}
	}

	POKE(CH, 0xFF);
	for(;;)
	{
		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			keypress = cgetc();
		}
		if((keypress >= '1') && (keypress <= '6'))
		{
			return keypress - '0';
		}
		if(keypress == 27)
		{
			return 7;
		}
	}
}

void reset_game()
{
	current_status = STATUS_WAITFORPLAYER;
	scores[0] = scores[1] = 0;
}

void inverse_custom_width()
{
	inverse_at(custom_width + 11, 10);
}

void inverse_custom_height()
{
	inverse_at(custom_height + 11, 13);
}

void inverse_width_label()
{
	signed char i;
	for(i = 5; i < 13; ++i)
	{
		inverse_at(i, 11);
	}
}

void inverse_height_label()
{
	signed char i;
	for(i = 5; i < 14; ++i)
	{
		inverse_at(i, 14);
	}
}

void inverse_labels()
{
	inverse_height_label();
	inverse_width_label();
}

void decrease_custom_size(signed char* value)
{
	if(*value > 4)
	{
		*value -= 2;
	}
}

void increase_custom_size(signed char* value)
{
	if(*value < 22)
	{
		*value += 2;
	}
}

unsigned int get_parameters()
{
	signed char i;
	unsigned char edited_row = 0;
	unsigned char keypress;
	
	prepare_graphics_mode();	
	setup_menu_colors();
	gotoxy(10, 6);
	printf("Set width and height");
	gotoxy(13, 7);
	printf("and press FIRE");
	
	// Little numbers
	for(i = 0; i < 2; ++i)
	{
		POKE(screenmem + 40 * 10 + 15 + i * 40 * 3, 91);
		POKE(screenmem + 40 * 10 + 17 + i * 40 * 3, 92);
		POKE(screenmem + 40 * 10 + 19 + i * 40 * 3, 93);
		POKE(screenmem + 40 * 10 + 21 + i * 40 * 3, 94);
		POKE(screenmem + 40 * 10 + 23 + i * 40 * 3, 95);
		POKE(screenmem + 40 * 10 + 25 + i * 40 * 3, 123);
		POKE(screenmem + 40 * 10 + 27 + i * 40 * 3, 29);
		POKE(screenmem + 40 * 10 + 29 + i * 40 * 3, 125);
		POKE(screenmem + 40 * 10 + 31 + i * 40 * 3, 126);
		POKE(screenmem + 40 * 10 + 33 + i * 40 * 3, 127);
	}
	
	gotoxy(7, 11);
	printf("Width:  ,+-+-+*+-+-+*+-+-+\\");
	gotoxy(6, 14);
	printf("Height:  ,+-+-+*+-+-+*+-+-+\\");
	
	inverse_custom_width();
	inverse_custom_height();
	inverse_width_label();
	
	POKE(CH, 0xFF);
	for(;;)
	{
		keypress = PEEK(CH);
		if(keypress != 0xFF)
		{
			keypress = cgetc();
			if(keypress == 27)
			{
				// ESC
				return 1;
			}
		}
		POKE(CH, 0xFF);

		joy = joy_read(JOY_1);
		if(JOY_BTN_FIRE(joy))
		{
			return 0;
		}
		else if(JOY_BTN_UP(joy) || JOY_BTN_DOWN(joy))
		{
			++edited_row;
			inverse_labels();
			synchro(25);
		}
		else
		{
			if(JOY_BTN_LEFT(joy))
			{
				if(edited_row & 1)
				{
					inverse_custom_height();
					decrease_custom_size(&custom_height);
					inverse_custom_height();
					
				}
				else
				{
					inverse_custom_width();
					decrease_custom_size(&custom_width);
					inverse_custom_width();
				}
			}
			else if(JOY_BTN_RIGHT(joy))
			{
				if(edited_row & 1)
				{
					inverse_custom_height();
					increase_custom_size(&custom_height);
					inverse_custom_height();
					
				}
				else
				{
					inverse_custom_width();
					increase_custom_size(&custom_width);
					inverse_custom_width();
				}
			}
			synchro(7);
		}
	}
}

void print_instruction_line(signed char y, const char* text)
{
	gotoxy(1, y);
	for(y = 0; y < strlen(text); ++y)
	{
		printf("%c", text[y]);
	}
}

void print_instruction_border()
{
	unsigned char y, x;
	for(y = 0; y < 22; ++y)
	{
		for(x = 1; x < 38; ++x)
		{
			// Draw top and bottom borders
			if(0 == y)
			{
				gotoxy(x, y);
				printf("%c", 18);
				gotoxy(x, y + 22);
				printf("%c", 18);
			}
		}

		// Draw left and right borders
		if(y > 0)
		{
			gotoxy(0, y);
			printf("|");
			gotoxy(38, y);
			printf("|");
		}
	}

	// Draw corners
	gotoxy(0, 0);
	printf("%c", 17);
	gotoxy(0, 22);
	printf("%c", 26);
	gotoxy(38, 0);
	printf("%c", 5);
	gotoxy(38, 22);
	printf("%c", 3);	
}

void show_instructions()
{
	prepare_graphics_mode();
	POKE(CHBAS, 224);
	print_instruction_border();
	//setup_menu_colors();
	
	print_instruction_line(1, "  Once again you have a great pleasu-");
	print_instruction_line(2, "re to  join the  Poor Dog Anthony du-");
	print_instruction_line(3, "ring his journey through the wild.");
	print_instruction_line(4, "  Your task is to  reach Die Wurst as");
	print_instruction_line(5, "quickly as possible. Stepping on dif-");
	print_instruction_line(6, "ferent terrain  costs you a different");
	print_instruction_line(7, "amount  of  energy.  Roads  are  most");
	print_instruction_line(8, "friendly and  swamps are most exhaus-");
	print_instruction_line(9, "tive.");
	print_instruction_line(10, "  Select your next  step using a joy-");
	print_instruction_line(11, "stick,  verify  the  points  you will");
	print_instruction_line(12, "get and  press -FIRE- to confirm  the");
	print_instruction_line(13, "step.");
	print_instruction_line(14, "  Remember - in  this  game  less  is");
	print_instruction_line(15, "better,  so try to go to  destination");
	print_instruction_line(16, "collecting as less points  as possib-");
	print_instruction_line(17, "le.");
	print_instruction_line(18, "                                     ");
	print_instruction_line(19, "                                     ");
	print_instruction_line(20, "                                     ");
	print_instruction_line(21, "             Press -FIRE- to continue");
	wait_for_fire();

	print_instruction_line(1, "  On EASY difficulty you are only al-");
	print_instruction_line(2, "lowed to  move vertically or horizon-");
	print_instruction_line(3, "tally.                               ");
	print_instruction_line(4, "  On HARD difficulty  you are able to");
	print_instruction_line(5, "move  diagonally,  but  bear  in mind");
	print_instruction_line(6, "that it costs  you sqrt(2) times more");
	print_instruction_line(7, "energy.                              ");
	print_instruction_line(8, "                                     ");
	print_instruction_line(9, "  Greetings to all Atari folks around");
	print_instruction_line(10, "the world!                           ");
	print_instruction_line(11, "                                     ");
	print_instruction_line(12, "                                     ");
	print_instruction_line(13, "    Good lick!                       ");
	print_instruction_line(14, "                                     ");
	print_instruction_line(15, "                                     ");
	print_instruction_line(16, "                                     ");
	print_instruction_line(17, "                                     ");
	print_instruction_line(18, "                                     ");
	print_instruction_line(19, "                                     ");
	print_instruction_line(20, "                                     ");
	print_instruction_line(21, "             Press -FIRE- to continue");
	wait_for_fire();
	
	POKE(CHBAS,((unsigned int) &font_base)/256);
}

unsigned int main()
{
	signed char tmp, redraw = 1;
	_graphics(0);		
	hide_sprites();
	joy_install(&joy_driver);
	
	for(;;)
	{
		switch(do_menu(redraw))
		{
		case 1:
			game_mode = GM_EPISODE;
			current_status = STATUS_WAITFORPLAYER;
			current_level = 0; // [0..19]	- classic episode
			do_game(0);
			redraw = 1;
			break;
		case 2:
			game_mode = GM_RANDOM;
			current_level = RANDOM_LEVEL;
			current_status = STATUS_WAITFORPLAYER;
			show_messagebox("Generating random level...", 1);
			generate_random_level();
			do_game(0);
			reset_game();
			redraw = 1;
			break;
		case 3:
			if(readfilename())
			{
				tmp = loadmap(1);
				if(0 == tmp)
				{
					show_messagebox("Map loaded correctly", 0);
				}
				else
				{
					show_messagebox(strerror(tmp), 0);
					redraw = 0;
					break;
				}
				init_drawing_offset();
				game_mode = GM_RANDOM;
				current_status = STATUS_WAITFORPLAYER;
				current_level = CUSTOM_LEVEL;
				do_game(1);
				redraw = 1;
				reset_game();
			}
			else
			{
				redraw = 0;
			}
			break;
		case 4:
			game_mode = GM_EDITOR;
			if(0 == get_parameters())
			{
				do_editor();
			}
			redraw = 1;
			break;
		case 5:
			switch_difficulty();
			redraw = 0;
			show_difficulty(27, 12);
			break;
		case 6:
			show_instructions();
			redraw = 1;
			break;
		case 7:
			// Going to DOS
			restore_displaylist();
			POKE(CHBAS, 224);
			return 0;
		}
	}
	
	joy_uninstall();
	return 0;
}
