/* $Filename:       WormWars/Source/fets.c $
 * $VER:            WormWars 4.0 (1.11.98) $
 * $Description:    Field editor and title screen for Worm Wars 4.0 $
 *
 * © Copyright 1993-1998 James R. Jacobs of Amigan Software.
 * Freely distributable.
 *
 * Release version.
 
#INCLUDES -------------------------------------------------------------- */

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

// EXTERNAL VARIABLES -----------------------------------------------------

IMPORT	struct MEDPlayerBase*	MEDPlayerBase;

IMPORT	struct FileRequester*	ASLRqPtr;
IMPORT	struct InputEvent		GameEvent;
IMPORT	struct Gadget*			CycleGadgetPtr[4];
IMPORT	struct Gadget*			GListPtr;
IMPORT	struct Gadget*			StringGadgetPtr[HISCORES + 1];
IMPORT	struct Image			Image;
IMPORT	UWORD chip				ImageData[ARRAYSIZE + 1][SQUAREY * 4];
IMPORT	struct MsgPort*			JoyPortPtr;
IMPORT	struct Menu*			MenuPtr;
IMPORT	struct NewMenu			NewMenu[MENUENTRIES];
IMPORT	struct Screen*			ScreenPtr;
IMPORT	struct MMD0*			SongPtr;
IMPORT	struct TeleportStruct	teleport[MAXLEVELS + 1][4];
IMPORT	struct timerequest*		TimerRqPtr;
IMPORT	APTR					VisualInfoPtr;
IMPORT	struct Window*			MainWindowPtr;
		struct InputEvent		InputEvent;
		struct IEPointerPixel	NewPixel;
IMPORT	struct IOStdReq*		InputRqPtr;

IMPORT	BYTE					a;
IMPORT	UBYTE					board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1];
IMPORT	BOOL					classic;
IMPORT	UBYTE					eachworm[4][3][9];
IMPORT	BOOL					fxable, musicable;
IMPORT	struct HiScoreStruct	hiscore[HISCORES + 1];
IMPORT	BYTE					ice;
IMPORT	BOOL					ignore;
IMPORT	BOOL					joy;
IMPORT	BOOL					leftdown;
IMPORT	BYTE					level;
IMPORT	BYTE					levels;
IMPORT	BYTE					lo, hi;
IMPORT	BOOL					menu;
IMPORT	BYTE					mode;
IMPORT	BOOL					modified;
IMPORT	STRPTR					pathname;
IMPORT	ULONG					r;
IMPORT	BYTE					reallevel;
IMPORT	BOOL					rightdown;
IMPORT	BYTE					startx[MAXLEVELS + 1];
IMPORT	BYTE					starty[MAXLEVELS + 1];
IMPORT	BOOL					trainer;
IMPORT	struct WormStruct		worm[4];

// GLOBAL VARIABLES -------------------------------------------------------

		BOOL					clearthem = FALSE, sticky = FALSE;
		BYTE					brush = STONE;

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

void activate(BYTE which)
{	NewPixel.iepp_Screen		= (struct Screen *) ScreenPtr;
	NewPixel.iepp_Position.X	= 472;
	NewPixel.iepp_Position.Y	= 121 + (which * HISCOREDISTANCE);
	InputEvent.ie_EventAddress	= (APTR) &NewPixel;
	InputEvent.ie_NextEvent		= NULL;
	InputEvent.ie_Class			= IECLASS_NEWPOINTERPOS;
	InputEvent.ie_SubClass		= IESUBCLASS_PIXEL;
	InputEvent.ie_Code			= IECODE_NOBUTTON;
	InputEvent.ie_Qualifier		= NULL;
	InputRqPtr->io_Data			= (APTR) &InputEvent;
	InputRqPtr->io_Length		= sizeof (struct InputEvent);
	InputRqPtr->io_Command		= IND_WRITEEVENT;
	DoIO((struct IORequest *) InputRqPtr);

	NewPixel.iepp_Screen		= (struct Screen *) ScreenPtr;
	InputEvent.ie_NextEvent		= NULL;
	InputEvent.ie_Class			= IECLASS_RAWKEY;
	InputEvent.ie_Code			= 0x00;
	InputEvent.ie_Qualifier		= IEQUALIFIER_LCOMMAND + IEQUALIFIER_LALT;
	InputRqPtr->io_Data			= (APTR) &InputEvent;
	InputRqPtr->io_Length		= sizeof (struct InputEvent);
	InputRqPtr->io_Command		= IND_WRITEEVENT;
	DoIO((struct IORequest *) InputRqPtr);

	NewPixel.iepp_Screen		= (struct Screen *) ScreenPtr;
	InputEvent.ie_NextEvent		= NULL;
	InputEvent.ie_Class			= IECLASS_RAWMOUSE;
	InputEvent.ie_Code			= 0x00 + IECODE_UP_PREFIX;
	InputEvent.ie_Qualifier		= NULL;
	InputRqPtr->io_Data			= (APTR) &InputEvent;
	InputRqPtr->io_Length		= sizeof (struct InputEvent);
	InputRqPtr->io_Command		= IND_WRITEEVENT;
	DoIO((struct IORequest *) InputRqPtr);
}

void clearhiscores(void)
{	BYTE which;
	
	clearthem = FALSE;
	for (which = 0; which <= HISCORES; which++)
	{	hiscore[which].player = -1;
		hiscore[which].level = 0;
		hiscore[which].score = 0L;
		hiscore[which].name[0] = 0;
		hiscore[which].fresh = FALSE;
}   }

void clearstats(void)
{	SetAPen(MainWindowPtr->RPort, BLACK);
	RectFill(MainWindowPtr->RPort, 0, STARTYPIXEL, STARTXPIXEL - 1, ENDYPIXEL);
	RectFill(MainWindowPtr->RPort, ENDXPIXEL + 1, STARTYPIXEL, SCREENXPIXEL, SCREENYPIXEL);
    if (mode == MUSIC)
		draw(MUSICICON, ICONY, MUSIC);
	else if (mode == FX)
		draw(MUSICICON, ICONY, FX);
    if (sticky)
		draw(STICKYICON, ICONY, STICKY);
}

void fileabout(void)
{	BOOL					done = FALSE;
	BYTE					line;
	UWORD					code, qual;
	ULONG					class;
	struct Window*			AboutWindowPtr;
	struct IntuiMessage*	MsgPtr;
	
	effect(FXFILEABOUT);
	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
	{	SetAPen(AboutWindowPtr->RPort, ORANGE);
		RectFill(AboutWindowPtr->RPort, 8, 13, ABOUTXPIXEL - 11, ABOUTYPIXEL - 6);
		SetAPen(AboutWindowPtr->RPort, DARKYELLOW);
		Move(AboutWindowPtr->RPort, 7, ABOUTYPIXEL - 5);
		Draw(AboutWindowPtr->RPort, 7, 12);
		Draw(AboutWindowPtr->RPort, ABOUTXPIXEL - 10, 12);
		SetAPen(AboutWindowPtr->RPort, YELLOW);
		Draw(AboutWindowPtr->RPort, ABOUTXPIXEL - 10, ABOUTYPIXEL - 5);
		Draw(AboutWindowPtr->RPort, 8, ABOUTYPIXEL - 5);
		SetAPen(AboutWindowPtr->RPort, BLACK);
		SetBPen(AboutWindowPtr->RPort, ORANGE);
		for (line = 0; line <= ABOUTLINES; line++)
		{	Move(AboutWindowPtr->RPort, about[line].x, about[line].y);
			Text(AboutWindowPtr->RPort, about[line].text, (BYTE) strlen(about[line].text));
		}
		while (!done)
		{	while (MsgPtr = (struct IntuiMessage *) GetMsg(AboutWindowPtr->UserPort))
			{	class = MsgPtr->Class;
				code  = MsgPtr->Code;
				qual  = MsgPtr->Qualifier;
				ReplyMsg(MsgPtr);
				if (class == IDCMP_CLOSEWINDOW)
					done = TRUE;
                else if (class == 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);
								exit(EXIT_SUCCESS);
                        }	}
						else done = TRUE;
        }   }   }
		CloseWindow(AboutWindowPtr);
		clearkybd();
}	}

void fieldedit(void)
{	UBYTE					oldbrush = ANYTHING, stamp;
	BYTE					deltax = 0, deltay = 0, lastx, lasty, pointerx, pointery, which, x, y;
	UWORD					code, qual;
	ULONG					class;
	struct IntuiMessage*	MsgPtr;
	BOOL					timer = FALSE;

ClearMenuStrip(MenuPtr);
menu = FALSE;
NewMenu[MODULESTART + 1 + IN_TS].nm_Flags = CHECKIT | MENUTOGGLE;
NewMenu[MODULESTART + 1 + IN_FE].nm_Flags = CHECKIT | MENUTOGGLE | CHECKED | NM_ITEMDISABLED;
NewMenu[LEVELSTART].nm_Flags = 0;
if (!(MenuPtr = (struct Menu *) CreateMenus(NewMenu, TAG_DONE)))
{	DisplayAlert(AT_Recovery, ALERTMENUCREATE, 24);
	exit(EXIT_FAILURE);
}
if (!(LayoutMenus(MenuPtr, VisualInfoPtr, TAG_DONE)))
{	DisplayAlert(AT_Recovery, ALERTMENULAYOUT, 24);
	if (modified)
		savefields(EMERGENCYNAME);
	exit(EXIT_FAILURE);
}
SetMenuStrip(MainWindowPtr, MenuPtr);
menu = TRUE;

say("Field Editor", WHITE);
setpointer(brush);
if (level > levels)
	level = 1;

// draw pseudo-gadgets

clearstats();
for (which = 0; which <= 7; which++)
	DrawBevelBox(MainWindowPtr->RPort, STARTXPIXEL - 5 - (SQUAREX * 3), 46 + 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 + (   ONEGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F6:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + (   TWOGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F7:", 3);
Move(MainWindowPtr->RPort, STARTXPIXEL - (FONTX * 8), STARTYPIXEL + 5 + ( STARTGADGET * SQUAREY));
Text(MainWindowPtr->RPort, "F8:", 3);
draw(GADGETX,   GOLDGADGET,   GOLD);
draw(GADGETX, SILVERGADGET, SILVER);
draw(GADGETX,  EMPTYGADGET,  EMPTY);
draw(GADGETX,   WOODGADGET,   WOOD);
draw(GADGETX,  STONEGADGET,  STONE);
draw(GADGETX,    ONEGADGET,	   ONE);
draw(GADGETX,    TWOGADGET,    TWO);
draw(GADGETX,  STARTGADGET,  START);
underline(brush);

renderboard();
clearkybd();
saylevel(WHITE);
leftdown = rightdown = FALSE;
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:
			if (code != MENUNULL)
			{	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);
                        filesaveas(FALSE);
						break;
					case IN_SAVEAS:
                        effect(FXFILESAVEAS);
                        filesaveas(TRUE);
						break;
					case IN_ABOUT:
						fileabout();
						break;
					case IN_QUIT:
						if (verify())
							exit(EXIT_SUCCESS);
						break;
					default:
						break;
					}
					break;
				case MN_MODULE:
					a = GAMEOVER;
					break;
				case MN_LEVEL:
					switch (ITEMNUM(code))
					{
					case IN_INSERT:
						levelinsert();
						break;
					case IN_DELETE:
						leveldelete();
						break;
					case IN_ERASE:
						levelerase();
						break;
					case IN_APPEND:
						levelappend();
						break;
					default:
						break;
					}
					break;
				default:
					break;
			}	}
			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);
					renderboard();
				}
				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);
					renderboard();
				}
				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())
							exit(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);
                    filesaveas(FALSE);
				}
				break;
			case A:
				if (!(qual & IEQUALIFIER_REPEAT))
                {   effect(FXFILESAVEAS);
                    filesaveas(TRUE);
                }
                break;
			case Q:
				if (!(qual & IEQUALIFIER_REPEAT))
                {   if (verify())
						exit(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))
					leveldelete();
				break;
			case E:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
					levelerase();
				break;
			case K:
				effect(FXCLICK);
				if (!(qual & IEQUALIFIER_REPEAT))
					levelappend();
				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 = ONE;
				break;
			case ALPHASEVEN:
				effect(FXCLICK);
				stamp = TWO;
				break;
			case ALPHAEIGHT:
				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);
					setbrush(ONE);
				}
				break;
			case F7:
				if (!(qual & IEQUALIFIER_REPEAT))
				{	effect(FXCLICK);
					setbrush(TWO);
				}
				break;
			case F8:
				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;
						blitmode(BLACK);
						draw(STICKYICON, ICONY, ANYTHING);
						blitmode(NORMAL);
				}	}
				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 == NOSQUARE)
				{	blitmode(WHITE);
					draw(x, y, ANYTHING);
					blitmode(NORMAL);
			}	}
			break;
		case IDCMP_MOUSEBUTTONS:
			updatesquare(x, y);
			switch (code)
			{
			case SELECTUP:
				if (ignore)
					ignore = FALSE;
				else leftdown = FALSE;
				break;
			case SELECTDOWN:
				if (!ignore)
				{	effect(FXCLICK);
					leftdown = TRUE;
				}
				break;
			case MENUUP:
				rightdown = FALSE;
				brush = oldbrush;
                oldbrush = ANYTHING;
				break;
			default:
				// MENUDOWN
				break;
			}
			break;
		case IDCMP_ACTIVEWINDOW:
			ignore = TRUE;
			break;
		case IDCMP_CLOSEWINDOW:
			exit(EXIT_SUCCESS);
			break;
        case IDCMP_REFRESHWINDOW:
			GT_BeginRefresh(MainWindowPtr);
			GT_EndRefresh(MainWindowPtr, TRUE);
            break;
		default:
            // IDCMP_MENUVERIFY, IDCMP_INTUITICKS
			break;
	}	}
	else if (joy)
	{	if (GetMsg(JoyPortPtr))
		{	if (GameEvent.ie_Code == IECODE_LBUTTON)
				stamp = brush;
			deltax = (BYTE) GameEvent.ie_position.ie_xy.ie_x;
			deltay = (BYTE) 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);
			    if (stamp == NOSQUARE)
			    {	blitmode(WHITE);
			      	draw(x, y, ANYTHING);
					blitmode(NORMAL);
                }
                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 == 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 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;
				modified = TRUE;
				clearthem = TRUE;
}	}	}	}

// exit to title screen

if (timer)
{	AbortIO(TimerRqPtr);
	WaitIO(TimerRqPtr);
	timer = FALSE;
}
if (oldbrush != ANYTHING)
	brush = oldbrush;
setpointer(NORMAL);
clearstats();
if (clearthem)
	clearhiscores();
matchteleports();

ClearMenuStrip(MenuPtr);
menu = FALSE;
NewMenu[MODULESTART + 1 + IN_TS].nm_Flags  = CHECKIT | MENUTOGGLE | CHECKED | NM_ITEMDISABLED;
NewMenu[MODULESTART + 1 + IN_FE].nm_Flags  = CHECKIT | MENUTOGGLE;
NewMenu[LEVELSTART].nm_Flags = NM_MENUDISABLED;
if (!(MenuPtr = (struct Menu *) CreateMenus(NewMenu, TAG_DONE)))
{	DisplayAlert(AT_Recovery, ALERTMENUCREATE, 24);
	exit(EXIT_FAILURE);
}
if (!(LayoutMenus(MenuPtr, VisualInfoPtr, TAG_DONE)))
{	DisplayAlert(AT_Recovery, ALERTMENULAYOUT, 24);
	if (modified)
		savefields(EMERGENCYNAME);
	exit(EXIT_FAILURE);
}
SetMenuStrip(MainWindowPtr, MenuPtr);
menu = TRUE;
}

void updatesquare(BYTE x, BYTE y)
{	BYTE	which;

	if (startx[level] == x && starty[level] == y)
		draw(x, y, START);
	else if (board[level][x][y] == TELEPORT)
	{	for (which = 0; which <= 1; which++)
			if (teleport[level][which].alive == TRUE && teleport[level][which].x == x && teleport[level][which].y == y)
				draw(x, y, ONE + which);
	} else draw(x, y, board[level][x][y]);
}

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)
				renderboard();
			else hiscores();
        } else
        {   strcpy(temp1, "Couldn't open");
            strcat(temp1, newpathname);
            strcat(temp1, "!");
            say(temp1, WHITE);
    }   }
	if (a == GAMEOVER)
		anykey(TRUE);
}

void filesaveas(BOOL flag)
{   BOOL    cont = TRUE;
    TEXT    newpathname[255], temp1[SAYLIMIT + 1] = "Saved ", 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)
    {   if (savefields(newpathname))
        {   strcpy(pathname, newpathname);
            strcat(temp1, pathname);
            strcat(temp1, " (");
            stci_d(temp2, levels);
            strcat(temp1, temp2);
            strcat(temp1, " levels).");
        } else
        {   strcpy(temp1, "Couldn't save ");
            strcpy(temp1, newpathname);
            strcpy(temp1, "!");
        }
        say(temp1, WHITE);
        if (a == GAMEOVER)
            anykey(TRUE);
}   }

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

/* render hiscores

darkdarkdarkdarkdarkdarkdarkdarkdark
a   a   a   a                      w
r   r   r   r                      h
k   k   k   k                      i
|   |   |   |                      t
whitewhitewhitewhitewhitewhitewhitee */

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, 161, 105 + which * HISCOREDISTANCE, 479, 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, 160, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 160, 104 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 480, 104 + which * HISCOREDISTANCE);

	if (hiscore[which].player != -1)
	{	// divider bars
	
		Move(MainWindowPtr->RPort, 184, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 184, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 256, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 256, 114 + which * HISCOREDISTANCE);
		Move(MainWindowPtr->RPort, 288, 104 + which * HISCOREDISTANCE);
		Draw(MainWindowPtr->RPort, 288, 114 + which * HISCOREDISTANCE);
	}
	
	SetAPen(MainWindowPtr->RPort, WHITE);
	Move(MainWindowPtr->RPort, 161, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 480, 115 + which * HISCOREDISTANCE);
	Draw(MainWindowPtr->RPort, 480, 105 + which * HISCOREDISTANCE);
	SetAPen(MainWindowPtr->RPort, BLACK);

	if (hiscore[which].player != -1)
	{	stci_d(tempstring, which + 1);
		tempstring[1] = '.';
		Move(MainWindowPtr->RPort, 163, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 2);
		stci_d(tempstring, hiscore[which].score);
		align(tempstring, 7, ' ');
		Move(MainWindowPtr->RPort, 195, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 7);
		stci_d(tempstring, hiscore[which].level);
		align(tempstring, 2, ' ');
		Move(MainWindowPtr->RPort, 267, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, tempstring, 2);
		Move(MainWindowPtr->RPort, 294, 112 + which * HISCOREDISTANCE);
		Text(MainWindowPtr->RPort, hiscore[which].name, strlen(hiscore[which].name));
}	}
SetDrMd(MainWindowPtr->RPort, JAM2);
}

void hiscorenames(void)
{
ULONG					class;
BOOL					done;
BYTE					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);
		activate(which);
		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)
					activate(which);
				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();
}	}

void newhiscores(void)
{
STATIC TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R.J. Mical", "Dave Morse"};
BYTE iwhich, player, which;

for (player = lo; player <= hi; player++)
	for (which = 0; which <= HISCORES; which++)
		if (worm[player].control != NONE && worm[player].score >= hiscore[which].score)
		{	// push all worse hiscores down

			if (which < HISCORES)
				for (iwhich = HISCORES; iwhich >= which + 1; iwhich--)
				{	hiscore[iwhich].player	= hiscore[iwhich - 1].player;
					hiscore[iwhich].score	= hiscore[iwhich - 1].score;
					hiscore[iwhich].level	= hiscore[iwhich - 1].level;
					hiscore[iwhich].fresh	= hiscore[iwhich - 1].fresh;
					strcpy(hiscore[iwhich].name, hiscore[iwhich - 1].name);
				}

			modified = TRUE;
			hiscore[which].player = player;
			hiscore[which].score = worm[player].score;
			hiscore[which].level = worm[player].levelreached;
			if (worm[player].control == AMIGA)
			{	strcpy(hiscore[which].name, amiganame[player]);
				hiscore[which].fresh = FALSE;
			} else
			{	strcpy(hiscore[which].name, "(New)");
				hiscore[which].fresh = TRUE;
			}
			break;
}		}

void levelappend(void)
{	UBYTE oldlevel;

	if (levels < MAXLEVELS)
	{	oldlevel = level;
		level = ++levels;
		newfield();
		level = oldlevel;
		saylevel(WHITE);
}	}

void leveldelete(void)
{	BYTE i;

	// pull boards

	if (levels > 1)
	{	if (level < levels)
			for (i = level; i < levels; i++)
				copyfield(i + 1, i);
		else
			level--;
		levels--;
		saylevel(WHITE);
		renderboard();
}	}

void levelerase(void)
{	newfield();
	renderboard();
}

void levelinsert(void)
{	BYTE i;

	// push boards

	if (levels < MAXLEVELS)
	{	for (i = levels; i >= level; i--)
			copyfield(i, i + 1);
		levels++;
		saylevel(WHITE);
		newfield();
		renderboard();
}	}

BOOL loadfields(STRPTR fieldname)
{   BYTE                i, j, IOBuffer[7 + ((FIELDX + 1) * (FIELDY + 1))], x, y;
    LONG                readcount;
    struct FileHandle*  FileHandle;
    TEXT                message[81] = "Opening ";

    /* V4.0 .fset format (private):

    BYTE WORMWARSMAGIC, levels;
    struct
    {   BYTE startx, starty;
        BOOL alive;
        struct
        {   BYTE x, y;
        } teleport[2];
        BYTE field[FIELDX + 1][FIELDY + 1];
    } perlevel[levels];
	struct HiScoreStruct
	{	BYTE player, level;
		ULONG score;
		TEXT name[NAMELENGTH + 1];
	} hiscore[HISCORES + 1];
    TEXT VERSION; */

    strcat(message, fieldname);
    strcat(message, "...");
    say(message, WHITE);
    if (!(FileHandle = (struct FileHandle *) Open(fieldname, MODE_OLDFILE)))
        return(FALSE);
    readcount = Read((BPTR) FileHandle, IOBuffer, 2L);
    if (readcount == -1 || readcount < 2 || IOBuffer[0] != WORMWARSMAGIC)
    {   Close((BPTR) FileHandle);
        return(FALSE);
    }
    levels = IOBuffer[1];
    for (i = 0; i <= levels; i++)
    {   readcount = Read((BPTR) FileHandle, IOBuffer, 7L + ((FIELDX + 1) * (FIELDY + 1)));
        if (readcount == -1 || readcount < (7 + ((FIELDX + 1) * (FIELDY + 1))))
        {   Close((BPTR) FileHandle);
            return(FALSE);
        }
        startx[i] = IOBuffer[0];
        starty[i] = IOBuffer[1];
        teleport[i][0].alive = teleport[i][1].alive = IOBuffer[2];
        teleport[i][0].x = IOBuffer[3];
        teleport[i][0].y = IOBuffer[4];
        teleport[i][1].x = IOBuffer[5];
        teleport[i][1].y = IOBuffer[6];
        for (x = 0; x <= FIELDX; x++)
            for (y = 0; y <= FIELDY; y++)
                board[i][x][y] = IOBuffer[7 + (x * (FIELDY + 1)) + y];
    }
	for (i = 0; i <= HISCORES; i++)
	{	readcount = Read((BPTR) FileHandle, IOBuffer, NAMELENGTH + 1 + 6);
		if (readcount == -1 || readcount < NAMELENGTH + 1 + 6)
		{	Close((BPTR) FileHandle);
			return(FALSE);
		}
		hiscore[i].player = IOBuffer[0];
		hiscore[i].level = IOBuffer[1];
		hiscore[i].score = (IOBuffer[3] * 65536) + (IOBuffer[4] * 256) + IOBuffer[5];
		for (j = 0; j <= strlen(&IOBuffer[6]); j++)
			hiscore[i].name[j] = IOBuffer[j + 6];
		hiscore[i].fresh = FALSE;
    }
	Close((BPTR) FileHandle);
    modified = FALSE;
    return(TRUE);
}

BOOL savefields(STRPTR fieldname)
{   BYTE                i, j, IOBuffer[7 + ((FIELDX + 1) * (FIELDY + 1))], x, y;
    LONG                writecount;
    struct FileHandle*  FileHandle;
    TEXT                message[64] = {"Saving "};

    strcat(message, fieldname);
    strcat(message, "...");
    say(message, WHITE);
	matchteleports();
    IOBuffer[0] = WORMWARSMAGIC;
    IOBuffer[1] = levels;
    if (!(FileHandle = (struct FileHandle *) Open(fieldname, MODE_NEWFILE)))
		return(FALSE);
    writecount = Write(FileHandle, IOBuffer, 2L);
    if (writecount == -1 || writecount < 2)
    {   Close((BPTR) FileHandle);
        return(FALSE);
    }
    for (i = 0; i <= levels; i++)
    {   IOBuffer[0] = startx[i];
        IOBuffer[1] = starty[i];
        IOBuffer[2] = teleport[i][0].alive;
        IOBuffer[3] = teleport[i][0].x;
        IOBuffer[4] = teleport[i][0].y;
        IOBuffer[5] = teleport[i][1].x;
        IOBuffer[6] = teleport[i][1].y;
        for (x = 0; x <= FIELDX; x++)
            for (y = 0; y <= FIELDY; y++)
                IOBuffer[7 + (x * (FIELDY + 1)) + y] = board[i][x][y];
        writecount = Write(FileHandle, IOBuffer, 7L + ((FIELDX + 1) * (FIELDY + 1)));
        if (writecount == -1 || writecount < (7L + ((FIELDX + 1) * (FIELDY + 1))))
        {   Close((BPTR) FileHandle);
            return(FALSE);
    }   }
	if (clearthem)
		clearhiscores();
	for (i = 0; i <= HISCORES; i++)
	{	IOBuffer[0] = hiscore[i].player;
		IOBuffer[1] = hiscore[i].level;
		IOBuffer[2] = 0;
		IOBuffer[3] = hiscore[i].score / 65536;
		IOBuffer[4] = (hiscore[i].score % 65536) / 256;
		IOBuffer[5] = (hiscore[i].score % 65536) % 256;
		for (j = 0; j <= NAMELENGTH; j++)
			IOBuffer[j + 6] = hiscore[i].name[j];
		writecount = Write(FileHandle, IOBuffer, 6L + NAMELENGTH + 1);
        if (writecount == -1 || writecount < (6L + NAMELENGTH + 1))
        {   Close((BPTR) FileHandle);
            return(FALSE);
    }	}
	Write(FileHandle, VERSION, strlen(VERSION)); // rather pointless to check return value this time
    Close(FileHandle);
    modified = FALSE;
    return(TRUE);
}

void setpointer(UBYTE 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, 14,  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;
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 titlescreen(void)
{	BOOL					done[4], lisa = 0, stamped;
	BYTE					object, player;
	UBYTE					count, segment, waiter = DESCRIPTIONWAIT;
	ULONG					class;
	UWORD					code, qual;
	struct IntuiMessage*	MsgPtr;
	struct Gadget*			WhichGadgetPtr;

	effect(FXTITLESCREEN);
	Forbid();
	MainWindowPtr->Flags = WFLG_BORDERLESS | WFLG_ACTIVATE;
	Permit();
	SetAPen(MainWindowPtr->RPort, BLACK);
	RectFill(MainWindowPtr->RPort, STARTXPIXEL, STARTYPIXEL, ENDXPIXEL, ENDYPIXEL);

	if (!menu)
	{	SetMenuStrip(MainWindowPtr, MenuPtr);
		menu = TRUE;
	}
	for (player = 0; player <= 3; player++)
		GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);

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

	// worm animations

	done[0] = done[1] = done[2] = done[3] = FALSE;
	worm[0].x = FIELDX / 2 - 14;
	worm[0].y = 1;
	worm[1].x = FIELDX / 2 -  5;
	worm[1].y = 1;
	worm[2].x = FIELDX / 2 +  1;
	worm[2].y = 5;
	worm[3].x = FIELDX / 2 +  8;
	worm[3].y = 5;
	object = -1;
	segment = 0;
	a = GAMEOVER;
	clearkybd();
	leftdown = rightdown = FALSE;
	stamped = FALSE;
	blitmode(BLACK);
	draw(CLOCKICON, ICONY, ANYTHING);
	blitmode(NORMAL);
	hiscores();
	do
	{	TimerRqPtr->tr_node.io_Command	= TR_ADDREQUEST;
		TimerRqPtr->tr_time.tv_secs		= 0;
		TimerRqPtr->tr_time.tv_micro	= ANIMDELAY;
		SendIO(TimerRqPtr);

		// w     w  oooo  rrrrr  mm   mm
		// w     w o    o r    r m m m m
		// w  w  w o    o rrrrr  m  m  m
		// w w w w o    o r r    m     m
		// ww   ww  oooo  r  rrr m     m

		if (done[0] == FALSE || done[1] == FALSE || done[2] == FALSE || done[3] == FALSE)
		{	for (player = 0; player <= 3; player++)
				if (done[player] == FALSE && anim[player][segment].x != SENTINEL)
				{	draw(worm[player].x, worm[player].y, FIRSTTAIL + player);
					worm[player].x += anim[player][segment].x;
					worm[player].y += anim[player][segment].y;
					draw(worm[player].x, worm[player].y, eachworm[player][0][anim[player][segment].x + 1 + (anim[player][segment].y + 1) * 3]);
				} else done[player] = TRUE;
			segment++;
		} else if (!stamped)
		{	effect(FXSTAMPED);
			stamped = TRUE;
			DrawImage(MainWindowPtr->RPort, &Logo, 203, 50);
		}

		// object descriptions

		if (++waiter > DESCRIPTIONWAIT)
		{	waiter = 0;
			if (++object > LASTOBJECT)
				object = 0;
			say(objectdesc[object], WHITE);
			Image.ImageData = ImageData[object];
			DrawImage(MainWindowPtr->RPort, &Image, FIRSTDESCX, DESCY);
			DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
		}
		
		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:
						classic = FALSE;
						a = PLAYGAME;
						break;
					case ESCAPE:
						if (verify())
							exit(EXIT_SUCCESS);
						break;
					case BACKSPACE:
						classic = TRUE;
						a = PLAYGAME;
						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:
				if (code != MENUNULL)
				{	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())
								exit(EXIT_SUCCESS);
							break;
						default:
							break;
						}
						break;
					case MN_MODULE:
						a = FIELDEDIT;
						break;
					default:
						break;
				}	}
				break;
			case IDCMP_MOUSEBUTTONS:
				if (ignore)
				{	if (code == SELECTUP)
						ignore = FALSE;
				} else a = PLAYGAME;
				break;
			case IDCMP_REFRESHWINDOW:
				GT_BeginRefresh(MainWindowPtr);
				GT_EndRefresh(MainWindowPtr, TRUE);
				break;
			case IDCMP_GADGETUP:
				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:
				exit(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 (classic)
			{	count = 0;
				for (player = 0; player <= 3; player++)
					if (worm[player].control != NONE)
						count++;
				if (count == 1)
				{	say("Classic mode needs 2-4 worms!", WHITE);
					anykey(TRUE);
					a = GAMEOVER;
			}	}
			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
		{	blitmode(BLACK);
			draw(CLOCKICON, ICONY, ANYTHING);
			blitmode(NORMAL);
		}
		WaitIO(TimerRqPtr);
	} while (a == GAMEOVER);

	for (player = 0; player <= 3; player++)
		GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
	Image.ImageData = ImageData[object];
    blitmode(BLACK);
	DrawImage(MainWindowPtr->RPort, &Image, FIRSTDESCX, DESCY);
	DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
    blitmode(NORMAL);
	if (a == FIELDEDIT)
		fieldedit();
	else
	{	if (worm[0].control != NONE)
			lo = 0;
		else if (worm[1].control != NONE)
			lo = 1;
		else if (worm[2].control != NONE)
			lo = 2;
		else lo = 3;
		if (worm[3].control != NONE)
			hi = 3;
		else if (worm[2].control != NONE)
			hi = 2;
		else if (worm[1].control != NONE)
			hi = 1;
		else hi = 0;

		r = -1;
		trainer = FALSE;
		ice = -1;
		reallevel = 0;
		a = PLAYGAME;

		level = 1;
		clearstats();
		newlevel(rand() % 4);
		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 +  (SCORELINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[LIFE];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 +  (LIVESLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[BIAS];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 +   (BIASLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[NITRO];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 +  (SPEEDLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[AMMO];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 +   (AMMOLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[POWER];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 +  (POWERLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[eachworm[player][2][1]];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 + (ARMOURLINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
				Image.ImageData = ImageData[eachworm[player][1][1]];
				DrawImage(MainWindowPtr->RPort, &Image, worm[player].statx * (ENDXPIXEL + FONTX), STARTYPIXEL + 1 + (TONGUELINE * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - ((LINES + 2) * FONTY))));
		}	}
		clearkybd();
		Forbid();
		MainWindowPtr->Flags = WFLG_BORDERLESS | WFLG_ACTIVATE | WFLG_RMBTRAP;
		Permit();
}	}

void toggle(BYTE key)
{	if (key == F)
	{	if (mode == FX)					// F in FX mode: no sound
		{	kill8();
			mode = FALSE;
			blitmode(BLACK);
			draw(MUSICICON, ICONY, ANYTHING);
			blitmode(NORMAL);
		} else if (fxable)				// F otherwise: change to FX mode
		{	if (mode == MUSIC)
			{	StopPlayer();
				FreePlayer();
			}
			initsound();
			effect(FXFX);
	}	}
	else
	{	if (mode == MUSIC)				// M in MUSIC mode: no sound
		{	StopPlayer();
			FreePlayer();
			mode = FALSE;
			blitmode(BLACK);
			draw(MUSICICON, ICONY, ANYTHING);
			blitmode(NORMAL);
		} else if (musicable == TRUE)	// M otherwise: change to music mode
		{	if (mode == FX)
				kill8();
			if (GetPlayer(0))
			{	say("No channels for music!", YELLOW);
				anykey(TRUE);
				mode = FALSE;
				blitmode(BLACK);
				draw(MUSICICON, ICONY, ANYTHING);
				blitmode(NORMAL);		
			} else
			{	ContModule(SongPtr);
				mode = MUSIC;
				draw(MUSICICON, ICONY, MUSIC);
}	}	}	}

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

void underline(BYTE 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. */

STATIC	WORD oldy = -1;
		WORD 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 ONE:
		y = UNDERLINEOFFSET + (SQUAREY * 15);
		break;
	case TWO:
		y = UNDERLINEOFFSET + (SQUAREY * 18);
		break;
	case START:
		y = UNDERLINEOFFSET + (SQUAREY * 21);
		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;
}	}

void setbrush(BYTE newbrush)
{	brush = newbrush;
	setpointer(brush);
	underline(brush);
}

// Must have blank line at EOF.
