// FrotzCEView.cpp : implementation of the CFrotzCEView class
//

#include "stdafx.h"
#include "FrotzCE.h"

#include "FrotzCEDoc.h"
#include "FrotzCEView.h"
#include "MainFrm.h"

extern "C"
{
#include "Frotz/Frotz.h"

extern char euro_substitute[];
}

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CFrotzCEView

IMPLEMENT_DYNCREATE(CFrotzCEView, CView)

BEGIN_MESSAGE_MAP(CFrotzCEView, CView)
	//{{AFX_MSG_MAP(CFrotzCEView)
	ON_WM_CHAR()
	ON_WM_SYSCHAR()
	ON_WM_KEYDOWN()
	ON_MESSAGE(WM_CREATE_CURSOR, OnCreateCursor)
	ON_MESSAGE(WM_UPDATE_CURSOR, OnUpdateCursor)
	ON_MESSAGE(WM_SHOW_CURSOR, OnShowCursor)
	ON_MESSAGE(WM_TAKE_FOCUS, OnTakeFocus)
	ON_COMMAND(ID_DOWN, OnDown)
	ON_COMMAND(ID_EAST, OnEast)
	ON_COMMAND(ID_INVENTORY, OnInventory)
	ON_COMMAND(ID_LOOK, OnLook)
	ON_COMMAND(ID_NORTH, OnNorth)
	ON_COMMAND(ID_SOUTH, OnSouth)
	ON_COMMAND(ID_TAKE, OnTake)
	ON_COMMAND(ID_UP, OnUp)
	ON_COMMAND(ID_WEST, OnWest)
	ON_WM_LBUTTONDOWN()
	ON_COMMAND(ID_RESTORE, OnRestore)
	ON_COMMAND(ID_SAVE, OnSave)
	ON_COMMAND(ID_UNDO, OnUndo)
	ON_COMMAND(ID_OPEN, OnOpen)
	ON_COMMAND(ID_GO, OnGo)
	ON_COMMAND(ID_DROP, OnDrop)
	ON_COMMAND(ID_QUIT, OnQuit)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFrotzCEView construction/destruction

CFrotzCEView::CFrotzCEView()
{
	m_pScreen = NULL;
	m_pAttributes = NULL;

	m_pcReadKeyBuffer = m_acKeyBuffer;
	m_pcWriteKeyBuffer = m_acKeyBuffer;

	m_bExpectingInput = FALSE;

	m_bRedraw = TRUE;

	m_bInstantRefresh = FALSE;

	m_nState = STATE_NONE;

	m_hCurrentFont = NULL;

	// Set up pallette
	m_acPallette[PALLETTE_BLACK] = RGB(0,0,0);
	m_acPallette[PALLETTE_DARKGREY] = RGB(0x7f,0x7f,0x7f);
	m_acPallette[PALLETTE_LIGHTGREY] = RGB(0xaf,0xaf,0xaf);
	m_acPallette[PALLETTE_WHITE] = RGB(0xff,0xff,0xff);
}

CFrotzCEView::~CFrotzCEView()
{
	// Free font object
	if (m_hCurrentFont) ::DeleteObject( m_hCurrentFont );

	// Free screen buffers
	if (m_pScreen) ::LocalFree( m_pScreen );
	if (m_pAttributes) ::LocalFree( m_pAttributes );
}

BOOL CFrotzCEView::PreCreateWindow(CREATESTRUCT& cs)
{
	return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CFrotzCEView drawing

void CFrotzCEView::OnDraw( CDC* pDC )
{
	// Is there a screen to draw?
	if (m_pScreen)
	{
		::EnterCriticalSection( &m_cScrnCriticalSection );

		BYTE nOldColour = m_nCurrentColour;
		int nOldStyle = m_nCurrentStyle;

		// Set default colour
		m_nCurrentColour = DEF_TEXT_COLOUR;

		// Set default style
		m_nCurrentStyle = NORMAL_STYLE;

		// Update the entire screen
		UpdateScreenArea( pDC, 0, 0, m_nHeight-1, m_nWidth-1 );

		// Restore previous style and colours
		if (nOldColour != m_nCurrentColour) SetColour( pDC, nOldColour );
		if (nOldStyle != m_nCurrentStyle) 
		{
		HFONT hOldFont;

			hOldFont = SetStyle( pDC, nOldStyle );
			pDC->SelectObject( hOldFont );
		}

		::LeaveCriticalSection( &m_cScrnCriticalSection );
	}
	else CView::OnDraw( pDC );
}

void CFrotzCEView::UpdateScreenArea( CDC* pDC, int nTop, int nLeft, int nBottom, int nRight )
{
HFONT hOldFont;

	// Hide cursor
	SendMessage( WM_SHOW_CURSOR, FALSE );

	// Set colour
	SetColour( pDC, m_nCurrentColour );

	// Select font into device context
	hOldFont = SetStyle( pDC, m_nCurrentStyle );

	// Go through each row in the screen buffer
	for (int nRow = nTop, nYPos = nTop*m_nCharHeight; 
		nRow <= nBottom; 
		nRow++, nYPos += m_nCharHeight) 
	{
		for (int nCol = nLeft, nXPos = nLeft*m_nCharWidth, nChars = 0;;)
		{
			// Count no. of chars with current colour and style
			for (nChars = 0; (nCol + nChars) <= nRight
				&& COLOUR(m_pAttributes[(nRow*m_nWidth) + (nCol + nChars)]) == m_nCurrentColour
				&& STYLE(m_pAttributes[(nRow*m_nWidth) + (nCol + nChars)]) == m_nCurrentStyle;
				nChars++);

			// Any chars to output with this style?
			if (nChars)
			{
				// Output the char to the device context
				pDC->ExtTextOut( nCol*m_nCharWidth, nRow*m_nCharHeight, ETO_OPAQUE, 
					CRect( nCol*m_nCharWidth, nRow*m_nCharHeight,  
					(nCol+1)*m_nCharWidth, (nRow+1)*m_nCharHeight), 
					(LPCTSTR) &m_pScreen[(nRow*m_nWidth) + nCol], 
					nChars, NULL );

				nCol += nChars;
				nXPos += (nChars * m_nCharWidth);
			}

			// Not reached end of line?
			if (nCol <= nRight)
			{
				// Colour changed?
				if (COLOUR(m_pAttributes[(nRow*m_nWidth) + nCol]) != m_nCurrentColour)
				{
					// Set current colour
					SetColour( pDC, COLOUR(m_pAttributes[(nRow*m_nWidth) + nCol]) );
				}
				// Style changed?
				if (STYLE(m_pAttributes[(nRow*m_nWidth) + nCol]) != m_nCurrentStyle)
				{
					// Select previous font into device context
					pDC->SelectObject( hOldFont );
					// Set current style
					hOldFont = SetStyle( pDC, (BYTE) STYLE(m_pAttributes[(nRow*m_nWidth) + nCol]) );
				}
			}
			else break;
		}
	}

	// Select original font back into device context
	pDC->SelectObject( hOldFont );

	// Show cursor
	SendMessage( WM_SHOW_CURSOR, TRUE );
}

/////////////////////////////////////////////////////////////////////////////
// CFrotzCEView diagnostics

#ifdef _DEBUG
void CFrotzCEView::AssertValid() const
{
	CView::AssertValid();
}

void CFrotzCEView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CFrotzCEDoc* CFrotzCEView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CFrotzCEDoc)));
	return (CFrotzCEDoc*)m_pDocument;
}
#endif //_DEBUG

// Initialises the display
void CFrotzCEView::InitialiseScreen()
{
CRect cRect;
CDC *pDC;
HFONT hOldFont;

	// Copy screen info
	m_bUseStyles = FROTZCEAPP->m_bUseStyles;
	m_nDefaultStyle = FROTZCEAPP->m_nDefaultStyle;
	m_nDefaultFontHeight = FROTZCEAPP->m_nFontHeight;
	m_nDefaultFontWidth = FROTZCEAPP->m_nFontWidth;
	m_bUseEuroChars = FROTZCEAPP->m_bUseEuroChars;
	m_bUseColours = FROTZCEAPP->m_bUseColours;

	// Create keypress event object
	m_hKeyEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL );

	// Initialise critical sections
	memset( &m_cKbdCriticalSection, 0, sizeof( m_cKbdCriticalSection ) );
	::InitializeCriticalSection( &m_cKbdCriticalSection );
	memset( &m_cScrnCriticalSection, 0, sizeof( m_cScrnCriticalSection ) );
	::InitializeCriticalSection( &m_cScrnCriticalSection );

	// Set cursor state
	m_bCursorOn = FALSE;
	m_bCursorSet = TRUE;

	// Reset widths etc.
	m_nWidth = 0;
	m_nCharWidth = 0;
	m_nHeight = 0;
	m_nCharHeight = 0;

#if _WIN32_WCE < 200
	// Get frame window rectangle
	MAINFRAME->GetClientRect( cRect );

	// Subtract size of command bar if it's showing
	if (MAINFRAME->m_bShowCommandBar) 
		cRect.top += MAINFRAME->m_nCmdBarHeight;

	// Resize the view to cover the entire frame window client area
	MoveWindow( cRect, FALSE );
#endif

	// Get window rectangle
	GetClientRect( cRect );

	// Get device context
	pDC = GetDC();

	// Set colour
	SetColour( pDC, DEF_TEXT_COLOUR );

	// Using text styles?
	if (m_bUseStyles)
	{
		// Select font into device context
		hOldFont = SetStyle( pDC, NORMAL_STYLE | BOLDFACE_STYLE | EMPHASIS_STYLE, TRUE );
	}
	else
	{
		// Select font into device context
		hOldFont = SetStyle( pDC, NORMAL_STYLE, TRUE );
	}

#if _WIN32_WCE < 200
	TEXTMETRIC sTextMetrics;

	// Get the font metrics
	pDC->GetTextMetrics( &sTextMetrics );

	// Save character dimensions
	m_nCharWidth = (USHORT) sTextMetrics.tmMaxCharWidth + sTextMetrics.tmOverhang;
	m_nCharHeight = (USHORT) sTextMetrics.tmHeight;
#else
	// Get the width of a string
	CSize sTextExtent = pDC->GetTextExtent( CString( TEXT( "WWWWWWWWWW" ) ) );

	// Save character dimensions
	m_nCharWidth = (USHORT) sTextExtent.cx / 10;
	m_nCharHeight = (USHORT) sTextExtent.cy;
#endif

	// Set colour
	SetColour( pDC, DEF_TEXT_COLOUR );

	// Using text styles?
	if (m_bUseStyles)
	{
		// Select previous font into device context
		pDC->SelectObject( hOldFont );
		// Select font into device context
		hOldFont = SetStyle( pDC, m_nDefaultStyle );
	}

	// Select previous font into device context
	pDC->SelectObject( hOldFont );

	// Release device context
	ReleaseDC( pDC );

	// Save screen dimensions
	m_nWidth = cRect.Width() / m_nCharWidth;
	m_nHeight = cRect.Height() / m_nCharHeight;

	// Allocate screen buffers
	m_pScreen = 
		(TCHAR *) ::LocalAlloc( LPTR, m_nWidth * m_nHeight * sizeof( TCHAR ) );
	m_pAttributes = 
		(BYTE *) ::LocalAlloc( LPTR, m_nWidth * m_nHeight );

	// Clear the screen
	ClearScreen( FALSE );

	// Send a message to the view to create the caret
	SendMessage( WM_CREATE_CURSOR );

	// Create timer to do update
	m_hUpdateTimer = SetTimer( ID_UPDATE_TIMER, FROTZCE_UPDATE_MS, NULL );
}

void CFrotzCEView::ResetScreen()
{
CRect cRect;

	// Free event object
	::CloseHandle( m_hKeyEvent );

	// Release critical sections
	::DeleteCriticalSection( &m_cKbdCriticalSection );
	::DeleteCriticalSection( &m_cScrnCriticalSection );

	// Free font object
	if (m_hCurrentFont) 
	{
		::DeleteObject( m_hCurrentFont );
		m_hCurrentFont = NULL;
	}

	// Free screen buffers
	if (m_pScreen) 
	{
		::LocalFree( m_pScreen );
		m_pScreen = NULL;
	}
	if (m_pAttributes)
	{
		::LocalFree( m_pAttributes );
		m_pAttributes = NULL;
	}

	// Get frame window rectangle
	MAINFRAME->GetClientRect( cRect );
	cRect.top += MAINFRAME->m_nCmdBarHeight;

	// Resize the view to cover the entire frame window client area
	MoveWindow( cRect, FALSE );

	// Free timer
	if (m_hUpdateTimer) KillTimer( m_hUpdateTimer );

	// Clear screen
	Invalidate( TRUE );
}

void CFrotzCEView::ClearScreen( BOOL bUpdate )
{
	// Erase the entire screen
	EraseScreenArea( 0, 0, m_nHeight-1, m_nWidth-1 );

	// Set the cursor position to upper left-hand corner
	SetCursorPos( 0, 0 );
}

void CFrotzCEView::EraseScreenArea( int nTop, int nLeft, int nBottom, int nRight )
{
	// Make sure extents are in range
	if (nBottom > m_nHeight-1) nBottom = m_nHeight-1;
	if (nRight > m_nWidth-1) nRight = m_nWidth - 1;

	::EnterCriticalSection( &m_cScrnCriticalSection );

	// Fill screen buffer with spaces
	for (int nRow = nTop; nRow <= nBottom; nRow++)
	{
		for (int nCol = nLeft; nCol <= nRight; nCol++)
		{
			m_pScreen[(nRow * m_nWidth) + nCol] = _T(' ');
			m_pAttributes[(nRow * m_nWidth) + nCol] = 
				(m_nCurrentColour << NUM_STYLE_BITS) | m_nCurrentStyle;
		}
	}

	::LeaveCriticalSection( &m_cScrnCriticalSection );

	// Refresh instantly?
	if (m_bInstantRefresh)
	{
		// Get device context
		CDC *pDC = GetDC();

		::EnterCriticalSection( &m_cScrnCriticalSection );

		// Update scrolled area
		UpdateScreenArea( pDC, nTop, nLeft, nBottom, nRight );

		::LeaveCriticalSection( &m_cScrnCriticalSection );

		// Release device context
		ReleaseDC( pDC );
	}
	else
	{
		// Flag that screen must be redrawn
		m_bRedraw = TRUE;
	}
}

void CFrotzCEView::GetCursorPos( int *pnRow, int *pnCol )
{
	// Save cursor position
	*pnRow = m_nCursorRow;
	*pnCol = m_nCursorCol;
}

void CFrotzCEView::SetCursorPos( int nRow, int nCol, BOOL bUpdate )
{
	// Make sure row and column are in range
	if (nRow < 0) nRow = 0;
	else if (nRow > m_nHeight-1) nRow = m_nHeight - 1;
	if (nCol < 0) nCol = 0;
	else if (nCol > m_nWidth-1) nCol = m_nWidth - 1;

	// Save cursor position
	m_nCursorRow = nRow;
	m_nCursorCol = nCol;

	// Update caret position
	if (bUpdate) UpdateCaretPos();
}


void CFrotzCEView::UpdateCaretPos()
{
	// Send a message to the view to update the caret
	SendMessage( WM_UPDATE_CURSOR );
}

void CFrotzCEView::OnCreateCursor()
{
	// Create a cursor
	CreateSolidCaret( m_nCharWidth, m_nCharHeight );
}

void CFrotzCEView::OnUpdateCursor()
{
POINT sCaretPos;

	// Calculate new caret position
	sCaretPos.x = m_nCursorCol * m_nCharWidth;
	sCaretPos.y = m_nCursorRow * m_nCharHeight;

	SetCaretPos( sCaretPos );

	// Cursor state needs setting
	if (!m_bCursorSet)
	{
		m_bCursorSet = TRUE;
		
		// Show or hide cursor
		OnShowCursor( m_bCursorOn );
	}
}

void CFrotzCEView::OnShowCursor( UINT bShow )
{
	// Show or hide cursor
	if (bShow) ShowCaret();
	else HideCaret();
}

void CFrotzCEView::OnTakeFocus()
{
	SetFocus();
}

void CFrotzCEView::SetCursorState( BOOL bCursorOn )
{
	// Changing state?
	if (m_bCursorOn != bCursorOn)
	{
		// Set cursor state
		m_bCursorOn  = bCursorOn;
		// Flag that state has changed
		m_bCursorSet = FALSE;

		// Update caret
		UpdateCaretPos();
	}
}

void CFrotzCEView::SetColour( CDC* pDC, BYTE nColour )
{
	// Set current text colour
	m_nCurrentColour = nColour;

	// Reverse mode?
	if (m_nCurrentStyle & REVERSE_STYLE)
	{
		// Set text and background colours to reverse
		pDC->SetTextColor( 
			pDC->GetNearestColor( m_acPallette[m_nCurrentColour>>2] ) );
		pDC->SetBkColor( 
			pDC->GetNearestColor( m_acPallette[m_nCurrentColour&3] ) );
	}
	else
	{
		// Set text and background colours
		pDC->SetTextColor( 
			pDC->GetNearestColor( m_acPallette[m_nCurrentColour&3] ) );
		pDC->SetBkColor( 
			pDC->GetNearestColor( m_acPallette[m_nCurrentColour>>2] ) );
	}
}

HFONT CFrotzCEView::SetStyle( CDC* pDC, BYTE nStyle, BOOL bForce )
{
	// Not using text styles?
	if (!m_bUseStyles)
	{
		// Remove style flags
		nStyle &= ~BOLDFACE_STYLE;
		nStyle &= ~EMPHASIS_STYLE;
	}

	// Use default style
	nStyle |= m_nDefaultStyle;

	// Style changed?
	if (nStyle != m_nCurrentStyle || bForce)
	{
	LOGFONT sLogFont;
	HFONT hNewFont;

		// Reverse style changing?
		if ((m_nCurrentStyle & REVERSE_STYLE) != (nStyle & REVERSE_STYLE))
		{
			// Set current text style
			m_nCurrentStyle = nStyle;

			// Set current text colour
			SetColour( pDC, m_nCurrentColour );
		}
		// Set current text style
		else m_nCurrentStyle = nStyle;

		// Set up logical font structure
		memset ((char *) &sLogFont, 0, sizeof( sLogFont ));
		sLogFont.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;

#if _WIN32_WCE < 200
		sLogFont.lfCharSet = ANSI_CHARSET;

		// Set font quality
		sLogFont.lfQuality = DRAFT_QUALITY;

		// Small font?
		if (m_nDefaultFontWidth == SMALL_FONT_WIDTH)
		{
			// Set up font name
			#define COURIER_FONT TEXT("Courier New")
			memcpy( sLogFont.lfFaceName, COURIER_FONT, sizeof( COURIER_FONT ) );
			#undef COURIER_FONT

			// Set font width
			sLogFont.lfWidth = m_nDefaultFontWidth;
			// Set font height
			sLogFont.lfHeight = m_nDefaultFontHeight;
		}
		else
		{
			// Set font height
			if (m_nCharHeight) sLogFont.lfHeight = m_nCharHeight;
			else 
#endif
			sLogFont.lfHeight = m_nDefaultFontHeight;

#if _WIN32_WCE < 200
			// Set font width
			if (m_nCharWidth) sLogFont.lfWidth = m_nCharWidth;
			else sLogFont.lfWidth = m_nDefaultFontWidth;
		}
#endif

		// Bold?
		if (m_nCurrentStyle & BOLDFACE_STYLE) 
			sLogFont.lfWeight = FW_BOLD;

		// Italic?
		if (m_nCurrentStyle & EMPHASIS_STYLE)
		{
			sLogFont.lfWeight = FW_BOLD;
			sLogFont.lfItalic = TRUE;
		}

		// Create font
		if ((hNewFont = ::CreateFontIndirect( &sLogFont )) != NULL)
		{
			// Replace current font
			if (m_hCurrentFont) ::DeleteObject( (HGDIOBJ) m_hCurrentFont );
			m_hCurrentFont = hNewFont;

			// Select font into device context
			return (HFONT) pDC->SelectObject( hNewFont );
		}
		else return NULL;
	}
	else
	{
		// Select current font into device context
		return (HFONT) pDC->SelectObject( m_hCurrentFont );
	}
}

void CFrotzCEView::DisplayChar( char ch, BOOL bUpdateCaret )
{
	// Check for special characters
	switch( ch )
	{
	// Paragraph indent?
	case 11:
		DisplayChar( ' ' );
	// Tab?
	case 9:
		DisplayChar( ' ' );
		ch = ' ';
	// Everything else
	default:
		int nCurrPos;
		
		// Save cursor pos.
		nCurrPos = m_nCursorCol;

		// Update cursor position
		m_nCursorCol++;
		if (bUpdateCaret) UpdateCaretPos();

		// On screen?
		if (nCurrPos < m_nWidth && m_nCursorRow < m_nHeight)
		{
			::EnterCriticalSection( &m_cScrnCriticalSection );
	
			// Put the char into the screen buffer
			m_pScreen[(m_nCursorRow * m_nWidth) + nCurrPos] = (TCHAR) ch;

			m_pAttributes[(m_nCursorRow * m_nWidth) + nCurrPos] = 
				(BYTE) ((m_nCurrentColour << NUM_STYLE_BITS) | m_nCurrentStyle);

			// Refresh instantly?
			if (m_bInstantRefresh || m_bExpectingInput)
			{
			HFONT hOldFont;

				// Get device context
				CDC *pDC = GetDC();

				// Set colour
				SetColour( pDC, m_nCurrentColour );

				// Select font into device context
				hOldFont = SetStyle( pDC, m_nCurrentStyle );

				// Output the char to the device context
				pDC->ExtTextOut( nCurrPos*m_nCharWidth, m_nCursorRow*m_nCharHeight, ETO_OPAQUE, 
					CRect( nCurrPos*m_nCharWidth, m_nCursorRow*m_nCharHeight, (nCurrPos+1)*m_nCharWidth, (m_nCursorRow+1)*m_nCharHeight), 
					(LPCTSTR) &m_pScreen[(m_nCursorRow * m_nWidth) + nCurrPos], 
					1, NULL );

				// Select previous font into device context
				pDC->SelectObject( hOldFont );

				// Release device context
				ReleaseDC( pDC );
			}
			else
			{
				// Flag that screen must be redrawn
				m_bRedraw = TRUE;
			}

			::LeaveCriticalSection( &m_cScrnCriticalSection );
		}
	}
}

void CFrotzCEView::DisplayString( const char* acString )
{
const unsigned char *pcCurr = (unsigned char *) acString;
int nStartCol = m_nCursorCol;
int nCurrentStyle;
int nCurrentColour;


	::EnterCriticalSection( &m_cScrnCriticalSection );

	// Save current style and colours
	nCurrentStyle = m_nCurrentStyle;
	nCurrentColour = m_nCurrentColour;

	::LeaveCriticalSection( &m_cScrnCriticalSection );

	// Process string
	for (; *pcCurr; pcCurr++)
	{
		// New style?
		if (*pcCurr == NEW_FONT || *pcCurr == NEW_STYLE) 
		{
			// Style change?
			if (*pcCurr == NEW_STYLE)
			{
				pcCurr++;

				// Set current style
				nCurrentStyle = *pcCurr;

				// Not using text styles?
				if (!m_bUseStyles)
				{
					// Remove style flags
					nCurrentStyle &= ~BOLDFACE_STYLE;
					nCurrentStyle &= ~EMPHASIS_STYLE;
				}

				// Use default style
				nCurrentStyle |= m_nDefaultStyle;
			}
			else
			{
				pcCurr++;
			}
		}
		else 
		{
			// European char?
			if (m_bUseEuroChars && *pcCurr >= EURO_MIN && *pcCurr <= EURO_MAX)
			{
			int c1 = euro_substitute[2 * (*pcCurr - EURO_MIN)];
			int c2 = euro_substitute[2 * (*pcCurr - EURO_MIN) + 1];

				// On screen?
				if (m_nCursorCol < m_nWidth && m_nCursorRow < m_nHeight)
				{
					// Put the char into the screen buffer
					m_pScreen[(m_nCursorRow * m_nWidth) + m_nCursorCol] = (TCHAR) c1;
					m_pAttributes[(m_nCursorRow * m_nWidth) + m_nCursorCol] = 
						(BYTE) ((nCurrentColour << NUM_STYLE_BITS) | nCurrentStyle); 
					m_nCursorCol++;
				}

				// Second char to display?
				if (c2 != ' ') 
				{
					// On screen?
					if (m_nCursorCol < m_nWidth && m_nCursorRow < m_nHeight)
					{
						// Put the char into the screen buffer
						m_pScreen[(m_nCursorRow * m_nWidth) + m_nCursorCol] = (TCHAR) c2;
						m_pAttributes[(m_nCursorRow * m_nWidth) + m_nCursorCol] = 
							(BYTE) ((nCurrentColour << NUM_STYLE_BITS) | nCurrentStyle); 
						m_nCursorCol++;
					}
				}
			}
			// Display char
			else
			{
			char ch = (char) *pcCurr;

				// Check for special characters
				switch( *pcCurr )
				{
				// Paragraph indent?
				case 11:
					// On screen?
					if (m_nCursorCol < m_nWidth && m_nCursorRow < m_nHeight)
					{
						// Put the char into the screen buffer
						m_pScreen[(m_nCursorRow * m_nWidth) + m_nCursorCol] = _T(' ');
						m_pAttributes[(m_nCursorRow * m_nWidth) + m_nCursorCol] = 
							(BYTE) ((nCurrentColour << NUM_STYLE_BITS) | nCurrentStyle); 
						m_nCursorCol++;
					}
				// Tab?
				case 9:
					// On screen?
					if (m_nCursorCol < m_nWidth && m_nCursorRow < m_nHeight)
					{
						// Put the char into the screen buffer
						m_pScreen[(m_nCursorRow * m_nWidth) + m_nCursorCol] = _T(' ');
						m_pAttributes[(m_nCursorRow * m_nWidth) + m_nCursorCol] = 
							(BYTE) ((nCurrentColour << NUM_STYLE_BITS) | nCurrentStyle); 
						m_nCursorCol++;
					}
					ch = ' ';
				// Everything else
				default:
					// On screen?
					if (m_nCursorCol < m_nWidth && m_nCursorRow < m_nHeight)
					{
						// Put the char into the screen buffer
						m_pScreen[(m_nCursorRow * m_nWidth) + m_nCursorCol] = (TCHAR) ch;
						m_pAttributes[(m_nCursorRow * m_nWidth) + m_nCursorCol] = 
							(BYTE) ((nCurrentColour << NUM_STYLE_BITS) | nCurrentStyle); 
						m_nCursorCol++;
					}
					break;
				}
			}
		}
    }

	// Refresh instantly?
	if (m_bInstantRefresh)
	{
		// Get device context
		CDC *pDC = GetDC();

		::EnterCriticalSection( &m_cScrnCriticalSection );

		// Anything to display?
		if (m_nCursorCol > nStartCol)
		{
			// Update display
			UpdateScreenArea( pDC, m_nCursorRow, nStartCol, m_nCursorRow, m_nCursorCol-1 );
		}

		// Set style?
		if (nCurrentStyle != m_nCurrentStyle) 
		{
		HFONT hOldFont;

			hOldFont = SetStyle( pDC, nCurrentStyle );

			// Select previous font into device context
			pDC->SelectObject( hOldFont );
		}


		::LeaveCriticalSection( &m_cScrnCriticalSection );

		// Release device context
		ReleaseDC( pDC );
	}
	else
	{
		::EnterCriticalSection( &m_cScrnCriticalSection );

		// Set style
		if (nCurrentStyle != m_nCurrentStyle) 
			m_nCurrentStyle = nCurrentStyle;

		::LeaveCriticalSection( &m_cScrnCriticalSection );

		// Flag that screen must be redrawn
		m_bRedraw = TRUE;
	}
}

// Gets a key
int CFrotzCEView::GetKey( int nTimeOut )
{
DWORD dwTimeOut = INFINITE;
unsigned char ch = 0;

	// Update cursor position
	UpdateCaretPos();

	// Timeout specified?
	if (nTimeOut > 0) dwTimeOut = nTimeOut * 100;

	// Set flag to indicate we're expecting input
	m_bExpectingInput = TRUE;

	// Got a key?
	if (m_pcReadKeyBuffer != m_pcWriteKeyBuffer 
		|| ::WaitForSingleObject( m_hKeyEvent, dwTimeOut ) != WAIT_TIMEOUT)
	{
		// Was a key pressed?
		if (m_pcReadKeyBuffer != m_pcWriteKeyBuffer)
		{
			// Get key from buffer
			ch = *m_pcReadKeyBuffer;
			m_pcReadKeyBuffer++;

			// Gone past end of buffer?
			if (m_pcReadKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
			{
				// Go back to beginning of buffer
				m_pcReadKeyBuffer = &m_acKeyBuffer[0];
			}
		}
	}

	m_bExpectingInput = FALSE;

	return (int) ch;
}

#define MAX_LINE 80
#define MAX_HISTORY 5

// Gets a line
int CFrotzCEView::GetLine( char *acBuffer, int nMax, int nTimeOut )
{
DWORD dwTimeOut = INFINITE;
int ch = 0;
int nCurrChar = strlen( acBuffer );
static nNextHistoryPos = 0, nCurrHistoryPos = 0;
static char acHistory[MAX_HISTORY][MAX_LINE];

	// Timeout specified?
	if (nTimeOut > 0) dwTimeOut = nTimeOut * 100;

	// Update cursor position
	UpdateCaretPos();

	// Set flag to indicate we're expecting input
	m_bExpectingInput = TRUE;

	do
	{
		// Was a key pressed?
		if (m_pcReadKeyBuffer != m_pcWriteKeyBuffer)
		{
			// Get key from buffer
			ch = *m_pcReadKeyBuffer;
			m_pcReadKeyBuffer++;

			// Gone past end of buffer?
			if (m_pcReadKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
			{
				// Go back to beginning of buffer
				m_pcReadKeyBuffer = &m_acKeyBuffer[0];
			}
		}
		else
		{
			// Received a keypress notification?
			if (::WaitForSingleObject( m_hKeyEvent, dwTimeOut ) != WAIT_TIMEOUT)
			{
				continue;
			}
			else ch = 0;
		}

		// Add character to buffer
		if (ch && !is_terminator( (int) ch )) 
		{
			// Check key pressed
			switch (ch)
			{
			case BACKSPACE_KEY:
				// At beginning of line?
				if (nCurrChar > 0)
				{
					nCurrChar--;
					m_nCursorCol--;
					DisplayChar( ' ', TRUE );
					m_nCursorCol--;
					UpdateCaretPos();
				}
				break;

			case TAB_KEY:
				char acExt[16];
				int nExtSize;

				// Terminate buffer
				acBuffer[nCurrChar] = '\0';

				// Completion successful?
				completion( acBuffer, acExt );
				if ((nExtSize = strlen( acExt )) > 0)
				{
					// Copy chars into buffer and display them
					for (int i=0; i < nExtSize && nCurrChar <= nMax; i++)
					{
						acBuffer[nCurrChar++] = acExt[i];
						DisplayChar( acExt[i], TRUE );
					}
				}
				break;

			case ESC_KEY:
				// Go until beginning of line
				while (nCurrChar > 0)
				{
					nCurrChar--;
					m_nCursorCol--;
					DisplayChar( ' ', TRUE );
					m_nCursorCol--;
				}
				UpdateCaretPos();
				break;

			case UP_ARROW_KEY:
			case DOWN_ARROW_KEY:
				int nHistorySize;

				// Go until beginning of line
				while (nCurrChar > 0)
				{
					nCurrChar--;
					m_nCursorCol--;
					DisplayChar( ' ', TRUE );
					m_nCursorCol--;
				}
				UpdateCaretPos();

				// Up arrow?
				if (ch == UP_ARROW_KEY) 
				{
					nCurrHistoryPos--;
					if (nCurrHistoryPos < 0) 
						nCurrHistoryPos = MAX_HISTORY - 1;
				}
				else 
				{
					nCurrHistoryPos++;
					if (nCurrHistoryPos >= MAX_HISTORY) 
						nCurrHistoryPos = 0;
				}

				// Got anything?
				if ((nHistorySize = strlen( acHistory[nCurrHistoryPos] )) > 0)
				{
					// Copy chars into buffer and display them
					for (int i=0; i < nHistorySize && nCurrChar <= nMax; i++)
					{
						acBuffer[nCurrChar++] = acHistory[nCurrHistoryPos][i];
						DisplayChar( acHistory[nCurrHistoryPos][i], TRUE );
					}

					nNextHistoryPos = nCurrHistoryPos;
					UpdateCaretPos();
				}
				break;

			case SPECIAL_KEY_HOME:
			case SPECIAL_KEY_END:
			case SPECIAL_KEY_DELETE:
			case SPECIAL_KEY_INSERT:
			case SPECIAL_KEY_PAGE_UP:
			case SPECIAL_KEY_PAGE_DOWN:
			case LEFT_ARROW_KEY:
			case RIGHT_ARROW_KEY:
				break;

			// Any other key...
			default:
				acBuffer[nCurrChar++] = (unsigned char) ch;
				DisplayChar( ch, TRUE );
				UpdateCaretPos();
				break;
			}
		}
		else break;
	}
	while (nCurrChar <= nMax);

	// Null-terminate buffer
	acBuffer[nCurrChar] = '\0';

	// Anything entered?
	if (strlen( acBuffer ) > 0 && acBuffer[0] != '\n')
	{
		// Copy string to history buffer
		strcpy( acHistory[nNextHistoryPos], acBuffer );
		nNextHistoryPos++;
		nCurrHistoryPos = nNextHistoryPos;
		if (nNextHistoryPos >= MAX_HISTORY) nNextHistoryPos = 0;
	}

	m_bExpectingInput = FALSE;
	m_nState = STATE_NONE;

	return ch;
}

// Scrolls an area of the screen
void CFrotzCEView::ScrollScreenArea( int nTop, int nLeft, int nBottom, int nRight, int nUnits )
{
int nRow;

	// Scroll each line upwards
	for (nRow = nTop + nUnits; nRow <= nBottom; nRow++)
	{
		memcpy( &m_pScreen[((nRow - nUnits) * m_nWidth) + nLeft],
			&m_pScreen[(nRow * m_nWidth) + nLeft], 
			((nRight - nLeft) + 1) * sizeof( TCHAR ) );
		memcpy( &m_pAttributes[((nRow - nUnits) * m_nWidth) + nLeft],
			&m_pAttributes[(nRow * m_nWidth) + nLeft], 
			((nRight - nLeft) + 1) );
	}

	::EnterCriticalSection( &m_cScrnCriticalSection );

	// Fill rows below scrolled area with spaces
	for (nRow = nBottom; nRow > nBottom - nUnits; nRow--)
	{
		for (int nCol = nLeft; nCol <= nRight; nCol++)
		{
			m_pScreen[(nRow * m_nWidth) + nCol] = _T(' ');
			m_pAttributes[(nRow * m_nWidth) + nCol] = 
				(BYTE) ((m_nCurrentColour << NUM_STYLE_BITS) | m_nCurrentStyle);
		}
	}

	::LeaveCriticalSection( &m_cScrnCriticalSection );

	// Refresh instantly?
	if (m_bInstantRefresh)
	{
		// Get device context
		CDC *pDC = GetDC();

		::EnterCriticalSection( &m_cScrnCriticalSection );

		// Update scrolled area
		UpdateScreenArea( pDC, nTop, nLeft, nBottom, nRight );

		::LeaveCriticalSection( &m_cScrnCriticalSection );

		// Release device context
		ReleaseDC( pDC );
	}	
	else
	{
		// Flag that screen must be redrawn
		m_bRedraw = TRUE;
	}
}

// Sets the current text style
void CFrotzCEView::SetTextStyle( int nStyle )
{
	// Style change?
	if (nStyle != m_nCurrentStyle)
	{
	HFONT hOldFont;

		::EnterCriticalSection( &m_cScrnCriticalSection );

		CDC *pDC = GetDC();

		// Set current style
		hOldFont = SetStyle( pDC, nStyle );

		// Select previous font into device context
		pDC->SelectObject( hOldFont );

		// Release device context
		ReleaseDC( pDC );

		::LeaveCriticalSection( &m_cScrnCriticalSection );
	}
}

// Sets the current text colour
void CFrotzCEView::SetTextColour( int nFgCol, int nBkCol )
{
BYTE nColour = 0;

	// Use colours?
	if (m_bUseColours)
	{
		// Map foreground colour
		switch (nFgCol)
		{
			case WHITE_COLOUR:
				nColour = PALLETTE_WHITE;
				break;
			case GREY_COLOUR:
			case RED_COLOUR:
			case GREEN_COLOUR:
			case BLUE_COLOUR:
				nColour = PALLETTE_DARKGREY;
				break;
			case MEDIUMGREY_COLOUR:
			case YELLOW_COLOUR:
			case MAGENTA_COLOUR:
			case CYAN_COLOUR:
				nColour = PALLETTE_LIGHTGREY;
				break;
			case DARKGREY_COLOUR:
			case BLACK_COLOUR:
			case 0:
			default:
				nColour = PALLETTE_BLACK;
				break;
		}

		// Map background colour
		switch (nBkCol)
		{
			case WHITE_COLOUR:
			case 0:
				nColour |= (PALLETTE_WHITE<<2);
				break;
			case GREY_COLOUR:
			case RED_COLOUR:
			case GREEN_COLOUR:
			case BLUE_COLOUR:
				nColour |= (PALLETTE_DARKGREY<<2);
				break;
			case MEDIUMGREY_COLOUR:
			case YELLOW_COLOUR:
			case MAGENTA_COLOUR:
			case CYAN_COLOUR:
				nColour |= (PALLETTE_LIGHTGREY<<2);
				break;
			case DARKGREY_COLOUR:
			case BLACK_COLOUR:
			default:
				nColour |= (PALLETTE_BLACK<<2);
				break;
		}
	}
	else nColour = DEF_TEXT_COLOUR;

	::EnterCriticalSection( &m_cScrnCriticalSection );

	// Colour change?
	if (nColour != m_nCurrentColour)
	{
	CDC *pDC = GetDC();

		// Set current colour
		SetColour( pDC, nColour );

		// Release device context
		ReleaseDC( pDC );
	}

	::LeaveCriticalSection( &m_cScrnCriticalSection );
}

void CFrotzCEView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// Expecting input?
	if (m_bExpectingInput)
	{
		::EnterCriticalSection( &m_cKbdCriticalSection );

		// Add key to buffer
		*m_pcWriteKeyBuffer = nChar;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}

		::LeaveCriticalSection( &m_cKbdCriticalSection );

		// Signal that a keypress event has occurred
		::SetEvent( m_hKeyEvent );
	}
	else CView::OnChar( nChar, nRepCnt, nFlags);
}

void CFrotzCEView::OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Expecting input?
	if (m_bExpectingInput)
	{
		// Alt key pressed?
		if ((nFlags & (1 << 13)) != 0)
		{
		int ch;

			// Check for special keys
			switch (nChar)
			{
			case 'H':
			case 'h':
				ch = HOT_KEY_HELP;
				break;

			case 'D':			
			case 'd':
				ch = HOT_KEY_DEBUGGING;
				break;

			case 'N':			
			case 'n':
				ch = HOT_KEY_RESTART;
				break;

			case 'P':			
			case 'p':
				ch = HOT_KEY_PLAYBACK;
				break;

			case 'R':			
			case 'r':
				ch = HOT_KEY_RECORDING;
				break;

			case 'S':			
			case 's':
				ch = HOT_KEY_SEED;
				break;
			
			case 'U':			
			case 'u':
				ch = HOT_KEY_UNDO;
				break;

			case 'X':
			case 'x':
				ch = HOT_KEY_QUIT;
				break;

			default:
				ch = 0;
				break;
			}

			// Got anything?
			if (ch)
			{
				::EnterCriticalSection( &m_cKbdCriticalSection );

				// Add hotkey to buffer
				*m_pcWriteKeyBuffer = ch;
				m_pcWriteKeyBuffer++;

				// Gone past end of buffer?
				if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
				{
					// Go back to beginning of buffer
					m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
				}

				::LeaveCriticalSection( &m_cKbdCriticalSection );

				// Signal that a keypress event has occurred
				::SetEvent( m_hKeyEvent );
			}
			else CView::OnSysChar( nChar, nRepCnt, nFlags );
		}
	}
	else CView::OnSysChar( nChar, nRepCnt, nFlags );
}

void CFrotzCEView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// Expecting input?
	if (m_bExpectingInput)
	{
	int ch;

		// Check for recognised keys
		switch (nChar)
		{
		// Left arrow
		case 37:
			ch = LEFT_ARROW_KEY;
			break;
		// Right arrow
		case 39:
			ch = RIGHT_ARROW_KEY;
			break;
		// Up arrow
		case 38:
			ch = UP_ARROW_KEY;
			break;
		// Down arrow
		case 40:
			ch = DOWN_ARROW_KEY;
			break;
		// Page up
		case 33:
			ch = SPECIAL_KEY_PAGE_UP;
			break;
		// Page down
		case 34:
			ch = SPECIAL_KEY_PAGE_DOWN;
			break;
		// Home
		case 36:
			ch = SPECIAL_KEY_HOME;
			break;
		// End
		case 35:
			ch = SPECIAL_KEY_END;
			break;
		// Del
		case 46:
			ch = SPECIAL_KEY_DELETE;
			break;
		// Insert
		case 45:
			ch = SPECIAL_KEY_INSERT;
			break;
		default:
			ch = 0;
			break;
		}

		// Got anything?
		if (ch)
		{
			::EnterCriticalSection( &m_cKbdCriticalSection );

			// Add hotkey to buffer
			*m_pcWriteKeyBuffer = ch;
			m_pcWriteKeyBuffer++;

			// Gone past end of buffer?
			if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
			{
				// Go back to beginning of buffer
				m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
			}

			::LeaveCriticalSection( &m_cKbdCriticalSection );

			// Signal that a keypress event has occurred
			::SetEvent( m_hKeyEvent );
		}
		else CView::OnKeyDown(nChar, nRepCnt, nFlags);
	}
	else CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CFrotzCEView::StuffKeyboard( const char *pchChars, BOOL bReturn, BOOL bEsc )
{
	::EnterCriticalSection( &m_cKbdCriticalSection );

	// Clear input first?
	if (bEsc)
	{
		// Add ESC key to buffer
		*m_pcWriteKeyBuffer = ESC_KEY;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}
	}

	// Stuff chars into buffer
	while (*pchChars)
	{
		// Add char to buffer
		*m_pcWriteKeyBuffer = (int) *pchChars;
		pchChars++;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}
	}

	// Want a return?
	if (bReturn)
	{
		// Add return key to buffer
		*m_pcWriteKeyBuffer = ENTER_KEY;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}
	}

	::LeaveCriticalSection( &m_cKbdCriticalSection );

	// Signal that a keypress event has occurred
	::SetEvent( m_hKeyEvent );
}

void CFrotzCEView::OnRestore() 
{
	if (m_bExpectingInput) StuffKeyboard( "Restore", TRUE, TRUE );
}

void CFrotzCEView::OnSave() 
{
	if (m_bExpectingInput) StuffKeyboard( "Save", TRUE, TRUE );
}

void CFrotzCEView::OnUndo() 
{
	// Expecting input?
	if (m_bExpectingInput)
	{
		::EnterCriticalSection( &m_cKbdCriticalSection );

		// Add char to buffer
		*m_pcWriteKeyBuffer = HOT_KEY_UNDO;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}

		::LeaveCriticalSection( &m_cKbdCriticalSection );

		// Signal that a keypress event has occurred
		::SetEvent( m_hKeyEvent );
	}
}

void CFrotzCEView::OnDown() 
{
	if (m_bExpectingInput) StuffKeyboard( "Down", TRUE, TRUE );
}

void CFrotzCEView::OnEast() 
{
	if (m_bExpectingInput) 
	{
	BOOL bReturn = (m_nState == STATE_MOVING);

		m_nState = STATE_MOVING;

		StuffKeyboard( "East", bReturn );
	}
}

void CFrotzCEView::OnInventory() 
{
	if (m_bExpectingInput) StuffKeyboard( "Inventory", TRUE, TRUE );
}

void CFrotzCEView::OnLook() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_LOOKING;

		StuffKeyboard( "Look", FALSE, TRUE );
	}
}

void CFrotzCEView::OnOpen() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_OPENING;

		StuffKeyboard( "Open ", FALSE, TRUE );
	}
}

void CFrotzCEView::OnNorth() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_MOVING;

		StuffKeyboard( "North", FALSE, TRUE );
	}
}

void CFrotzCEView::OnSouth() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_MOVING;

		StuffKeyboard( "South", FALSE, TRUE );
	}
}

void CFrotzCEView::OnTake() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_TAKING;

		StuffKeyboard( "Take ", FALSE, TRUE );
	}
}

void CFrotzCEView::OnDrop() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_TAKING;

		StuffKeyboard( "Drop ", FALSE, TRUE );
	}
}

void CFrotzCEView::OnGo() 
{
	if (m_bExpectingInput) 
	{
		m_nState = STATE_GOING;

		StuffKeyboard( "Go to ", FALSE, TRUE );
	}
}

void CFrotzCEView::OnUp() 
{
	if (m_bExpectingInput) StuffKeyboard( "Up", TRUE, TRUE );
}

void CFrotzCEView::OnWest() 
{
	if (m_bExpectingInput) 
	{
	BOOL bReturn = (m_nState == STATE_MOVING);

		m_nState = STATE_MOVING;

		StuffKeyboard( "West", bReturn );
	}
}

void CFrotzCEView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// Expecting input?
	if (m_bExpectingInput) 
	{
	int nCharRow, nCharCol;
	char *pszBuffer = new char[m_nWidth+1];
	int i = 0;
	BOOL bReturn = TRUE;

		// Calculate char position
		nCharRow = point.y / m_nCharHeight;
		nCharCol = point.x / m_nCharWidth;

		// Go backwards until beginning of word
		while (nCharCol)
		{
			if (m_pScreen[(nCharRow * m_nWidth) + nCharCol-1] != _T(' ')) nCharCol--;
			else break;
		}

		// Valid row?
		if (nCharRow < m_nHeight)
		{
			// Copy word to buffer
			for (; nCharCol < m_nWidth; i++)
			{
				pszBuffer[i] = (char) m_pScreen[(nCharRow * m_nWidth) + nCharCol + i];

				if (!((pszBuffer[i] >= 'A' && pszBuffer[i] <= 'Z') || 
					(pszBuffer[i] >= 'a' && pszBuffer[i] <= 'z'))) break;
			}
		}

		pszBuffer[i] = '\0';

		// Check current state
		switch (m_nState)
		{
		case STATE_NONE:
			if (strlen( pszBuffer )) bReturn = FALSE;
			break;
		case STATE_LOOKING:
			if (strlen( pszBuffer )) StuffKeyboard( " at ", FALSE );
			break;
		case STATE_TAKING:
			if (!strlen( pszBuffer )) StuffKeyboard( "all", FALSE );
			break;
		case STATE_OPENING:
			if (!strlen( pszBuffer )) StuffKeyboard( "Door", FALSE );
			break;
		case STATE_MOVING:
			pszBuffer[0] = '\0';
			break;
		}

		// Copy buffer into keyboard buffer
		StuffKeyboard( pszBuffer, bReturn );

		// Add a space if we're not adding a return
		if (!bReturn) StuffKeyboard( " ", FALSE );

		delete [] pszBuffer;
	}

	CView::OnLButtonDown(nFlags, point);
}

void CFrotzCEView::OnQuit() 
{
	// Expecting input?
	if (m_bExpectingInput) 
	{
		::EnterCriticalSection( &m_cKbdCriticalSection );

		// Add hotkey to buffer
		*m_pcWriteKeyBuffer = HOT_KEY_QUIT;
		m_pcWriteKeyBuffer++;

		// Gone past end of buffer?
		if (m_pcWriteKeyBuffer > &m_acKeyBuffer[KEYBUFFER_SIZE-1])
		{
			// Go back to beginning of buffer
			m_pcWriteKeyBuffer = &m_acKeyBuffer[0];
		}

		::LeaveCriticalSection( &m_cKbdCriticalSection );

		// Signal that a keypress event has occurred
		::SetEvent( m_hKeyEvent );
	}
}

void CFrotzCEView::OnTimer(UINT nIDEvent) 
{
	// Need to do a refresh?
	if (m_bRedraw)
	{
		// Using text styles?
		if (m_bUseStyles)
		{
			// Invalidate and clear client area
 			Invalidate( TRUE );
		}
		else
		{
			// Invalidate client area
 			Invalidate( FALSE );
		}

		// Flag that refresh has been done
		m_bRedraw = FALSE;
	}
}
