<pre>
/*
File: popview.cpp
Uploaded by joecheng on Sun Dec 16 20:46:13 PST 2001
*/

// PopView.cpp : implementation of the CPopView class
//

#include "stdafx.h"
#include "ChildFrm.h" //For CChildFrame class to use _cSplitterWnd.  RR.
#include "game.h"
#include "listener.h"
#include "MainFrm.h" //For CMainFrame class to call SetMessageText.  RR.
#include "sprite.h" //For DF_ drawflags constants
#include "Pop.h"
#include "PopView.h"
#include "graphicsMFC.h"
#include "graphicsOpenGL.h"
#include "critterviewer.h"
//#include "gamestub3d.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//#define USEONDESTROY
	/* If have USEONDESTROY we delete our _pgraphics in the CView::OnDestroy overload,
	otherwise we delete _pgraphics in the CPopView destructor. It doesn't seem to mattter
	which way we do it, in either case we still get a non-crash warning message from the
	cGraphicsOpenGL destructor, not all the time, but pretty often. Since USEONDESTROY doesn't
	seem to fix the problem we don't use it. */ 
#define AVOIDGRAPHICSDELETE
	/* We use AVOIDGRAPHICSDELETE to avoid the annoying ~cGraphicsOpenGL warning messages when in 
	debug mode.	If we comment in AVOIDGRAPHICSDELETE, we use the the _pgraphicsMFCsingleton and
	 _pgraphicsMFCsingleton "singleton" pointers to avoid having to delete andreallocate over and over
	if we swap back and forth between MFC and OpenGL for a given view.  The cost is that
	after as swap, you end up having of sometimes having both kinds of cGraphics objects allocated 
	and they are expensive in that they each hold an offscreen pixel buffer. 
	If we comment out AVOIDGRAPHICSDELETE, we don't use the _pgraphicsMFCsingleton or 
	 _pgraphicsMFCsingleton	and instead just delete and new the appropriate cGraphics* _pgraphics 
	each time we swap graphics mode.  */
//#define TRACEONKEY //Used for debugging the OnKeyDown key handling
//#define AUTOSHOWOPENGLINFO
	/* Comment this in to automatically pop up an OpenGL system dialog the first
	time you enter OpenGL mode. */
//#define SHOWREFRESHRATEINSTATUSBAR
	/* Show the hardware refresh rate in the status bar. Normally you don't want this, as
	the user will mistakenly read this as the app refresh rate.  */
/////////////////////////////////////////////////////////////////////////////
// CPopView

 //============== CPopView Statics =============
 BOOL CPopView::STARTBITMAPBACKGROUNDFLAG = FALSE;
 BOOL CPopView::STARTSOLIDBACKGROUNDFLAG = FALSE;
 COLORREF CPopView::GAMEOVEREDGECOLOR = RGB(64, 64, 64 );
 COLORREF CPopView::GAMEACTIVEEDGECOLOR = RGB(224, 224, 224 );
//******** CONST STATICS **********************************************
/* These are drawflags used by my various draw(CDC*, CRealPixelConverter&, int)
 functions of cSpriteBubble, cCritter, and cSprite. */
 const int CPopView::DF_STANDARD =0;
 const int CPopView::DF_FOCUS = 1;
 const int CPopView::DF_THINEDGE = 4;
 const int CPopView::DF_WIREFRAME = 8;
 const int CPopView::DF_USEBACKGROUNDBITMAP = 16;
 const int CPopView::DF_USESOLIDBACKGROUND = 32;


IMPLEMENT_DYNCREATE(CPopView, CView)

BEGIN_MESSAGE_MAP(CPopView, CView)
	//{{AFX_MSG_MAP(CPopView)
	ON_WM_MOUSEMOVE()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_COMMAND(ID_VIEW_COLOREDBUBBLES, OnViewColoredbubbles)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLOREDBUBBLES, OnUpdateViewColoredbubbles)
	ON_COMMAND(ID_VIEW_XRAYBUBBLES, OnViewXraybubbles)
	ON_WM_LBUTTONDOWN()
	ON_COMMAND(ID_VIEW_DRAGGERCURSOR, OnViewDraggercursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_DRAGGERCURSOR, OnUpdateViewDraggercursor)
	ON_COMMAND(ID_VIEW_PINCURSOR, OnViewPincursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_PINCURSOR, OnUpdateViewPincursor)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONUP()
	ON_UPDATE_COMMAND_UI(ID_VIEW_XRAYBUBBLES, OnUpdateViewXraybubbles)
	ON_WM_KEYDOWN()
	ON_WM_MOUSEWHEEL()
	ON_WM_KEYUP()
	ON_WM_SETFOCUS()
	ON_COMMAND(ID_VIEW_ARROWCURSOR, OnViewArrowcursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ARROWCURSOR, OnUpdateViewArrowcursor)
	ON_COMMAND(ID_VIEW_SPAWNCURSOR, OnViewSpawncursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SPAWNCURSOR, OnUpdateViewSpawncursor)
	ON_COMMAND(ID_VIEW_ZAPCURSOR, OnViewZapcursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ZAPCURSOR, OnUpdateViewZapcursor)
	ON_COMMAND(ID_VIEW_REPLICATECURSOR, OnViewReplicatecursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_REPLICATECURSOR, OnUpdateViewReplicatecursor)
	ON_WM_ERASEBKGND()
	ON_COMMAND(ID_GAME_BACKGROUND, OnGameBackground)
	ON_UPDATE_COMMAND_UI(ID_GAME_BACKGROUND, OnUpdateGameBackground)
	ON_COMMAND(ID_VIEW_SHOOTCURSOR, OnViewShootcursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SHOOTCURSOR, OnUpdateViewShootcursor)
	ON_COMMAND(ID_VIEW_OPENGLGRAPHICS, OnViewOpenglgraphics)
	ON_UPDATE_COMMAND_UI(ID_VIEW_OPENGLGRAPHICS, OnUpdateViewOpenglgraphics)
	ON_COMMAND(ID_VIEW_MFCGRAPHICS, OnViewMfcgraphics)
	ON_UPDATE_COMMAND_UI(ID_VIEW_MFCGRAPHICS, OnUpdateViewMfcgraphics)
	ON_COMMAND(ID_VIEW_TRANSLATE, OnViewTranslate)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TRANSLATE, OnUpdateViewTranslate)
	ON_COMMAND(ID_VIEW_RIDE, OnViewRide)
	ON_UPDATE_COMMAND_UI(ID_VIEW_RIDE, OnUpdateViewRide)
	ON_COMMAND(ID_VIEW_FLY, OnViewFly)
	ON_UPDATE_COMMAND_UI(ID_VIEW_FLY, OnUpdateViewFly)
	ON_COMMAND(ID_VIEW_KEEPPLAYERINVIEW, OnViewKeepplayerinview)
	ON_UPDATE_COMMAND_UI(ID_VIEW_KEEPPLAYERINVIEW, OnUpdateViewKeepplayerinview)
	ON_COMMAND(ID_VIEW_ORBIT, OnViewOrbit)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ORBIT, OnUpdateViewOrbit)
	ON_COMMAND(ID_VIEW_SOLIDBACKGROUND, OnViewSolidbackground)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SOLIDBACKGROUND, OnUpdateViewSolidbackground)
	ON_COMMAND(ID_VIEW_NOBACKGROUND, OnViewNobackground)
	ON_UPDATE_COMMAND_UI(ID_VIEW_NOBACKGROUND, OnUpdateViewNobackground)
	ON_COMMAND(ID_VIEW_RESTORESTANDARDVIEWPOSITION, OnViewRestoreViewpoint)
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPopView construction/destruction

CPopView::CPopView():		//Put defaults in for your data fields
	_pgraphics(NULL), //We'll set this with the setGraphicsClass call in OnCreate
	_pgraphicsMFCsingleton(NULL), //We'll set this if needed in setGraphicsClass
	_pgraphicsOpenGLsingleton(NULL), //We'll set this if needed in setGraphicsClass
	_drawflags(CPopView::DF_STANDARD),
	_hCursor(NULL),
	_pviewpointcritter(NULL)
{
	setUseBackgroundBitmap(CPopView::STARTBITMAPBACKGROUNDFLAG); //Flip some bits in _drawflags.
	setUseSolidBackground(CPopView::STARTSOLIDBACKGROUNDFLAG);
	/* We put all the rest of the initialization in OnCreate. Here in the constructor we don't
		have a good pointer to a CPopDoc yet, and the pgame() method will only return a NULL.
		We might also need to use somethign from CPopApp,  but we can't do that here either,
		because in here, CPopApp hasn't yet executed CWinApp::InitInstance, and isn't fully
		initialized. In general it's safer to do any substantive intialization in OnCreate. */
}

BOOL CPopView::PreCreateWindow(CREATESTRUCT& cs)
{
//Do the base class call first, and bail if it fails.
	if (!CView::PreCreateWindow(cs)) 
		return FALSE;
    cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; /* This is a non-standard move
		that we must do in order for the window to be compatible with 
		OpenGL graphics. It doensn't seem to cause any trouble in standard
		MFC graphics, so we just do it all the time. */
	return TRUE;
}

int CPopView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
// Base class call comes first.  Have to to this before the call to GetDocument hidden in pgame().
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1; 
	_pviewpointcritter = new cCritterViewer(this); /* Looks at pgame()->border(), so wait
		till we're in OnCreate, and CPopDoc is fully initialized and we can access pgame().
		Do this before setGraphicsClass, as setGraphicsClass likes to set the 
		_pviewpointcritter's listener. */
	setGraphicsClass(RUNTIME_CLASS(cGraphics)); 
		/* We need to set up a _pgraphics  object right away, so it we can call it in the
		upcoming OnSize call. We will end up having to get rid of this cGraphics object
		quite soon when we install the default game, so we just put in a do-nothing base
		class cGraphics. */
	OnUpdate(NULL, CPopDoc::VIEWHINT_STARTGAME, NULL);  /* You need this OnUpdate,
		as this view wasn't here to pick up the OnUpdate issued by the 
		by the CPopDoc::setGameClass in the CPopDoc	constructor. */
	return 0;
}

CPopView::~CPopView()
{
#ifndef USEONDESTROY
#ifndef AVOIDGRAPHICSDELETE
	if (_pgraphics)
		delete _pgraphics;
#else // AVOIDGRAPHICSDELETE
	delete _pgraphicsMFCsingleton;
	delete _pgraphicsOpenGLsingleton;
#endif //AVOIDGRAPHICSDELETE
	delete _pviewpointcritter; //Get rid of this guy or he just might ask about graphics.
	_pgraphics = NULL;
	_pgraphicsMFCsingleton = NULL;
	_pgraphicsOpenGLsingleton = NULL;
	_pviewpointcritter = NULL;
#endif //USEONDESTROY
} 

void CPopView::Serialize(CArchive& ar)
{
	CRuntimeClass *pgraphicsclass = NULL; 	/* This is a dummy to hold the graphics class type.  Rather
		than serializing graphics, we only have to serialize the class.  Note that we don't have
		to delete pgraphicsclass, as MFCtakes care of CRuntimeClass memory. */
	int cursorid;
 	CObject::Serialize(ar);
 	if (ar.IsStoring()) // Save 
	{
		pgraphicsclass = _pgraphics->GetRuntimeClass();
			//Use the CArchive::ReadClass and WriteClass methods to serialize runtime classes.
		ar.WriteClass(pgraphicsclass);
		cursorid = ((CPopApp*)AfxGetApp())->cursorToID(_hCursor);
 		ar << _pviewpointcritter << _drawflags << cursorid;
	}
 	else //Load 
	{
	/* When you read in a pointer, you actually are creating a new object,
	so we need to delete the existing pointer fields in case they've been
	initialized.  They are NULL from the constructor, so they may be
	still NULL, but maybe I've hit OnCreate before here. */
		pgraphicsclass = ar.ReadClass();
		setGraphicsClass(pgraphicsclass);
		delete _pviewpointcritter;
		ar >> _pviewpointcritter  >> _drawflags >> cursorid;
		_pviewpointcritter->setOwnerView(this);
		_hCursor = ((CPopApp*)AfxGetApp())->IDToCursor(cursorid);
	}
}

void CPopView::setGraphicsClass(CRuntimeClass *pruntimeclass)
{
	/* set _pgraphics to the appropriate kind of pointer.  Even though its typed as a
plain cGraphics*, _pgraphics really remains whatever kind of child class is
described by the pruntimeclass variable, and will use the child class' overloads
of any virtual methods.  */  
#ifndef AVOIDGRAPHICSDELETE
	delete _pgraphics;  /* This particular call throws an "Invalid Address specified 
		to RtlFreeHeap" user breakpoint message when running in the debugger if we
		are deleteing a cGraphicsOpenGL.  See the comment in grahpicsopengl.cpp on
		the ~cGraphicsOpenGL destructor code. The message doesn't in fact matter,
		as it doesn't occur unless you're in the debugger, and there we press F5
		to continue. */ 
	_pgraphics = (cGraphics*)(pruntimeclass->CreateObject());
	_pgraphics->setOwnerView(this);
#else // AVOIDGRAPHICSDELETE
	if (_pgraphics && _pgraphics->GetRuntimeClass() == RUNTIME_CLASS(cGraphics))
	{
		delete _pgraphics; //This was just a dummy
		_pgraphics = NULL;
	}
	if (pruntimeclass == RUNTIME_CLASS(cGraphics))
	{
		_pgraphics = new cGraphics();
		_pgraphics->setOwnerView(this);
	}
	if (pruntimeclass == RUNTIME_CLASS(cGraphicsMFC))
	{
		if (_pgraphicsMFCsingleton == NULL)
		{
			_pgraphicsMFCsingleton = new cGraphicsMFC();
			_pgraphicsMFCsingleton->setOwnerView(this);
		}
		_pgraphics = _pgraphicsMFCsingleton;
	}
	if (pruntimeclass == RUNTIME_CLASS(cGraphicsOpenGL))
	{
		if (_pgraphicsOpenGLsingleton == NULL)
		{
			_pgraphicsOpenGLsingleton = new cGraphicsOpenGL();
			_pgraphicsOpenGLsingleton->setOwnerView(this); //Calls initializeOpenGL 

		}
		_pgraphics = _pgraphicsOpenGLsingleton;
	}
#endif //AVOIDGRAPHICSDELETE
	_pgraphics->activate();
	_pgraphics->setRealBox(pgame()->border());
	_pgraphics->setViewport(cx(), cy());
	ASSERT(_pviewpointcritter); //To be safe
	pgame()->initializeCritterViewer(_pviewpointcritter); /* This will set the
		preferred listener and viewpoint for this graphics mode. */
	if (pgraphicsclass()==(RUNTIME_CLASS(cGraphicsOpenGL)))
	{
		if (cGraphicsOpenGL::FIRSTTIME)
		{
#ifdef AUTOSHOWOPENGLINFO
			CPopApp *ppopapp = (CPopApp *)(::AfxGetApp());
			ppopapp->OnOpenglinfodialog(); //Show the info dialog.
#endif //AUTOSHOWOPENGLINFO
			cGraphicsOpenGL::FIRSTTIME = FALSE;
		}
	}
	_pgraphics->installLightingModel();
}

/////////////////////////////////////////////////////////////////////////////
// CPopView drawing

void CPopView::OnDraw(CDC* pDC)
{
	if (pDC->IsPrinting())
		return; //Don't try to deal with printing case.

//Wake up the graphics.
	_pgraphics->activate();

//Tell the cGraphics to get rid of any extra unused image resources.
	_pgraphics->garbageCollect();
//Graphically show the status of the game.
	if (pgame()->gameover()) //Dim the lights
		_pgraphics->setClearColor(CPopView::GAMEOVEREDGECOLOR);
	else //turn the lights on
		_pgraphics->setClearColor(CPopView::GAMEACTIVEEDGECOLOR);

//Clear the graphics background.
	CRect targetrect;
	pDC->GetClipBox(&targetrect);
	_pgraphics->clear(targetrect);

//Install the projection and view matrices.
	_pviewpointcritter->loadProjectionMatrix(); /* Initializes the PROJECTION matrix or,
		in the case of cGraphicsMFC, initializes the cRealPixelConvertor. */
	_pviewpointcritter->loadViewMatrix(); //Initializes the MODELVIEW matrix

//Draw the world, by default as a background and a foreground rectangle.
	pgame()->drawWorld(_pgraphics, _drawflags); 

//Draw the critters.
	pgame()->drawCritters(_pgraphics, _drawflags);

//Send the graphics to your video display.  cGraphicsMFC needs to draw foreground again in here.
	_pgraphics->display(this, pDC);
}

/////////////////////////////////////////////////////////////////////////////
// CPopView diagnostics

#ifdef _DEBUG
void CPopView::AssertValid() const
{
	CView::AssertValid();
}

void CPopView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CPopDoc* CPopView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPopDoc)));
	return (CPopDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CPopView message handlers

BOOL CPopView::OnEraseBkgnd(CDC* pDC) 
{
/* We normally don't want to erase the background because our onDraw will cover it up with 
the _cMemDC copyTo.  If we did erase the background, we'd get flicker.
This is also true with OpenGL. */
	return TRUE; //Don't call baseclass method, CView::OnEraseBkgnd(pDC);
}

void CPopView::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy); //Machine code.
//	if ( 0 >= cx || 0 >= cy )
//		return;
	//TRACE("%d\n", (int)this);
	if (_pgraphics)
	{
		_pgraphics->activate(); //Wake up the graphics
		_pgraphics->setViewport(cx, cy);
	}
	pviewpointcritter()->setAspect((Real)cx/(Real)cy); // width/height ratio
}

void CPopView::OnLButtonDown(UINT nFlags, CPoint point) 
{
//My Code.  RR.
	SetCapture(); /* This is so that as long as the mouse button is down,  this
		window gets messages from the mouse even when the mouse is outside
		the window. */
	pgame()->onLButtonDown(this, nFlags, point);
//Don't need to figure out the gameworld pos, it's set as pgame()->pbiota()->_cursorpos in OnSetCursor.
}

void CPopView::OnMouseMove(UINT nFlags, CPoint point) 
{
	/* Normally I track the cursor position in OnSetCursor, but this method doesn't get called during
		dragging, so I need to do it here */
	if ((nFlags & MK_LBUTTON) || (nFlags & MK_RBUTTON) ) //You're dragging.
	{
#define PASSTOSETCURSOR //this is better than trying to replicate some OnSetCursor code here.
#ifdef PASSTOSETCURSOR
		OnSetCursor(this, HTCLIENT, WM_LBUTTONDOWN);
			//OnSetCursor args are the window of the mouse, the hit-test code, the mouse message.
#else
/* If we are riding the critter, we want to pick a point on the yon wall, that is, the viewer's
far clip plane.  Given that we're on the critter, that distance from us will the
viwpointcritter's toFarZ(), inlined as {return fabs(_zfar - _position.z());}*/
		if (_pviewpointcritter->plistener()->IsKindOf(RUNTIME_CLASS(cListenerViewerRide)))
			pgame()->setCursorPos(pixelToPlayerYonWallVector(point.x, point.y, 
				_pviewpointcritter->toFarZ() ));
		else
			pgame()->setCursorPos(pixelToPlayerPlaneVector(point.x, point.y));
#endif //PASSTOSETCURSOR
	}
	pgame()->onMouseMove(this, nFlags, point);
}


void CPopView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	ReleaseCapture(); /* Now that the mouse button is up,  this
		window stops getting mouse messages from outside the window. */
//Don't need to set the gameworld location as pgame()->pbiota()->_cursorpos was set by OnSetCursor.
	pgame()->onLButtonUp(this, nFlags, point);
}

void CPopView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	/* We will use the Windows-defined VK_??? symbols to stand for the various nChar values.  Most of 
	these are defined in the Visual C++ include file winuser.h.  A few that should be defined are not,
	so we fix this, see	the controller.h header file for details.  */

	UINT repeated = (nFlags & (1<<14))?cController::REPEATEDBIT:0; //Is repeated?
		/*Although the doucmentatoin says nRepCnt gives you the number of repeated
		messages from a keypress, this seems not to be true with modern keyboards.
		So far as I can tell, nRepCnt is always 1, and can't be used to 
		distinguish any cases.
		The doc says Bit number 14 of nFlags tells me if a prior OnKeyDown message	has already
		been sent from this particular keypress.  If this bit is on, the key is
		being held down. You access bit 14 via (fFlags & (1<<14)).  But this isn't reliable either,
		and we end up sending a number OnKeyDown without the key being repeated.
		 */
	UINT control = (0x8000 & ::GetAsyncKeyState(VK_CONTROL))?cController::CONTROLBIT:0; 
		/* Is control key down?  The GetAsyncKeyState method returns a short unsigned int that has
		a one in its high bit if the key in question is down.  On the Microsoft Natural Keyboard, 
		the control key blocks the INSERT key, so we can't count on using the Ctrl+INSERT combination
		for anything.  In the same vein, note that when the Alt key is down, you dont' get any 
		OnKeyDown messages at all.  This contradicts the Microsoft doc that says the nFlags have
		a bit (#13) to tell you if the Alt key is down. */
	UINT shift = (0x8000 & ::GetAsyncKeyState(VK_SHIFT))?cController::SHIFTBIT:0; //Is shift key down?
	nFlags = repeated | control | shift;
#ifdef TRACEONKEY
TRACE("CPopView::OnKeyDown: control %u, nchar %u, repeated %u, shift %u, keyage %f\n",
		control, nChar, repeated, shift, pgame()->pcontroller()->keystateage(nChar));
#endif //TRACEONKEY
	pgame()->onKeyDown(this, nChar, nFlags);
}

void CPopView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	pgame()->onKeyUp(this, nChar); 
}

void CPopView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{/* This method gets called from cPopDoc, once every dt timestep update. cPopDoc passes
		the dt information inside the pHint. */ 
//If you've just loaded a new game, use the game's initialization code on this view.
	if (lHint == CPopDoc::VIEWHINT_LOADINGARCHIVE)
	{
		if (pHint && pHint->IsKindOf(RUNTIME_CLASS(cArchiveHint)))
		{
			CArchive *parchive = ((cArchiveHint*)pHint)->parchive();
			Serialize(*parchive);
		}
		return;
	}
//If you've just loaded a new game, use the game's initialization code on this view.
	if (lHint == CPopDoc::VIEWHINT_STARTGAME)
	{
		pgame()->initializeView(this);
		pgame()->initializeCritterViewer(_pviewpointcritter);
		return;
	}
/* If we get this far, we're in the normal case, as called by CPopDocument::stepDoc, with an
	lHint of 0 and a pHint holding a dt. */
//Animate the viewer
	Real dt = 0.0;
	if (pHint && pHint->IsKindOf(RUNTIME_CLASS(cTimeHint)))
		dt = ((cTimeHint*)pHint)->dt();
	if (isActiveView())
	{
		_pviewpointcritter->feellistener(dt); /* Listen only if this view has focus,
			otherwise if you have multiple views, you'll inadvertantly move the
			viewpoint in all of them at once. */
		updateStatusBar(); /* Before calling updateStatusBar, see if you are the focus view.
			Show changed score and object counts.  We only do it for the focus view,
			because otherwise if we have a splitter window or several documents
			and views then the Status line gets written for each view, and it flickers
			with different information --- remember there's only one Status bar, no
			matter how many views you have open. */
	}
	_pviewpointcritter->move(dt); 
	_pviewpointcritter->update(dt, this); //possibly feel forces
	_pviewpointcritter->animate(dt);
//Invalidate the window to force a call to OnDraw
	Invalidate(); /* We don't worry about calling Invalidate(FALSE) so as not to
		erase the background, becasue our OnEraseBkgnd is set do do nothing anyway. */
}

BOOL CPopView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
 {
 	/* This method get called whenever the cursor is over the client area of
 			the view.  Don't call the baseclass handler. 
 			CView::OnSetCursor(pWnd, nHitTest, message), //DON'T CALL THIS
 			In particular, don't call base CView::OnSetCursor last becuase then you'll
 			get the default IDC_ARROW cursor back!*/
	cGame* pgamedoc = pgame();
 //(1) Set the correct cursor for this view.
 	::SetCursor(_hCursor); 

//(2) Turn off the cursor if you are attached to the player. */
 	if(pgamedoc->playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
 	{
 		cListenerCursor *plisten = 
 			(cListenerCursor*)(pgamedoc->pplayer()->plistener());
				//Do this cast so we can check the attached status, though currently its always TRUE.
 		if (plisten->attached())
 			::SetCursor(NULL);
 	}
 //(3) If autofocus is on make sure this view is active.
  	if (!isActiveView())
 		/* Autofocus is handy thing if you plan to change the cursor tool by using the
 			accerlator keys (1 and 2) or the Mouse wheel.  But it's not always practical
 			for general use, as if you move your mouse up to the menu bar, whatever 
 			window 	you cross last has the focus, so its impractical to do menu things 
 			to windows that are to the right or down low. It's such a handy feature for 
 			play, though, that we add a Window|Autofocus control to turn it on and off.
 			The _autofocus switch lives in mainframe. */
 	{
 		CMainFrame* mainframe = (CMainFrame*)::AfxGetMainWnd();
 		if (mainframe->_autofocus)
 		{
 				/* The first two lines set the focus to the child
 					frame around this window.  Now, since this child frame
 					holds a splitter, we still need to put the focus into
 					the correct pane of the splitter, and that's what
 					the second two lines are for. */
 			CChildFrame *childframe = (CChildFrame *)GetParentFrame();
 			mainframe->MDIActivate(childframe); 
 			CSplitterWnd* psplitter = (CSplitterWnd*)GetParent();
 			while (psplitter->GetActivePane()!=this)
 				psplitter->ActivateNext();
 		}
 	}
// (4)  
 	if (isActiveView())
 	{
 			//Get the cursor position in a client area coordinates.
 		CPoint point;
 		::GetCursorPos(&point); //This global function gets screen coordinates
 		ScreenToClient(&point); //A CView conversion method
/* If we are riding the critter, we want to pick a point on the yon wall, that is, the viewer's
far clip plane.  Given that we're on the critter, that distance from us will the
viwpointcritter's toFarZ(), inlined as {return fabs(_zfar - _position.z());}*/
		if (_pviewpointcritter->plistener()->IsKindOf(RUNTIME_CLASS(cListenerViewerRide)))
			pgame()->setCursorPos(pixelToPlayerYonWallVector(point.x, point.y, 
				0.5 * _pviewpointcritter->toFarZ()));
		else
/* Otherwise we pick a point in the plane of the player's body, that is, his tangent and normal plane. */
			pgame()->setCursorPos(pixelToPlayerPlaneVector(point.x, point.y));
 	}

 	return TRUE;
}

/* The purpose of the CPopView::OnFocus override is as follows.  When we open a
modal dialog or shift focus to another app, the timer keeps running and a large
interval of _dt will accumulate before we come back into the Pop program.  If
you don't do anything to correct this, you get a big jump in the positions.  
So we do a timer tick() so that we only count the interval of time starting when
the program becomes active again.  This has to be handled by CView rather than in
CMainFrame because the CMainFrame keeps focus while a modal dialog is open, and
does not get an OnSetFocus when the modal dialog closes. */

void CPopView::OnSetFocus(CWnd* pOldWnd) 
{
	CView::OnSetFocus(pOldWnd);
	
	((CPopApp*)::AfxGetApp())->_timer.tick();	//RR.
	
}

BOOL CPopView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
/* Swap the cursor functionality, depending which cursors the game uses.
We just use the sign of zdelta, which will be some positive or negative number,
depending on which way and how far you turned the wheel. */
	_hCursor = pgame()->nextHCURSOR(_hCursor, zDelta);
	return TRUE;
}

void CPopView::setCursorPosToCritter(cCritter *pcritter)
{
/* 	Call this if you are using a ListenerCursor, to move the cursor to match the
	current position of the player.  This code seems to work pretty well, but we tend to
	be down by a vertical amount the size of the menu and tool bar height. 
	It gets worse when I'm in a zoomed in view of a long thin world with cGraphicsMFC,
	it's totally screwy.  So just comment the code out for now. */
	int intcrittx, intcritty;
	Real zbuff;
	vectorToPixel(pcritter->position(), intcrittx, intcritty, zbuff);
	CPoint intcritterpos(intcrittx, intcritty); 
	ClientToScreen(&intcritterpos);
	::SetCursorPos(intcritterpos.x, intcritterpos.y);
}

//=============Methods involving _pgraphics ========================================

cGraphics* CPopView::pgraphics()
{
	ASSERT(_pgraphics);
	return _pgraphics;
}

CRuntimeClass* CPopView::pgraphicsclass()
{
	ASSERT(_pgraphics);
	return _pgraphics->GetRuntimeClass();
}

void CPopView::setRealBox(cRealBox border)
{
	_pgraphics->setRealBox(border); //Only affects cGraphicsMFC, don't need activate
}

void CPopView::vectorToPixel(cVector position, int &xpix, int &ypix, Real &zbuff)
{
	_pgraphics->activate(); //Wake up the graphics
	_pgraphics->vectorToPixel(position, xpix, ypix, zbuff);
}

cVector CPopView::pixelToVector( int xpix, int ypix, Real zbuff)
{
	_pgraphics->activate(); //Wake up the graphics
	return _pgraphics->pixelToVector(xpix, ypix, zbuff);
}

cVector CPopView::pixelToPlayerPlaneVector(int xpix, int ypix)
{
	return pixelToCritterPlaneVector(xpix, ypix, pgame()->pplayer());
}

cVector CPopView::pixelToCritterPlaneVector(int xpix, int ypix, cCritter *pcritter)
{
	_pgraphics->activate(); //Wake up the graphics
	return _pgraphics->pixelAndPlaneToVector(xpix, ypix, pcritter->plane());
}

cVector CPopView::pixelToPlayerYonWallVector(int xpix, int ypix, Real distancetoyon)
{
	return pixelToCritterYonWallVector(xpix, ypix, pgame()->pplayer(), distancetoyon);
}

cVector CPopView::pixelToCritterYonWallVector(int xpix, int ypix, cCritter *pcritter, Real distancetoyon)
{
	_pgraphics->activate(); //Wake up the graphics
	return _pgraphics->pixelAndPlaneToVector(xpix, ypix, cPlane(
		pcritter->position() + distancetoyon*pcritter->attitudeTangent(), -pcritter->attitudeTangent()));
		//The two args to cPlane constructor are (origin, binormal), origin meaning a point on the plane.
}

COLORREF CPopView::sniff(cVector sniffpoint)
{
	_pgraphics->activate(); //Wake up the graphics
	return _pgraphics->sniff(sniffpoint);
}

/*
void CPopView::applyBackgroundBitmap(CDC *pDC, cRealBox2 bitmapborder)
{
	_pgraphics->applyBackgroundBitmap(pDC, bitmapborder);
}
*/

//=================Special CPopView Methods======================

void CPopView::updateStatusBar()
{
	CMainFrame* cMainFrame = (CMainFrame*)::AfxGetMainWnd();
	CString statusmessage = pgame()->statusMessage();
#ifdef SHOWREFRESHRATEINSTATUSBAR
	CString cStrRefreshrate;
	int refreshrate = ::GetDeviceCaps(cMainFrame->GetDC()->GetSafeHdc(), VREFRESH);
	cStrRefreshrate.Format("  Graphics refresh per second: %d.", refreshrate);
	statusmessage += cStrRefreshrate;
#endif //SHOWREFRESHRATEINSTATUSBAR
	cMainFrame->SetMessageText(statusmessage); //Write to status bar
}

BOOL CPopView::isActiveView()
{
	CMDIChildWnd *childframe = (CMDIChildWnd*)GetParentFrame();
	CMDIFrameWnd *mainframe = (CMDIFrameWnd*)(childframe->GetParentFrame());
	return (mainframe->GetActiveFrame() == childframe && childframe->GetActiveView() == this);
/* This alternate method doesn't work, as at the first call, pChild comes out NULL.
	CMDIFrameWnd *pFrame =(CMDIFrameWnd*)AfxGetMainWnd();
	CMDIChildWnd *pChild = pFrame->MDIGetActive();
	CPopView *pView = (CPopView *) pChild->GetActiveView();
	return pView == this;
*/
}

//Accessors

cGame* CPopView::pgame()
{
	CPopDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	return pDoc->pgame();
}

int CPopView::cx()
{
	CRect drawrect;
	GetClientRect(&drawrect);
	return drawrect.Width();
}

int CPopView::cy()
{
	CRect drawrect;
	GetClientRect(&drawrect);
	return drawrect.Height();
}

//========================Mutators==============================

void CPopView::setUseBackgroundBitmap(BOOL onoff)
{
	if (onoff)
		_drawflags |= CPopView::DF_USEBACKGROUNDBITMAP;
	else
		_drawflags &= ~CPopView::DF_USEBACKGROUNDBITMAP;
}


void CPopView::setUseSolidBackground(BOOL onoff)
{
	if (onoff)
		_drawflags |= CPopView::DF_USESOLIDBACKGROUND;
	else
		_drawflags &= ~CPopView::DF_USESOLIDBACKGROUND;
}

//===================Menu Message Handlers=========================

void CPopView::OnViewColoredbubbles() 
{ //This makes all sprites "solid", not just those of the bubbles.
	_drawflags &= ~CPopView::DF_WIREFRAME;
	pgame()->pbiota()->setNewgeometryflag(TRUE);
	OnUpdate(NULL, 0, NULL); /* Do this rather than calling Invalidate directly,
		in case we change the way we do the OnUpdate */
}
void CPopView::OnUpdateViewColoredbubbles(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(_drawflags & CPopView::DF_WIREFRAME?0:1);	
}
void CPopView::OnViewXraybubbles() 
{//This makes all sprites "wireframe", not just those of the bubbles.
	_drawflags |= CPopView::DF_WIREFRAME;
	pgame()->pbiota()->setNewgeometryflag(TRUE);
	OnUpdate(NULL, 0, NULL);
}
void CPopView::OnUpdateViewXraybubbles(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(_drawflags & CPopView::DF_WIREFRAME?1:0);	
}
void CPopView::OnViewShootcursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorPlay;
}

void CPopView::OnUpdateViewShootcursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorPlay;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewArrowcursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorArrow;
}

void CPopView::OnUpdateViewArrowcursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorArrow;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewDraggercursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorDragger;
}

void CPopView::OnUpdateViewDraggercursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorDragger;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewPincursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorPin;
}

void CPopView::OnUpdateViewPincursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorPin;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewSpawncursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorSpawn;
}

void CPopView::OnUpdateViewSpawncursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorSpawn;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewZapcursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorZap;
}

void CPopView::OnUpdateViewZapcursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorZap;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnViewReplicatecursor() 
{
	_hCursor = ((CPopApp*)::AfxGetApp())->_hCursorReplicate;
}

void CPopView::OnUpdateViewReplicatecursor(CCmdUI* pCmdUI) 
{
	cGame* pgamedoc = pgame();
	HCURSOR cursortool = ((CPopApp*)::AfxGetApp())->_hCursorReplicate;
	pCmdUI->Enable((pgamedoc->validHCURSOR(cursortool) && !pgamedoc->pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))?1:0);
	pCmdUI->SetCheck(_hCursor == cursortool);	
}

void CPopView::OnGameBackground() 
{
	setUseBackgroundBitmap(useBackgroundBitmap()?FALSE:TRUE);
}

void CPopView::OnUpdateGameBackground(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable((pgame()->menuflags() & cGame::MENU_BACKGROUND)?TRUE:FALSE);	
	pCmdUI->SetCheck(useBackgroundBitmap()?1:0);
}

void CPopView::OnViewSolidbackground() 
{
	setUseBackgroundBitmap(FALSE);
	setUseSolidBackground(TRUE);
}

void CPopView::OnUpdateViewSolidbackground(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable((pgame()->menuflags() & cGame::MENU_BACKGROUND)?TRUE:FALSE);	
	pCmdUI->SetCheck(!useBackgroundBitmap() && useSolidBackground()?1:0);
}

void CPopView::OnViewNobackground() 
{
	setUseBackgroundBitmap(FALSE);
	setUseSolidBackground(FALSE);
}

void CPopView::OnUpdateViewNobackground(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(!useBackgroundBitmap() && !useSolidBackground()?1:0);
}

void CPopView::OnViewOpenglgraphics() 
{
	if(pgraphicsclass()==(RUNTIME_CLASS(cGraphicsOpenGL)))
		return;
	setGraphicsClass(RUNTIME_CLASS(cGraphicsOpenGL));
}

void CPopView::OnUpdateViewOpenglgraphics(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(pgraphicsclass()==(RUNTIME_CLASS(cGraphicsOpenGL))?1:0);
}

void CPopView::OnViewMfcgraphics() 
{
	if(pgraphicsclass()==(RUNTIME_CLASS(cGraphicsMFC)))
		return;
	setGraphicsClass(RUNTIME_CLASS(cGraphicsMFC));
}

void CPopView::OnUpdateViewMfcgraphics(CCmdUI* pCmdUI) 
{
//	pCmdUI->Enable((pgame()->IsKindOf(RUNTIME_CLASS(cGameStub3D)))?0:1);
	//Comment the Enable in if you don't want to let cGraphicsMFC try and show 3D.
	pCmdUI->SetCheck(pgraphicsclass()==(RUNTIME_CLASS(cGraphicsMFC))?1:0);		
}

void CPopView::OnViewTranslate() 
{
	_pviewpointcritter->setListener(new cListenerViewerOrtho());
}

void CPopView::OnUpdateViewTranslate(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(_pgraphics->is3D()?0:1); //Don't use Ortho View in 3D
	pCmdUI->SetCheck(_pviewpointcritter->plistener()->GetRuntimeClass()
		== (RUNTIME_CLASS(cListenerViewerOrtho))?1:0);
}

void CPopView::OnViewRide() 
{
	if (_pviewpointcritter->plistener()->GetRuntimeClass()
		!= (RUNTIME_CLASS(cListenerViewerRide)))
		_pviewpointcritter->setListener(new cListenerViewerRide());
	else
		_pviewpointcritter->setListener(new cListenerViewerFly());

}

void CPopView::OnUpdateViewRide(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(pgame()->visibleplayer() && _pgraphics->is3D()?1:0);
	pCmdUI->SetCheck(_pviewpointcritter->plistener()->GetRuntimeClass()
		== (RUNTIME_CLASS(cListenerViewerRide))?1:0);
}

void CPopView::OnViewFly() 
{
	_pviewpointcritter->setListener(new cListenerViewerFly());
}

void CPopView::OnUpdateViewFly(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(_pgraphics->is3D()?1:0);
	pCmdUI->SetCheck(_pviewpointcritter->plistener()->GetRuntimeClass()
		== (RUNTIME_CLASS(cListenerViewerFly))?1:0);
}

void CPopView::OnViewOrbit() 
{
	_pviewpointcritter->setListener(new cListenerViewerOrbit());
}

void CPopView::OnUpdateViewOrbit(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(_pgraphics->is3D()?1:0);
	pCmdUI->SetCheck(_pviewpointcritter->plistener()->GetRuntimeClass()
		== (RUNTIME_CLASS(cListenerViewerOrbit))?1:0);
}

void CPopView::OnViewKeepplayerinview() 
{
	_pviewpointcritter->setTrackplayer(_pviewpointcritter->trackplayer()^TRUE);
		//Toggle the value.
}

void CPopView::OnUpdateViewKeepplayerinview(CCmdUI* pCmdUI) 
{
	BOOL isok = pgame()->visibleplayer() && //Only track visible.
		!(_pviewpointcritter->plistener()->GetRuntimeClass()== 
			RUNTIME_CLASS(cListenerViewerRide)); //Don't track and ride.
	pCmdUI->Enable(isok?TRUE:FALSE);
	pCmdUI->SetCheck(_pviewpointcritter->trackplayer()?1:0);
}

void CPopView::OnViewRestoreViewpoint() 
{
	_pviewpointcritter->setViewpoint();	
}

void CPopView::OnDestroy() 
{
#ifdef USEONDESTROY
#ifndef AVOIDGRAPHICSDELETE
	if (_pgraphics)
		delete _pgraphics;
#else //AVOIDGRAPHICSDELETE
	delete _pgraphicsMFCsingleton;
	delete _pgraphicsOpenGLsingleton;
#endif //AVOIDGRAPHICSDELETE
	delete _pviewpointcritter; //Get rid of this guy or he just might ask about graphics.
	_pgraphics = NULL;
	_pgraphicsMFCsingleton = NULL;
	_pgraphicsOpenGLsingleton = NULL;
	_pviewpointcritter = NULL;
#endif //USEONDESTROY
	CWnd::OnDestroy();
}

</pre>

