/*    SCCS Id: @(#)winami.c    3.1    93/01/07 */
/* Copyright (c) Gregg Wonderly, Naperville, Illinois,  1991,1992,1993. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "wintype.h"
#include "winami.h"
#include "func_tab.h"

#ifdef AMIGA_INTUITION

/* These should probably not even be options, but, I will leave them
 * for now... GGW
 */

/*#define   TOPL_GETLINE	/* Don't use a window for getlin() */
/*#define   WINDOW_YN		/* Use a window for y/n questions */

#include <exec/types.h>
#include <exec/io.h>
#include <exec/alerts.h>
#include <exec/devices.h>
#include <devices/console.h>
#include <devices/conunit.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dosextens.h>
#include <ctype.h>
#undef  strcmpi
#include <string.h>
#include <errno.h>

#ifdef  IDCMP_CLOSEWINDOW
# define	INTUI_NEW_LOOK
#endif

#ifdef AZTEC_C
#include <functions.h>
#else
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/console.h>
#include <proto/diskfont.h>

/* kludge - see amirip for why */
#undef red
#undef green
#undef blue
#include <proto/graphics.h>
#ifndef __SASC_60
#undef index
# define index strchr
#endif

#include <proto/intuition.h>
#endif

static void RandomWindow( char * );

/* cw->data[x] contains 2 characters worth of special information.  These
 * characters are stored at the offsets as described here.
 */
#define VATTR	  0	/* Video attribute is in this slot */
#define SEL_ITEM  1	/* If this is a select item, slot is 1 else 0 */
#define SOFF	  2	/* The string starts here.  */

/* Nethack defines NULL as ((char *)0) which is very inconvienent... */
#undef NULL
#define NULL 0L

/*
 * Versions we need of various libraries.  We can't use LIBRARY_VERSION
 * as defined in <exec/types.h> because some of the libraries we need
 * don't have that version number in the 1.2 ROM.
 */

#define INTUITION_VERSION	33L
#define GRAPHICS_VERSION	33L
#define DISKFONT_VERSION	34L
#define ICON_VERSION		34L

/* These values are just sorta suggestions in use, but are minimum requirements
 * in reality...
 */
#define WINDOWHEIGHT	192
#define SCREENHEIGHT	200
#define WIDTH		640

/* This character is a solid block (cursor) in Hack.font */
#define CURSOR_CHAR	0x90

/* All we currently support is hack.font at 8x8... */
#define FONTWIDTH	8
#define FONTHEIGHT	8
#define FONTBASELINE	8

/* If Compiling with the "New Look", redefine these now */
#ifdef  INTUI_NEW_LOOK
#define NewWindow ExtNewWindow
#define NewScreen ExtNewScreen
#endif

/* Get the prototypes for our files */
#include "Amiga:winami.p"
#include "Amiga:amiwind.p"
#include "Amiga:amidos.p"

/* Interface definition, for use by windows.c and winprocs.h to provide
 * the intuition interface for the amiga...
 */
struct window_procs amii_procs =
{
    "amii",
    amii_init_nhwindows,
    amii_player_selection,
    amii_askname,
    amii_get_nh_event,
    amii_exit_nhwindows,
    amii_suspend_nhwindows,
    amii_resume_nhwindows,
    amii_create_nhwindow,
    amii_clear_nhwindow,
    amii_display_nhwindow,
    amii_destroy_nhwindow,
    amii_curs,
    amii_putstr,
    amii_display_file,
    amii_start_menu,
    amii_add_menu,
    amii_end_menu,
    amii_select_menu,
    amii_update_inventory,
    amii_mark_synch,
    amii_wait_synch,
#ifdef CLIPPING
    amii_cliparound,
#endif
    amii_print_glyph,
    amii_raw_print,
    amii_raw_print_bold,
    amii_nhgetch,
    amii_nh_poskey,
    amii_bell,
    amii_doprev_message,
    amii_yn_function,
    amii_getlin,
#ifdef COM_COMPL
    amii_get_ext_cmd,
#endif /* COM_COMPL */
    amii_number_pad,
    amii_delay_output,
    /* other defs that really should go away (they're tty specific) */
    amii_delay_output,
    amii_delay_output,
};

extern struct GfxBase *GfxBase;
extern struct Library *DiskfontBase;
extern struct IntuitionBase *IntuitionBase;

/* All kinds of shared stuff */
extern struct TextAttr Hack80;
extern struct Screen *HackScreen;
extern struct Window *pr_WindowPtr;
extern struct Menu HackMenu[];
extern unsigned char KbdBuffered;
extern struct TextFont *HackFont;
extern struct IOStdReq ConsoleIO;
extern struct Library *ConsoleDevice;
extern struct MsgPort *HackPort;
extern char Initialized;
extern char toplines[BUFSZ];

char morc;  /* the character typed in response to a --more-- prompt */
char spaces[] =
"                                                                           ";
extern winid WIN_MESSAGE;
extern winid WIN_MAP;
extern winid WIN_STATUS;
extern winid WIN_INVEN;

winid WIN_BASE = WIN_ERR;

/* Changed later during window/screen opens... */
int txwidth=FONTWIDTH, txheight=FONTHEIGHT, txbaseline = FONTBASELINE;

/* If a 240 or more row screen is in front when we start, this will be
 * set to 1, and the windows will be given borders to allow them to be
 * arranged differently.  The Message window may eventually get a scroller...
 */
int bigscreen = 0;

static void outmore(struct WinDesc *);
static void outsubstr(struct WinDesc *,char *,int);
static void dismiss_nhwindow(winid);

/* This gadget data is replicated for menu/text windows... */
static struct PropInfo PropScroll = { AUTOKNOB+FREEVERT,
					0xffff,0xffff, 0xffff,0xffff, };
static struct Image Image1 = { 0,0, 7,102, 0, NULL, 0x0000,0x0000, NULL };
static struct Gadget MenuScroll = {
    NULL, -15,10, 15,-19, GRELRIGHT|GRELHEIGHT,
    RELVERIFY|FOLLOWMOUSE|RIGHTBORDER,
    PROPGADGET, (APTR)&Image1, NULL, NULL, NULL, (APTR)&PropScroll,
    1, NULL
};

int wincnt=0;   /* # of nh windows opened */

/* We advertise a public screen to allow some people to do other things
 * while they are playing...  like compiling...
 */

#ifdef  INTUI_NEW_LOOK
struct TagItem tags[] =
{
    { WA_PubScreenName, (ULONG)"NetHack", },
    { TAG_DONE, 0, },
};
#endif

/*
 * The default dimensions and status values for each window type.  The
 * data here is generally changed in create_nhwindow(), so beware that
 * what you see here may not be exactly what you get.
 */
struct win_setup {
    struct NewWindow newwin;
    xchar offx,offy,maxrow,rows,maxcol,cols;	/* CHECK TYPES */
} new_wins[] = {

    /* First entry not used, types are based on 1 */
    {{0}},

    /* NHW_MESSAGE */
    {{0,1,640,11,0xff,0xff,
    RAWKEY,
    BORDERLESS|ACTIVATE|SMART_REFRESH
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)"Messages",NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,1,1,80,80},

    /* NHW_STATUS */
    {{0,181,640,24,0xff,0xff,RAWKEY|MENUPICK|DISKINSERTED,
    BORDERLESS|ACTIVATE|SMART_REFRESH
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)"Game Status",NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,2,2,78,78},

    /* NHW_MAP */
    {{0,0,WIDTH,WINDOWHEIGHT,0xff,0xff,
    RAWKEY|MENUPICK|MOUSEBUTTONS|ACTIVEWINDOW
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP,
    NULL,NULL,(UBYTE*)"Dungeon Map",NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,22,22,80,80},

    /* NHW_MENU */
    {{400,10,10,10,80,30,
    RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE|MOUSEBUTTONS|
    GADGETUP|GADGETDOWN|CLOSEWINDOW|VANILLAKEY|NEWSIZE
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH,
    &MenuScroll,NULL,(UBYTE*)"Pick an Item",
    NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,1,1,22,78},

    /* NHW_TEXT */
    {{0,0,640,200,0xff,0xff,
    RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE|
    GADGETUP|CLOSEWINDOW|VANILLAKEY|NEWSIZE
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH,
    &MenuScroll,NULL,(UBYTE*)NULL,NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,1,1,22,78},

    /* NHW_BASE */
    {{0,0,WIDTH,WINDOWHEIGHT,0xff,0xff,
    RAWKEY|MENUPICK|MOUSEBUTTONS
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP,
    NULL,NULL,(UBYTE*)NULL,NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN,
#ifdef  INTUI_NEW_LOOK
    tags,
#endif
    },
    0,0,22,22,80,80},
};

/* The physical screen information */
struct DisplayDesc *amiIDisplay;

/* The opened windows information */
struct WinDesc *wins[MAXWIN + 1];

/* The last Window event is stored here for reference. */
extern WEVENT lastevent;

static const char winpanicstr[]="Bad winid %d in %s()";
#define         SIZEOF_DISKNAME 8

/* The current color map */
unsigned short amii_curmap[] = {
#define C_BLACK		0
#define C_WHITE		1
#define C_BROWN		2
#define C_CYAN		3
#define C_GREEN		4
#define C_MAGENTA	5
#define C_BLUE		6
#define C_RED		7
    0x0AAA, /* color #0 */
    0x0FFF, /* color #1 */
    0x0620, /* color #2 */
    0x0B08, /* color #3 */
    0x0181, /* color #4 */
    0x0C06, /* color #5 */
    0x023E, /* color #6 */
    0x0D00  /* color #7 */
};

#ifdef  INTUI_NEW_LOOK
UWORD scrnpens[] = {
    C_BROWN,
    C_BLACK,
    C_WHITE,
    C_WHITE,
    C_BROWN,
    C_CYAN,
    C_BROWN,
    C_BLACK,
    C_RED,
};
struct TagItem scrntags[] =
{
    { SA_PubName, (ULONG)"NetHack" },
    { SA_Pens, (ULONG)scrnpens },
    { TAG_DONE, 0 },
};
#endif

struct NewScreen NewHackScreen =
{
    0, 0, WIDTH, SCREENHEIGHT, DEPTH,
    C_BLACK, C_WHITE,     /* DetailPen, BlockPen */
    HIRES,
    CUSTOMSCREEN
#ifdef  INTUI_NEW_LOOK
    |NS_EXTENDED
#endif
    ,
    &Hack80,  /* Font */
    (UBYTE *)" NetHack 3.1",
    NULL,     /* Gadgets */
    NULL,     /* CustomBitmap */
#ifdef  INTUI_NEW_LOOK
    scrntags,
#endif
};

/* Make sure the user sees a text string when no windowing is available */

void
amii_raw_print(s)
    register const char *s;
{
    if( !s )
	return;
    if(amiIDisplay)amiIDisplay->rawprint++;

    if( Initialized == 0 && WIN_BASE == WIN_ERR )
	    init_nhwindows();

    if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	amii_putstr( WIN_MAP, 0, s );
    else if( WIN_BASE != WIN_ERR && wins[ WIN_BASE ] )
	amii_putstr( WIN_BASE, 0, s );
    else {
	printf("%s\n", s);
	fflush(stdout);
    }
}

/* Make sure the user sees a bold text string when no windowing
 * is available
 */

void
amii_raw_print_bold(s)
    register const char *s;
{
    if( !s )
	return;

    if(amiIDisplay)amiIDisplay->rawprint++;

    if( Initialized == 0 && WIN_BASE == WIN_ERR )
	    init_nhwindows();

    if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	amii_putstr( WIN_MAP, 1, s );
    else if( WIN_BASE != WIN_ERR && wins[ WIN_BASE ] )
	amii_putstr( WIN_BASE, 1, s );
    else {
	printf("\33[1m%s\33[0m\n",s);
	fflush(stdout);
    }
}

/* Start building the text for a menu */
void
amii_start_menu(window)
    register winid window;
{
    register struct WinDesc *cw;

    if(window == WIN_ERR || (cw = wins[window]) == NULL || cw->type != NHW_MENU)
	panic(winpanicstr,window, "start_menu");

    amii_clear_nhwindow(window);
    if( cw->resp )
	*cw->resp = 0;
    cw->maxrow = cw->maxcol = 0;

    return;
}

/* Add a string to a menu */
void
amii_add_menu(window,ch,attr,str)
    register winid window;
    register char ch;
    register int attr;
    register const char *str;
{
    register struct WinDesc *cw;
    char tmpbuf[2];

    if(str == NULL)return;

    if(window == WIN_ERR || (cw = wins[window]) == NULL || cw->type != NHW_MENU)
	panic(winpanicstr,window, "add_menu");

    /* this should translate fonts if a title line */
    amii_putstr(window, attr, str);

    if( !cw->resp )
	panic("No response buffer in add_menu()");

    if( !cw->data )
	panic("No data buffer in add_menu()");

    if(ch != '\0')
    {
	tmpbuf[0]=ch;
	tmpbuf[1]=0;
	Strcat(cw->resp, tmpbuf);
	cw->data[ cw->cury - 1 ][ SEL_ITEM ] = 1;
    }
    else
    {
	/* Use something as a place holder.  ^A is probably okay */

	tmpbuf[0]=1;
	tmpbuf[1]=0;
	Strcat(cw->resp, tmpbuf);
	cw->data[ cw->cury - 1 ][ SEL_ITEM ] = 0;
    }
}

/* Done building a menu. */

void
amii_end_menu(window,cancel,str,morestr)
    register winid window;
    register char cancel;
    register const char *str;
    register const char *morestr;
{
    register struct WinDesc *cw;

    if(window == WIN_ERR || (cw=wins[window]) == NULL || cw->type != NHW_MENU
	  || cw->canresp)
	panic(winpanicstr,window, "end_menu");

    if(str)
    {
	cw->canresp = (char*) alloc(strlen(str)+2);
	cw->canresp[0]=cancel;
	Strcpy(&cw->canresp[1],str);

	if( !cw->resp )
	    panic("No response buffer in end_menu()");

	strncat(cw->resp, str, 1);
    }

    if(morestr)
    {
	cw->morestr =(char *) alloc(strlen(morestr)+1);
	Strcpy(cw->morestr, morestr);
    }
}

/* Select something from the menu. */

char
amii_select_menu(window)
    register winid window;
{
    register struct WinDesc *cw;

    if( window == WIN_ERR || ( cw=wins[window] ) == NULL ||
	  cw->type != NHW_MENU )
	panic(winpanicstr,window, "select_menu");

    morc = 0;                       /* very ugly global variable */
    amii_display_nhwindow(window,TRUE); /* this waits for input */
    dismiss_nhwindow(window);       /* Now tear it down */
    return morc;
}

/* Rebuild/update the inventory if the window is up.  This is currently
 * a noop for us because we always take any menu window off of the
 * screen by deleting it when the user makes a selection, or cancels
 * the menu.
 */
void
amii_update_inventory()
{
    register struct WinDesc *cw;

    if( WIN_INVEN != WIN_ERR && ( cw = wins[ WIN_INVEN ] ) &&
      cw->type == NHW_MENU && cw->win )
    {
	display_inventory( NULL, FALSE );
	WindowToFront( cw->win );
	ActivateWindow( cw->win );
    }
}

/* Humm, doesn't really do anything useful */

void
amii_mark_synch()
{
    if(!amiIDisplay)
	fflush(stderr);
/* anything else?  do we need this much? */
}

/* Wait for everything to sync.  Nothing is asynchronous, so we just
 * ask for a key to be pressed.
 */
void
amii_wait_synch()
{
    if(!amiIDisplay || amiIDisplay->rawprint)
    {
	if(amiIDisplay) amiIDisplay->rawprint=0;
    }
    else
    {
	display_nhwindow(WIN_MAP,TRUE);
	flush_glyph_buffer( wins[ WIN_MAP ]->win );
    }
}

#ifdef CLIPPING
void
amii_setclipped()
{
    clipping = TRUE;
    clipx=clipy=0;
    clipxmax=CO;        /* WRONG */
    clipymax=LI-5;      /* WRONG */
}

void
amii_cliparound(x,y)
    register int x,y;
{
/* pull this from wintty.c - LATER */
}
#endif

/*
 * plname is filled either by an option (-u Player  or  -uPlayer) or
 * explicitly (by being the wizard) or by askname.
 * It may still contain a suffix denoting pl_character.
 * Always called after init_nhwindows() and before display_gamewindows().
 */
void
amii_askname()
{
    *plname = 0;
    do {
	getlin( "Who are you?", plname );
    } while( strlen( plname ) == 0 );

    if( *plname == '\33' )
    {
	clearlocks();
	exit_nhwindows(NULL);
	terminate(0);
    }
}

#include "Amiga:char.c"

/* Get the player selection character */

void
amii_player_selection()
{
    extern const char *roles[];
    register struct Window *cwin;
    register struct IntuiMessage *imsg;
    register int aredone = 0;
    register struct Gadget *gd;
    static int once=0;
    long class, code;
    int i;

    amii_clear_nhwindow( WIN_BASE );
    if( *pl_character ){
	pl_character[ 0 ] = toupper( pl_character[ 0 ] );
	if( index( pl_classes, pl_character[ 0 ] ) )
	    return;
    }

    if( !once ){
	if( bigscreen ){
	    Type_NewWindowStructure1.TopEdge =
	      (HackScreen->Height/2) - (Type_NewWindowStructure1.Height/2);
	}
	for( gd = Type_NewWindowStructure1.FirstGadget; gd;
	  gd = gd->NextGadget )
	{
	    if( gd->GadgetID != 0 )
		SetBorder( gd );
	}
	once = 1;
    }

    Type_NewWindowStructure1.Screen = HackScreen;
    if( ( cwin = OpenShWindow( (void *)&Type_NewWindowStructure1 ) ) == NULL )
    {
	return;
    }

    while( !aredone )
    {
	WaitPort( cwin->UserPort );
	while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL )
	{
	    class = imsg->Class;
	    code = imsg->Code;
	    gd = (struct Gadget *)imsg->IAddress;
	    ReplyMsg( (struct Message *)imsg );

	    switch( class )
	    {
	    case VANILLAKEY:
		if( index( pl_classes, toupper( code ) ) )
		{
		    pl_character[0] = toupper( code );
		    aredone = 1;
		}
		else if( code == ' ' || code == '\n' || code == '\r' )
		{
#ifdef  TOURIST
		    strcpy( pl_character, roles[ rnd( 11 ) ] );
#else
		    strcpy( pl_character, roles[ rnd( 10 ) ] );
#endif
		    aredone = 1;
		    amii_clear_nhwindow( WIN_BASE );
		    CloseShWindow( cwin );
		    RandomWindow( pl_character );
		    return;
		}
		else if( code == 'q' || code == 'Q' )
		{
		CloseShWindow( cwin );
		clearlocks();
		exit_nhwindows(NULL);
		terminate(0);
		}
		else
		    DisplayBeep( NULL );
		break;

	    case GADGETUP:
		switch( gd->GadgetID )
		{
		case 1: /* Random Character */
#ifdef  TOURIST
		    strcpy( pl_character, roles[ rnd( 11 ) ] );
#else
		    strcpy( pl_character, roles[ rnd( 10 ) ] );
#endif
		    amii_clear_nhwindow( WIN_BASE );
		    CloseShWindow( cwin );
		    RandomWindow( pl_character );
		    return;

		default:
		    pl_character[0] = gd->GadgetID;
		    break;
		}
		aredone = 1;
		break;

	    case CLOSEWINDOW:
		CloseShWindow( cwin );
		clearlocks();
		exit_nhwindows(NULL);
		terminate(0);
		break;
	    }
	}
    }
    amii_clear_nhwindow( WIN_BASE );
    CloseShWindow( cwin );
}

#ifndef KENI_S_WAY
#include "Amiga:randwin.c"

static void
RandomWindow( name )
    char *name;
{
    struct MsgPort *tport;
    struct timerequest *trq;
    static int once = 0;
    struct Gadget *gd;
    struct Window *w;
    struct IntuiMessage *imsg;
    int ticks = 0, aredone = 0, timerdone = 0;
    long mask, got;

    tport = CreatePort( 0, 0 );
    trq = (struct timerequest *)CreateExtIO( tport, sizeof( *trq ) );
    if( tport == NULL || trq == NULL )
    {
allocerr:
	if( tport ) DeletePort( tport );
	if( trq ) DeleteExtIO( (struct IORequest *)trq );
	Delay( 8 * 50 );
    }

    if( OpenDevice( TIMERNAME, UNIT_VBLANK, (struct IORequest *)trq, 0L ) != 0 )
	goto allocerr;

    trq->tr_node.io_Command = TR_ADDREQUEST;
    trq->tr_time.tv_secs = 8;
    trq->tr_time.tv_micro = 0;

    SendIO( (struct IORequest *)trq );

    /* Place the name in the center of the screen */
    Rnd_IText5.IText = name;
    Rnd_IText6.LeftEdge = Rnd_IText4.LeftEdge +
		(strlen(Rnd_IText4.IText)+1)*txwidth;
    Rnd_NewWindowStructure1.Width = (
	    (strlen( Rnd_IText2.IText )+1) * txwidth ) +
	    HackScreen->WBorLeft + HackScreen->WBorRight;
    Rnd_IText5.LeftEdge = (Rnd_NewWindowStructure1.Width -
	    (strlen(name)*txwidth))/2;

    gd = Rnd_NewWindowStructure1.FirstGadget;
    gd->LeftEdge = (Rnd_NewWindowStructure1.Width - gd->Width)/2;
	/* Chose correct modifier */
    Rnd_IText6.IText = "a";
    switch( *name )
    {
    case 'a': case 'e': case 'i': case 'o':
    case 'u': case 'A': case 'E': case 'I':
    case 'O': case 'U':
	Rnd_IText6.IText = "an";
	break;
    }

    if( !once )
    {
	if( bigscreen )
	{
	    Rnd_NewWindowStructure1.TopEdge =
		(HackScreen->Height/2) - (Rnd_NewWindowStructure1.Height/2);
	}
	for( gd = Rnd_NewWindowStructure1.FirstGadget; gd; gd = gd->NextGadget )
	{
	    if( gd->GadgetID != 0 )
		SetBorder( gd );
	}
	once = 1;
    }

    Rnd_NewWindowStructure1.Screen = HackScreen;
    if( ( w = OpenShWindow( (void *)&Rnd_NewWindowStructure1 ) ) == NULL )
    {
	AbortIO( (struct IORequest *)trq );
	WaitIO( (struct IORequest *)trq );
	CloseDevice( (struct IORequest *)trq );
	DeleteExtIO( (struct IORequest *) trq );
	DeletePort( tport );
	Delay( 50 * 8 );
	return;
    }
    PrintIText( w->RPort, &Rnd_IntuiTextList1, 0, 0 );

    mask = (1L << tport->mp_SigBit)|(1L << w->UserPort->mp_SigBit);
    while( !aredone )
    {
	got = Wait( mask );
	if( got & (1L << tport->mp_SigBit ) )
	{
	    aredone = 1;
	    timerdone = 1;
	    GetMsg( tport );
	    CloseShWindow( w );
	    w = NULL;
        }
        while( w && ( imsg = (struct IntuiMessage *) GetMsg( w->UserPort ) ) )
        {
	    switch( imsg->Class )
	    {
		/* Must be up for a little while... */
	    case INACTIVEWINDOW:
		if( ticks >= 40 )
		    aredone = 1;
		break;

	    case INTUITICKS:
		++ticks;
		break;

	    case GADGETUP:
		aredone = 1;
		break;
	    }
	    ReplyMsg( (struct Message *)imsg );
        }
    }

    if( !timerdone )
    {
	AbortIO( (struct IORequest *)trq );
	WaitIO( (struct IORequest *)trq );
    }

    CloseDevice( (struct IORequest *)trq );
    DeleteExtIO( (struct IORequest *) trq );
    DeletePort( tport );
    if(w) CloseShWindow( w );
}
#endif

/* this should probably not be needed (or be renamed)
void
flush_output(){} */

void
amii_destroy_nhwindow(win)      /* just hide */
    register winid win;
{
    register struct WinDesc *cw;
    register int i;

    if( win == WIN_ERR || ( cw = wins[win] ) == NULL )
    {
	panic(winpanicstr,win,"destroy_nhwindow");
    }

    /* Tear down the Intuition stuff */
    dismiss_nhwindow(win);

    if( cw->data && cw->type == NHW_MESSAGE ||
			    cw->type == NHW_MENU || cw->type == NHW_TEXT )
    {
	for( i = 0; i < cw->maxrow; ++i )
	{
	    if( cw->data[ i ] )
		free( cw->data[ i ] );
	}
	free( cw->data );
	cw->data = NULL;
    }

    if( cw->resp )
	free( cw->resp );
    cw->resp = NULL;

    if( cw->canresp )
	free( cw->canresp );
    cw->canresp = NULL;

    if( cw->morestr )
	free( cw->morestr );
    cw->morestr = NULL;

    free( cw );
    wins[win] = NULL;
}

amii_create_nhwindow(type)
    register int type;
{
    register struct Window *w = NULL;
    register struct NewWindow *nw = NULL;
    register struct WinDesc *wd = NULL;
    struct Window *mapwin = NULL, *stwin = NULL, *msgwin = NULL;
    register int newid;

    if( WIN_STATUS != WIN_ERR && wins[ WIN_STATUS ] )
	stwin = wins[ WIN_STATUS ]->win;

    if( WIN_MESSAGE != WIN_ERR && wins[ WIN_MESSAGE ] )
	msgwin = wins[ WIN_MESSAGE ]->win;

    if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	mapwin = wins[ WIN_MAP ]->win;

    /* Create Port anytime that we need it */

    if( HackPort == NULL )
    {
	HackPort = CreatePort( NULL, 0 );
	if( !HackPort )
	    panic( "no memory for msg port" );
    }

    nw = &new_wins[ type ].newwin;
    nw->Width = amiIDisplay->xpix;
    nw->Screen = HackScreen;

    if( type == NHW_MAP || type == NHW_BASE )
    {
	nw->DetailPen = C_WHITE;
	nw->BlockPen = C_MAGENTA;
	nw->TopEdge = 1;
	nw->LeftEdge = 0;
	nw->Height = amiIDisplay->ypix - nw->TopEdge;
	if( bigscreen && type == NHW_MAP )
	{
	    int h;
	    if( msgwin && stwin )
	    {
		h = (stwin->TopEdge - nw->TopEdge - 1) -
				(msgwin->TopEdge + msgwin->Height + 1);
		h = h - ( (22 * stwin->RPort->TxHeight) +
				    stwin->BorderTop + stwin->BorderBottom );
		/* h is now the available space excluding the map window so
		 * divide by 2 and use it to space out the map window.
		 */
		if( h > 0 )
		    h /= 2;
		else
		    h = 0;
		nw->TopEdge = msgwin->TopEdge + msgwin->Height + 1 + h;
		nw->Height = stwin->TopEdge + 1 - nw->TopEdge - h;
	    }
	    else
	    {
		h = amiIDisplay->ypix - (22 * FONTHEIGHT) - 12 - 10;
		if( h > 0 )
		{
		    nw->TopEdge = h / 2;
		    nw->Height = (22 * FONTHEIGHT) + 12 + 10;
		}
		else
		{
		    nw->Height -= 85;
		    nw->TopEdge += 35;
		}
	    }
	}
    }
    else if( type == NHW_STATUS )
    {
	nw->DetailPen = C_CYAN;
	nw->BlockPen = C_CYAN;
	if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	    w = wins[ WIN_MAP ]->win;
	else if( WIN_BASE != WIN_ERR && wins[ WIN_BASE ] )
	    w = wins[ WIN_BASE ]->win;
	else
	    panic( "No window to base STATUS location from" );

	/* Status window is relative to bottom of WIN_BASE/WIN_MAP */

	if( mapwin && bigscreen )
	{
	    nw->TopEdge = mapwin->TopEdge + mapwin->Height;
	    nw->Height = amiIDisplay->ypix - nw->TopEdge - 1;
	}
	else
	{
	    /* Expand the height of window by borders */
	    if( bigscreen )
		nw->Height = (txheight * 2) + 17;

	    nw->TopEdge = amiIDisplay->ypix - nw->Height;
	    nw->LeftEdge = w->LeftEdge;
	    if( nw->LeftEdge + nw->Width >= amiIDisplay->xpix )
		nw->LeftEdge = 0;
	    if( nw->Width >= amiIDisplay->xpix - nw->LeftEdge )
		nw->Width = amiIDisplay->xpix - nw->LeftEdge;
	}
    }
    else if( type == NHW_MESSAGE )
    {
	nw->DetailPen = C_RED;
	nw->BlockPen = C_RED;
	if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	    w = wins[ WIN_MAP ]->win;
	else if( WIN_BASE != WIN_ERR && wins[ WIN_BASE ] )
	    w = wins[ WIN_BASE ]->win;
	else
	    panic( "No window to base STATUS location from" );

	nw->TopEdge = 1;
	if( bigscreen )
	{
	    if( mapwin )
	    {
		nw->Height = mapwin->TopEdge - 2;
	    }
	    else
	    {
		nw->Height += 15;
	    }
	}
    }
    else
    {
	nw->DetailPen = C_WHITE;
	nw->BlockPen = C_MAGENTA;
    }

    /* When Interlaced, there is "Room" for all this stuff */
/* WRONG - still not enough space on MAP for right border */
    if( bigscreen && type != NHW_BASE )
    {
	nw->Flags &= ~( BORDERLESS | BACKDROP );
#if 1
	nw->Flags |= ( WINDOWDRAG | WINDOWDEPTH );
#else
	nw->Flags |= ( WINDOWDRAG | WINDOWDEPTH | SIZEBRIGHT );
	if( type == NHW_MAP )
	    nw->Flags |= WINDOWSIZING;
#endif
    }
    /* No titles on a hires only screen */
    if( !bigscreen )
	nw->Title = 0;

    /* Don't open MENU or TEXT windows yet */

    if( type == NHW_MENU || type == NHW_TEXT )
	w = NULL;
    else
	w=OpenShWindow( (void *)nw );

    if( w == NULL && type != NHW_MENU && type != NHW_TEXT )
	panic("bad openwin %d",type);

    /* Check for an empty slot */

    for(newid = 0; newid<MAXWIN + 1; newid++)
    {
	if(wins[newid] == 0)
	    break;
    }

    if(newid==MAXWIN+1)
	panic("time to write re-alloc code\n");

    /* Set wincnt accordingly */

    if( newid > wincnt )
	wincnt = newid;

    /* Do common initialization */

    wd = (struct WinDesc *)alloc(sizeof(struct WinDesc));
    memset( wd, 0, sizeof( struct WinDesc ) );
    wins[newid] = wd;

    wd->newwin = NULL;
    wd->win = w;
    wd->type = type;
    wd->flags = 0;
    wd->active = FALSE;
    wd->curx=wd->cury = 0;
    wd->resp = wd->canresp = wd->morestr = 0;   /* CHECK THESE */
    wd->offx = new_wins[type].offx;
    wd->offy = new_wins[type].offy;
    wd->maxrow = new_wins[type].maxrow;
    wd->maxcol = new_wins[type].maxcol;

    if( type != NHW_TEXT && type != NHW_MENU )
    {
	wd->rows = ( w->Height - w->BorderTop - w->BorderBottom ) /(txheight+1);
	wd->cols = ( w->Width - w->BorderLeft - w->BorderRight ) / txwidth;
    }

    /* Okay, now do the individual type initialization */

    switch(type)
    {
	/* History lines for MESSAGE windows are stored in cw->data[?].
	 * maxcol and maxrow are used as cursors.  maxrow is the count
	 * of the number of history lines stored.  maxcol is the cursor
	 * to the last line that was displayed by ^P.
	 */
	case NHW_MESSAGE:
	    if(flags.msg_history<20)flags.msg_history=20;
	    if(flags.msg_history>200)flags.msg_history=200;
	    flags.window_inited=TRUE;
	    wd->data = (char **)alloc( flags.msg_history*sizeof( char * ) );
	    memset( wd->data, 0, flags.msg_history * sizeof( char * ) );
	    wd->maxrow = wd->maxcol = 0;
	    /* Indicate that we have not positioned the cursor yet */
	    wd->curx = -1;
	    break;

	    /* A MENU contains a list of lines in wd->data[?].  These
	     * lines are created in amii_putstr() by reallocating the size
	     * of wd->data to hold enough (char *)'s.  wd->rows is the
	     * number of (char *)'s allocated.  wd->maxrow is the number
	     * used.  wd->maxcol is used to track how wide the menu needs
	     * to be.  wd->resp[x] contains the characters that correspond
	     * to selecting wd->data[x].  wd->resp[x] corresponds to
	     * wd->data[x] for any x. Elements of wd->data[?] that are not
	     * valid selections have the corresponding element of
	     * wd->resp[] set to a value of '\01';  i.e. a ^A which is
	     * not currently a valid keystroke for responding to any
	     * MENU or TEXT window.
	     */
	case NHW_MENU:
	    wd->resp=(char*)alloc(256);
	    wd->resp[0]=0;
	    wd->rows = wd->maxrow = 0;
	    wd->maxcol = 0;
	    wd->data = NULL;
	    break;

	    /* See the explanation of MENU above.  Except, wd->resp[] is not
	     * used for TEXT windows since there is no selection of a
	     * a line performed/allowed.  The window is always full
	     * screen width.
	     */
	case NHW_TEXT:
	    wd->rows = wd->maxrow = 0;
	    wd->maxcol = wd->cols = amiIDisplay->cols;
	    wd->data = NULL;
	    wd->morestr = NULL;
	    break;

	    /* The status window has only two lines.  These are stored in
	     * wd->data[], and here we allocate the space for them.
	     */
	case NHW_STATUS:
	    /* wd->cols is the number of characters which fit across the
	     * screen.
	     */
	    wd->data=(char **)alloc(3*sizeof(char *));
	    wd->data[0] = (char *)alloc(wd->cols + 10);
	    wd->data[1] = (char *)alloc(wd->cols + 10);
	    wd->data[2] = NULL;
	    break;

	    /* NHW_MAP does not use wd->data[] or the other text
	     * manipulating members of the WinDesc structure.
	     */
	case NHW_MAP:
	    SetMenuStrip(w, HackMenu);
	    break;

	    /* The base window must exist until CleanUp() deletes it. */
	case NHW_BASE:
	    /* Make our requesters come to our screen */
	    {
		register struct Process *myProcess =
					(struct Process *) FindTask(NULL);
		pr_WindowPtr = (struct Window *)(myProcess->pr_WindowPtr);
		myProcess->pr_WindowPtr = (APTR) w;
	    }

	    /* Need this for RawKeyConvert() */

	    ConsoleIO.io_Data = (APTR) w;
	    ConsoleIO.io_Length = sizeof( struct Window * );
	    ConsoleIO.io_Message.mn_ReplyPort = CreatePort(NULL, 0L);
	    if( OpenDevice("console.device", 0L,
				(struct IORequest *) &ConsoleIO, 0L) != 0)
	    {
		Abort(AG_OpenDev | AO_ConsoleDev);
	    }

	    ConsoleDevice = (struct Library *) ConsoleIO.io_Device;

	    KbdBuffered = 0;

#ifdef HACKFONT
	    if( HackFont )
		SetFont(w->RPort, HackFont);
#endif
	    txwidth = w->RPort->TxWidth;
	    txheight = w->RPort->TxHeight;
	    txbaseline = w->RPort->TxBaseline;
	    break;

	default:
	    panic("bad create_nhwindow( %d )\n",type);
	    return WIN_ERR;
    }

    return( newid );
}

/* Initialize the windowing environment */

void
amii_init_nhwindows()
{
    if (IntuitionBase)
	panic( "init_nhwindow() called twice", 0 );

    WIN_MESSAGE = WIN_ERR;
    WIN_MAP = WIN_ERR;
    WIN_STATUS = WIN_ERR;
    WIN_INVEN = WIN_ERR;
    WIN_BASE = WIN_ERR;

    if ( (IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", INTUITION_VERSION)) == NULL)
    {
	Abort(AG_OpenLib | AO_Intuition);
    }

    if ( (GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", GRAPHICS_VERSION)) == NULL)
    {
	Abort(AG_OpenLib | AO_GraphicsLib);
    }

#ifdef HACKFONT
    /*
     *  Force our own font to be loaded, if possible.
     *  If we can open diskfont.library, but not our font, we can close
     *  the diskfont.library again since it just wastes memory.
     *  Even if we can open the font, we don't need the diskfont.library
     *  anymore, since CloseFont is a graphics.library function.
     */

    if ((HackFont = OpenFont(&Hack80)) == NULL)
    {
	if (DiskfontBase =
			OpenLibrary("diskfont.library", DISKFONT_VERSION))
	{
	    Hack80.ta_Name -= SIZEOF_DISKNAME;
	    HackFont = OpenDiskFont(&Hack80);
	    Hack80.ta_Name += SIZEOF_DISKNAME;
	    CloseLibrary(DiskfontBase);
	    DiskfontBase = NULL;
	}
    }
#endif

    amiIDisplay=(struct DisplayDesc *)alloc(sizeof(struct DisplayDesc));
    memset( amiIDisplay, 0, sizeof( struct DisplayDesc ) );

    /* Use Intuition sizes for overscan screens... */

    amiIDisplay->ypix = GfxBase->NormalDisplayRows;
    amiIDisplay->xpix = GfxBase->NormalDisplayColumns;

    amiIDisplay->cols = amiIDisplay->xpix / FONTWIDTH;

    amiIDisplay->toplin=0;
    amiIDisplay->rawprint=0;
    amiIDisplay->lastwin=0;

    if( bigscreen == 0 )
    {
	if( ( GfxBase->ActiView->ViewPort->Modes & LACE ) == LACE )
	{
	    amiIDisplay->ypix *= 2;
	    NewHackScreen.ViewModes |= LACE;
	    bigscreen = 1;
	}
	else if( GfxBase->NormalDisplayRows >= 240 )
	{
	    bigscreen = 1;
	}
    }
    else if( bigscreen == -1 )
	bigscreen = 0;
    else if( bigscreen )
    {
	/* If bigscreen requested and we don't have enough rows in
	 * noninterlaced mode, switch to interlaced...
	 */
	if( GfxBase->NormalDisplayRows < 240 )
	{
	    amiIDisplay->ypix *= 2;
	    NewHackScreen.ViewModes |= LACE;
	}
    }
    amiIDisplay->rows = amiIDisplay->ypix / FONTHEIGHT;

    /* This is the size screen we want to open, within reason... */

    NewHackScreen.Width = max( WIDTH, amiIDisplay->xpix );
    NewHackScreen.Height = max( SCREENHEIGHT, amiIDisplay->ypix );

    if( ( HackScreen = OpenScreen( (void *)&NewHackScreen ) ) == NULL )
	Abort( AN_OpenScreen & ~AT_DeadEnd );
#ifdef  INTUI_NEW_LOOK
    if( IntuitionBase->LibNode.lib_Version >= 37 )
    {
	PubScreenStatus( HackScreen, 0 );
    }
#endif

#ifdef TEXTCOLOR
    LoadRGB4(&HackScreen->ViewPort, amii_curmap, 1L << DEPTH );
#endif

    /* Display the copyright etc... */

    if( WIN_BASE == WIN_ERR )
	WIN_BASE = amii_create_nhwindow( NHW_BASE );
    amii_clear_nhwindow( WIN_BASE );
    amii_putstr( WIN_BASE, 0, "" );
    amii_putstr( WIN_BASE, 0, "" );
    amii_putstr( WIN_BASE, 0, "" );
    amii_putstr( WIN_BASE, 0,
      "NetHack, Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993.");
    amii_putstr( WIN_BASE, 0,
	"         By Stichting Mathematisch Centrum and M. Stephenson.");
    amii_putstr( WIN_BASE, 0, "         See license for details.");
    amii_putstr( WIN_BASE, 0, "");

    Initialized = 1;
}

#ifdef COM_COMPL
/* Read in an extended command - doing command line completion for
 * when enough characters have been entered to make a unique command.
 */
void
amii_get_ext_cmd(bufp)
register char *bufp;
{
    register char *obufp = bufp;
    register int c;
    int com_index, oindex;
    struct WinDesc *cw;
    struct Window *w;
    int colx;

    if( WIN_MESSAGE == WIN_ERR || ( cw = wins[ WIN_MESSAGE ] ) == NULL )
	panic(winpanicstr, WIN_MESSAGE, "get_ext_cmd");
    amii_clear_nhwindow( NHW_MESSAGE );
    pline("# ");
    colx = 3;
    w = cw->win;

    while((c = WindowGetchar()) != EOF)
    {
	amii_curs( WIN_MESSAGE, colx, 0 );
	if(c == '?' )
	{
	    int win, i, sel;
	    char buf[ 100 ];

	    win = amii_create_nhwindow( NHW_MENU );
	    amii_start_menu( win );

	    for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
	    {
		sprintf( buf, "%-10s - %s ",
			 extcmdlist[ i ].ef_txt,
			 extcmdlist[ i ].ef_desc );
		amii_add_menu( win, extcmdlist[i].ef_txt[0], 0, buf );
	    }

	    amii_end_menu( win, i, "\33", NULL );
	    sel = amii_select_menu( win );
	    amii_destroy_nhwindow( win );

	    if( sel == '\33' )
	    {
		*obufp = '\33';
		obufp[ 1 ] = 0;
	    }
	    else
	    {
		for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
		{
		    if( sel == extcmdlist[i].ef_txt[0] )
			break;
		}

		/* copy in the text */
		amii_clear_nhwindow( WIN_MESSAGE );
		strcpy( obufp, extcmdlist[ i ].ef_txt );
		colx = 0;
		pline( "# " );
		pline( obufp );
		bufp = obufp + 2;
	    }
	    return;
	}
	else if(c == '\033')
	{
	    *obufp = c;
	    obufp[1] = 0;
	    return;
	}
	else if(c == '\b')
	{
	    if(bufp != obufp)
	    {
		bufp--;
		amii_curs( WIN_MESSAGE, --colx, 0);
		Text( w->RPort, spaces, 1 );
		amii_curs( WIN_MESSAGE, colx, 0);
	    }
	    else
		DisplayBeep( NULL );
	}
	else if( c == '\n' || c == '\r' )
	{
	    *bufp = 0;
	    return;
	}
	else if(' ' <= c && c < '\177')
	{
		/* avoid isprint() - some people don't have it
		   ' ' is not always a printing char */
	    *bufp = c;
	    bufp[1] = 0;
	    oindex = 0;
	    com_index = -1;

	    while(extcmdlist[oindex].ef_txt != NULL)
	    {
		if(!strncmpi(obufp, extcmdlist[oindex].ef_txt, strlen(obufp)))
		{
		    if(com_index == -1) /* No matches yet*/
			com_index = oindex;
		    else /* More than 1 match */
			com_index = -2;
		}
		oindex++;
	    }

	    if(com_index >= 0 && *obufp )
	    {
		Strcpy(obufp, extcmdlist[com_index].ef_txt);
		/* finish printing our string */
		Text( w->RPort, bufp, strlen( bufp ) );
		amii_curs( WIN_MESSAGE, colx += strlen( bufp ), 0);
		bufp = obufp; /* reset it */
		if(strlen(obufp) < BUFSZ-1 && strlen(obufp) < COLNO)
		    bufp += strlen(obufp);
	    }
	    else
	    {
		Text( w->RPort, bufp, strlen( bufp ) );
		amii_curs( WIN_MESSAGE, colx += strlen( bufp ), 0);
		if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
		    bufp++;
	    }
	}
	else if(c == ('X'-64) || c == '\177')
	{
	    colx = 0;
	    amii_clear_nhwindow( WIN_MESSAGE );
	    pline( "# " );
	    bufp = obufp;
	} else
	    DisplayBeep( NULL );
    }
    *bufp = 0;
    return;
}
#endif /* COM_COMPL */

#ifdef WINDOW_YN
SHORT Ask_BorderVectors1[] = { 0,0, 29,0, 29,11, 0,11, 0,0 };
struct Border Ask_Border1 = { -1,-1, 3,0,JAM1, 5, Ask_BorderVectors1, NULL };
struct IntuiText Ask_IText1 = { 3,0,JAM2, 2,1, NULL, "(?)", NULL };

struct Gadget Ask_Gadget1 = {
    NULL, 9,4, 28,10, NULL, RELVERIFY, BOOLGADGET, (APTR)&Ask_Border1,
    NULL, &Ask_IText1, NULL, NULL, NULL, NULL
};

#define Ask_GadgetList1 Ask_Gadget1

struct IntuiText Ask_IText2 = { 1,0,JAM2, 44,5, NULL, NULL, NULL };

#define Ask_IntuiTextList1 Ask_IText2

struct NewWindow Ask_Window = {
    75,85, 524,18, 0,1, GADGETUP+VANILLAKEY, ACTIVATE+NOCAREREFRESH,
    &Ask_Gadget1, NULL, NULL, NULL, NULL, 5,5, -1,-1, CUSTOMSCREEN
};
#endif

/* Ask a question and get a response */

char amii_yn_function( prompt, resp, def )
    const char *prompt,*resp;
    char def;
{
    char ch;
#ifndef WINDOW_YN
    char buf[ 80 ];

    if( def && def!='q')
    {
	sprintf( buf, "%s [%c] ", prompt, def );
	amii_addtopl( buf );
    } else {
	amii_addtopl( prompt );
    }

    cursor_on( WIN_MESSAGE );
    do {
	ch = WindowGetchar();
	if( ch == '\33' )
	    break;
	else if( def && ( ch == '\n' || ch == '\r' ) )
	{
	    ch = def;
	    break;
	}
    } while( resp && *resp && index( resp, ch ) == 0 );

    cursor_off( WIN_MESSAGE );
    if( ch == '\33' )
    {
	/*amii_addtopl( " you change your mind..." );*/
	ch = 0;
    }
    /* Try this to make topl behave more appropriately? */
    clear_nhwindow( WIN_MESSAGE );
#else
    struct Window *cwin;
    struct IntuiMessage *imsg;
    int aredone = 0, xcor;
    struct Gadget *gd;
    char buf[ 4 ];

    *StrString = 0;

    Ask_Window.Screen = HackScreen;
    Ask_IText1.IText = buf;
    Ask_IText2.IText = prompt;
    if( def )
    {
	sprintf( buf, "[%c]", def );
	Ask_Window.FirstGadget = &Ask_Gadget1;
	Ask_IText2.LeftEdge = 44;
    }
    else
    {
	Ask_Window.FirstGadget = NULL;
	Ask_IText2.LeftEdge = 9;
    }

    /* Use this RPort to get needed dimensions */

    if( cwin = wins[WIN_MAP]->win )
    {
    Ask_IText1.LeftEdge =
	(Ask_Gadget1.Width - cwin->BorderLeft - cwin->BorderRight) / 2;
    Ask_Window.Width =
	 Ask_IText2.LeftEdge + (5 + strlen( prompt )) * txwidth;
    xcor = Ask_IText2.LeftEdge + (2 + strlen( prompt )) * txwidth;
    }

    if( ( cwin = OpenShWindow( (void *)&Ask_Window ) ) == NULL )
	return( 0 );

    PrintIText( cwin->RPort, &Ask_IntuiTextList1, 0, 0 );

    while( !aredone )
    {
	WaitPort( cwin->UserPort );
	while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL )
	{
	    switch( imsg->Class )
	    {
		case VANILLAKEY:
		    Move( cwin->RPort, xcor,
			Ask_IText2.TopEdge + txheight - 1 );
		    ch = imsg->Code;
		    if( ch != '\33' )
		    {
			Text( cwin->RPort, &ch, 1 );
			if( resp == NULL || *resp == 0 ||
							index( resp, ch ) )
			{
			    aredone = 1;
			    Delay( 45 );
			}
			else
			    DisplayBeep( NULL );
		    }
		    else
		    {
			aredone = 1;
		    }
		    break;

		case GADGETUP:
		    gd = (struct Gadget *) imsg->IAddress;
		    switch( gd->GadgetID )
		    {
			case 1:
			    ch = def;
			    aredone = 1;
			    break;
		    }
		    break;
	    }
	    ReplyMsg( (struct Message *) imsg );
	}
    }

    CloseShWindow( cwin );
#endif
    return( ch );
}

/* Add a line in the message window */

void
amii_addtopl(s)
    const char *s;
{
    amii_putstr(WIN_MESSAGE,0,s);   /* is this right? */
}

void
TextSpaces( rp, nr )
    struct RastPort *rp;
    int nr;
{
    if( nr < 1 )
	return;

    while (nr > sizeof(spaces) - 1)
    {
	Text(rp, spaces, (long)sizeof(spaces) - 1);
	nr -= sizeof(spaces) - 1;
    }
    if (nr > 0)
	Text(rp, spaces, (long)nr);
}

/* Put a string into the indicated window using the indicated attribute */

void
amii_putstr(window,attr,str)
    winid window;
    int attr;
    const char *str;
{
    struct Window *w;
    register struct WinDesc *cw;
    register char *ob;
    int i, j, n0;

    /* Always try to avoid a panic when there is no window */
    if( window == WIN_ERR )
    {
	window = WIN_BASE;
	if( window == WIN_ERR )
	    window = WIN_BASE = amii_create_nhwindow( NHW_BASE );
    }

    if( window == WIN_ERR || ( cw = wins[window] ) == NULL )
    {
	/* tty does this differently - is this OK? */
	flags.window_inited=0;
	panic(winpanicstr,window, "putstr");
    }

    w = cw->win;

    if(!str)return;
    amiIDisplay->lastwin=window;    /* do we care??? */

    /* NHW_MENU windows are not opened immediately, so check if we
     * have the window pointer yet
     */

    if( w )
    {
	/* Force the drawing mode and pen colors */

	SetDrMd( w->RPort, JAM2 );
	if( cw->type == NHW_STATUS )
	    SetAPen( w->RPort, attr ? C_BLUE : C_CYAN );
	else if( cw->type == NHW_MESSAGE )
	    SetAPen( w->RPort, attr ? C_RED : C_WHITE );
	else
	    SetAPen( w->RPort, attr ? C_WHITE : C_RED );
	SetBPen( w->RPort, C_BLACK );
    }
    else if( cw->type != NHW_MENU && cw->type != NHW_TEXT )
    {
	panic( "NULL window pointer in putstr 2" );
    }

    /* Okay now do the work for each type */

    switch(cw->type)
    {
#define MORE_FUDGE  10  /* 8 for --more--, 1 for preceeding sp, 1 for */
		/* putstr pad */
    case NHW_MESSAGE:
	strncpy( toplines, str, sizeof( toplines ) );
	toplines[ sizeof( toplines ) - 1 ] = 0;
	    /* Needed for initial message to be visible */
	if( cw->curx == -1 )
	{
	    amii_curs( WIN_MESSAGE, 1, 0 );
	    cw->curx = 0;
	}

	if(strlen(str) >= (cw->cols-MORE_FUDGE))
	{
	    int i;
	    char *p;

	    while( strlen( str ) >= (cw->cols-MORE_FUDGE) )
	    {
		for(p=(&str[ cw->cols ])-MORE_FUDGE; !isspace(*p) && p != str;)
		{
		    --p;
		}
		if( p == str )
		    p = &str[ cw->cols ];
		outsubstr( cw, str, i = (long)p-(long)str );
		cw->curx += i;
		amii_cl_end( cw, cw->curx );
		str = p+1;
	    }
	    if( *str )
	    {
		outsubstr( cw, str, i = strlen( str ) );
		cw->curx += i;
		amii_cl_end( cw, cw->curx );
	    }
	}
	else
	{
	    outsubstr( cw, str, i = strlen( str ) );
	    cw->curx += i;
	    amii_cl_end( cw, cw->curx );
	}

	    /* If used all of history lines, move them down */

	    if( cw->maxrow == flags.msg_history )
	    {
		if( cw->data[ 0 ] )
		    free( cw->data[ 0 ] );
		memcpy( cw->data, &cw->data[ 1 ],
		    ( flags.msg_history - 1 ) * sizeof( char * ) );
		cw->data[ flags.msg_history - 1 ] =
				(char *) alloc( strlen( toplines ) + 1 );
		strcpy( cw->data[ flags.msg_history - 1 ], toplines );
	    }
	    else
	    {
		/* Otherwise, allocate a new one and copy the line in */
		cw->data[ cw->maxrow ] = (char *)
					    alloc( strlen( toplines ) + 1 );
		strcpy( cw->data[ cw->maxrow++ ], toplines );
	    }
	    cw->maxcol = cw->maxrow;
	    break;

    case NHW_STATUS:
	if( cw->data[ cw->cury ] == NULL )
	    panic( "NULL pointer for status window" );
	ob = &cw->data[cw->cury][j = cw->curx];
	if(flags.botlx) *ob = 0;

	    /* Display when beam at top to avoid flicker... */
	WaitTOF();
	Text(w->RPort,str,strlen(str));
	if( cw->cols > strlen( str ) )
	    TextSpaces( w->RPort, cw->cols - strlen( str ) );

	(void) strncpy(cw->data[cw->cury], str, cw->cols );
	cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */
	cw->cury = (cw->cury+1) % 2;
	cw->curx = 0;
	break;

    case NHW_MAP:
    case NHW_BASE:
	amii_curs(window, cw->curx+1, cw->cury);
	Text(w->RPort,str,strlen(str));
	cw->curx = 0;
	    /* CR-LF is automatic in these windows */
	cw->cury++;
	break;

    case NHW_MENU:
    case NHW_TEXT:

	/* always grows one at a time, but alloc 12 at a time */

	if( cw->cury >= cw->rows || !cw->data ) {
	    char **tmp;

		/* Allocate 12 more rows */
	    cw->rows += 12;
	    tmp = (char**) alloc(sizeof(char*) * cw->rows);

		/* Copy the old lines */
	    for(i=0; i<cw->cury; i++)
		tmp[i] = cw->data[i];

	    if( cw->data )
		free( cw->data );

	    cw->data = tmp;

		/* Null out the unused entries. */
	    for(i=cw->cury; i<cw->rows; i++)
		cw->data[i] = 0;
	}

	if( !cw->data )
	    panic("no data storage");

	    /* Shouldn't need to do this, but... */

	if( cw->data && cw->data[cw->cury] )
	    free( cw->data[cw->cury] );

	n0 = strlen(str)+1;
	cw->data[cw->cury] = (char*) alloc(n0+SOFF);

	    /* avoid nuls, for convenience */
	cw->data[cw->cury][VATTR] = attr+1;
	cw->data[cw->cury][SEL_ITEM] = 0;
	Strcpy( cw->data[cw->cury] + SOFF, str);

	if(n0 > cw->maxcol) cw->maxcol = n0;
	if(++cw->cury > cw->maxrow) cw->maxrow = cw->cury;
	break;

    default:
	panic("Invalid or unset window type in putstr()");
    }
}

static void
outmore( cw )
    register struct WinDesc *cw;
{
    struct Window *w = cw->win;

    Text( w->RPort, " --more--", 9 );
	/* Allow mouse clicks to clean --more-- */
    WindowGetevent( );
    amii_curs( WIN_MESSAGE, 1, 0 );
    cw->curx = 0;
    amii_cl_end( cw, cw->curx );
}

static void
outsubstr( cw, str, len )
    register struct WinDesc *cw;
    char *str;
    int len;
{
    struct Window *w = cw->win;

    if( cw->curx )
    {
	/* Check if this string and --more-- fit, if not,
	 * then put out --more-- and wait for a key.
	 */
	if( (len + MORE_FUDGE ) + cw->curx >= cw->cols )
	{
	    outmore( cw );
	}
	else
	{
	    /* Otherwise, move and put out a blank separator */
	    Text( w->RPort, spaces, 1 );
	    cw->curx += 1;
	}
    }

    Text(w->RPort,str,len);
}

/* Put a graphics character onto the screen */

void
amii_putsym( st, i, y, c )
    winid st;
    int i, y;
    CHAR_P c;
{
    char buf[ 2 ];
    amii_curs( st, i, y );
    buf[ 0 ] = c;
    buf[ 1 ] = 0;
    amii_putstr( st, 0, buf );
}

/* Clear the indicated window */

void
amii_clear_nhwindow(win)
    register winid win;
{
    register struct WinDesc *cw;
    register struct Window *w;

    if( win == WIN_ERR || ( cw = wins[win] ) == NULL )
	panic( winpanicstr, win, "clear_nhwindow" );

    if( w = cw->win )
	SetDrMd( w->RPort, JAM2);

    cursor_off( win );

    /* should be: clear the rastport, reset x,y etc */

    if( cw->type == NHW_MENU || cw->type == NHW_TEXT )
    {
    /* Window might not be opened yet */

	if( w )
	{
	    SetAPen( w->RPort, 0 );
	    SetBPen( w->RPort, 0 );
	    RectFill( w->RPort, w->BorderLeft, w->BorderTop,
	      w->Width - w->BorderRight-1,
	      w->Height - w->BorderBottom-1 );
	    SetAPen( w->RPort, 1 );
	}
    }
    else if( w )
    {
	if( cw->type == NHW_MESSAGE )
	{
	    amii_curs( win, 1, 0 );
	    TextSpaces( w->RPort, cw->cols );
	}
	else
	{
	    SetAPen( w->RPort, 0 );
	    SetBPen( w->RPort, 0 );
	    RectFill( w->RPort, w->BorderLeft, w->BorderTop,
	      w->Width - w->BorderRight-1,
	      w->Height - w->BorderBottom-1 );
	    SetAPen( w->RPort, 1 );
	}
    }

    cw->curx = cw->cury = 0;
    amii_curs( win, 1, 0 );
}

/* Dismiss the window from the screen */

static void
dismiss_nhwindow(win)
    register winid win;
{
    register struct Window *w;
    register struct WinDesc *cw;

    if( win == WIN_ERR || ( cw = wins[win] ) == NULL )
    {
	panic(winpanicstr,win, "dismiss_nhwindow");
    }

    w = cw->win;

    if( w )
    {
	/* Map Window has this stuff attached to it. */
	if( win == WIN_MAP )
	    ClearMenuStrip( w );

	/* Close the window? */

	CloseShWindow( w );
	cw->win = NULL;

	if( cw->newwin )
	    FreeNewWindow( (void *)cw->newwin );
	cw->newwin = NULL;
    }

    if( cw->canresp )
	free( cw->canresp );
    cw->canresp = NULL;

    if( cw->morestr )
	free( cw->morestr );
    cw->morestr = NULL;

    cw->maxrow = cw->maxcol = 0;
}

void
amii_exit_nhwindows(str)
    const char *str;
{
    /* Seems strange to have to do this... but we need the BASE window
     * left behind...
     */
    kill_nhwindows( 0 );
    if( str ) raw_print( str );
}

amii_nh_poskey(x, y, mod)
    int*x, *y, *mod;
{
    struct WinDesc *cw;
    WETYPE type;
    struct RastPort *rp;
    struct Window *w;

    if( WIN_MAP != WIN_ERR && (cw = wins[ WIN_MAP ]) && ( w = cw->win ) )
	cursor_on( WIN_MAP );
    else
	panic( "no MAP window opened for nh_poskey\n" );

    rp = w->RPort;
    while( 1 )
    {
	type = WindowGetevent( );
	if( type == WEMOUSE )
	{
	    *mod = CLICK_1;
	    if( lastevent.u.mouse.qual )
		*mod = 0;

	    /* X coordinates are 1 based, Y are zero based. */
	    *x = ( (lastevent.u.mouse.x - w->BorderLeft) / txwidth ) + 1;
	    *y = ( ( lastevent.u.mouse.y - w->BorderTop-txbaseline ) /
			txheight );
	    return( 0 );
	}
	else if( type == WEKEY )
	{
	    lastevent.type = WEUNK;
	    return( lastevent.u.key );
	}
    }
}

int
amii_nhgetch()
{
    int ch;

    if( WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
	cursor_on( WIN_MAP );
    ch = WindowGetchar();
    return( ch );
}

void
amii_get_nh_event()
{
    /* nothing now - later I have no idea.  Is this just a Mac hook? */
}

void
amii_remember_topl()
{
    /* ignore for now.  I think this will be done automatically by
     * the code writing to the message window, but I could be wrong.
     */
}

int
amii_doprev_message()
{
    struct WinDesc *cw;
    struct Window *w;
    char *str;

    if( WIN_MESSAGE == WIN_ERR ||
	    ( cw = wins[ WIN_MESSAGE ] ) == NULL || ( w = cw->win ) == NULL )
    {
	panic(winpanicstr,WIN_MESSAGE, "doprev_message");
    }

    if( --cw->maxcol < 0 )
    {
	cw->maxcol = 0;
	DisplayBeep( NULL );
	str = "No more history saved...";
    }
    else
	str = cw->data[ cw->maxcol ];

    amii_cl_end(cw, 0);
    amii_curs( WIN_MESSAGE, 1, 0 );
    Text(w->RPort,str,strlen(str));
    cw->curx = cw->cols + 1;

    return( 0 );
}

void
amii_display_nhwindow(win,blocking)
    winid win;
    boolean blocking;
{
    int i;
    static int lastwin = -1;
    struct Window *w;
    struct WinDesc *cw;

    if( !Initialized )
	return;
    lastwin = win;

    if( win == WIN_ERR || ( cw = wins[win] ) == NULL )
	panic(winpanicstr,win,"display_nhwindow");

    if( cw->type == NHW_MAP || cw->type == NHW_STATUS ||
						cw->type == NHW_MESSAGE )
    {
	return;
    }

    if(cw->type==NHW_MESSAGE)
	flags.window_inited=TRUE;

    /* should be:
	if type != map, WindowToFront
	if type == map && autoshow, unblock area around cursor
	    (or something like that)
     */

    w = cw->win;
    if( w )
    {
	WindowToFront( w );
	ActivateWindow( w );
    }

    if( blocking && WIN_MAP != WIN_ERR && wins[ WIN_MAP ] )
    {
	flush_glyph_buffer( wins[ WIN_MAP ]->win );
    }

    if( cw->type == NHW_MENU || cw->type == NHW_TEXT )
    {
	DoMenuScroll( win, blocking );
    }
    else if( cw->type==NHW_MAP )
    {
	end_glyphout( win );
	for( i = 0; i < MAXWIN; ++i )
	{
	    if( cw = wins[i] )
	    {
		if( cw->type == NHW_STATUS || cw->type == NHW_MESSAGE )
		{
		    if( cw->win )
		    {
			WindowToFront(cw->win);
		    }
		}
	    }
	}

	/* Do more if it is time... */
	if( blocking == TRUE )
	    outmore( wins[ win ] );
    }
}

void
amii_display_file(fn, complain)
const char *fn;
boolean complain;
{
    register struct WinDesc *cw;
    register int win;
    register FILE *fp;
    register char *t;
    register char buf[ 200 ];

    if( fn == NULL )
	panic("NULL file name in display_file()");

    if( ( fp = fopen( fn, "r" ) ) == NULL )
    {
	if (complain) {
	    sprintf( buf, "Can't display %s: %s", fn,
#ifdef  __SASC_60
			__sys_errlist[ errno ]
#else
			sys_errlist[ errno ]
#endif
			);
	    amii_addtopl( buf );
	}
	return;
    }
    win = amii_create_nhwindow( NHW_TEXT );

    /* Set window title to file name */
    if( cw = wins[ win ] )
	cw->morestr = fn;

    while( fgets( buf, sizeof( buf ), fp ) != NULL )
    {
	if( t = index( buf, '\n' ) )
	    *t = 0;
	amii_putstr( win, 0, buf );
    }
    fclose( fp );

    /* If there were lines in the file, display those lines */

    if( wins[ win ]->cury > 0 )
	amii_display_nhwindow( win, TRUE );

    amii_destroy_nhwindow( win );
}

void
amii_curs(window, x, y)
winid window;
register int x, y;  /* not xchar: perhaps xchar is unsigned and
	       curx-x would be unsigned as well */
{
    register struct WinDesc *cw;
    register struct Window *w;
    register struct RastPort *rp;

    if( window == WIN_ERR || ( cw = wins[window] ) == NULL )
	panic(winpanicstr,  window, "curs");
    if( (w = cw->win) == NULL )
    {
	if( cw->type == NHW_MENU || cw->type == NHW_TEXT )
	    return;
	else
	    panic( "No window open yet in curs() for winid %d\n", window );
    }
    amiIDisplay->lastwin = window;

    /* Make sure x is within bounds */
    if( x > 0 )
	--x;    /* column 0 is never used */
    else
	x = 0;

    cw->curx = x;
    cw->cury = y;
#ifdef DEBUG
    if(x<0 || y<0 || y >= cw->rows || x >= cw->cols)
    {
	char *s = "[unknown type]";
	switch(cw->type)
	{
	    case NHW_MESSAGE: s = "[topl window]"; break;
	    case NHW_STATUS: s = "[status window]"; break;
	    case NHW_MAP: s = "[map window]"; break;
	    case NHW_MENU: s = "[menu window]"; break;
	    case NHW_TEXT: s = "[text window]"; break;
	}
	impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
	return;
    }
#endif
    x += cw->offx;
    y += cw->offy;

#ifdef CLIPPING
    if(clipping && window == WIN_MAP)
    {
	x -= clipx;
	y -= clipy;
    }
#endif

    /* Output all saved output before doing cursor movements for MAP */

    if( cw->type == NHW_MAP )
	flush_glyph_buffer( w );

    /* Actually do it */

    rp = w->RPort;
    if( cw->type == NHW_MENU || cw->type == NHW_TEXT )
    {
	Move( rp, (x * txwidth) + w->BorderLeft + 3,
	    (y*(txheight+1) ) + w->RPort->TxBaseline + w->BorderTop + 1 );
    }
    else if( cw->type == NHW_MAP || cw->type == NHW_BASE )
    {
	/* These coordinate calculations must be synced with those
	 * in flush_glyph_buffer() in amiwind.c.  curs_on_u() will
	 * use this code, all other drawing occurs through the glyph
	 * code.  In order for the cursor to appear on top of the hero,
	 * the code must compute X,Y in the same manner relative to
	 * the RastPort coordinates.
	 */
	Move( rp, (x * txwidth) + w->BorderLeft,
		    w->BorderTop + ( (y + 1) * txheight ) + txbaseline + 1 );
    }
    else if( cw->type == NHW_MESSAGE )
    {
	Move( rp, (x * txwidth) + w->BorderLeft + 2,
					    w->BorderTop + txbaseline + 3 );
    }
    else
    {
	Move( rp, (x * txwidth) + w->BorderLeft + 2,
			    (y*txheight) + w->BorderTop + txbaseline + 2 );
    }
}

/*
 *  print_glyph
 *
 *  Print the glyph to the output device.  Don't flush the output device.
 *
 *  Since this is only called from show_glyph(), it is assumed that the
 *  position and glyph are always correct (checked there)!
 */

void
amii_print_glyph(win,x,y,glyph)
    winid win;
    xchar x,y;
    int glyph;
{
    struct WinDesc *cw;
    uchar   ch;
    register int offset;
#ifdef TEXTCOLOR
    int     color;
    extern int zapcolors[];

    if( win == WIN_ERR || (cw=wins[win]) == NULL || cw->type != NHW_MAP)
	panic(winpanicstr,win,"print_glyph");

#define zap_color(n)  color = flags.use_color ? zapcolors[n] : NO_COLOR
#define cmap_color(n) color = flags.use_color ? defsyms[n].color : NO_COLOR
#define trap_color(n) color = flags.use_color ? \
		(((n) == WEB) ? defsyms[S_web ].color  : \
			    defsyms[S_trap].color) : \
			NO_COLOR
#define obj_color(n)  color = flags.use_color ? objects[n].oc_color : NO_COLOR
#define mon_color(n)  color = flags.use_color ? mons[n].mcolor : NO_COLOR
#define pet_color(n)  color = flags.use_color ? mons[n].mcolor :          \
		/* If no color, try to hilite pets; black  */ \
		/* should be HI                */ \
		    ((flags.hilite_pet) ? BLACK : NO_COLOR)

# else /* no text color */

#define zap_color(n)
#define cmap_color(n)
#define trap_color(n)
#define obj_color(n)
#define mon_color(n)
#define pet_color(n)

#endif

    /*
     *  Map the glyph back to a character.
     *
     *  Warning:  For speed, this makes an assumption on the order of
     *        offsets.  The order is set in display.h.
     */
    if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {  /* swallow */
	/* see swallow_to_glyph()in display.c */
	ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
	mon_color(offset >> 3);
    } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {       /* zap beam */
	ch = showsyms[S_vbeam + (offset & 0x3)];
	zap_color((offset >> 2));
    } else if( ( offset = (glyph - GLYPH_CMAP_OFF) ) >= 0 ) {   /* cmap */
	ch = showsyms[offset];
	cmap_color(offset);
    } else if ( ( offset = (glyph - GLYPH_TRAP_OFF) ) >= 0 ) {  /* trap */
	ch = (offset == WEB) ? showsyms[S_web] : showsyms[S_trap];
	trap_color(offset);
    } else if( ( offset = (glyph - GLYPH_OBJ_OFF) ) >= 0 ) {    /* object */
	ch = oc_syms[objects[offset].oc_class];
	obj_color(offset);
    } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {  /* a corpse */
	ch = oc_syms[objects[CORPSE].oc_class];
	mon_color(offset);
    } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {   /* a pet */
	ch = (uchar) monsyms[mons[offset].mlet];
	pet_color(offset);
    } else /*if( glyph_is_monster(glyph) )*/ {      /* a monster */
	ch = (uchar) monsyms[mons[glyph].mlet];
	mon_color(glyph);
    }

    /* Move the cursor. */
#ifdef CLIPPING
    if (!win_curs(x, y)) return;
#else
    amii_curs(win,x,y+2);
#endif

#ifdef TEXTCOLOR
    /* Turn off color if rogue level. */
# ifdef REINCARNATION
    if (Is_rogue_level(&u.uz))
	color = NO_COLOR;
#  endif

    amiga_print_glyph(win,color,ch);
#else
    g_putch(ch);    /* print the character */
#endif
    cw->curx++;     /* one character over */
}

void
DoMenuScroll( win, blocking )
    int win, blocking;
{
    register struct Window *w;
    register struct NewWindow *nw;
    struct PropInfo *pip;
    register struct WinDesc *cw;
    struct IntuiMessage *imsg;
    struct Gadget *gd;
    register int wheight, xsize, ysize, aredone = 0;
    register int txwd, txh;
    long mics, secs, class, code;
    long oldmics = 0, oldsecs = 0;
    int aidx, oidx, topidx, hidden;
    char *t;
    SHORT mx, my;
    char title[ 100 ];
    int dosize = 1;
    struct Screen *scrn = HackScreen;

    if( win == WIN_ERR || ( cw = wins[ win ] ) == NULL )
	panic(winpanicstr,win,"DoMenuScroll");

    /* Check to see if we should open the window, should usually need to */

    txwd = txwidth;
    txh = txheight + 1; /* +1 for interline space */
    w = cw->win;
    if( w == NULL )
    {
	/* Humm, there is only so much... */
	if( scrn )
	     xsize = scrn->WBorLeft + scrn->WBorRight + MenuScroll.Width + 2;
	else
	     xsize = 5 + 5 + MenuScroll.Width + 2;
	xsize += (txwd * cw->maxcol);
	if( xsize > amiIDisplay->xpix )
	    xsize = amiIDisplay->xpix;

	    /* If next row not off window, use it, else use the bottom */

	ysize = ( txh * cw->maxrow ) +          /* The text space */
	  HackScreen->WBorTop + txheight + 1 +    /* Top border */
	  HackScreen->WBorBottom + 1;     /* The bottom border */
	if( ysize > amiIDisplay->ypix )
	    ysize = amiIDisplay->ypix;

	/* Adjust the size of the menu scroll gadget */

	nw = (void *)DupNewWindow( (void *)(&new_wins[ cw->type ].newwin) );
	cw->newwin = (void *)nw;
	if( nw == NULL )
	    panic("No NewWindow Allocated" );

	nw->Screen = HackScreen;

	if( win == WIN_INVEN ){
	    sprintf( title, "%s the %s's Inventory", plname, pl_character );
	    nw->Title = title;
	}
	else if( cw->morestr )
	    nw->Title = cw->morestr;

	/* Adjust the window coordinates and size now that we know
	 * how many items are to be displayed.
	 */

	nw->Width = xsize;
	nw->Height = ysize;
	nw->TopEdge = 0;
	if( cw->type == NHW_TEXT && ysize < amiIDisplay->ypix )
	    nw->TopEdge += ( amiIDisplay->ypix - ysize ) / 2;
	nw->LeftEdge = amiIDisplay->xpix - xsize;
	if( cw->type == NHW_TEXT && xsize < amiIDisplay->xpix )
	    nw->LeftEdge -= ( amiIDisplay->xpix - xsize ) / 2;


	/* Now, open the window */
	w = cw->win = OpenShWindow( (void *)nw );

	if( w == NULL )	panic("No Window Opened For Menu" );
    }

    /* Find the scroll gadget */

    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; gd = gd->NextGadget )
	continue;

    if( !gd ) panic("Can't find scroll gadget" );

    wheight = ( w->Height - w->BorderTop - w->BorderBottom ) / txh;
    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight
	    /* + MenuScroll.Width*/ ) / txwd;
    morc = 0;
    oidx = -1;
    topidx = 0;

    /* Make the prop gadget the right size and place */

    DisplayData( win, topidx, -1 );

    SetPropInfo( w, gd, wheight, cw->maxrow, topidx );
    oldsecs = oldmics = 0;

    while( !aredone )
    {
	/* Process window messages */

	WaitPort( w->UserPort );
	while( imsg = (struct IntuiMessage * ) GetMsg( w->UserPort ) )
	{
	    class = imsg->Class;
	    code = imsg->Code;
	    mics = imsg->Micros;
	    secs = imsg->Seconds;
	    gd = (struct Gadget *) imsg->IAddress;
	    mx = imsg->MouseX;
	    my = imsg->MouseY;

	    /* Only do our window or VANILLAKEY from other windows */

	    if( imsg->IDCMPWindow != w && class != VANILLAKEY &&
							class != RAWKEY )
	    {
		ReplyMsg( (struct Message *) imsg );
		continue;
	    }

	    /* Do DeadKeyConvert() stuff if RAWKEY... */
	    if( class == RAWKEY )
	    {
		class = VANILLAKEY;
		code = ConvertKey( imsg );
	    }
	    ReplyMsg( (struct Message *) imsg );

	    switch( class )
	    {
		case NEWSIZE:
		    /* Ignore every other newsize, no action needed */

		    if( !dosize )
		    {
			dosize = 1;
			break;
		    }

		    /* Find the gadget */

		    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			gd = gd->NextGadget;

		    if( !gd )
			panic("Can't find scroll gadget" );

		    wheight = ( w->Height - w->BorderTop -
						w->BorderBottom - 1) / txh;
		    cw->cols = ( w->Width -
				w->BorderLeft - w->BorderRight ) / txwd;
		    if( wheight < 2 )
			wheight = 2;

		    /* Make the prop gadget the right size and place */

		    DisplayData( win, topidx, oidx );
		    SetPropInfo( w, gd, wheight, cw->maxrow, topidx );

		    /* Force the window to a text line boundry <= to
		     * what the user dragged it to.  This eliminates
		     * having to clean things up on the bottom edge.
		     */

		    SizeWindow( w, 0, ( wheight * txh) +
			    w->BorderTop + w->BorderBottom + 1 - w->Height );

		    /* Don't do next NEWSIZE, we caused it */
		    dosize = 0;
		    oldsecs = oldmics = 0;
		    break;

		case VANILLAKEY:
#define CTRL(x)     ((x)-'@')
		    if( code == CTRL('D') || code == CTRL('U') )
		    {
			int endcnt, i;

			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			endcnt = wheight / 2;
			if( endcnt == 0 )
			    endcnt = 1;

			for( i = 0; i < endcnt; ++i )
			{
			    if( code == CTRL('D') )
			    {
				if( topidx + wheight < cw->maxrow )
				    ++topidx;
				else
				    break;
			    }
			    else
			    {
				if( topidx > 0 )
				    --topidx;
				else
				    break;
			    }

			    /* Make prop gadget the right size and place */

			    DisplayData( win, topidx, oidx );
			    SetPropInfo( w,gd, wheight, cw->maxrow, topidx );
			}
			oldsecs = oldmics = 0;
		    }
		    else if( code == '\b' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			if( topidx - wheight - 2 < 0 )
			{
			    topidx = 0;
			}
			else
			{
			    topidx -= wheight - 2;
			}
			DisplayData( win, topidx, oidx );
			SetPropInfo( w, gd, wheight, cw->maxrow, topidx );
			oldsecs = oldmics = 0;
		    }
		    else if( code == ' ' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			if( topidx + wheight >= cw->maxrow )
			{
			    morc = 0;
			    aredone = 1;
			}
			else
			{
			    /*  If there are still lines to be seen */

			    if( cw->maxrow > topidx + wheight )
			    {
				if( wheight > 2 )
				    topidx += wheight - 2;
				else
				    ++topidx;
				DisplayData( win, topidx, oidx );
				SetPropInfo( w, gd, wheight,
						    cw->maxrow, topidx );
			    }
			    oldsecs = oldmics = 0;
			}
		    }
		    else if( code == '\n' || code == '\r' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			/* If all line displayed, we are done */

			if( topidx + wheight >= cw->maxrow )
			{
			    morc = 0;
			    aredone = 1;
			}
			else
			{
			    /*  If there are still lines to be seen */

			    if( cw->maxrow > topidx + 1 )
			    {
				++topidx;
				DisplayData( win, topidx, oidx );
				SetPropInfo( w, gd, wheight,
						    cw->maxrow, topidx );
			    }
			    oldsecs = oldmics = 0;
			}
		    }
		    else if( cw->resp && index( cw->resp, code ) )
		    {
			morc = code;
			aredone = 1;
		    }
		    else if( code == '\33' || code == 'q' || code == 'Q' )
		    {
			morc = '\33';
			aredone = 1;
		    }
		    break;

		case CLOSEWINDOW:
		    aredone = 1;
		    morc = '\33';
		    break;

		case GADGETUP:
		    break;

		case MOUSEMOVE:
		    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			gd = gd->NextGadget;

		    pip = (struct PropInfo *)gd->SpecialInfo;
		    hidden = max( cw->maxrow - wheight, 0 );
		    aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16;
		    if( aidx != topidx )
			DisplayData( win, topidx = aidx, oidx );
		    break;

		case MOUSEBUTTONS:
		    if( ( code == SELECTUP || code == SELECTDOWN ) &&
						    cw->type == NHW_MENU )
		    {
			/* Which one is the mouse pointing at? */

			aidx = (( (my - w->TopEdge) - w->BorderTop + 1 ) /
							txh) + topidx;

			/* If different lines, don't select double click */

			if( aidx != oidx )
			{
			    oldsecs = 0;
			    oldmics = 0;
			}

			/* If releasing, check for double click */

			if( code == SELECTUP )
			{
			    if( aidx == oidx )
			    {
				if( DoubleClick( oldsecs,
						    oldmics, secs, mics ) )
				{
				    morc = cw->resp[ aidx ];
				    aredone = 1;
				}
				oldsecs = secs;
				oldmics = mics;
			    }
			}
			else if( aidx < cw->maxrow && code == SELECTDOWN )
			{
			    /* Remove old highlighting if visible */

			    if( oidx > topidx && oidx - topidx < wheight )
			    {
				t = cw->data[ oidx ] + SOFF;
				amii_curs( win, 1, oidx - topidx );
				SetDrMd( w->RPort, JAM2 );
				SetAPen( w->RPort, C_WHITE );
				SetBPen( w->RPort, C_BLACK );
				Text( w->RPort, t, strlen( t ) );
				oidx = -1;
			    }

			    t = cw->data[ aidx ];
			    if( t[ SEL_ITEM ] == 1 )
			    {
				oidx = aidx;

				amii_curs( win, 1, aidx - topidx );
				SetDrMd( w->RPort, JAM2 );
				SetAPen( w->RPort, C_BLUE );
				SetBPen( w->RPort, C_WHITE );
				t += SOFF;
				Text( w->RPort, t, strlen( t ) );
			    }
			    else
			    {
				DisplayBeep( NULL );
				oldsecs = 0;
				oldmics = 0;
			    }
			}
		    }
		    else
		    {
			DisplayBeep( NULL );
		    }
		    break;
	    }
	}
    }
}

void
DisplayData( win, start, where )
    int win;
    int start;
    int where;
{
    register struct WinDesc *cw;
    register struct Window *w;
    register int i, len, wheight;
    int col = -1;

    if( win == WIN_ERR || !(cw = wins[win]) || !( w = cw->win ) )
    {
	panic( winpanicstr, win, "No Window in DisplayData" );
    }

    SetDrMd( w->RPort, JAM2 );
    wheight = ( w->Height - w->BorderTop - w->BorderBottom ) / ( txheight + 1 );

    for( i = start; i < start + wheight; ++i )
    {
	amii_curs( win, 1, i - start );

	if( where == i )
	{
	    if( col != 1 )
	    {
		SetAPen( w->RPort, C_BLUE );
		SetBPen( w->RPort, C_WHITE );
		col = 1;
	    }
	}
	else if( col != 2 )
	{
	    SetAPen( w->RPort, C_WHITE );
	    SetBPen( w->RPort, C_BLACK );
	    col = 2;
	}

	/* Next line out, truncate if too long */

	len = 0;
	if( i < cw->maxrow )
	{
	    register char *t;

	    t = cw->data[ i ] + SOFF;
	    len = strlen( t );
	    if( len > cw->cols )
		len = cw->cols;
	    Text( w->RPort, t, len );
	}
	amii_cl_end( cw, len );
    }
    return;
}

void SetPropInfo( win, gad, vis, total, top )
    register struct Window *win;
    register struct Gadget *gad;
    register long vis, total, top;
{
    register long hidden;
    register int body, pot;

    hidden = max( total-vis, 0 );

    /* Force the last section to be just to the bottom */

    if( top > hidden )
	top = hidden;

    /* Scale the body position. */
    /* 2 lines overlap */

    if( hidden > 0 && total != 0 )
	body = (ULONG) ((vis - 2) * MAXBODY) / (total - 2);
    else
	body = MAXBODY;

    if( hidden > 0 )
	pot = (ULONG) (top * MAXPOT) / hidden;
    else
	pot = 0;

    NewModifyProp( gad, win, NULL,
			    AUTOKNOB|FREEVERT, 0, pot, MAXBODY, body, 1 );
}

void
kill_nhwindows( all )
    register int all;
{
    register int i;
    register struct WinDesc *cw;

    /* Foreach open window in all of wins[], CloseShWindow, free memory */

    for( i = 0; i < MAXWIN; ++i )
    {
	if( (cw = wins[ i ]) && (cw->type != NHW_BASE || all) )
	{
	    CloseShWindow( cw->win );
	    free( cw );
	    wins[ i ] = NULL;
	}
    }
}

void
amii_cl_end( cw, i )
    register struct WinDesc *cw;
    register int i;
{
    register struct Window *w = cw->win;
    register int oy, ox;

    if( !w )
	panic("NULL window pointer in amii_cl_end()");

    oy = w->RPort->cp_y;
    ox = w->RPort->cp_x;

    TextSpaces( w->RPort, cw->cols - i );

    Move( w->RPort, ox, oy );
}

void cursor_off( window )
    winid window;
{
    register struct WinDesc *cw;
    register struct Window *w;
    register struct RastPort *rp;
    int curx, cury;
    long dmode;
    short apen, bpen;
    unsigned char ch;

    if( window == WIN_ERR || ( cw = wins[window] ) == NULL )
    {
	/* tty does this differently - is this OK? */
	flags.window_inited=0;
	panic(winpanicstr,window, "cursor_off");
    }

    if( !(cw->flags & FLMAP_CURSUP ) )
	return;
    w = cw->win;

    if( !w )
	return;

    cw->flags &= ~FLMAP_CURSUP;
    rp = w->RPort;

    /* Save the current information */
    curx = rp->cp_x;
    cury = rp->cp_y;
    dmode = rp->DrawMode;
    apen = rp->FgPen;
    bpen = rp->BgPen;
    SetAPen( rp, cw->curs_apen );
    SetBPen( rp, cw->curs_bpen );
    SetDrMd( rp, COMPLEMENT );

    ch = CURSOR_CHAR;
    Move( rp, cw->cursx, cw->cursy );
    Text( rp, &ch, 1 );

    /* Put back the other stuff */

    Move( rp, curx, cury );
    SetDrMd( rp, dmode );
    SetAPen( rp, apen );
    SetBPen( rp, bpen );
}

void cursor_on( window )
    winid window;
{
    register struct WinDesc *cw;
    register struct Window *w;
    register struct RastPort *rp;
    unsigned char ch;
    long dmode;
    short apen, bpen;

    if( window == WIN_ERR || ( cw = wins[window] ) == NULL )
    {
	/* tty does this differently - is this OK? */
	flags.window_inited=0;
	panic(winpanicstr,window, "cursor_on");
    }

    if( (cw->flags & FLMAP_CURSUP ) )
	cursor_off( window );

    w = cw->win;

    if( !w )
	return;

    cw->flags |= FLMAP_CURSUP;
    rp = w->RPort;

    /* Save the current information */

    cw->cursx = rp->cp_x;
    cw->cursy = rp->cp_y;
    apen = rp->FgPen;
    bpen = rp->BgPen;
    dmode = rp->DrawMode;
    ch = CURSOR_CHAR;

    /* Draw in complement mode. The cursor body will be C_WHITE */

    cw->curs_apen = C_WHITE;
    cw->curs_bpen = C_WHITE;
    SetAPen( rp, cw->curs_apen );
    SetBPen( rp, cw->curs_bpen );
    SetDrMd( rp, COMPLEMENT );
    Move( rp, cw->cursx, cw->cursy );
    Text( rp, &ch, 1 );
    Move( rp, cw->cursx, cw->cursy );

    SetDrMd( rp, dmode );
    SetAPen( rp, apen );
    SetBPen( rp, bpen );
}

void
amii_getret()
{
    register int c;

    raw_print( "" );
    raw_print( "Press Return..." );

    c = 0;

    while( c != '\n' && c != '\r' )
    {
	if( HackPort )
	    c = WindowGetchar();
	else
	    c = getchar();
    }
    return;
}

UBYTE UNDOBUFFER[300];
SHORT BorderVectors1[] = { 0,0, 57,0, 57,11, 0,11, 0,0 };
struct Border Border1 = { -1,-1, 3,0,JAM1, 5, BorderVectors1, NULL };
struct IntuiText IText1 = { 3,0,JAM2, 4,1, NULL, (UBYTE *)"Cancel", NULL };
struct Gadget Gadget2 = {
    NULL, 9,15, 56,10, NULL, RELVERIFY, BOOLGADGET, (APTR)&Border1,
    NULL, &IText1, NULL, NULL, 1, NULL
};
UBYTE StrStringSIBuff[300];
struct StringInfo StrStringSInfo = {
    StrStringSIBuff, UNDOBUFFER, 0, 300, 0, 0,0,0,0,0, 0, 0, NULL
};
SHORT BorderVectors2[] = { 0,0, 439,0, 439,11, 0,11, 0,0 };
struct Border Border2 = { -1,-1, 3,0,JAM1, 5, BorderVectors2, NULL };
struct Gadget String = {
    &Gadget2, 77,15, 438,10, NULL, RELVERIFY+STRINGCENTER, STRGADGET,
    (APTR)&Border2, NULL, NULL, NULL, (APTR)&StrStringSInfo, 2, NULL
};

#define StrString \
   ((char *)(((struct StringInfo *)(String.SpecialInfo))->Buffer))

struct NewWindow StrWindow = {
    57,74, 526,31, 0,1, GADGETUP+CLOSEWINDOW+ACTIVEWINDOW+VANILLAKEY,
    WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,
    &String, NULL, NULL, NULL, NULL, 5,5, 0xffff,0xffff, CUSTOMSCREEN
};

/* Generate a requester for a string value. */

void amii_getlin(prompt,bufp)
    const char *prompt;
    char *bufp;
{
    getlind(prompt,bufp,0);
}

/* and with default */
void getlind(prompt,bufp, dflt)
    const char *prompt;
    char *bufp;
    const char *dflt;
{
#ifndef TOPL_GETLINE
    register struct Window *cwin;
    register struct IntuiMessage *imsg;
    register long class, code, qual;
    register int aredone = 0;
    register struct Gadget *gd;
    static int once;

    *StrString = 0;
    if( dflt )
	strcpy( StrString, dflt );
    StrWindow.Title = (UBYTE *)prompt;
    StrWindow.Screen = HackScreen;

    if( !once )
    {
	if( bigscreen )
	    StrWindow.TopEdge = (HackScreen->Height/2) - (StrWindow.Height/2);
	SetBorder( &String );
	SetBorder( &Gadget2 );
	once = 1;
    }

    if( ( cwin = OpenWindow( (void *)&StrWindow ) ) == NULL )
    {
	return;
    }

    while( !aredone )
    {
	WaitPort( cwin->UserPort );
	while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL )
	{
	    class = imsg->Class;
	    code = imsg->Code;
	    qual = imsg->Qualifier;
	    gd = (struct Gadget *) imsg->IAddress;

	    switch( class )
	    {
		case VANILLAKEY:
		    if( code == '\033' && (qual &
			    (IEQUALIFIER_LALT|IEQUALIFIER_RALT|
			    IEQUALIFIER_LCOMMAND|IEQUALIFIER_RCOMMAND) ) == 0 )
		    {
			if( bufp )
			{
			    bufp[0]='\033';
			    bufp[1]=0;
			}
			aredone = 1;
		    }
		    else
		    {
			ActivateGadget( &String, cwin, NULL );
		    }
		    break;

		case ACTIVEWINDOW:
		    ActivateGadget( &String, cwin, NULL );
		    break;

		case GADGETUP:
		    switch( gd->GadgetID )
		    {
			case 2:
			    aredone = 1;
			    if( bufp )
				strcpy( bufp, StrString );
			    break;

			case 1:
			    if( bufp )
			    {
				bufp[0]='\033';
				bufp[1]=0;
			    }
			    aredone = 1;
			    break;
		    }
		    break;

		case CLOSEWINDOW:
		    if( bufp )
			strcpy( bufp, StrString );
		    aredone = 1;
		    break;
	    }
	    ReplyMsg( (struct Message *) imsg );
	}
    }

    CloseWindow( cwin );
#else
    struct WinDesc *cw;
    struct Window *w;
    int colx, ocolx, c;
    char *obufp;

    amii_clear_nhwindow( WIN_MESSAGE );
    amii_putstr( WIN_MESSAGE, 0, prompt );
    cw = wins[ WIN_MESSAGE ];
    w = cw->win;
    ocolx = colx = strlen( prompt ) + 1;

    obufp = bufp;
    while((c = WindowGetchar()) != EOF)
    {
	amii_curs( WIN_MESSAGE, colx, 0 );
	if(c == '\033')
	{
	    *obufp = c;
	    obufp[1] = 0;
	    return;
	}
	else if(c == '\b')
	{
	    if(bufp != obufp)
	    {
		bufp--;
		amii_curs( WIN_MESSAGE, --colx, 0);
		Text( w->RPort, "\177 ", 2 );
		amii_curs( WIN_MESSAGE, colx, 0);
	    }
	    else
		DisplayBeep( NULL );
	}
	else if( c == '\n' || c == '\r' )
	{
	    *bufp = 0;
	    return;
	}
	else if(' ' <= c && c < '\177')
	{
		/* avoid isprint() - some people don't have it
		   ' ' is not always a printing char */
	    *bufp = c;
	    bufp[1] = 0;

	    Text( w->RPort, bufp, 1 );
	    Text( w->RPort, "\177", 1 );
	    if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
	    {
		colx++;
		bufp++;
	    }
	}
	else if(c == ('X'-64) || c == '\177')
	{
	    amii_curs( WIN_MESSAGE, ocolx, 0 );
	    Text( w->RPort,
		"                                                            ",
		colx - ocolx );
	    amii_curs( WIN_MESSAGE, colx = ocolx, 0 );
	} else
	    DisplayBeep( NULL );
    }
    *bufp = 0;
#endif
}

void amii_suspend_nhwindows( str )
    char *str;
{
    if( HackScreen )
	ScreenToBack( HackScreen );
}

void amii_resume_nhwindows()
{
    if( HackScreen )
	ScreenToFront( HackScreen );
}

void amii_bell()
{
    DisplayBeep( NULL );
}

#define GADBLUEPEN      2
#define GADREDPEN       3
#define GADGREENPEN     4
#define GADCOLOKAY      5
#define GADCOLCANCEL    6

#include "colorwin.c"

void
EditColor()
{
    int i, done = 0, okay = 0;
    long code, qual, class;
    register struct Gadget *gd, *dgad;
    register struct Window *nw;
    register struct IntuiMessage *imsg;
    register struct PropInfo *pip;
    register struct Screen *scrn;
    long aidx;
    int msx, msy;
    int curcol = 0, drag = 0;
    int bxorx, bxory, bxxlen, bxylen;
    UWORD colors[ 1L << DEPTH ];
    static int once = 0;

    bxylen = Col_NewWindowStructure1.Height -
			    ( Col_BluePen.TopEdge + Col_BluePen.Height + 6 );
    bxxlen = Col_BluePen.Width;
    bxorx = Col_BluePen.LeftEdge;
    bxory = Col_BluePen.TopEdge + Col_BluePen.Height + 2;
    scrn = HackScreen;

    if( !once )
    {
	SetBorder( &Col_Okay );
	SetBorder( &Col_Cancel );
	once = 1;
    }

    for( i = 0; i < (1L << DEPTH); ++i )
    {
	colors[ i ] = GetRGB4( scrn->ViewPort.ColorMap, i );
    }

    Col_NewWindowStructure1.Screen = scrn;
#ifdef  INTUI_NEW_LOOK
    if( IntuitionBase->LibNode.lib_Version >= 37 )
    {
	((struct PropInfo *)Col_BluePen.SpecialInfo)->Flags |= PROPNEWLOOK;
	((struct PropInfo *)Col_RedPen.SpecialInfo)->Flags |=  PROPNEWLOOK;
	((struct PropInfo *)Col_GreenPen.SpecialInfo)->Flags |= PROPNEWLOOK;
    }
#endif
    nw = OpenWindow( (void *)&Col_NewWindowStructure1 );

    DrawCol( nw, curcol, colors );
    while( !done )
    {
	WaitPort( nw->UserPort );

	while( imsg = (struct IntuiMessage * )GetMsg( nw->UserPort ) )
	{
	    gd = (struct Gadget *)imsg->IAddress;
	    code = imsg->Code;
	    class = imsg->Class;
	    qual = imsg->Qualifier;
	    msx = imsg->MouseX;
	    msy = imsg->MouseY;

	    ReplyMsg( (struct Message *)imsg );

	    switch( class )
	    {
		case VANILLAKEY:
		    if( code == 'v' && qual == AMIGALEFT )
			okay = done = 1;
		    else if( code == 'b' && qual == AMIGALEFT )
			okay = 0, done = 1;
		    else if( code == 'o' || code == 'O' )
			okay = done = 1;
		    else if( code == 'c' || code == 'C' )
			okay = 0, done = 1;
		    break;

		case CLOSEWINDOW:
		    done = 1;
		    break;

		case GADGETUP:
		    drag = 0;
		    if( gd->GadgetID == GADREDPEN ||
					    gd->GadgetID == GADBLUEPEN ||
						gd->GadgetID == GADGREENPEN )
		    {
			pip = (struct PropInfo *)gd->SpecialInfo;
			aidx = pip->HorizPot / (MAXPOT/15);
			aidx = aidx;
			if( gd->GadgetID == GADREDPEN )
			{
			    colors[ curcol ] =
				( colors[ curcol ] & ~0xf00 ) | (aidx << 8);
			    LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
			}
			else if( gd->GadgetID == GADBLUEPEN )
			{
			    colors[ curcol ] =
					( colors[ curcol ] & ~0xf ) | aidx;
			    LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
			}
			else if( gd->GadgetID == GADGREENPEN )
			{
			    colors[ curcol ] =
				( colors[ curcol ] & ~0x0f0 ) | (aidx << 4);
			    LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
			}
			DispCol( nw, curcol, colors );
		    }
		    else if( gd->GadgetID == GADCOLOKAY )
		    {
			done = 1;
			okay = 1;
		    }
		    else if( gd->GadgetID == GADCOLCANCEL )
		    {
			done = 1;
			okay = 0;
		    }
		    break;

		case GADGETDOWN:
		    drag = 1;
		    dgad = gd;
		    break;

		case MOUSEMOVE:
		    if( !drag )
			break;
		    pip = (struct PropInfo *)dgad->SpecialInfo;
		    aidx = pip->HorizPot / (MAXPOT/15);
		    aidx = aidx;
		    if( dgad->GadgetID == GADREDPEN )
		    {
			colors[ curcol ] =
				( colors[ curcol ] & ~0xf00 ) | (aidx << 8);
			LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
		    }
		    else if( dgad->GadgetID == GADBLUEPEN )
		    {
			colors[ curcol ] = ( colors[ curcol ] & ~0xf ) | aidx;
			LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
		    }
		    else if( dgad->GadgetID == GADGREENPEN )
		    {
			colors[ curcol ] =
				( colors[ curcol ] & ~0x0f0 ) | (aidx << 4);
			LoadRGB4( &scrn->ViewPort, colors, 1l << DEPTH );
		    }
		    DispCol( nw, curcol, colors );
		    break;

		case MOUSEBUTTONS:
		    if( code == SELECTDOWN )
		    {
			if( msy > bxory && msy < bxory + bxylen - 1 &&
				msx > bxorx && msx < bxorx + bxxlen - 1 )
			{
			    curcol = ( msx - bxorx )/(bxxlen / (1l << DEPTH));
			    DrawCol( nw, curcol, colors );
			}
		    }
		    break;
	    }
	}
    }

    if( okay )
    {
	for( i = 0; i < ( 1L << DEPTH ); ++i )
	    amii_curmap[ i ] = colors[ i ];
    }

    LoadRGB4( &scrn->ViewPort, amii_curmap, 1L << DEPTH );
    CloseWindow( nw );
}

/* The colornames, and the default values for the pens */
static struct
{
    char *name, *defval;
} colnames[] =
{
    "Black","(aaa)",
    "White","(fff)",
    "Brown","(620)",
    "Cyan","(b08)",
    "Green","(181)",
    "Magenta","(c06)",
    "Blue","(23e)",
    "Red","(d00)",
};

void
DrawCol( w, idx, colors )
    struct Window *w;
    int idx;
    UWORD *colors;
{
    int bxorx, bxory, bxxlen, bxylen;
    int i, incx, incy, r, g, b;
    long flags;

    bxylen = Col_NewWindowStructure1.Height - Col_Okay.Height - 4 -
		    ( Col_BluePen.TopEdge + Col_BluePen.Height + 6 );
    bxxlen = Col_BluePen.Width;
    bxorx = Col_BluePen.LeftEdge;
    bxory = Col_BluePen.TopEdge + Col_BluePen.Height + 2;

    incx = bxxlen / (1L << DEPTH);
    incy = bxylen - 2;

    SetAPen( w->RPort, 1 );
    SetBPen( w->RPort, 0 );
    SetDrMd( w->RPort, JAM2 );
    RectFill( w->RPort, bxorx, bxory, bxorx + bxxlen - 1, bxory + bxylen );
    SetAPen( w->RPort, 0 );
    RectFill( w->RPort, bxorx+2, bxory+1,
				    bxorx + bxxlen - 4, bxory + bxylen - 1);

    for( i = 0; i < (1L << DEPTH); ++i )
    {
	if( i == idx )
	    SetAPen( w->RPort, 1 );
	else
	    SetAPen( w->RPort, 0 );
	SetBPen( w->RPort, 0 );
	SetDrMd( w->RPort, JAM2 );
	RectFill( w->RPort, bxorx + 3 + (i*incx)+1, bxory + 2,
				    bxorx + ((i+1)*incx)-2, bxory+bxylen - 2);

	SetAPen( w->RPort, 0 );
	SetBPen( w->RPort, 0 );
	SetDrMd( w->RPort, JAM2 );
	RectFill( w->RPort, bxorx + 3 + (i*incx)+2, bxory + 3,
				    bxorx + ((i+1)*incx)-4, bxory+bxylen - 3);

	SetAPen( w->RPort, i );
	SetBPen( w->RPort, 0 );
	SetDrMd( w->RPort, JAM2 );
	RectFill( w->RPort, bxorx + 3 +(i*incx)+4, bxory + 4,
				    bxorx + ((i+1)*incx)-6, bxory+bxylen - 4 );
    }

    DispCol( w, idx, colors );

    r = (colors[ idx ] & 0xf00) >> 8;
    g = (colors[ idx ] & 0x0f0) >> 4;
    b = colors[ idx ] & 0x00f;

    flags = AUTOKNOB|FREEHORIZ;
#ifdef  INTUI_NEW_LOOK
    if( IntuitionBase->LibNode.lib_Version >= 37 )
    {
	flags |= PROPNEWLOOK;
    }
#endif
    NewModifyProp( &Col_RedPen, w, NULL, flags, (r * MAXPOT ) / 15, 0,
							    MAXPOT/15, 0, 1 );
    NewModifyProp( &Col_GreenPen, w, NULL, flags, (g * MAXPOT ) / 15, 0,
							    MAXPOT/15, 0, 1 );
    NewModifyProp( &Col_BluePen, w, NULL, flags, (b * MAXPOT ) / 15, 0,
							    MAXPOT/15, 0, 1 );
}

void
DispCol( w, idx, colors )
    struct Window *w;
    int idx;
    UWORD *colors;
{
    char buf[ 50 ];

    Move( w->RPort, Col_Okay.LeftEdge + Col_Okay.Width +
	txwidth, Col_Cancel.TopEdge + txbaseline + 2 );
    sprintf( buf, "%s=%03x %s%s", colnames[idx].name, colors[idx],
	colnames[idx].defval,
	"        "+strlen(colnames[idx].name)+1 );
    SetAPen( w->RPort, C_WHITE );
    SetBPen( w->RPort, 0 );
    SetDrMd( w->RPort, JAM2 );
    Text( w->RPort, buf, strlen( buf ) );
}

void
amii_setpens()
{
    /* If the pens are set in NetHack.cnf, we can get called before
     * HackScreen has been opened...
     */
    if( HackScreen != NULL )
    {
	LoadRGB4( &HackScreen->ViewPort, amii_curmap, 1L << DEPTH );
    }
}

/* Put a 3-D motif border around the gadget.  String gadgets or those
 * which do not have highlighting are rendered down.  Boolean gadgets
 * are rendered in the up position by default.
 */

void
SetBorder( gd )
    register struct Gadget *gd;
{
    register struct Border *bp;
    register short *sp;
    register int i, inc = -1, dec = -1;
    int borders = 6;

    /* Allocate two border structures one for up image and one for down
     * image, plus vector arrays for the border lines.
     */

    if( gd->GadgetType == STRGADGET )
	borders = 12;

    if( ( bp = (struct Border *)alloc( ( ( sizeof( struct Border ) * 2 ) +
			( sizeof( short ) * borders ) ) * 2 ) ) == NULL )
    {
	return;
    }

    /* For a string gadget, we expand the border beyond the area where
     * the text will be entered.
     */

    /* Remove any special rendering flags to avoid confusing intuition
     */

    gd->Flags &= ~(GADGHIGHBITS|GADGIMAGE);

    sp = (short *)(bp + 4);
    if( gd->GadgetType == STRGADGET || ( gd->GadgetType == BOOLGADGET &&
			    ( gd->Flags & GADGHIGHBITS ) == GADGHNONE ) )
    {
	sp[0] = -1;
	sp[1] = gd->Height - 1;
	sp[2] = -1;
	sp[3] = -1;
	sp[4] = gd->Width - 1;
	sp[5] = -1;

	sp[6] = gd->Width + 1;
	sp[7] = -2;
	sp[8] = gd->Width + 1;
	sp[9] = gd->Height + 1;
	sp[10] = -2;
	sp[11] = gd->Height + 1;

	sp[12] = -2;
	sp[13] = gd->Height;
	sp[14] = -2;
	sp[15] = -2;
	sp[16] = gd->Width;
	sp[17] = -2;
	sp[18] = gd->Width;
	sp[19] = gd->Height;
	sp[20] = -2;
	sp[21] = gd->Height;

	for( i = 0; i < 3; ++i )
	{
	    bp[ i ].LeftEdge = bp[ i ].TopEdge = -1;
	    bp[ i ].FrontPen = ( i == 0 || i == 1 ) ? C_BROWN : C_WHITE;

	    /* Have to use JAM2 so that the old colors disappear. */
	    bp[ i ].BackPen = C_BLACK;
	    bp[ i ].DrawMode = JAM2;
	    bp[ i ].Count = ( i == 0 || i == 1 ) ? 3 : 5;
	    bp[ i ].XY = &sp[ i*6 ];
	    bp[ i ].NextBorder = ( i == 2 ) ? NULL : &bp[ i + 1 ];
	}

	/* bp[0] and bp[1] two pieces for the up image */
	gd->GadgetRender = (APTR) bp;

	/* No image change for select */
	gd->SelectRender = (APTR) bp;

	gd->LeftEdge++;
	gd->TopEdge++;
	gd->Flags |= GADGHCOMP;
    }
    else
    {
	/* Create the border vector values for up and left side, and
	 * also the lower and right side.
	 */

	sp[0] = dec;
	sp[1] = gd->Height + inc;
	sp[2] = dec;
	sp[3] = dec;
	sp[4] = gd->Width + inc;
	sp[5] = dec;

	sp[6] = gd->Width + inc;
	sp[7] = dec;
	sp[8] = gd->Width + inc;
	sp[9] = gd->Height + inc;
	sp[10] = dec;
	sp[11] = gd->Height + inc;

	/* We are creating 4 sets of borders, the two sides of the
	 * rectangle share the border vectors with the opposite image,
	 * but specify different colors.
	 */

	for( i = 0; i < 4; ++i )
	{
	    bp[ i ].TopEdge = bp[ i ].LeftEdge = 0;

	    /* A GADGHNONE is always down */

	    if( gd->GadgetType == BOOLGADGET &&
			    ( gd->Flags & GADGHIGHBITS ) != GADGHNONE )
	    {
		bp[ i ].FrontPen =
			    ( i == 1 || i == 2 ) ? C_BROWN : C_WHITE;
	    }
	    else
	    {
		bp[ i ].FrontPen =
			    ( i == 1 || i == 3 ) ? C_WHITE : C_BROWN;
	    }

	    /* Have to use JAM2 so that the old colors disappear. */
	    bp[ i ].BackPen = C_BLACK;
	    bp[ i ].DrawMode = JAM2;
	    bp[ i ].Count = 3;
	    bp[ i ].XY = &sp[ 6 * ((i &1) != 0) ];
	    bp[ i ].NextBorder =
			    ( i == 1 || i == 3 ) ? NULL : &bp[ i + 1 ];
	}

	/* bp[0] and bp[1] two pieces for the up image */
	gd->GadgetRender = (APTR) bp;

	/* bp[2] and bp[3] two pieces for the down image */
	gd->SelectRender = (APTR) (bp + 2);
	gd->Flags |= GADGHIMAGE;
    }
}

#ifdef  PORT_HELP
void
port_help()
{
    display_file( PORT_HELP, 1 );
}
#endif

#endif /* AMIGA_INTUITION */
