/* $Filename:    WormWars/Source/system.c $
 * $VER:         Worm Wars 5.2 $
 * $Description: API calls for Amiga $
 *
 * © Copyright 1999 James R. Jacobs. Freely distributable.
 *        _
 *       //		-=AMIGA=-
 *      //
 * _   //
 * \\ //
 *  \X/
 *
 * Release version.

#INCLUDES -------------------------------------------------------------- */

#include "system.h"				// #includes everything else

// GLOBAL VARIABLES (owned by engine.c, imported by amiga.lib) ------------

AGLOBAL	struct Library*			TimerBase						= NULL;

// EXTERNAL VARIABLES (owned by engine.c, imported by system.c) -----------

IMPORT	struct Library*			SysBase;

IMPORT	ABOOL					clearthem, modified;
IMPORT	SBYTE					a, board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
								brush, eachworm[4][2][9],
								field[FIELDX + 1][FIELDY + 1],
								level, levels, players,
								startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
IMPORT	STRPTR					pathname;
IMPORT	SWORD					secondsleft, secondsperlevel;
IMPORT	ULONG					delay, r;
IMPORT	struct HiScoreStruct	hiscore[HISCORES + 1];
IMPORT	struct TeleportStruct	teleport[MAXLEVELS + 1][4];
IMPORT	struct WormStruct		worm[4];
IMPORT	TEXT					date[DATELENGTH + 1],
								times[TIMELENGTH + 1];

// MODULE VARIABLES (used only within system.c) ---------------------------

MODULE	struct ASLBase*			ASLBase							= NULL;
MODULE	struct DiskFontBase*	DiskFontBase					= NULL;
MODULE	struct GadToolsBase*	GadToolsBase					= NULL;
MODULE	struct GfxBase*			GfxBase							= NULL;
MODULE	struct IconBase*		IconBase						= NULL;
MODULE	struct IntuitionBase*	IntuitionBase					= NULL;
MODULE	struct MEDPlayerBase*	MEDPlayerBase					= NULL;
MODULE	struct UtilityBase*		UtilityBase						= NULL;

MODULE	struct RDArgs*			ArgsPtr							= NULL;
MODULE	struct FileRequester*	ASLRqPtr						= NULL;
MODULE	ABOOL					AudioClosed						= TRUE;
MODULE	struct MsgPort*			AudioPortPtr[4]					= {NULL, NULL, NULL, NULL};
MODULE	struct IOAudio*			AudioRqPtr[4]					= {NULL, NULL, NULL, NULL};
MODULE	struct Gadget*			CheckboxGadgetPtr				= NULL;
MODULE	SBYTE					Controller						= GPCT_NOCONTROLLER;
MODULE	struct timeval*			CurrentValPtr					= NULL;
MODULE	struct Gadget*			CycleGadgetPtr[4]				= {NULL, NULL, NULL, NULL};
MODULE	ABOOL					eversent[4];
MODULE	UBYTE*					fbase							= NULL;
MODULE	ULONG					fsize;
MODULE	BPTR					FilePtr							= NULL;
MODULE	struct TextFont*		FontPtr							= NULL;
MODULE	SBYTE					fxable = 2, musicable = 2;
MODULE	struct InputEvent		GameEvent;
MODULE	struct Gadget*			GListPtr						= NULL;
MODULE	ABOOL				iconfailed			= FALSE;
MODULE	ABOOL					ignore							= FALSE;
MODULE	SBYTE					InputClosed						= TRUE;
MODULE	struct MsgPort*  		InputPortPtr					= NULL;
MODULE	struct IOStdReq*		InputRqPtr						= NULL;
MODULE	ABOOL					iso								= TRUE;
MODULE	ABOOL					joy								= FALSE;
MODULE	SBYTE					JoyClosed						= TRUE;
MODULE	struct MsgPort*			JoyPortPtr						= NULL;
MODULE	struct IOStdReq*		JoyRqPtr						= NULL;
MODULE	ULONG					length[SAMPLES + 1][2];
MODULE	struct Window*			MainWindowPtr					= NULL;
MODULE	struct Menu*			MenuPtr							= NULL;
MODULE	SBYTE					mode							= FALSE;
MODULE	SBYTE					OldPri							= 0;
MODULE	APTR					OldWindowPtr					= NULL;
MODULE	struct timeval*			PausedValPtr					= NULL;
MODULE	SWORD					pixy;
MODULE	struct Gadget*			PrevGadgetPtr					= NULL;
MODULE	struct Process*			ProcessPtr						= NULL;
MODULE	ULONG					receipter[4]					= {(ULONG) -1, (ULONG) -1, (ULONG) -1, (ULONG) -1};
MODULE	UBYTE*					sbase[SAMPLES + 1];
MODULE	struct Screen*			ScreenPtr						= NULL;
MODULE	struct MMD0*			SongPtr							= NULL;
MODULE	ULONG					speed[SAMPLES + 1];
MODULE	ULONG					ssize[SAMPLES + 1];
MODULE	struct timeval*			StartValPtr						= NULL;
MODULE	ABOOL					sticky							= FALSE;
MODULE	struct Gadget*			StringGadgetPtr[6]				= {NULL, NULL, NULL, NULL, NULL};
MODULE	SBYTE					TimerClosed						= TRUE;
MODULE	struct MsgPort*			TimerPortPtr					= NULL;
MODULE	struct timerequest*		TimerRqPtr						= NULL;
MODULE	struct VisualInfo*		VisualInfoPtr					= NULL;
MODULE	ULONG					yes[SAMPLES + 1];
MODULE	struct WBArg*			WBArg							= NULL;
MODULE	struct WBStartup*		WBMsg							= NULL;
MODULE	struct TextAttr			WormWars8 =
{	(STRPTR) "WormWars.font", 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED
};
MODULE	struct TextAttr			Topaz8 =
{	(STRPTR) "Topaz.font", 8, FS_NORMAL, FPF_ROMFONT | FPF_DESIGNED
};

// FUNCTIONS --------------------------------------------------------------

int main(int argc, char** argv)
{
BPTR				OldDir;
SLONG				i;
ABOOL				argument				= FALSE,
					success					= FALSE;
SBYTE				error					= 0,
					player, which;
TEXT				saystring[SAYLIMIT + 1];
UWORD				Pens[10] =
{	BLACK,		// DETAILPEN		text in title bar
	WHITE,		// BLOCKPEN			fill title bar
	WHITE,		// TEXTPEN			regular text on BACKGROUNDPEN
	LIGHTGREY,	// SHINEPEN			bright edge
	DARKGREY,	// SHADOWPEN		dark edge
	PURPLE,		/* FILLPEN			filling active window borders
									and selected gadgets */
	BLACK,		// FILLTEXTPEN		text rendered over FILLPEN
	BLACK,		// BACKGROUNDPEN	background colour
	RED,		// HIGHLIGHTTEXTPEN	highlighted text on BACKGROUNDPEN
	(UWORD) ~0	// and used against BLOCKPEN in ASL save requesters
};
SLONG				args[9]					= {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
struct ColorSpec	Colours[21] =
{   /* colour   red   green   blue   description */
    {   0,      0x0,    0x0,    0x0},   //        BLACK
    {   1,      0xF,    0xF,    0xF},   //        WHITE
    {   2,      0x3,    0x3,    0x3},   //    DARKGREY
    {   3,      0x6,    0x6,    0x6},   //  MEDIUMGREY
    {   4,      0xA,    0xA,    0xA},   //   LIGHTGREY
    {   5,      0x0,    0x0,    0x0},   //        black
    {   6,      0xA,    0x2,    0xA},   //        PURPLE
    {   7,      0x8,    0x4,    0x2},   //        brown
    {   8,      0x3,    0x9,    0x3},   //    DARKGREEN
    {   9,      0x4,    0xE,    0x4},   //  light GREEN
    {   10,     0xF,    0x1,    0x1},   //    DARKRED
    {   11,     0xF,    0x4,    0x4},   //  light RED
    {   12,     0x3,    0x3,    0xF},   //    DARKBLUE
    {   13,     0x6,    0x6,    0xF},   //  light BLUE
    {   14,     0xA,    0x8,    0x3},   //    DARKYELLOW
    {   15,     0xC,    0xC,    0x2},   //  light YELLOW
    {   16,     0x0,    0x0,    0x0},   // pointer: transparent
    {   17,     0xE,    0x4,    0x4},   // pointer: fill
    {   18,     0x3,    0x3,    0x3},   // pointer: shadow
    {   19,     0xC,    0xC,    0xC},   // pointer: shine
    {   -1,     NULL,   NULL,   NULL
}   };

/* Start of program.

version embedding into executable */

if (0) // that is, never
	say(VERSION, ANYTHING);

if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)))
{	Write(Output(), OLDKICKSTART, strlen(OLDKICKSTART));
	cleanexit(EXIT_FAILURE);
}

// From this point onwards, we can be sure we have Kickstart 2.04+...

for (which = 0; which <= SAMPLES; which++)
    sbase[which] = NULL;
enginesetup();
ProcessPtr = (struct Process *) FindTask(NULL);

// argument parsing

if (argc) // started from CLI
{	if (!(ArgsPtr = ReadArgs
	(	"-F=NOFX/S,-M=NOMUSIC/S,-P=PRI/K/N,-O=OVERHEAD/S,"
		"GREEN/K,RED/K,BLUE/K,YELLOW/K,FILE",
		args,
		NULL
	)))
	{	Printf("Usage: %s [-f=NOFX] [-m=NOMUSIC] [-p=PRI <priority>]\n[-o=OVERHEAD][[FILE=]<fieldset>]\n", argv[0]);
		cleanexit(EXIT_FAILURE);
	}
	if (args[0])
		fxable = 3;
	if (args[1])
		musicable = 3;
	if (args[2])
	{	if (args[2] < -128 || args[2] > 5)
	   	{   Printf("%s: Priority range is -128 to +5\n", argv[0]);
	   	    cleanexit(EXIT_FAILURE);
	 	}
	    OldPri = SetTaskPri((struct Task *) ProcessPtr, args[2]);
	}
	if (args[3])
		iso = FALSE;
	if (args[4])
	{	if (!stricmp(args[4], "HUMAN"))
			worm[0].control = HUMAN;
		else if (!stricmp(args[4], "AMIGA"))
			worm[0].control = AMIGA;
		else if (!stricmp(args[4], "NONE"))
			worm[0].control = NONE;
		else
		{	Printf("%s: Green worm control must be HUMAN, AMIGA or NONE\n", argv[0]);
			cleanexit(EXIT_FAILURE);
	}	}
	if (args[5])
	{	if (!stricmp(args[5], "HUMAN"))
			worm[1].control = HUMAN;
		else if (!stricmp(args[5], "AMIGA"))
			worm[1].control = AMIGA;
		else if (!stricmp(args[5], "NONE"))
			worm[1].control = NONE;
		else
		{	Printf("%s: Red worm control must be HUMAN, AMIGA or NONE\n", argv[0]);
			cleanexit(EXIT_FAILURE);
	}	}
	if (args[6])
	{	if (!stricmp(args[6], "HUMAN"))
			worm[2].control = HUMAN;
		else if (!stricmp(args[6], "AMIGA"))
			worm[2].control = AMIGA;
		else if (!stricmp(args[6], "NONE"))
			worm[2].control = NONE;
		else
		{	Printf("%s: Blue worm control must be HUMAN, AMIGA or NONE\n", argv[0]);
			cleanexit(EXIT_FAILURE);
	}	}
	if (args[7])
	{	if (!stricmp(args[7], "AMIGA"))
			worm[3].control = AMIGA;
		else if (!stricmp(args[7], "NONE"))
			worm[3].control = NONE;
		else
		{	Printf("%s: Yellow worm control must be AMIGA or NONE\n", argv[0]);
			cleanexit(EXIT_FAILURE);
	}	}
	if (args[8])
	{	argument = TRUE;
		pathname = args[8];
}	}
else // started from WB
{	// open icon.library

	if (IconBase = (struct IconBase *) OpenLibrary("icon.library", 0L))
	{	WBMsg = (struct WBStartup *) argv;
		WBArg = WBMsg->sm_ArgList; // head of the arg list

		for (i = 0;
			 i < WBMsg->sm_NumArgs;
			 i++, WBArg++)
		{	if (WBArg->wa_Lock)
			{	// something that does not support locks
				parsewb();
			}
			else
			{	// locks supported, change to the proper directory
				OldDir = CurrentDir(WBArg->wa_Lock);
				parsewb();
				CurrentDir(OldDir);
	}	}	}
	else iconfailed = TRUE;
}

if (SysBase->lib_Version < 36L)
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Need exec.library V36+!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open graphics.library!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(GadToolsBase = (struct GadToolsBase *) OpenLibrary("gadtools.library", 37L)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open GadTools.library V37+!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(ASLBase = (struct ASLBase *) OpenLibrary("asl.library", 0L)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open ASL.library!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(UtilityBase = (struct UtilityBase *) OpenLibrary("utility.library", 0L)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open utility.library!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(DiskFontBase = (struct DiskFontBase *) OpenLibrary("diskfont.library", 0L)))
	error = 1;
	
if (!(InputPortPtr = (struct MsgPort *) CreateMsgPort()))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open input.device!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(InputRqPtr = (struct IOStdReq *) CreateIORequest(InputPortPtr, sizeof(struct IOStdReq))))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create input I/O request!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (InputClosed = OpenDevice("input.device", 0, InputRqPtr, 0))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open input.device!\0", 24);
	cleanexit(EXIT_FAILURE);
}
	
if (!(StartValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
{	DisplayAlert(AT_Recovery, ALERTTIMERVALUE, 24);
	cleanexit(EXIT_FAILURE);
}
if (!(CurrentValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
{	DisplayAlert(AT_Recovery, ALERTTIMERVALUE, 24);
	cleanexit(EXIT_FAILURE);
}
if (!(PausedValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
{	DisplayAlert(AT_Recovery, ALERTTIMERVALUE, 24);
	cleanexit(EXIT_FAILURE);
}
if (!(TimerPortPtr = (struct MsgPort *) CreateMsgPort()))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't allocate timer message port!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(TimerRqPtr = (struct timerequest *) CreateIORequest(TimerPortPtr, sizeof(struct timerequest))))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create timer I/O request!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (TimerClosed = OpenDevice(TIMERNAME, UNIT_VBLANK, TimerRqPtr, 0))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open timer.device!\0", 24);
	cleanexit(EXIT_FAILURE);
}
TimerBase = (struct Library *) TimerRqPtr->tr_node.io_Device;

/* PREPARE DISPLAY -----------------------------------------------

font and screen */

if ((!error) && (FontPtr = (struct TextFont *) OpenDiskFont(&WormWars8)))
{	/* loaded WormWars.font

	It would be possible to pass WA_Interleaved on any OS version,
	provided that it was ignored by 2.04-2.1. */

	if (IntuitionBase->LibNode.lib_Version >= 39) // if we're running on OS3.0+
	{	ScreenPtr = (struct Screen *) OpenScreenTags
		(	NULL,
			SA_Width,			640,
			SA_Height,			256,
			SA_Depth,			DEPTH,
			SA_DisplayID,		HIRES_KEY,
			SA_Title,			TITLEBAR,
			SA_Colors,			Colours,
			SA_Font,			&WormWars8,
			SA_Pens,			Pens,
			SA_Interleaved,		TRUE,
			TAG_DONE
		);
	} else
	{	ScreenPtr = (struct Screen *) OpenScreenTags
		(	NULL,
			SA_Width,			640,
			SA_Height,			256,
			SA_Depth,			DEPTH,
			SA_DisplayID,		HIRES_KEY,
			SA_Title,			TITLEBAR,
			SA_Colors,			Colours,
			SA_Font,			&WormWars8,
			SA_Pens,			Pens,
			TAG_DONE
		);
	}
	if (!ScreenPtr)
	{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen!\0", 24);
		cleanexit(EXIT_FAILURE);
}	}
else
{	if (!error)
		error = 2;

	if (IntuitionBase->LibNode.lib_Version >= 39) // if we're running on OS3.0+
	{	ScreenPtr = (struct Screen *) OpenScreenTags
		(	NULL,
			SA_Width,			640,
			SA_Height,			256,
			SA_Depth,			DEPTH,
			SA_DisplayID,		HIRES_KEY,
			SA_Title,			TITLEBAR,
    			SA_Colors,			Colours,
    			SA_Font,            &Topaz8,
			SA_Pens,			Pens,
			SA_Interleaved,			TRUE,
			TAG_DONE
		);
	} else
	{	ScreenPtr = (struct Screen *) OpenScreenTags
		(	NULL,
			SA_Width,			640,
			SA_Height,			256,
			SA_Depth,			DEPTH,
			SA_DisplayID,		HIRES_KEY,
			SA_Title,			TITLEBAR,
    			SA_Colors,			Colours,
    			SA_Font,            &Topaz8,
			SA_Pens,			Pens,
			TAG_DONE
		);
	}
	if (!ScreenPtr)
	{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen nor font!\0", 24);
		cleanexit(EXIT_FAILURE);
}	}

/* For some reason, we lose 2 memory chunks: 264 bytes and 10 bytes. This
seems perhaps to not be our fault. */

// GadTools

if (!(CycleGadget.ng_VisualInfo = StringGadget.ng_VisualInfo = CheckboxGadget.ng_VisualInfo = VisualInfoPtr = (APTR) GetVisualInfo(ScreenPtr, TAG_DONE)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't get GadTools visual info!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(MenuPtr = (struct Menu *) CreateMenus(NewMenu, TAG_DONE)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create menus!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(LayoutMenus(MenuPtr, VisualInfoPtr, TAG_DONE)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't lay out menus!\0", 24);
	cleanexit(EXIT_FAILURE);
}
if (!(PrevGadgetPtr = (struct Gadget *) CreateContext(&GListPtr)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools context!\0", 24);
	cleanexit(EXIT_FAILURE);
}
for (player = 0; player <= 3; player++)
{	CycleGadget.ng_TopEdge = 184 + (player * (FONTX + 8));
	CycleGadget.ng_GadgetText = CycleText[player];
	CycleGadgetPtr[player] = PrevGadgetPtr = (struct Gadget *) CreateGadget
	(	CYCLE_KIND,
		PrevGadgetPtr,
		&CycleGadget,
		GTCY_Labels, CycleOptions[player],
		GTCY_Active, worm[player].control,
		GT_Underscore, '_',
		GA_Disabled, TRUE,
		TAG_DONE
	);
}
CheckboxGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
(	CHECKBOX_KIND,
	PrevGadgetPtr,
	&CheckboxGadget,
	GTCB_Checked, iso,
	GT_Underscore, '_',
	GA_Disabled, TRUE,
	TAG_DONE
);

// main window

if (!(MainWindowPtr = (struct Window *) OpenWindowTags(NULL,
	WA_Top,				11,
	WA_Width,			SCREENXPIXEL,
	WA_Height,			SCREENYPIXEL,
    WA_IDCMP,           IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_MENUPICK | IDCMP_MENUVERIFY | CYCLEIDCMP | STRINGIDCMP | CHECKBOXIDCMP | IDCMP_REFRESHWINDOW | IDCMP_INTUITICKS,
	WA_Gadgets,			GListPtr,
	WA_CustomScreen,	ScreenPtr,
	WA_Borderless,		TRUE,
	WA_Activate,		TRUE,
	WA_SmartRefresh,	TRUE,
	WA_RptQueue,		16,
	TAG_DONE)))
	{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open main window!\0", 24);
		cleanexit(EXIT_FAILURE);
	}

// redirection of AmigaDOS system requesters
	
OldWindowPtr = ProcessPtr->pr_WindowPtr;
ProcessPtr->pr_WindowPtr = (APTR) MainWindowPtr;

if (!(ASLRqPtr = AllocAslRequestTags(ASL_FileRequest, ASL_Pattern, PATTERN, ASL_Window, MainWindowPtr, TAG_DONE)))
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create ASL request!\0", 24);
	cleanexit(EXIT_FAILURE);
}

/* String gadgets: first the window is opened with the cycle and
checkbox gadgets.
	It is necessary not to display the string gadgets yet, so the
gadgets are then created and added to the gadget list. You will note
there is no command given to render them as yet. When the attributes
are modified at highscore time, the applicable gadget is refreshed
then. */

for (which = 0; which <= HISCORES; which++)
{	StringGadget.ng_TopEdge = 104 + (which * HISCOREDISTANCE);
	StringGadgetPtr[which] = PrevGadgetPtr = (struct Gadget *) CreateGadget(STRING_KIND, PrevGadgetPtr, &StringGadget, GTST_MaxChars, NAMELENGTH, STRINGA_ReplaceMode, TRUE, GA_Disabled, TRUE, TAG_DONE);
}
if (!PrevGadgetPtr)
{	DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools gadgets!\0", 24);
	cleanexit(EXIT_FAILURE);
}

if (iconfailed)
{	say("Can't open icon.library!", RED);
	anykey(TRUE);
}
if (error == 1)
{	say("Can't open diskfont.library!", RED);
	anykey(TRUE);
} else if (error == 2)
{	say("Can't open WormWars.font!", RED);
	anykey(TRUE);
}

if (!(JoyPortPtr = (struct MsgPort *) CreateMsgPort()))
{	say("Can't create joystick message port!", BLUE);
	anykey(TRUE);
} else if (!(JoyRqPtr = (struct IOStdReq *) CreateIORequest(JoyPortPtr, sizeof(struct IOStdReq))))
{	say("Can't create joystick I/O request!", BLUE);
	anykey(TRUE);
} else if (JoyClosed = OpenDevice("gameport.device", 1, JoyRqPtr, 0))
{	say("Can't open gameport.device!", BLUE);
	anykey(TRUE);
} else
{	Forbid();
	JoyRqPtr->io_Command		= GPD_ASKCTYPE;
	JoyRqPtr->io_Length			= 1;
	JoyRqPtr->io_Flags			= IOF_QUICK;
	JoyRqPtr->io_Data			= (APTR) &Controller;
	DoIO(JoyRqPtr);
	if (Controller == GPCT_NOCONTROLLER)
	{	Controller				= GPCT_ABSJOYSTICK;
		JoyRqPtr->io_Command	= GPD_SETCTYPE;
		JoyRqPtr->io_Length 	= 1;
		JoyRqPtr->io_Data		= (APTR) &Controller;
		DoIO(JoyRqPtr);
		success = TRUE;
	}
	Permit();
	/* Note that say(), anykey() calls must be outside the
	Forbid()/Permit() pair. */
	if (success)
	{	JoyRqPtr->io_Command	= GPD_SETTRIGGER;
		JoyRqPtr->io_Data		= (APTR) &Trigger;
		JoyRqPtr->io_Length		= sizeof(struct GamePortTrigger);
		DoIO(JoyRqPtr);
		sendreadrequest();
		joy = TRUE;
	} else
	{	say("Gameport already in use!", BLUE);
		anykey(TRUE);
}	}

/* By default, preload music and sound effects at startup.
musicable and fxable values are:

0: tried and failed
1: tried and succeeded
2: untried, will load at startup
3: untried, will load when needed */

if (musicable == 2)
	loadthemusic();
if (fxable == 2)
	loadthefx();

if (fxable == 1)
	toggle(F);
else if (musicable == 1)
	toggle(M);

say("Loading fieldset...", WHITE);
if (argument)
	if (loadfields(pathname))
	{	strcpy(saystring, "Can't open ");
		strcat(saystring, pathname);
		strcat(saystring, "!");
		say(saystring, RED);
		anykey(TRUE);
		argument = FALSE;
		pathname = DEFAULTSET;
	}
if (!argument)
	if (loadfields(DEFAULTSET))
	{	strcpy(saystring, "Can't open ");
		strcat(saystring, DEFAULTSET);
		strcat(saystring, "!");
		say(saystring, RED);
		anykey(TRUE);
		newfields();
	}

while (1)
{	titlescreen();

	// MAIN GAME LOOP -------------------------------------------------

	while (a == PLAYGAME)
	{	if (!(++r % VERYSLOW))
		{	GetSysTime(CurrentValPtr);
			SubTime(CurrentValPtr, StartValPtr);
			secondsleft = secondsperlevel - CurrentValPtr->tv_secs;
			timeloop();
		}
		TimerRqPtr->tr_node.io_Command	= TR_ADDREQUEST;
		TimerRqPtr->tr_time.tv_secs		= 0;
		TimerRqPtr->tr_time.tv_micro	= delay;
		SendIO(TimerRqPtr);
		gameloop();
		if (CheckIO(TimerRqPtr))
			draw(CLOCKICON, ICONY, CLOCK);
		else
			draw(CLOCKICON, ICONY, BLACKENED);
		WaitIO(TimerRqPtr);
	}
	say("Title Screen", WHITE);
}
}

// SUPPORT FUNCTIONS -----------------------------------------------------

/* NAME     anykey -- wait for a user press
SYNOPSIS    anykey(ABOOL);
FUNCTION    Waits for a user response. Optional automatic timeout.
INPUTS      timeout - timeout?
RESULTS     FALSE if user presses Escape, TRUE otherwise.
FILE        system.c */

ABOOL anykey(ABOOL timeout)
{	ABOOL					done		= FALSE;
	SBYTE					count		= 0;
	UWORD					code, qual;
	ULONG					class;
	struct IntuiMessage*	MsgPtr;

	clearkybd();
	Forbid();
	MainWindowPtr->Flags |= WFLG_RMBTRAP;
	Permit();
	
	while (!done)
	{	if (joy && GetMsg(JoyPortPtr))
		{	if (GameEvent.ie_Code == IECODE_LBUTTON)
				done = TRUE;
			sendreadrequest();
		}
		if (joy)
		{	Wait((1L << MainWindowPtr->UserPort->mp_SigBit) | (1L << JoyPortPtr->mp_SigBit));
			while (GetMsg(JoyPortPtr))
			{	if (GameEvent.ie_Code == IECODE_LBUTTON)
					done = TRUE;
				sendreadrequest();
		}	}
		else Wait(1L << MainWindowPtr->UserPort->mp_SigBit);
		while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
		{	class = MsgPtr->Class;
			code  = MsgPtr->Code;
			qual  = MsgPtr->Qualifier;
			GT_ReplyIMsg(MsgPtr);
			switch(class)
			{
			case IDCMP_RAWKEY:
				if ((!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP && (code < FIRSTQUALIFIER || code > LASTQUALIFIER))
				{	done = TRUE;
					if (code == M)
						toggle(M);
					else if (code == F)
						toggle(F);
					else if (code == ESCAPE)
					{	if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						{	if (verify())
								cleanexit(EXIT_SUCCESS);
                        } else
						{	Forbid();
							MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
							Permit();
							return FALSE;
            	}   }	}
				break;
			case IDCMP_CLOSEWINDOW:
				cleanexit(EXIT_SUCCESS);
				break;
			case IDCMP_ACTIVEWINDOW:
				ignore = TRUE;
				break;
			case IDCMP_MOUSEBUTTONS:
				if ((code == SELECTDOWN || code == MENUDOWN) && !(qual & IEQUALIFIER_REPEAT))
					if (ignore)
						ignore = FALSE;
					else done = TRUE;
				break;
			case IDCMP_INTUITICKS:
				if (timeout && ++count > PATIENCE)
					done = TRUE;
				break;
			default:
				break;
	}	}	}

	Forbid();
	MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
	Permit();
    return TRUE;
}

void celebrate(void)
{	ABOOL					done = FALSE;
	ULONG					class;
	UWORD					code, qual;
	struct IntuiMessage*	MsgPtr;
    UBYTE                   player;

    for (player = 0; player <= 3; player++)
        if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
             worm[player].hiscore = worm[player].score;
    waitasec();
	clearkybd();
    while (!done)
	{	while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
		{	class = MsgPtr->Class;
			code  = MsgPtr->Code;
			qual  = MsgPtr->Qualifier;
			GT_ReplyIMsg(MsgPtr);
			switch (class)
			{
			case IDCMP_RAWKEY:
				if (code == ESCAPE)
				{	if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
					{	if (verify())
							cleanexit(EXIT_SUCCESS);
					} else
						done = TRUE;
				}
				else if (code == RETURN || code == ENTER || code == SPACEBAR)
					done = TRUE;
				else if (code == M)
					toggle(M);
				else if (code == F)
					toggle(F);
				break;
			case IDCMP_MOUSEBUTTONS:
				if (code == SELECTDOWN && !(qual & IEQUALIFIER_REPEAT))
					if (ignore)
						ignore = FALSE;
					else done = TRUE;
				break;
			case IDCMP_ACTIVEWINDOW:
				ignore = TRUE;
				break;
			case IDCMP_CLOSEWINDOW:
				cleanexit(EXIT_SUCCESS);
				break;
			default:
				break;
		}	}
		if (joy && GetMsg(JoyPortPtr))
		{	if (GameEvent.ie_Code == IECODE_LBUTTON)
				done = TRUE;
			sendreadrequest();
		}
		SetRGB4(&ScreenPtr->ViewPort, 17, rand() % 16, rand() % 16, rand() % 16);
		draw(rand() % (FIELDX + 1), rand() % (FIELDY + 1), rand() % LASTOBJECT);
	}
	SetRGB4(&ScreenPtr->ViewPort, 17, 14, 4, 4);
	a = GAMEOVER;
}

void cleanexit(SLONG rc)
{   SBYTE i;
	
if (TimerRqPtr && (!CheckIO(TimerRqPtr)))
					{	AbortIO(TimerRqPtr);
						WaitIO(TimerRqPtr);								}
						freefx();
					    for (i = 0; i <= SAMPLES; i++)
                            if (sbase[i])
                                FreeMem(sbase[i], ssize[i]);
if (mode == MUSIC)		StopPlayer();
if (SongPtr)			UnLoadModule(SongPtr);
if (MEDPlayerBase)	{	FreePlayer();
						CloseLibrary(MEDPlayerBase);					}

if (joy)			{	AbortIO(JoyRqPtr);
						WaitIO(JoyRqPtr);								}
if (Controller != GPCT_NOCONTROLLER)
					{	Forbid();
						Controller				= GPCT_NOCONTROLLER;
						JoyRqPtr->io_Command	= GPD_SETCTYPE;
						JoyRqPtr->io_Length		= 1;
						JoyRqPtr->io_Data		= (APTR) &Controller;
						DoIO(JoyRqPtr);
						Permit();										}
if (!JoyClosed)			CloseDevice(JoyRqPtr);
if (JoyRqPtr)			DeleteIORequest(JoyRqPtr);
if (JoyPortPtr)			DeleteMsgPort(JoyPortPtr);

if (ASLRqPtr)			FreeAslRequest(ASLRqPtr);
if (OldWindowPtr)		ProcessPtr->pr_WindowPtr = OldWindowPtr;
if (MainWindowPtr)	{	clearkybd();
						ClearMenuStrip(MainWindowPtr);
						CloseWindow(MainWindowPtr);						}
if (GListPtr)			FreeGadgets(GListPtr);
if (MenuPtr)			FreeMenus(MenuPtr);
if (VisualInfoPtr)		FreeVisualInfo(VisualInfoPtr);
if (ScreenPtr)			CloseScreen(ScreenPtr);
if (FontPtr)			CloseFont(FontPtr);
if (!TimerClosed)		CloseDevice(TimerRqPtr);
if (TimerRqPtr)			DeleteIORequest(TimerRqPtr);
if (TimerPortPtr)		DeleteMsgPort(TimerPortPtr);
if (PausedValPtr)		FreeMem(PausedValPtr, sizeof(struct timeval));
if (CurrentValPtr)		FreeMem(CurrentValPtr, sizeof(struct timeval));
if (StartValPtr)		FreeMem(StartValPtr, sizeof(struct timeval));
if (!InputClosed)		CloseDevice(InputRqPtr);
if (InputRqPtr)			DeleteIORequest(InputRqPtr);
if (InputPortPtr)		DeleteMsgPort(InputPortPtr);
if (DiskFontBase)		CloseLibrary(DiskFontBase);
if (UtilityBase)		CloseLibrary(UtilityBase);
if (ASLBase)			CloseLibrary(ASLBase);
if (GadToolsBase)		CloseLibrary(GadToolsBase);
if (GfxBase)			CloseLibrary(GfxBase);
if (IconBase)			CloseLibrary(IconBase);
if (IntuitionBase)	{	OpenWorkBench();
						CloseLibrary(IntuitionBase);					}
						SetTaskPri((struct Task *) ProcessPtr, OldPri);
if (ArgsPtr)			FreeArgs(ArgsPtr);
						exit(rc);				// End of program.
}

void clearjoystick(void)
{	if (joy)
		while (GetMsg(JoyPortPtr))
			sendreadrequest();
}

void clearkybd(void)
{	struct IntuiMessage* MsgPtr;

	while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
		GT_ReplyIMsg(MsgPtr);
}

void draw(SBYTE x, SBYTE y, SBYTE image)
{	SWORD pixx;
	
	/* Isometric methodology:
	
	read the colour of the pixel, then set the colour in the image
	to the same value.

	So if, for the top left pixel, we read colour 10 (= $A or %1010),
	we have to clear the relevant bits in plane 0 and 2, and set them
	in planes 1 and 3.

	plane 0 (least significant bit)
		1234567890123456 pixels for top row (y = 0)
		1234567890123456 pixels for next row (y = 1)
		: : :
	plane 1
		: : :

	 01234567890
	0..#########
	1..#########
	2.#########.
	3.#########.
	4#########..
	5#########..

	Image is effectively 9*6, but requires an 11*6 bitmap.

     31
     268421
	 731000521
     689942152631
     8426842684268421
     **.......**..... */

	if (a == PLAYGAME && iso)
	{	IsoImage.ImageData = IsoImageData[image];
		pixx = (x * ISOSQUAREX) + 20 + ((FIELDY - y) * 3);
		pixy = (y * ISOSQUAREY) + STARTYPIXEL;
		preserve(pixx,      0, image, 32768);
		preserve(pixx,      1, image, 32768);
		preserve(pixx,      2, image, 32768);
		preserve(pixx,      3, image, 32768);
		preserve(pixx +  1, 0, image, 16384);
		preserve(pixx +  1, 1, image, 16384);
		preserve(pixx +  9, 4, image,    64);
		preserve(pixx +  9, 5, image,    64);
		preserve(pixx + 10, 2, image,    32);
		preserve(pixx + 10, 3, image,    32);
		preserve(pixx + 10, 4, image,    32);
		preserve(pixx + 10, 5, image,    32);
		DrawImage
		(	MainWindowPtr->RPort,
			&IsoImage,
			pixx,
			pixy
		);
	} else
	{	// blit square

		Image.ImageData = ImageData[image];
		DrawImage
		(	MainWindowPtr->RPort,
			&Image,
			STARTXPIXEL + (SQUAREX * x),
			STARTYPIXEL + (SQUAREY * y)
		);
}	}

void preserve(SWORD x, SBYTE row, SBYTE image, UWORD amount)
{	LONG colour;
	
	/* x = image top-left corner coordinate on destination screen.
	row = y-row of the image.
	image = image number
	amount = value of the bit to be set/cleared. */

	colour = ReadPixel
	(	MainWindowPtr->RPort,
		x,
		pixy + row
	);
	if (colour & 8)
		IsoImageData[image][row + 18] |= amount;
	else IsoImageData[image][row + 18] &= ~amount;
	if (colour & 4)
		IsoImageData[image][row + 12] |= amount;
	else IsoImageData[image][row + 12] &= ~amount;
	if (colour & 2)
		IsoImageData[image][row + 6] |= amount;
	else IsoImageData[image][row + 6] &= ~amount;
	if (colour & 1)
		IsoImageData[image][row] |= amount;
	else IsoImageData[image][row] &= ~amount;
}

void effect(SBYTE index)
{           SBYTE	i;
            SBYTE	ok				= -1;
            ULONG   oldestreceipt	= (ULONG) -1L;
    PERSIST	ULONG   receipt			= 1L;

	/* oldestreceipt = temporary variable for ascertaining oldest
	sound still playing.
	receipt = next unused receipt number (monotonically incrementing). */

	if (mode == FX)
	{	for (i = 0; i <= 3; i++)
		{	// decide on a channel

			if (ok == -1)
			{	if (!eversent[i])
					ok = i;
				else if (CheckIO(AudioRqPtr[i]))
				{	WaitIO(AudioRqPtr[i]);
					ok = i;
		}	}	}
        if (ok == -1)
        {   for (i = 0; i <= 3; i++)
                if (receipter[i] < oldestreceipt)
                {   ok = i;
                    oldestreceipt = receipter[i];
                }
            AbortIO(AudioRqPtr[ok]);
            WaitIO(AudioRqPtr[ok]);
        }
        eversent[ok] = TRUE;
		AudioRqPtr[ok]->ioa_Cycles				= 1;
        AudioRqPtr[ok]->ioa_Request.io_Command  = CMD_WRITE;
        AudioRqPtr[ok]->ioa_Request.io_Flags    = ADIOF_PERVOL;
        AudioRqPtr[ok]->ioa_Request.io_Unit     = (1 << ok);
        AudioRqPtr[ok]->ioa_Volume              = samp[index].volume;
        AudioRqPtr[ok]->ioa_Period              = (UWORD) speed[index];
        AudioRqPtr[ok]->ioa_Request.io_Message.mn_ReplyPort
                                                = AudioPortPtr[ok];
        AudioRqPtr[ok]->ioa_Data                = (UBYTE *) sbase[index];
        AudioRqPtr[ok]->ioa_Length              = length[index][yes[index]];
        BeginIO(AudioRqPtr[ok]);
        receipter[ok] = receipt;
}   }

void fieldedit(void)
{		UBYTE					oldbrush = ANYTHING, stamp;
		SBYTE					deltax = 0, deltay = 0, lastx, lasty, pointerx, pointery, which, x, y;
		UWORD					code, qual;
		ULONG					class;
		struct IntuiMessage*	MsgPtr;
		ABOOL					leftdown = FALSE, rightdown = FALSE, timer = FALSE;
		struct MenuItem*		ItemPtr;
PERSIST	SBYTE					clipboard[FIELDX + 1][FIELDY + 1];
PERSIST	ABOOL					clipboarded = FALSE;

say("Field Editor", WHITE);
setpointer(brush);
if (level > levels)
	level = levels;
OnMenu(MainWindowPtr, FULLMENUNUM(MN_EDIT, NOITEM, NOSUB));
OnMenu(MainWindowPtr, FULLMENUNUM(MN_LEVEL, NOITEM, NOSUB));
if (!clipboarded)
	OffMenu(MainWindowPtr, FULLMENUNUM(MN_EDIT, IN_PASTE, NOSUB));

// draw pseudo-gadgets

clearscreen();
for (which = 0; which <= 8; which++)
	DrawBevelBox(MainWindowPtr->RPort, STARTXPIXEL - 5 - (SQUAREX * 3), 40 + STARTYPIXEL + (which * SQUAREY * 3), SQUAREX + 9, SQUAREY + 4, GT_VisualInfo, VisualInfoPtr);
SetAPen(MainWindowPtr->RPort, WHITE);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (  GOLDGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F1:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (SILVERGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F2:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + ( EMPTYGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F3:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (  WOODGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F4:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + ( STONEGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F5:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + ( METALGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F6:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (   ONEGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F7:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (   TWOGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F8:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + ( STARTGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F9:", 3);
draw(GADGETX,   GOLDGADGET,   GOLD);
draw(GADGETX, SILVERGADGET, SILVER);
draw(GADGETX,  EMPTYGADGET,  EMPTY);
draw(GADGETX,   WOODGADGET,   WOOD);
draw(GADGETX,  STONEGADGET,  STONE);
draw(GADGETX,  METALGADGET,  METAL);
draw(GADGETX,    ONEGADGET,    ONE);
draw(GADGETX,    TWOGADGET,    TWO);
draw(GADGETX,  STARTGADGET,  START);
SetAPen(MainWindowPtr->RPort, BLACK);
RectFill
(	MainWindowPtr->RPort,
	STARTXPIXEL - SQUAREX - 1,
	UNDERLINEOFFSET - 1,
	STARTXPIXEL - SQUAREX + 3,
	UNDERLINEOFFSET + 4 + (SQUAREY * 25)
);
underline(brush);

turborender();
saylevel(WHITE);
clearkybd();
x = lastx = startx[level];
y = lasty = starty[level];
	
while (a == FIELDEDIT)
{	stamp = NOSQUARE;

	if (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
	{	class = MsgPtr->Class;
		code  = MsgPtr->Code;
		qual  = MsgPtr->Qualifier;
		if (class == IDCMP_MENUVERIFY && MsgPtr->MouseX >= STARTXPIXEL && MsgPtr->MouseX <= ENDXPIXEL && MsgPtr->MouseY >= STARTYPIXEL && MsgPtr->MouseY <= ENDYPIXEL)
		{	MsgPtr->Code = MENUCANCEL;
			oldbrush = brush;
			brush = EMPTY;
			rightdown = TRUE;
		}
		ReplyMsg(MsgPtr);
		switch (class)
		{
		case IDCMP_MENUPICK:
			while (code != MENUNULL)
			{	ItemPtr = ItemAddress(MenuPtr, code);
				switch (MENUNUM(code))
				{
				case MN_PROJECT:
					switch (ITEMNUM(code))
					{
					case IN_NEW:
						effect(FXFILENEW);
						newfields();
						say("New done.", WHITE);
						break;
					case IN_OPEN:
						effect(FXFILEOPEN);
						fileopen();
						break;
					case IN_SAVE:
						effect(FXFILESAVE);
						if (modified)
						{	clearhiscores();
							modified = FALSE;
						}
						filesaveas(FALSE);
						break;
					case IN_SAVEAS:
						effect(FXFILESAVEAS);
						if (modified)
						{	clearhiscores();
							modified = FALSE;
						}
						filesaveas(TRUE);
						break;
					case IN_ABOUT:
						fileabout();
						break;
					case IN_QUIT:
						if (verify())
							cleanexit(EXIT_SUCCESS);
						break;
					default:
						break;
					}
					break;
				case MN_EDIT:
					switch (ITEMNUM(code))
					{
					case IN_CUT:
						effect(FXEDITCUT);
						for (x = 0; x <= FIELDX; x++)
							for (y = 0; y <= FIELDY; y++)
								clipboard[x][y] = board[level][x][y];
						leveldelete();
						clipboarded = TRUE;
						modified = TRUE;
						OnMenu(MainWindowPtr, FULLMENUNUM(MN_EDIT, IN_PASTE, NOSUB));
						break;
					case IN_COPY:
						effect(FXEDITCOPY);
						for (x = 0; x <= FIELDX; x++)
							for (y = 0; y <= FIELDY; y++)
								clipboard[x][y] = board[level][x][y];
						clipboarded = TRUE;
						OnMenu(MainWindowPtr, FULLMENUNUM(MN_EDIT, IN_PASTE, NOSUB));
						break;
					case IN_PASTE:
						effect(FXEDITPASTE);
						for (x = 0; x <= FIELDX; x++)
							for (y = 0; y <= FIELDY; y++)
								board[level][x][y] = clipboard[x][y];
						turborender();
						modified = TRUE;
						break;
					default:
						break;
					}
					break;
				case MN_LEVEL:
					switch (ITEMNUM(code))
					{
					case IN_INSERT:
						levelinsert();
						break;
					case IN_DELETE:
						effect(FXLEVELDELETE);
						leveldelete();
						break;
					case IN_ERASE:
						levelerase();
						break;
					case IN_APPEND:
						levelappend();
						break;
					default:
						break;
					}
					break;
				default:
					break;
				}
				code = ItemPtr->NextSelect;
			}
			break;
		case IDCMP_RAWKEY:
			lastx = x;
			lasty = y;
			switch(code)
			{
			case DELETE:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						level = 1;
					else if (--level < 0)
						level = levels;
					saylevel(WHITE);
					turborender();
				}
				break;
			case HELP:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						level = levels;
					else if (++level > levels)
						level = 0;
					saylevel(WHITE);
					turborender();
				}
				break;
			case M:
				if (!(qual & IEQUALIFIER_REPEAT))
					toggle(M);
				break;
			case F:
				if (!(qual & IEQUALIFIER_REPEAT))
					toggle(F);
				break;
			case ESCAPE:
				if (!(qual & IEQUALIFIER_REPEAT))
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
					{	if (verify())
						cleanexit(EXIT_SUCCESS);
					} else a = GAMEOVER;
			break;
			case SPACEBAR:
			case RETURN:
			case ENTER:
				if (!(qual & IEQUALIFIER_REPEAT))
					a = GAMEOVER;
			break;
			case N:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXFILENEW);
					newfields();
					say("New done.", WHITE);
				}
			break;
			case O:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXFILEOPEN);
					fileopen();
				}
			break;
			case S:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXFILESAVE);
					if (modified)
					{	clearhiscores();
						modified = FALSE;
					}
					filesaveas(FALSE);
				}
				break;
			case A:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXFILESAVEAS);
					if (modified)
					{	clearhiscores();
						modified = FALSE;
					}
					filesaveas(TRUE);
				}
			break;
			case Q:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	if (verify())
						cleanexit(EXIT_SUCCESS);
				}
			break;
			case NUMERICOPEN:
				effect(FXCLICK);
				setpointer(NORMAL);
				underline(-1);
				if (brush-- == 0 || brush > LASTOBJECT)
					brush = LASTOBJECT;
				stamp = brush;
				break;
			case NUMERICCLOSE:
				effect(FXCLICK);
				setpointer(NORMAL);
				underline(-1);
				if (++brush > LASTOBJECT)
					brush = 0;
				stamp = brush;
				break;
			case I:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
					levelinsert();
				break;
			case D:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT) && ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT)))
				{	effect(FXLEVELDELETE);
					leveldelete();
				}
				break;
			case E:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT) && ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT)))
					levelerase();
				break;
			case K:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
					levelappend();
			break;
			case C:
				effect(FXPENGUINDEATH); // interesting
				x = FIELDX / 2;
				y = FIELDY / 2;
			break;
			case ALPHAONE:
				effect(FXCLICK);
				stamp = GOLD;
				break;
			case ALPHATWO:
				effect(FXCLICK);
				stamp = SILVER;
				break;
			case ALPHATHREE:
				effect(FXCLICK);
				stamp = EMPTY;
				break;
			case ALPHAFOUR:
				effect(FXCLICK);
				stamp = WOOD;
				break;
			case ALPHAFIVE:
				effect(FXCLICK);
				stamp = STONE;
				break;
			case ALPHASIX:
				effect(FXCLICK);
				stamp = METAL;
				break;
			case ALPHASEVEN:
				effect(FXCLICK);
				stamp = ONE;
				break;
			case ALPHAEIGHT:
				effect(FXCLICK);
				stamp = TWO;
				break;
			case ALPHANINE:
				effect(FXCLICK);
				stamp = START;
				break;
			case F1:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(GOLD);
					else setbrush(GOLD);
				}
				break;
			case F2:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(SILVER);
					else setbrush(SILVER);
				}
				break;
			case F3:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(EMPTY);
					else setbrush(EMPTY);
				}
				break;
			case F4:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(WOOD);
					else setbrush(WOOD);
				}
				break;
			case F5:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(STONE);
					else setbrush(STONE);
				}
				break;
			case F6:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						fillfield(METAL);
					else setbrush(METAL);
				}
				break;
			case F7:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					setbrush(ONE);
				}
				break;
			case F8:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					setbrush(TWO);
				}
				break;
			case F9:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					setbrush(START);
				}
				break;
			case NUMERICZERO:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
				{	if (!sticky)
					{	sticky = TRUE;
						draw(STICKYICON, ICONY, STICKY);
						stamp = brush;
					} else
					{	sticky = FALSE;
						draw(STICKYICON, ICONY, BLACKENED);
				}	}
				break;
			case NUMERICDOT:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
					stamp = brush;
				break;
			case NUMERICFOUR:
			case LEFT:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
					x = 0;
				else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
					x = xwrap(x - ALTJUMP);
				else
					x = xwrap(x - 1);
				if (sticky)
					stamp = brush;
				break;
			case NUMERICSIX:
			case RIGHT:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
					x = FIELDX;
				else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
					x = xwrap(x + ALTJUMP);
				else
					x = xwrap(x + 1);
				if (sticky)
					stamp = brush;
				break;
			case NUMERICEIGHT:
			case UP:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
					y = 0;
				else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
					y = ywrap(y - ALTJUMP);
				else
					y = ywrap(y - 1);
				if (sticky)
					stamp = brush;
				break;
			case NUMERICFIVE:
			case NUMERICTWO:
			case DOWN:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
					y = FIELDY;
				else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
					y = ywrap(y + ALTJUMP);
				else
					y = ywrap(y + 1);
				if (sticky)
					stamp = brush;
				break;
			case NUMERICSEVEN:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
				{	x = 0;
					y = 0;
				} else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
				{	x = xwrap(x - ALTJUMP);
					y = ywrap(y - ALTJUMP);
				} else
				{	x = xwrap(x - 1);
					y = ywrap(y - 1);
				}
				if (sticky)
					stamp = brush;
				break;
			case NUMERICNINE:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
				{	x = FIELDX;
					y = 0;
				} else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
				{	x = xwrap(x + ALTJUMP);
					y = ywrap(y - ALTJUMP);
				} else
				{	x = xwrap(x + 1);
					y = ywrap(y - 1);
				}
				if (sticky)
					stamp = brush;
				break;
			case NUMERICONE:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
				{	x = 0;
					y = FIELDY;
				} else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
				{	x = xwrap(x - ALTJUMP);
					y = ywrap(y + ALTJUMP);
				} else
				{	x = xwrap(x - 1);
					y = ywrap(y + 1);
				}
				if (sticky)
					stamp = brush;
				break;
			case NUMERICTHREE:
				if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT) || (qual & IEQUALIFIER_CONTROL))
				{	x = FIELDX;
					y = FIELDY;
				} else if ((qual & IEQUALIFIER_LALT) || (qual & IEQUALIFIER_RALT))
				{	x = xwrap(x + ALTJUMP);
					y = ywrap(y + ALTJUMP);
				} else
				{	x = xwrap(x + 1);
					y = ywrap(y + 1);
				}
				if (sticky)
					stamp = brush;
				break;
			default:
				break;
			}
			if (x != lastx || y != lasty)
			{	updatesquare(lastx, lasty);
				if (stamp > LASTOBJECT)
					dot(x, y);
			}
			break;
		case IDCMP_MOUSEBUTTONS:
			updatesquare(x, y);
			switch (code)
			{
			case SELECTUP:
				leftdown = FALSE;
				break;
			case SELECTDOWN:
				effect(FXCLICK);
				leftdown = TRUE;
				break;
			case MENUUP:
				rightdown = FALSE;
				brush = oldbrush;
				oldbrush = ANYTHING;
			break;
			default:
				// MENUDOWN
			break;
			}
		break;
		case IDCMP_CLOSEWINDOW:
			cleanexit(EXIT_SUCCESS);
		break;
		case IDCMP_REFRESHWINDOW:
			GT_BeginRefresh(MainWindowPtr);
			GT_EndRefresh(MainWindowPtr, TRUE);
		break;
		default:
			// IDCMP_MENUVERIFY, IDCMP_INTUITICKS, IDCMP_ACTIVEWINDOW
		break;
	}	}
	else if (joy)
	{	if (GetMsg(JoyPortPtr))
		{	if (GameEvent.ie_Code == IECODE_LBUTTON)
				stamp = brush;
			deltax = (SBYTE) GameEvent.ie_position.ie_xy.ie_x;
			deltay = (SBYTE) GameEvent.ie_position.ie_xy.ie_y;
			if (GameEvent.ie_Qualifier == IEQUALIFIER_LEFTBUTTON)
				stamp = brush;
			sendreadrequest();
		}
		if (deltax || deltay)
		{	if (!timer)
			{	TimerRqPtr->tr_node.io_Command = TR_ADDREQUEST;
				TimerRqPtr->tr_time.tv_secs    = 0;
				TimerRqPtr->tr_time.tv_micro   = JOYDELAY;
				SendIO(TimerRqPtr);
				timer = TRUE;
			} else if (CheckIO(TimerRqPtr))
			{	lastx = x;
				lasty = y;
				x = xwrap(x + deltax);
				y = ywrap(y + deltay);	
				updatesquare(lastx, lasty);
				dot(x, y);
				timer = FALSE;
	}	}	}
	if (leftdown || rightdown)
	{	pointerx = xpixeltosquare(MainWindowPtr->MouseX);
		pointery = ypixeltosquare(MainWindowPtr->MouseY);
		if (valid(pointerx, pointery))
		{	x = pointerx;
			y = pointery;
			stamp = brush;
			updatesquare(lastx, lasty);
		}
		else if (leftdown && pointerx == GADGETX &&
		(pointery == GOLDGADGET
		|| pointery == SILVERGADGET
		|| pointery == EMPTYGADGET
		|| pointery == WOODGADGET
		|| pointery == STONEGADGET
		|| pointery == METALGADGET
		|| pointery == ONEGADGET
		|| pointery == TWOGADGET
		|| pointery == STARTGADGET))
		{	switch (pointery)
			{
			case GOLDGADGET:
				setbrush(GOLD);
				break;
			case SILVERGADGET:
				setbrush(SILVER);
				break;
			case EMPTYGADGET:
				setbrush(EMPTY);
				break;
			case WOODGADGET:
				setbrush(WOOD);
				break;
			case STONEGADGET:
				setbrush(STONE);
				break;
			case METALGADGET:
				setbrush(METAL);
				break;
			case ONEGADGET:
				setbrush(ONE);
				break;
			case TWOGADGET:
				setbrush(TWO);
				break;
			case STARTGADGET:
				setbrush(START);
				break;
			default:
				break;
	}	}	}

	if (stamp != NOSQUARE)
	{	if (stamp == START && board[level][x][y] != TELEPORT)
		{	if (x != startx[level] || y != starty[level])
			{	draw(startx[level], starty[level], EMPTY);
				board[level][startx[level]][starty[level]] = EMPTY;
				draw(x, y, START);
				startx[level] = x;
				starty[level] = y;
				modified = TRUE;
				clearthem = TRUE;
		}	}
		else if (x != startx[level] || y != starty[level])
		{	if (stamp == ONE || stamp == TWO)
			{	if (teleport[level][partner(stamp - ONE)].alive == FALSE || x != teleport[level][partner(stamp - ONE)].x || y != teleport[level][partner(stamp - ONE)].y)
				{	if (teleport[level][stamp - ONE].alive == TRUE)
					{	draw(teleport[level][stamp - ONE].x, teleport[level][stamp - ONE].y, EMPTY);
						board[level][teleport[level][stamp - ONE].x][teleport[level][stamp - ONE].y] = EMPTY;
					} else
						teleport[level][stamp - ONE].alive = TRUE;
					board[level][x][y] = TELEPORT;
					draw(x, y, stamp);
					teleport[level][stamp - ONE].x = x;
					teleport[level][stamp - ONE].y = y;
					modified = TRUE;
					clearthem = TRUE;
			}	}
			else
			{	if (board[level][x][y] == TELEPORT)
					if (teleport[level][0].alive == TRUE && x == teleport[level][0].x && y == teleport[level][0].y)
						teleport[level][0].alive = FALSE;
					else teleport[level][1].alive = FALSE;
				draw(x, y, stamp);
				board[level][x][y] = stamp;
				dot(x, y);
				modified = TRUE;
				clearthem = TRUE;
}	}	}	}

// exit to title screen

OffMenu(MainWindowPtr, FULLMENUNUM(MN_EDIT, NOITEM, NOSUB));
OffMenu(MainWindowPtr, FULLMENUNUM(MN_LEVEL, NOITEM, NOSUB));
if (timer)
{	AbortIO(TimerRqPtr);
	WaitIO(TimerRqPtr);
}
if (oldbrush != ANYTHING)
	brush = oldbrush;
setpointer(NORMAL);
if (clearthem)
	clearhiscores();
matchteleports();
}

void fileabout(void)
{	ABOOL					done = FALSE;
	SBYTE					line;
	UWORD					code, qual;
	ULONG					class;
	struct Window*			AboutWindowPtr;
	struct IntuiMessage*	MsgPtr;
	SLONG					projectval;
	TEXT					projectstring[6];

	effect(FXAMIGAN);
	if (!(AboutWindowPtr = (struct Window *) OpenWindowTags(NULL,
	WA_Left,			(SCREENXPIXEL / 2) - (ABOUTXPIXEL / 2),
	WA_Top,				(SCREENYPIXEL / 2) - (ABOUTYPIXEL / 2),
	WA_Width,			ABOUTXPIXEL,
	WA_Height,			ABOUTYPIXEL,
	WA_IDCMP,			IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
	WA_Title,			"About Worm Wars",
	WA_Gadgets,			NULL,
	WA_CustomScreen,	ScreenPtr,
	WA_DragBar,			TRUE,
	WA_CloseGadget,		TRUE,
	WA_NoCareRefresh,	TRUE,
	WA_Activate,		TRUE,
	TAG_DONE)))
	{	say("Can't open About... window!", RED);
		anykey(TRUE);
	} else
	{	// calculate project size
		
		projectval =
			11 														// header
		+	((HISCORES + 1) * (9 + NAMELENGTH + DATELENGTH + TIMELENGTH))
															 		// high scores
		+	((levels + 1) * (7 + ((FIELDX + 1) * (FIELDY + 1)))) 	// level data
		+	levels
		+	strlen(VERSION);										// version string

		SetAPen(AboutWindowPtr->RPort, worm[rand() % 4].colour);
		RectFill(AboutWindowPtr->RPort, 8, 13, ABOUTXPIXEL - 11, ABOUTYPIXEL - 6);
		SetAPen(AboutWindowPtr->RPort, ABOUTSHADOW);
		Move(AboutWindowPtr->RPort, 7, ABOUTYPIXEL - 5);
		Draw(AboutWindowPtr->RPort, 7, 12);
		Draw(AboutWindowPtr->RPort, ABOUTXPIXEL - 10, 12);
		SetAPen(AboutWindowPtr->RPort, ABOUTSHINE);
		Draw(AboutWindowPtr->RPort, ABOUTXPIXEL - 10, ABOUTYPIXEL - 5);
		Draw(AboutWindowPtr->RPort, 8, ABOUTYPIXEL - 5);
		SetAPen(AboutWindowPtr->RPort, BLACK);
		SetDrMd(AboutWindowPtr->RPort, JAM1);
		for (line = 0; line <= ABOUTLINES; line++)
		{	Move(AboutWindowPtr->RPort, about[line].x, about[line].y);
			Text(AboutWindowPtr->RPort, about[line].text, (SBYTE) strlen(about[line].text));
		}
		stcl_d(projectstring, projectval);
		align(projectstring, 5, ' ');
		Move(AboutWindowPtr->RPort, PROJECTX, PROJECTY);
		Text(AboutWindowPtr->RPort, projectstring, 5);
		DrawBevelBox(AboutWindowPtr->RPort, 18, 21, 49, 23, GT_VisualInfo, VisualInfoPtr);
		DrawImage(AboutWindowPtr->RPort, &About, 20, 23);

		while(!done)
		{	Wait(1L << AboutWindowPtr->UserPort->mp_SigBit);
			while (MsgPtr = (struct IntuiMessage *) GetMsg(AboutWindowPtr->UserPort))
			{	class = MsgPtr->Class;
				code  = MsgPtr->Code;
				qual  = MsgPtr->Qualifier;
				ReplyMsg(MsgPtr);
				switch(class)
				{
				case IDCMP_CLOSEWINDOW:
					done = TRUE;
					break;
				case IDCMP_RAWKEY:
            	   	if (code == SPACEBAR || code == RETURN || code == ENTER || code == HELP)
            	       done = TRUE;
            	    else if (code == ESCAPE)
            	    	if (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
						{	if (verify())
							{   CloseWindow(AboutWindowPtr);
								cleanexit(EXIT_SUCCESS);
						}	}
						else done = TRUE;
					break;
				default:
					break;
        }   }   }
		CloseWindow(AboutWindowPtr);
		clearkybd();
}	}

void fileopen(void)
{   TEXT temp1[81] = {"Opened "}, temp2[3], newpathname[255];

	strcpy(newpathname, pathname);
	if (AslRequestTags(ASLRqPtr, ASL_Hail, ASLOPENHAIL, ASL_FuncFlags, FILF_PATGAD, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
	{	strcpy(newpathname, ASLRqPtr->rf_Dir);
		AddPart(newpathname, ASLRqPtr->rf_File, 254);
		if (!loadfields(newpathname))
        {   strcpy(pathname, newpathname);
            strcat(temp1, pathname);
            strcat(temp1, " (");
            stci_d(temp2, levels);
            strcat(temp1, temp2);
            strcat(temp1, " levels).");
            say(temp1, WHITE);
			if (a == FIELDEDIT)
				turborender();
			else hiscores();
        } else
        {   strcpy(temp1, "Couldn't open ");
            strcat(temp1, newpathname);
            strcat(temp1, "!");
            say(temp1, WHITE);
    }   }
	if (a == GAMEOVER)
		anykey(TRUE);
}

void filesaveas(ABOOL flag)
{	ABOOL	cont = TRUE;
	TEXT	newpathname[255], temp1[SAYLIMIT + 1], temp2[3];

    // flag is TRUE for 'save as...', FALSE for 'save'.

	strcpy(newpathname, pathname);
    if (flag)
        if (AslRequestTags(ASLRqPtr, ASL_Hail, ASLSAVEHAIL, ASL_FuncFlags, FILF_PATGAD | FILF_SAVE, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
        {   strcpy(newpathname, ASLRqPtr->rf_Dir);
            AddPart(newpathname, ASLRqPtr->rf_File, 254);
        } else cont = FALSE;
    if (cont)
    {	strcpy(temp1, "Saving ");
	    strcat(temp1, pathname);
	    strcat(temp1, "...");
	    say(temp1, WHITE);
		if (savefields(newpathname))
        {   strcpy(pathname, newpathname);
            strcpy(temp1, "Saved ");
			strcat(temp1, pathname);
            strcat(temp1, " (");
            stci_d(temp2, levels);
            strcat(temp1, temp2);
            strcat(temp1, " levels).");
        } else
        {   strcpy(temp1, "Couldn't save ");
            strcat(temp1, newpathname);
            strcat(temp1, "!");
        }
        say(temp1, WHITE);
        if (a == GAMEOVER)
            anykey(TRUE);
}   }

void freefx(void)
{	SBYTE i;

    stopfx(0L);
	if (!AudioClosed)
	{	CloseDevice((struct IORequest *) AudioRqPtr[0]);
		AudioClosed = TRUE;
	}
	for (i = 0; i <= 3; i++)
	{	if (AudioRqPtr[i])
		{	DeleteIORequest(AudioRqPtr[i]);
			AudioRqPtr[i] = NULL;
		}
		if (AudioPortPtr[i])
		{	DeleteMsgPort(AudioPortPtr[i]);
			AudioPortPtr[i] = NULL;
	}	}
	if (fbase)
	{	FreeMem(fbase, fsize);
		fbase = NULL;
	}
    if (FilePtr)
    {   Close(FilePtr);
        FilePtr = NULL;
}	}

void gameinput(void)
{		ABOOL					done;
		UWORD					code, qual;
		ULONG					class;
		struct IntuiMessage*	MsgPtr;
        SBYTE                   keyplayer;
        UBYTE                   which;

for (which = 0; which <= NUMKEYS; which++)
    key[which].down = FALSE;

// keyboard

while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
{	class  = MsgPtr->Class;
	code   = MsgPtr->Code;
	qual   = MsgPtr->Qualifier;
	ReplyMsg(MsgPtr);
    if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP)
	{	switch(code) {
		case M:
			toggle(M);
        break;
		case F:
			toggle(F);
        break;
		case P:
			clearkybd();
			say("Paused...press P to unpause", WHITE);
			pausetimer();
			done = FALSE;
			while (!done)
			{	Wait(1L << MainWindowPtr->UserPort->mp_SigBit);
				while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
				{	class = MsgPtr->Class;
					code  = MsgPtr->Code;
					qual  = MsgPtr->Qualifier;
					ReplyMsg(MsgPtr);
					if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)))
					{	if (code == ESCAPE)
						{	if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
							{	if (verify())
									cleanexit(EXIT_SUCCESS);
							} else
							{	a = GAMEOVER;
								done = TRUE;
                                worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
						}	}
						else if (code == M)
							toggle(M);
						else if (code == F)
							toggle(F);
						else if (code == P)
						{	say("Unpaused", WHITE);
							done = TRUE;
			}	}	}	}
			unpausetimer();
        break;
		case ESCAPE:
			if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
			{	if (verify())
					cleanexit(EXIT_SUCCESS);
			} else
			{	a = GAMEOVER;
				worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
			}
        break;
		default:
			for (which = 0; which <= NUMKEYS; which++)
				if (code == key[which].scancode)
					key[which].down = TRUE;
        break;
	}	}	
	else if (class == IDCMP_CLOSEWINDOW)
		cleanexit(EXIT_SUCCESS);
    else if (class == IDCMP_REFRESHWINDOW)
    {   GT_BeginRefresh(MainWindowPtr);
        GT_EndRefresh(MainWindowPtr, TRUE);
}   }
/* Received but ignored: IDCMP_ACTIVEWINDOW, IDCMP_MOUSEBUTTONS and
IDCMP_INTUITICKS. */

for (which = 0; which <= NUMKEYS; which++)
{	if (key[which].down)
	{	if (key[which].special == ONEHUMAN)
		{	if (worm[0].control == HUMAN && worm[1].control != HUMAN)
				queue(0, key[which].deltax, key[which].deltay);
			else if (worm[0].control != HUMAN && worm[1].control == HUMAN)
				queue(1, key[which].deltax, key[which].deltay);
		} else if (key[which].special == MOVE || key[which].special == AMMO)
		{   if (worm[key[which].player].control == HUMAN)
            	keyplayer = key[which].player;
			else if (key[which].player == 1 && worm[0].control == HUMAN && worm[1].control != HUMAN)
                keyplayer = 0;
			else if (key[which].player == 0 && worm[1].control == HUMAN && worm[0].control != HUMAN)
                keyplayer = 1;
            else keyplayer = -1;
            if (keyplayer != -1)
            	queue(keyplayer, key[which].deltax, key[which].deltay);
        } else if (worm[1].lives) // assumes key[which].special == TRAINER
			train(key[which].scancode);
}	}

// joystick

if (worm[2].control == HUMAN)
	while (GetMsg(JoyPortPtr))
	{	if (GameEvent.ie_position.ie_xy.ie_x != 0 || GameEvent.ie_position.ie_xy.ie_y != 0)
			queue(2, GameEvent.ie_position.ie_xy.ie_x, GameEvent.ie_position.ie_xy.ie_y);
		if (GameEvent.ie_Code == IECODE_LBUTTON)
			queue(2, 0, 0);
		sendreadrequest();
}	}

void hiscores(void)
{	SBYTE					which;
	TEXT					tempstring[NAMELENGTH + 1];

/* render hiscores

#################################################### # = shadow
#   #   #   #                      #     #         % % = shine
#   #   #   #                      #     #         %
#   #   #   #                      #     #         %
#   #   #   #                      #     #         %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */

SetDrMd(MainWindowPtr->RPort, JAM1);
for (which = 0; which <= HISCORES; which++)
{	if (hiscore[which].player == -1)
		SetAPen(MainWindowPtr->RPort, LIGHTGREY);
	else SetAPen(MainWindowPtr->RPort, worm[hiscore[which].player].colour);
	RectFill(MainWindowPtr->RPort, 104, 105 + which * HISCOREDISTANCE, 536, 114 + which * HISCOREDISTANCE);
	if (hiscore[which].player == 0)
		SetAPen(MainWindowPtr->RPort, DARKGREEN);
	else if (hiscore[which].player == 1)
		SetAPen(MainWindowPtr->RPort, DARKRED);
	else if (hiscore[which].player == 2)
		SetAPen(MainWindowPtr->RPort, DARKBLUE);
	else if (hiscore[which].player == 3)
		SetAPen(MainWindowPtr->RPort, DARKYELLOW);
	else SetAPen(MainWindowPtr->RPort, DARKGREY);
	Move(MainWindowPtr->RPort, 103, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 103, 104 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 537, 104 + which * HISCOREDISTANCE);

	if (hiscore[which].player != -1)
	{	// divider bars
	
		Move(MainWindowPtr->RPort, 182 - 55, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 182 - 55, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 254 - 55, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 254 - 55, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 290 - 55, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 290 - 55, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 416, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 416, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 464, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 464, 114 + which * HISCOREDISTANCE);
	}
	
	SetAPen(MainWindowPtr->RPort, WHITE);
	Move(MainWindowPtr->RPort, 159 - 55, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 482 + 55, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 482 + 55, 105 + which * HISCOREDISTANCE);
	SetAPen(MainWindowPtr->RPort, BLACK);

	if (hiscore[which].player != -1)
	{	stci_d(tempstring, which + 1);
		tempstring[1] = '.';
		Move(MainWindowPtr->RPort, 161 - 55, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 2);
		stci_d(tempstring, hiscore[which].score);
		align(tempstring, 7, ' ');
		Move(MainWindowPtr->RPort, 193 - 55, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 7);
		if (hiscore[which].level == -1)
			strcpy(tempstring, "All");
		else
		{	stci_d(tempstring, hiscore[which].level);
			align(tempstring, 3, ' ');
		}
		Move(MainWindowPtr->RPort, 206, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 3);
		Move(MainWindowPtr->RPort, 241, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, hiscore[which].name, strlen(hiscore[which].name));
		Move(MainWindowPtr->RPort, 418, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, hiscore[which].time, strlen(hiscore[which].time));
		Move(MainWindowPtr->RPort, 466, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, hiscore[which].date, strlen(hiscore[which].date));
}	}
SetDrMd(MainWindowPtr->RPort, JAM2);
}

void hiscorenames(void)
{
ULONG					class;
ABOOL					done;
SBYTE					which;
struct IntuiMessage*	MsgPtr;

for (which = 0; which <= HISCORES; which++)
	if (hiscore[which].fresh)
	{	GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, FALSE, GTST_String, worm[hiscore[which].player].name, TAG_DONE);
		ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
		done = FALSE;
		while (!done)
		{	while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
			{	class = MsgPtr->Class;
				GT_ReplyIMsg(MsgPtr);
				if (class == IDCMP_GADGETUP)
					done = TRUE;
				else if (class == IDCMP_MOUSEBUTTONS)
					ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
				else if (class == IDCMP_REFRESHWINDOW)
				{	GT_BeginRefresh(MainWindowPtr);
					GT_EndRefresh(MainWindowPtr, TRUE);
		}	}	}
		GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
		effect(FXHISCORE);
		strcpy(hiscore[which].name, ((struct StringInfo *) (StringGadgetPtr[which]->SpecialInfo))->Buffer);
		if (hiscore[which].name[0] >= 97 && hiscore[which].name[0] <= 123)
			hiscore[which].name[0] -= 32;
		strcpy(worm[hiscore[which].player].name, hiscore[which].name);
		hiscore[which].fresh = FALSE;
		hiscores();
}	}

ABOOL beginfx(void)
{   		SBYTE	i;
	PERSIST	UBYTE	chan[]	= {15};

	for (i = 0; i <= 3; i++)
    {   eversent[i] = FALSE;
    	if (!(AudioPortPtr[i] = (struct MsgPort *) CreateMsgPort()))
        {   freefx();
            draw(MUSICICON, ICONY, BLACKENED);
            mode = FALSE;
            say("No port for effects!", RED);
            anykey(TRUE);
            return FALSE;
        } else if (!(AudioRqPtr[i] = (struct IOAudio *) CreateIORequest(AudioPortPtr[i], sizeof(struct IOAudio))))
        {   freefx();
            draw(MUSICICON, ICONY, BLACKENED);
            mode = FALSE;
            say("No I/O memory for effects!", RED);
            anykey(TRUE);
            return FALSE;
    }   }
    AudioRqPtr[0]->ioa_Request.io_Message.mn_ReplyPort      = AudioPortPtr[0];
    AudioRqPtr[0]->ioa_Request.io_Message.mn_Node.ln_Pri    = 127;
    AudioRqPtr[0]->ioa_AllocKey                             = 0;
    AudioRqPtr[0]->ioa_Data                                 = chan;
    AudioRqPtr[0]->ioa_Length                               = 1;
    if (AudioClosed = OpenDevice(AUDIONAME, 0L, (struct IORequest *) AudioRqPtr[0], 0L))
    {   freefx();
        draw(MUSICICON, ICONY, BLACKENED);
        mode = FALSE;
        say("Can't allocate all channels for effects!", RED);
        anykey(TRUE);
		return FALSE;
    } else
    {   for (i = 1; i <= 3; i++)
            CopyMem(AudioRqPtr[0], AudioRqPtr[i], sizeof(struct IOAudio));
		return TRUE;
}   }

void resettime(void)
{	GetSysTime(StartValPtr);
}

void rundown(SBYTE player)
{	TEXT	tempstring[6];
	SBYTE	i, j, x, y;
	UWORD	counter = 0;
	ABOOL	done = FALSE;
	ULONG	levelbonus, timebonus, tailbonus;
    UBYTE   multiply = worm[player].multi * players;

	clearscreen();
	SetAPen(MainWindowPtr->RPort, worm[player].colour);

	Image.ImageData = ImageData[TREASURE];
	DrawImage(MainWindowPtr->RPort, &Image, 201, 103);
	Move(MainWindowPtr->RPort, 218, 108);
	Text(MainWindowPtr->RPort, "Level Bonus:    ## x ##00 =", 27);
    stci_d(tempstring, multiply);
	align(tempstring, 2, ' ');
	Move(MainWindowPtr->RPort, 218 + (FONTX * 21), 108);
	Text(MainWindowPtr->RPort, tempstring, 2);
	stci_d(tempstring, level - 1);
	align(tempstring, 2, ' ');
	Move(MainWindowPtr->RPort, 218 + (FONTX * 16), 108);
	Text(MainWindowPtr->RPort, tempstring, 2);

	Image.ImageData = ImageData[CLOCK];
	DrawImage(MainWindowPtr->RPort, &Image, 201, 111);
	Move(MainWindowPtr->RPort, 218, 116);
	Text(MainWindowPtr->RPort, " Time Bonus: ##:## x  ##0 =", 27);
    stci_d(tempstring, multiply);
	align(tempstring, 2, ' ');
	Move(MainWindowPtr->RPort, 226 + (FONTX	 * 21), 116);
	Text(MainWindowPtr->RPort, tempstring, 2);
	stci_d(tempstring, secondsleft / 60);
	align(tempstring, 2, ' ');
	Move(MainWindowPtr->RPort, 226 + (FONTX * 12), 116);
	Text(MainWindowPtr->RPort, tempstring, 2);
	stci_d(tempstring, secondsleft % 60);
	align(tempstring, 2, '0');
	Move(MainWindowPtr->RPort, 226 + (FONTX * 15), 116);
	Text(MainWindowPtr->RPort, tempstring, 2);

	Image.ImageData = ImageData[FIRSTTAIL + player];
	DrawImage(MainWindowPtr->RPort, &Image, 201, 119);
	Move(MainWindowPtr->RPort, 218, 124);
	Text(MainWindowPtr->RPort, " Tail Bonus:  #### x   ## =", 27);
    stci_d(tempstring, multiply);
	align(tempstring, 2, ' ');
    Move(MainWindowPtr->RPort, 226 + (FONTX * 22), 124);
    Text(MainWindowPtr->RPort, tempstring, 2);
	for (x = 0; x <= FIELDX; x++)
		for (y = 0; y <= FIELDY; y++)
			if (field[x][y] == FIRSTTAIL + player)
				counter++;
    stci_d(tempstring, counter);
    align(tempstring, 4, ' ');
    Move(MainWindowPtr->RPort, 226 + (FONTX * 13), 124);
    Text(MainWindowPtr->RPort, tempstring, 4);

    levelbonus = (level - 1) * 100 * multiply;
    timebonus = secondsleft * 10 * multiply;
    tailbonus = counter * multiply;

	for (i = 0; i <= 3; i++)
		if (worm[i].control != NONE)
			for (j = 0; j <= LASTOBJECT; j++)
				stat(i, j);

	while (!done)
	{	SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(tempstring, levelbonus);
		align(tempstring, 5, ' ');
		Move(MainWindowPtr->RPort, 218 + (FONTX * 27), 108);
		Text(MainWindowPtr->RPort, tempstring, 5);

		stci_d(tempstring, timebonus);
		align(tempstring, 5, ' ');
		Move(MainWindowPtr->RPort, 226 + (FONTX * 26), 116);
		Text(MainWindowPtr->RPort, tempstring, 5);

		stci_d(tempstring, tailbonus);
		align(tempstring, 5, ' ');
		Move(MainWindowPtr->RPort, 226 + (FONTX * 26), 124);
		Text(MainWindowPtr->RPort, tempstring, 5);

		done = TRUE;
		if (levelbonus)
        {   levelbonus -= multiply;
			wormscore(player, 1);
			done = FALSE;
		}
		if (timebonus)
        {   timebonus -= multiply;
			wormscore(player, 1);
			done = FALSE;
		}
		if (tailbonus)
        {   tailbonus -= multiply;
			wormscore(player, 1);
			done = FALSE;
		}
		effect(FXRUNDOWN);
	}
	clearkybd();
	effect(FXENDOFLEVEL);
	anykey(FALSE);
}

void say(STRPTR sentence, COLOUR colour)
{	SBYTE length = (SBYTE) strlen(sentence);

	// clear text area

	SetAPen(MainWindowPtr->RPort, BLACK);
	RectFill(MainWindowPtr->RPort, STARTXPIXEL, 1, ENDXPIXEL, FONTY + 2);

	// truncate text

	if (length > SAYLIMIT)
	{	*(sentence + SAYLIMIT - 1) = *(sentence + SAYLIMIT - 2) = *(sentence + SAYLIMIT - 3) = '.';
		length = SAYLIMIT;
	}

	// render shadow text
	
	SetDrMd(MainWindowPtr->RPort, JAM1);
	SetAPen(MainWindowPtr->RPort, MEDIUMGREY);
	Move(MainWindowPtr->RPort, (SCREENXPIXEL / 2) - (length * FONTX / 2) + 1, FONTY + 1);
	Text(MainWindowPtr->RPort, sentence, length);
	
	// render actual text
	
	SetAPen(MainWindowPtr->RPort, colour);
	Move(MainWindowPtr->RPort, (SCREENXPIXEL / 2) - (length * FONTX / 2), FONTY);
	Text(MainWindowPtr->RPort, sentence, length);
	SetDrMd(MainWindowPtr->RPort, JAM2);
}
	
void sendreadrequest(void)
{	JoyRqPtr->io_Command	= GPD_READEVENT;
	JoyRqPtr->io_Flags		= NULL;
	JoyRqPtr->io_Length		= sizeof(struct InputEvent);
	JoyRqPtr->io_Data		= (APTR) &GameEvent;
	SendIO(JoyRqPtr);
}

void setpointer(SBYTE pointer) {
switch (pointer) {
case GOLD:
	SetRGB4(&ScreenPtr->ViewPort, 17, 10,  8,  3);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  8,  4,  2);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19, 12, 12,  2);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
case SILVER:
	SetRGB4(&ScreenPtr->ViewPort, 17, 10, 10, 10);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  6,  6,  6);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19, 15, 15, 15);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
case EMPTY:
	SetRGB4(&ScreenPtr->ViewPort, 17,  2,  2,  2);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  0,  0,  0);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19,  6,  6,  6);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
case WOOD:
	SetRGB4(&ScreenPtr->ViewPort, 17,  8,  4,  2);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  2,  2,  2);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19, 10,  8,  3);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
case STONE:
	SetRGB4(&ScreenPtr->ViewPort, 17,  0,  0,  0);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  2,  2,  2);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19,  6,  6,  6);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
case METAL:
	SetRGB4(&ScreenPtr->ViewPort, 17,  6,  6, 15);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  3,  3, 15);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19, 10, 10, 10);		// shine
	SetPointer(MainWindowPtr, &CustomPointer, 6, 5, -3, -2);
	break;
default:
	SetRGB4(&ScreenPtr->ViewPort, 17, 14,  4,  4);		// fill
	SetRGB4(&ScreenPtr->ViewPort, 18,  3,  3,  3);		// shadow
	SetRGB4(&ScreenPtr->ViewPort, 19, 12, 12, 12);		// shine
	ClearPointer(MainWindowPtr);
	break; }
}

void stat(SBYTE player, SBYTE line)
{	ABOOL	longscore = FALSE, print = TRUE;
	SBYTE	i, len, theline;
	TEXT	output[8];

	strcpy(output, "      "); // 6 spaces
	switch (line)
	{
	case BONUS:
		do
		{	worm[player].oldscore += (LIFEMODULO - (worm[player].oldscore % LIFEMODULO));
			if (worm[player].score >= worm[player].oldscore)
			{	worm[player].lives++;
				stat(player, LIFE);
			}
		} while (worm[player].score > worm[player].oldscore);
		worm[player].oldscore = worm[player].score;
		if (worm[player].multi > 1)
			SetAPen(MainWindowPtr->RPort, WHITE);
		else
			SetAPen(MainWindowPtr->RPort, worm[player].colour);
		if (worm[player].score > 999999L)
			longscore = TRUE;
		stcl_d(output, worm[player].score);
		for (i = 0; i <= 7; i++)
			if (!output[i])
				output[i] = ' ';
		theline = 0;
		len = 6;
		break;
	case LIFE:
		if (worm[player].lives > STARTLIVES)
		{	SetAPen(MainWindowPtr->RPort, WHITE);
			if (worm[player].lives > LIVESLIMIT)
				worm[player].lives = LIVESLIMIT;
		} else SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(output, worm[player].lives);
		for (i = 0; i <= 2; i++)
			if (!output[i])
			{	output[i] = ' ';
				break;
			}
		theline = 1;
		len = 3;
		break;
	case BIAS:
		if (worm[player].bias > 0)
		{	if (worm[player].bias > BIASLIMIT)
				worm[player].bias = BIASLIMIT;
			SetAPen(MainWindowPtr->RPort, WHITE);
		} else
			SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(output, worm[player].bias);
		for (i = 0; i <= 2; i++)
			if (!output[i])
			{	output[i] = ' ';
				break;
			}
		theline = 2;
		len = 3;
		break;
	case NITRO:
		SetAPen(MainWindowPtr->RPort, worm[player].colour);
		if (worm[player].speed == FAST)
			strcpy(output, STATFAST);
		else if (worm[player].speed == NORMAL)
			strcpy(output, STATNORMAL);
		else // assumes worm[player].speed == SLOW
			strcpy(output, STATSLOW);
		theline = 3;
		len = 6;
		break;
	case AMMO:
		if (worm[player].ammo)
		{	if (worm[player].ammo > AMMOLIMIT)
				worm[player].ammo = AMMOLIMIT;
			SetAPen(MainWindowPtr->RPort, WHITE);
		} else
			SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(output, worm[player].ammo);
		for (i = 0; i <= 2; i++)
			if (!output[i])
			{	output[i] = ' ';
				break;
			}
		if (iso)
			theline = 4;
		else theline = 5;
		len = 3;
		break;
	case POWER:
		switch(worm[player].power)
		{	case 0:
				SetAPen(MainWindowPtr->RPort, worm[player].colour);
				strcpy(output, STATSINGLE);
				break;
			case 2:
				SetAPen(MainWindowPtr->RPort, WHITE);
				strcpy(output, STATTRIPLE);
				break;
			case 4:
				SetAPen(MainWindowPtr->RPort, WHITE);
				strcpy(output, STATQUINTUPLE);
				break;
			case 6:
				SetAPen(MainWindowPtr->RPort, WHITE);
				strcpy(output, STATSEPTUPLE);
				break;
			default:
				break;
		}
		if (iso)
			theline = 5;
		else theline = 6;
		len = 6;
		break;
	case ARMOUR:
		if (worm[player].armour > MODELIMIT)
			worm[player].armour = MODELIMIT;
		if (worm[player].mode == ARMOUR)
			SetAPen(MainWindowPtr->RPort, WHITE);
		else
			SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(output, worm[player].armour);
		for (i = 0; i <= 2; i++)
			if (!output[i])
			{	output[i] = ' ';
				break;
			}
		if (iso)
			theline = 6;
		else theline = 8;
		len = 3;
		break;
	case TONGUE:
		if (worm[player].tongue > MODELIMIT)
			worm[player].tongue = MODELIMIT;
		if (worm[player].mode == TONGUE)
			SetAPen(MainWindowPtr->RPort, WHITE);
		else
			SetAPen(MainWindowPtr->RPort, worm[player].colour);
		stci_d(output, worm[player].tongue);
		for (i = 0; i <= 2; i++)
			if (!output[i])
			{	output[i] = ' ';
				break;
			}
		if (iso)
			theline = 7;
		else theline = 9;
		len = 3;
		break;
	default:
		print = FALSE;
		break;
	}

    if (print)
	{	if (iso)
			switch(player)
			{
			case 0:
				Move(MainWindowPtr->RPort, 0  ,   8 + (theline * FONTY));
				break;
			case 1:
				Move(MainWindowPtr->RPort, 528, 189 + (theline * FONTY));
				break;
			case 2:
				Move(MainWindowPtr->RPort, 66 ,   8 + (theline * FONTY));
				break;
			case 3:
				Move(MainWindowPtr->RPort, 592, 189 + (theline * FONTY));
				break;
			default:
				// assert(0);
				break;
			}
		else
		{	Move
			(	MainWindowPtr->RPort,
				(FONTX * 3) + (worm[player].statx * ENDXPIXEL),
				STARTYPIXEL + 6 + (theline * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
			);
		}
		Text(MainWindowPtr->RPort, output, len);
}	}

void stopfx(ULONG receipt)
{	SBYTE i;

    if (mode == FX)
        for (i = 0; i <= 3; i++)
            if (eversent[i] && (!(CheckIO(AudioRqPtr[i]))) && (receipt == 0L || receipt == receipter[i]))
            {   AbortIO(AudioRqPtr[i]);
                WaitIO(AudioRqPtr[i]);
}           }

void titlescreen(void)
{	SBYTE					lisa = 0, object = LASTOBJECT, player;
	SWORD					descx = FIRSTDESCX;
	ULONG					class;
	UWORD					code, qual;
	struct IntuiMessage*	MsgPtr;
	struct Gadget*			WhichGadgetPtr;
	struct MenuItem*		ItemPtr;

	effect(FXTITLESCREEN);
	Forbid();
	MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
	Permit();
	clearscreen();

	SetMenuStrip(MainWindowPtr, MenuPtr);
	for (player = 0; player <= 3; player++)
		GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
	GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);

	clearkybd();
	clearjoystick();
	hiscores();
	hiscorenames();

	effect(FXSTAMPED);
	DrawImage(MainWindowPtr->RPort, &Logo, 203, 22);

	do
	{	TimerRqPtr->tr_node.io_Command	= TR_ADDREQUEST;
		TimerRqPtr->tr_time.tv_secs		= 0;
		TimerRqPtr->tr_time.tv_micro	= ANIMDELAY;
		SendIO(TimerRqPtr);

		if (descx == FIRSTDESCX)
		{	if (++object > LASTOBJECT)
				object = 0;
			say(objectdesc[object], WHITE);
			Image.ImageData = ImageData[object];
			DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
		}
		SetAPen(MainWindowPtr->RPort, BLACK);
		Move(MainWindowPtr->RPort, descx - 1, DESCY - 1);
		Draw(MainWindowPtr->RPort, descx - 1, DESCY + 1 + SQUAREY);
		Move(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY - 1);
		Draw(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY + 1 + SQUAREY);
		Image.ImageData = ImageData[object];
		DrawImage(MainWindowPtr->RPort, &Image, descx, DESCY);
		if (++descx > SECONDDESCX)
			descx = FIRSTDESCX;

		while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
		{	WhichGadgetPtr = (struct Gadget *) MsgPtr->IAddress;
			class = MsgPtr->Class;
			code  = MsgPtr->Code;
			qual  = MsgPtr->Qualifier;
			GT_ReplyIMsg(MsgPtr);
            switch (class) {
			case IDCMP_RAWKEY:
				if (!(qual & IEQUALIFIER_REPEAT))
                    switch (code) {
					case F:
						toggle(F);
						break;
					case M:
						toggle(M);
						break;
					case SPACEBAR:
						effect(FXCLICK);
						a = FIELDEDIT;
						break;
					case F1:
					case ALPHAONE:
					case NUMERICONE:
						effect(FXCLICK);
						if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						{	if (--worm[0].control < 0)
								worm[0].control = 2;
						} else
						{	if (++worm[0].control > 2)
								worm[0].control = 0;
						}
						GT_SetGadgetAttrs(CycleGadgetPtr[0], MainWindowPtr, NULL, GTCY_Active, worm[0].control, TAG_DONE);
						break;
					case F2:
					case ALPHATWO:
					case NUMERICTWO:
						effect(FXCLICK);
						if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
						{	if (--worm[1].control < 0)
								worm[1].control = 2;
						} else
						{	if (++worm[1].control > 2)
								worm[1].control = 0;
						}
						GT_SetGadgetAttrs(CycleGadgetPtr[1], MainWindowPtr, NULL, GTCY_Active, worm[1].control, TAG_DONE);
						break;
					case F3:
					case ALPHATHREE:
					case NUMERICTHREE:
						effect(FXCLICK);
						if (joy)
						{	if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
							{	if (--worm[2].control < 0)
									worm[2].control = 2;
							} else
							{	if (++worm[2].control > 2)
									worm[2].control = 0;
						}	}
						else if (worm[2].control == NONE)
							worm[2].control = AMIGA;
						else
							worm[2].control = NONE;
						GT_SetGadgetAttrs(CycleGadgetPtr[2], MainWindowPtr, NULL, GTCY_Active, worm[2].control, TAG_DONE);
						break;
					case F4:
					case ALPHAFOUR:
					case NUMERICFOUR:
						effect(FXCLICK);
						if (worm[3].control == NONE)
							worm[3].control = AMIGA;
						else
							worm[3].control = NONE;
						GT_SetGadgetAttrs(CycleGadgetPtr[3], MainWindowPtr, NULL, GTCY_Active, worm[3].control, TAG_DONE);
						break;
					case RETURN:
					case ENTER:
						a = PLAYGAME;
						break;
					case I:
						effect(FXCLICK);
						iso = !iso;
						GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GTCB_Checked, iso, TAG_DONE);
						break;
					case ESCAPE:
						if (verify())
							cleanexit(EXIT_SUCCESS);
						break;
					case HELP:
						fileabout();
						break;
					case INTERNATIONALONE:
						if (lisa == 2)
						{	say(FIRSTLISA, RED);
							anykey(FALSE);
							lisa = 0;
						} else
							lisa = 1;
						break;
					case INTERNATIONALTWO:
						if (lisa == 1)
						{	say(SECONDLISA, BLUE);
							anykey(FALSE);
							lisa = 0;
						} else
							lisa = 2;
						break;
					default:
						break;
					}
				break;
			case IDCMP_MENUPICK:
				while (code != MENUNULL)
				{	ItemPtr = ItemAddress(MenuPtr, code);
					switch (MENUNUM(code))
					{
					case MN_PROJECT:
						switch (ITEMNUM(code))
						{
						case IN_NEW:
							effect(FXFILENEW);
							newfields();
							say("New done.", WHITE);
							anykey(TRUE);
							say(objectdesc[object], WHITE);
							break;
						case IN_OPEN:
							effect(FXFILEOPEN);
							fileopen();
							say(objectdesc[object], WHITE);
							break;
						case IN_SAVE:
                            effect(FXFILESAVE);
                            filesaveas(FALSE);
							say(objectdesc[object], WHITE);
							break;
						case IN_SAVEAS:
                            effect(FXFILESAVEAS);
							filesaveas(TRUE);
							say(objectdesc[object], WHITE);
							break;
						case IN_ABOUT:
							fileabout();
							break;
						case IN_QUIT:
							if (verify())
								cleanexit(EXIT_SUCCESS);
							break;
						default:
							break;
						}
						break;
					default:
						break;
					}
					code = ItemPtr->NextSelect;
				}
				break;
			case IDCMP_MOUSEBUTTONS:
				if (code == SELECTDOWN)
					if (ignore)
						ignore = FALSE;
					else a = PLAYGAME;
				break;
			case IDCMP_REFRESHWINDOW:
				GT_BeginRefresh(MainWindowPtr);
				GT_EndRefresh(MainWindowPtr, TRUE);
				break;
			case IDCMP_GADGETUP:
				if (WhichGadgetPtr == CheckboxGadgetPtr)
					iso = !iso;
				else
				{	for (player = 0; player <= 3; player++)
						if (WhichGadgetPtr == CycleGadgetPtr[player])
						{	if (code == HUMAN && player == 2 && (!joy))
							{	if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
									worm[2].control = NONE;
								else
									worm[2].control = AMIGA;
								GT_SetGadgetAttrs(CycleGadgetPtr[2], MainWindowPtr, NULL, GTCY_Active, worm[2].control, TAG_DONE);
							} else
								worm[player].control = code;
							break;
						}
				}
				break;
			case IDCMP_ACTIVEWINDOW:
				ignore = TRUE;
				break;
			case IDCMP_CLOSEWINDOW:
				cleanexit(EXIT_SUCCESS);
				break;
			default:
                // IDCMP_MENUVERIFY, IDCMP_INTUITICKS
                break;
		}	}
		if (joy && GetMsg(JoyPortPtr))
		{	if (GameEvent.ie_Code == IECODE_LBUTTON)
				a = PLAYGAME;
			sendreadrequest();
		}
		if (a == PLAYGAME)
		{	if (worm[0].control == NONE && worm[1].control == NONE && worm[2].control == NONE && worm[3].control == NONE)
			{	say("No worms active!", WHITE);
				anykey(TRUE);
				a = GAMEOVER;
		}	}
		if (CheckIO(TimerRqPtr))
			draw(CLOCKICON, ICONY, CLOCK);
		else draw(CLOCKICON, ICONY, BLACKENED);
		WaitIO(TimerRqPtr);
	} while (a == GAMEOVER);

	for (player = 0; player <= 3; player++)
		GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
	GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
	Image.ImageData = ImageData[BLACKENED];
	DrawImage(MainWindowPtr->RPort, &Image, FIRSTDESCX, DESCY);
	DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
	if (a == FIELDEDIT)
		fieldedit();
	else
	{	newgame();
		clearkybd();
		Forbid();
		MainWindowPtr->Flags |= WFLG_RMBTRAP;
		Permit();
}	}

void toggle(SBYTE key)
{	PERSIST ABOOL songstarted = FALSE;

	switch(key)
	{
	case F:
		if (mode == FX)					// F in FX mode: no sound
		{	freefx();
			mode = FALSE;
			draw(MUSICICON, ICONY, BLACKENED);
		} else if (fxable > 0)			// F otherwise: change to FX mode
		{	if (mode == MUSIC)			// stop any music that is playing
			{	StopPlayer();
				FreePlayer();
			}
			if (fxable == 3)			// load samples if needed
				loadthefx();
			if (fxable == 1)			// if we have samples in memory
			{	if (beginfx())
				{	mode = FX;
					effect(FXFX);
					draw(MUSICICON, ICONY, FX);
		}	}	}
		break;
	case M:
		if (mode == MUSIC)				// M in MUSIC mode: no sound
		{	StopPlayer();
			FreePlayer();
			mode = FALSE;
			draw(MUSICICON, ICONY, BLACKENED);
		} else if (musicable > 0)		// M otherwise: change to music mode
		{	if (mode == FX)				// stop any samples that are playing
				freefx();
			/* Of course, these statements are ordered in this
			way for a reason, so don't change it. */
			if (musicable == 3)
				loadthemusic();
			if (musicable == 1)
			{	if (GetPlayer(0))
				{	say("No channels for music!", RED);
					anykey(TRUE);
					mode = FALSE;
					draw(MUSICICON, ICONY, BLACKENED);
				} else
				{	if (songstarted)
						ContModule(SongPtr);
					else
					{	PlayModule(SongPtr);
						songstarted = TRUE;
					}
					mode = MUSIC;
					draw(MUSICICON, ICONY, MUSIC);
		}	}	}
		break;
	default:
		break;
}	}
	
void underline(SBYTE square)
{	/* Removes old underline, draws new underline.
	
	square: which square-type to underline, or -1 for clear only.
	Squares which do not correspond to any pseudo-gadgets
	(eg. objects) are converted to -1s. */

	PERSIST	SWORD oldy = -1;
			SWORD y;

	switch(square)
	{
	case GOLD:
		y = UNDERLINEOFFSET;
		break;
	case SILVER:
		y = UNDERLINEOFFSET + (SQUAREY *  3);
		break;
	case EMPTY:
		y = UNDERLINEOFFSET + (SQUAREY *  6);
		break;
	case WOOD:
		y = UNDERLINEOFFSET + (SQUAREY *  9);
		break;
	case STONE:
		y = UNDERLINEOFFSET + (SQUAREY * 12);
		break;
	case METAL:
		y = UNDERLINEOFFSET + (SQUAREY * 15);
		break;
	case ONE:
		y = UNDERLINEOFFSET + (SQUAREY * 18);
		break;
	case TWO:
		y = UNDERLINEOFFSET + (SQUAREY * 21);
		break;
	case START:
		y = UNDERLINEOFFSET + (SQUAREY * 24);
		break;
	default:
		square = -1;
		break;
	}

	if (oldy != -1)
	{	SetAPen(MainWindowPtr->RPort, BLACK);
		Move(MainWindowPtr->RPort, STARTXPIXEL - SQUAREX + 1, oldy);
		Draw(MainWindowPtr->RPort, STARTXPIXEL - SQUAREX + 1, oldy + SQUAREY + 3);
	}
	if (square != -1)
	{	SetAPen(MainWindowPtr->RPort, WHITE);
		Move(MainWindowPtr->RPort, STARTXPIXEL - SQUAREX + 1, y);
		Draw(MainWindowPtr->RPort, STARTXPIXEL - SQUAREX + 1, y + SQUAREY + 3);
		oldy = y;
}	}

ABOOL verify(void)
{	pausetimer();
	if (modified && (EasyRequest(MainWindowPtr, &EasyStruct, NULL) == 0))
		return FALSE;
	else return TRUE;
	unpausetimer();
}

void waitasec(void)
{   Delay(50);
}

void systemsetup(void)
{	worm[0].control	= NONE;
	worm[1].control	= HUMAN;
	worm[2].control	= NONE;
	worm[3].control = AMIGA;
}

ABOOL ZOpen(STRPTR fieldname, ABOOL write)
{	if (!write)
		if (FilePtr = Open(fieldname, MODE_OLDFILE))
			return TRUE;
		else return FALSE;
	else
		if (FilePtr = Open(fieldname, MODE_NEWFILE))
			return TRUE;
		else return FALSE;
}
ABOOL ZRead(STRPTR IOBuffer, ULONG length)
{	if (Read(FilePtr, IOBuffer, length) == length)
		return TRUE;
	else return FALSE;
}
ABOOL ZWrite(STRPTR IOBuffer, ULONG length)
{	if (Write(FilePtr, IOBuffer, length) == length)
		return TRUE;
	else return FALSE;
}
ABOOL ZClose(void)
{	if (Close(FilePtr))
	{	FilePtr = NULL;
		return TRUE;
	} else
	{	/* "If Close() returns DOSFALSE, the user has already cancelled an
		error requester and the function has already freed the FileHandle
		(and even marked it so any attempt to close it again will bring up
		the "Software Failure" requester). Therefore FilePtr should be set
		to zero in any case." - Jilles Tjoelker. */

		FilePtr = NULL;
		return FALSE;
}	}

void timing(void)
{	;
}

void loadthefx(void)
{	UBYTE*          p8data;

	TEXT			saystring[SAYLIMIT + 1];

	SBYTE           code = 0, i;
	SBYTE           iobuffer[8];        // buffer for 8SVX.VHDR 
	SBYTE*          psample[2];         // sample pointers
	struct Chunk*   p8Chunk;            // pointers for 8SVX parsing
	Voice8Header*   pVoice8Header;
	ULONG           rd8count;

	say("Loading sound effects...", WHITE);
	fxable = 1;

	for (i = 0; i <= SAMPLES; i++)
	    sbase[i] = NULL;
	for (i = 0; i <= SAMPLES; i++)
	{   if (!(FilePtr = Open(samp[i].filename, MODE_OLDFILE)))
	        code = 1;                               // can't open file
	    else
	    {   rd8count = Read(FilePtr, iobuffer, 8L);
	        if (rd8count == -1)
	            code = 2;                           // can't read file
	        else if (rd8count < 8)
	            code = 3;                           // not an IFF 8SVX; too short
	        else
	        {   p8Chunk = (struct Chunk *) iobuffer;
	            if (p8Chunk->ckID != ID_FORM)
	                code = 4;                       // not an IFF FORM
	            else if (!(fbase = (UBYTE *) AllocMem(fsize = p8Chunk->ckSize, MEMF_PUBLIC | MEMF_CLEAR)))
	                code = 5;                       // no memory for read
	            else
	            {   p8data = fbase;
	                rd8count = Read(FilePtr, p8data, p8Chunk->ckSize);
	                if (rd8count == -1)
	                    code = 6;                   // read error
	                else if (rd8count < p8Chunk->ckSize)
	                    code = 7;                   // malformed IFF; too short
	                else if (MAKE_ID(*p8data, *(p8data + 1), *(p8data + 2), *(p8data + 3)) != ID_8SVX)
	                    code = 8;                   // not an IFF 8SVX
	                else
	                {   p8data = p8data + 4;
                    	while (p8data < fbase + fsize)
                    	{   p8Chunk = (struct Chunk *) p8data;
                    	    switch(p8Chunk->ckID) {
                    	    case ID_VHDR:
                    	        pVoice8Header = (Voice8Header *) (p8data + 8L);
                    	        break;
                    	    case ID_BODY:
                    	        psample[0]    = (SBYTE *) (p8data + 8L);
                    	        psample[1]    = psample[0] + pVoice8Header->oneShotHiSamples;
                    	        length[i][0]  = (ULONG) pVoice8Header->oneShotHiSamples;
                    	        length[i][1]  = (ULONG) pVoice8Header->repeatHiSamples;

								/* To grab the volume level from the IFF
								8SVX file itself, add this here:

								samp[i].volume  = (SBYTE) (pVoice8Header->volume / 156); */

                   	        	break;
                   	    	default:
                   	    		break;
                        	}
                        	p8data += 8L + p8Chunk->ckSize;
                        	if (p8Chunk->ckSize & 1L == 1)
                        	    p8data++;
                    	}
                    	if (length[i][0] == 0)
                    	    yes[i] = 1;
                    	else yes[i] = 0;
                    	if (length[i][yes[i]] <= 102400)
                    	    ssize[i] = length[i][yes[i]];
                    	else ssize[i] = 102400;
                    	sbase[i] = (UBYTE *) AllocMem(ssize[i], MEMF_CHIP | MEMF_CLEAR);
                    	if (!sbase[i])
                    	    code = 9;               // no chip memory
                    	else
                    	{   CopyMem(psample[yes[i]], sbase[i], ssize[i]);
                    	    psample[yes[i]] += ssize[i];
                    	    speed[i] = PALCLOCK / pVoice8Header->samplesPerSec;
                    	    if (fbase)
                    	    {   FreeMem(fbase, fsize);
                    	        fbase = NULL;
                    	    }
                    	    if (FilePtr)
                    	    {   Close(FilePtr);
                    	        FilePtr = NULL;
    	}   }   }   }   }   }
    	if (code)
    	{   freefx();
    	    fxable = 0;
			strcpy(saystring, samp[i].filename);
			strcat(saystring, ": ");
    	    strcat(saystring, sfxerror[code]);
			say(saystring, RED);
			anykey(TRUE);
			break;
}	}   }

void loadthemusic(void)
{	if (!(MEDPlayerBase = (struct MEDPlayerBase *) OpenLibrary("medplayer.library", 0L)))
	{	say("Can't open MEDPlayer.library!", RED);
		anykey(TRUE);
	} else
	{	say("Loading music...", WHITE);
		if (SongPtr = (struct MMD0 *) LoadModule(MEDMODULE))
			musicable = TRUE;
		else
		{	say("Can't load music!", RED);
			anykey(TRUE);
}	}	}

void dot(SBYTE x, SBYTE y)
{	SWORD xx, yy;

	/* Squares are dotted as follows:
	
	 012345678
	0.........
	1.........
	2...WWW...
	3...WWWB..
	4....BBB..
	5......... */

	xx = (x * SQUAREX) + STARTXPIXEL;
	yy = (y * SQUAREY) + STARTYPIXEL;

	SetAPen(MainWindowPtr->RPort, WHITE);
	WritePixel(MainWindowPtr->RPort, xx + 3, yy + 2);
	WritePixel(MainWindowPtr->RPort, xx + 4, yy + 2);
	WritePixel(MainWindowPtr->RPort, xx + 5, yy + 2);
	WritePixel(MainWindowPtr->RPort, xx + 3, yy + 3);
	WritePixel(MainWindowPtr->RPort, xx + 4, yy + 3);
	WritePixel(MainWindowPtr->RPort, xx + 5, yy + 3);
	SetAPen(MainWindowPtr->RPort, BLACK);
	WritePixel(MainWindowPtr->RPort, xx + 6, yy + 3);
	WritePixel(MainWindowPtr->RPort, xx + 4, yy + 4);
	WritePixel(MainWindowPtr->RPort, xx + 5, yy + 4);
	WritePixel(MainWindowPtr->RPort, xx + 6, yy + 4);
}

void clearscreen(void)
{	SBYTE player;

	DrawImage
	(	MainWindowPtr->RPort,
		&Background,
		0,
		0
	);
    if (mode == MUSIC)
		draw(MUSICICON, ICONY, MUSIC);
	else if (mode == FX)
		draw(MUSICICON, ICONY, FX);
	else draw(MUSICICON, ICONY, BLACKENED);
    if (sticky)
		draw(STICKYICON, ICONY, STICKY);
	else draw(STICKYICON, ICONY, BLACKENED);
	draw(CLOCKICON, ICONY, CLOCK);
	
if (a != FIELDEDIT)
	if (!iso)
	{	for (player = 0; player <= 3; player++)
		{	if (worm[player].control != NONE)
			{	Image.ImageData = ImageData[BONUS];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1               + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[LIFE];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (1 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[BIAS];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (2 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[NITRO];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (3 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[AMMO];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (5 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[POWER];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (6 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[ARMOUR];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (8 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
				Image.ImageData = ImageData[TONGUE];
				DrawImage
				(	MainWindowPtr->RPort,
					&Image,
					(worm[player].statx * ENDXPIXEL) + FONTX,
					STARTYPIXEL + 1 + (9 * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
				);
	}	}	}
	else // assert (iso);
	{	if (worm[0].control != NONE || worm[2].control != NONE)
		{	Image.ImageData = ImageData[BONUS];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 4);
			Image.ImageData = ImageData[LIFE];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 12);
			Image.ImageData = ImageData[BIAS];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 20);
			Image.ImageData = ImageData[NITRO];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 28);
			Image.ImageData = ImageData[AMMO];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 36);
			Image.ImageData = ImageData[POWER];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 44);
			Image.ImageData = ImageData[ARMOUR];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 52);
			Image.ImageData = ImageData[TONGUE];
			DrawImage(MainWindowPtr->RPort, &Image, 54, 60);
		}
		if (worm[1].control != NONE || worm[3].control != NONE)
		{	Image.ImageData = ImageData[BONUS];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 183);
			Image.ImageData = ImageData[LIFE];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 191);
			Image.ImageData = ImageData[BIAS];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 199);
			Image.ImageData = ImageData[NITRO];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 207);
			Image.ImageData = ImageData[AMMO];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 215);
			Image.ImageData = ImageData[POWER];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 223);
			Image.ImageData = ImageData[ARMOUR];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 231);
			Image.ImageData = ImageData[TONGUE];
			DrawImage(MainWindowPtr->RPort, &Image, 579, 239);
}	}	}

void datestamp(void)
{	ULONG				seconds, micros;
	struct ClockData	Date;
	TEXT				temp[5];

	CurrentTime(&seconds, &micros);
	Amiga2Date(seconds, &Date);
	stci_d(times, Date.hour);			// hh
	align(times, 2, ' ');
	times[2] = ':';                     // hh:
	times[3] = 0;
	stci_d(temp, Date.min);
	align(temp, 2, '0');
	temp[2] = 0;
	strcat(times, temp);				// hh:mm

	stci_d(date, Date.mday);			// dd
	align(date, 2, ' ');
	date[2] = '/';
	date[3] = 0;						// dd/
	stci_d(temp, Date.month);
	align(temp, 2, ' ');
	temp[2] = 0;
	strcat(date, temp);					// dd/mm
	strcat(date, "/");					// dd/mm/
	stci_d(temp, Date.year);
	temp[0] = temp[2];
	temp[1] = temp[3];
	temp[2] = 0;
	strcat(date, temp);					// dd/mm/yy
}

void turborender(void)
{	SBYTE			x, y;

	for (x = 0; x <= FIELDX; x++)
		for (y = 0; y <= FIELDY; y++)
			draw(x, y, board[level][x][y]);

	if (a == FIELDEDIT)
	{	draw(startx[level], starty[level], START);
		if (teleport[level][0].alive)
		{	draw(teleport[level][0].x, teleport[level][0].y, ONE);
			draw(teleport[level][1].x, teleport[level][1].y, TWO);
}	}	}

SWORD xpixeltosquare(SWORD x)
{	x = (x - STARTXPIXEL) / SQUAREX;
	if (x < 0)
		x--;
	return (x);
}
SWORD ypixeltosquare(SWORD y)
{	y = (y - STARTYPIXEL) / SQUAREY;
	if (y < 0)
		y--;
	return (y);
}

void parsewb(void)
{	struct DiskObject*	DiskObject;
	char**				ToolArray;
	char*				s;

	if ((*WBArg->wa_Name) && (DiskObject = GetDiskObject(WBArg->wa_Name)))
	{	ToolArray = (char **) DiskObject->do_ToolTypes;
		
		if (s = (char *) FindToolType(ToolArray, "NOFX"))
			fxable = 3;
		if (s = (char *) FindToolType(ToolArray, "NOMUSIC"))
			musicable = 3;
		if (s = (char *) FindToolType(ToolArray, "OVERHEAD"))
			iso = FALSE;
		if (s = (char *) FindToolType(ToolArray, "FILE"))
			strcpy(pathname, WBArg->wa_Name);
		if (s = (char *) FindToolType(ToolArray, "GREEN"))
		{	if (MatchToolValue(s, "HUMAN"))
				worm[0].control = HUMAN;
			elif (MatchToolValue(s, "AMIGA"))
				worm[0].control = AMIGA;
			elif (MatchToolValue(s, "NONE"))
				worm[0].control = NONE;
		}
		if (s = (char *) FindToolType(ToolArray, "RED"))
		{	if (MatchToolValue(s, "HUMAN"))
				worm[1].control = HUMAN;
			elif (MatchToolValue(s, "AMIGA"))
				worm[1].control = AMIGA;
			elif (MatchToolValue(s, "NONE"))
				worm[1].control = NONE;
		}
		if (s = (char *) FindToolType(ToolArray, "BLUE"))
		{	if (MatchToolValue(s, "HUMAN"))
				worm[2].control = HUMAN;
			elif (MatchToolValue(s, "AMIGA"))
				worm[2].control = AMIGA;
			elif (MatchToolValue(s, "NONE"))
				worm[2].control = NONE;
		}
		if (s = (char *) FindToolType(ToolArray, "YELLOW"))
		{	if (MatchToolValue(s, "AMIGA"))
				worm[3].control = AMIGA;
			elif (MatchToolValue(s, "NONE"))
				worm[3].control = NONE;
		}
		FreeDiskObject(DiskObject);
}	}

void pausetimer(void)
{	GetSysTime(CurrentValPtr);
}

void unpausetimer(void)
{	GetSysTime(PausedValPtr);
	SubTime(PausedValPtr, CurrentValPtr);
	AddTime(StartValPtr, PausedValPtr);
}

// Must have blank line at EOF.
