GAME.CPP

//--------------------------------------------------------------------------- 
// Game.cpp
//---------------------------------------------------------------------------
// Sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Game.h"
#include "App.h"
#include "Spr.h"
#include "Score.h"
#include "SpruuidP.h"
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>


//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE


//---------------------------------------------------------------------------
// Forward Declares
//---------------------------------------------------------------------------
int DisplayScriptError(HINSTANCE hinst, HWND hwndParent, LPCSTR pszError, int ichError);


//---------------------------------------------------------------------------
// Image data
//---------------------------------------------------------------------------
#define ADDPIX( id,idNext,x,y,cx,cy) {iimg##idNext, x, y, cx, cy},
#define ADDPIXLAST(id,idNext,x,y,cx,cy) {iimg##idNext, x, y, cx, cy}
#define ADDPIXIMG
IMG Game_img[] = {
#include "SpruuidP.pix"
};
#undef ADDPIX
#undef ADDPIXLAST
#undef ADDPIXIMG


//---------------------------------------------------------------------------
// Various Globals
//---------------------------------------------------------------------------
CGame *g_pgame = NULL;
ITypeLib *g_ptlGameSubObj = NULL;
ITypeInfo *g_ptinfoClsGame = NULL;
ITypeInfo *g_ptinfoIntGame = NULL;
char *CGame::s_pszError = NULL;
ULONG g_clineOffset;


//---------------------------------------------------------------------------
// Cover for contstructor. Use instead of new CGame!
//---------------------------------------------------------------------------
HRESULT CGame::s_hr = E_FAIL;

HRESULT CGame::CreateGame
(
HINSTANCE hinst,
HWND hwndDlg,
HWND hwndPS,
HWND hwndStat,
IOleComponentManager *pcm,
CGame **ppgameOut
)
{
CGame *pgame;
HRESULT hr;

*ppgameOut = NULL;
pgame = new CGame(hinst, hwndDlg, hwndPS, hwndStat, pcm);
if (!pgame)
return E_OUTOFMEMORY;
else if (pgame->s_hr)
{
delete pgame;
return s_hr;
}

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
// Add the Game as a Named Item & load its code
g_clineOffset = 0;
hr = pgame->ParseFile(g_pszCodeFile, L"Game");
if (hr)
{
MessageBox(g_papp->m_hwndDlg, s_pszError ? s_pszError : "Unspecified Error", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
return hr;
}
// ##### END ACTIVEX SCRIPTING SUPPORT #####

// Create sprite classes based upon $OBJECT lines in file
for (int i=0; i<pgame->m_csubobj; i++)
{
CHECK(CSpriteClass::Create(pgame->m_pdisp, &pgame->m_rgpsc[i]));
pgame->m_rgpdispSubObjs[i] = pgame->m_rgpsc[i]->GetDispatch();
pgame->m_rgpdispSubObjs[i]->AddRef();
}

if (hr)
{
CleanUp:
delete pgame;
return hr;
}

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
// Start the script running...
hr = pgame->m_ps->SetScriptState(SCRIPTSTATE_CONNECTED);
if (hr)
{
delete pgame;
return hr;
}
// ##### END ACTIVEX SCRIPTING SUPPORT #####

// Success
*ppgameOut = pgame;
return S_OK;
}


//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------
CGame::CGame
(
HINSTANCE hinst,
HWND hwndDlg,
HWND hwndPS,
HWND hwndStat,
IOleComponentManager *pcm
)
{
INIT_SIGNATURE(SIG_Game);

// Setup globals
g_pgame = this;
s_hr = S_OK;

// Init members
m_hinst = hinst;
m_hwndDlg = hwndDlg;
m_hwndPS = hwndPS;
m_hwndStat = hwndStat;
m_cref = 1;
m_fPaused = FALSE;
m_fShipDead = FALSE;
m_fShipRestart = FALSE;
m_fGameOver = TRUE;
m_pcm = pcm;
m_ps = NULL;
m_psp = NULL;
VariantInit(&m_varTag);
m_pscore = NULL;
m_pdisp = NULL;
m_pdispBaseObject = NULL;
m_pgameoa = NULL;
m_csubobj = 0;

CoCreateGuid(&m_libidSubObj);
CoCreateGuid(&m_clsidSubObj);
CoCreateGuid(&m_iidSubObj);

m_pgameoa = new CGameOA(this);
if (!m_pgameoa)
{
s_hr = E_OUTOFMEMORY;
return;
}
this->SetBaseObject(m_pgameoa->GetDispatch());

m_pdisp = new CDisplay(hinst, hwndPS, GetDC(hwndPS),
this->GetUnknown(), CGameOA::FireCollide,
sizeof(Game_img)/sizeof(IMG), Game_img, ID_BMP_SPRITES);
if (!m_pdisp)
{
s_hr = E_OUTOFMEMORY;
return;
}

s_hr = CScore::CreateScore(
m_hinst, // hinst to load resources from
m_hwndDlg, // hwnd on which to draw score
m_hwndPS, // hwnd of PlaySurface
m_hwndStat, // hwnd of Status bar
2500, // scoreFirst1Up
5000, // scoreSecond1Up
2500, // dscoreNext1Up
3, // cshipStart;
ID_BMP_SHIP, // idbmpShip;
ID_BMP_PLUS, // idbmpPlus;
&m_pscore);
if (s_hr)
return;
ASSERT(m_pscore, "Good hr, but m_pscore==NULL");

// Default to VBScript
extern const CLSID CLSID_VBScript;
m_clsidEngine = CLSID_VBScript;

// Success
s_hr = S_OK;
}


//---------------------------------------------------------------------------
// Clean up allocated resources
//---------------------------------------------------------------------------
CGame::~CGame
(
void
)
{
CHECK_SIGNATURE(SIG_Game);
this->Close();
if (m_pgameoa)
delete m_pgameoa;
DESTROY_SIGNATURE(SIG_Game);
}


//---------------------------------------------------------------------------
// Clean up allocated resources
//---------------------------------------------------------------------------
void CGame::Close
(
void
)
{
CHECK_SIGNATURE(SIG_Game);

if (g_mode != MODE_PLAYBACK)
KillTimer(m_hwndPS, 0);

g_pgame = NULL;

// Clear this, since it may be an object
VariantClear(&m_varTag);

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
// Release the language engine, since it may hold on to us
if (m_psp)
m_psp->Release();
if (m_ps)
{
m_ps->Close();
m_ps->Release();
}
// ##### END ACTIVEX SCRIPTING SUPPORT #####

// Drop all sink's that have Advise()'d us, since they might keep us alive
m_pgameoa->m_cp.Close();

// Release all existing sprites
if (m_pdisp)
m_pdisp->DestroyAll();

// Close & Release the SpriteClass Sub Objects
for (int i=0; i<m_csubobj; i++)
{
delete [] m_rgpwszSubObjs[i];
if (m_rgpdispSubObjs[i])
{
m_rgpdispSubObjs[i]->Release();
m_rgpdispSubObjs[i] = NULL;
}
if (m_rgpsc[i])
{
m_rgpsc[i]->Close();
m_rgpsc[i]->Release();
m_rgpsc[i] = NULL;
}
}

// Now destroy the Score & Display objects
if (m_pscore)
{
delete m_pscore;
m_pscore = NULL;
}
if (m_pdisp)
{
delete m_pdisp;
m_pdisp = NULL;
}
}


//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::QueryInterface
(
REFIID iid,
LPVOID* ppvObjOut
)
{
if (!ppvObjOut)
return E_INVALIDARG;

*ppvObjOut = NULL;

if (iid == IID_IUnknown)
*ppvObjOut = this->GetUnknown();
else if (iid == IID_IDispatch)
*ppvObjOut = this->GetDispatch();
else if (iid == IID_IGameSubObjects)
*ppvObjOut = this->GetDispatch();
else if (iid == IID_IActiveScriptSite)
*ppvObjOut = (IActiveScriptSite *)this;
else if (iid == IID_IActiveScriptSiteWindow)
*ppvObjOut = (IActiveScriptSiteWindow *)this;
else if (iid == IID_IProvideClassInfo)
*ppvObjOut = (IProvideClassInfo *)this;
else if (iid == IID_IProvideMultipleClassInfo)
*ppvObjOut = (IProvideMultipleClassInfo *)this;
else if (iid == IID_IGame)
*ppvObjOut = (IGame *)(this->m_pgameoa);
else if (iid == IID_IConnectionPointContainer)
*ppvObjOut = (IConnectionPointContainer *)(this->m_pgameoa);

if (*ppvObjOut)
{
this->AddRef();
return S_OK;
}

return E_NOINTERFACE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGame::AddRef
(
void
)
{
return ++m_cref;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGame::Release
(
void
)
{
ASSERT(m_cref, "bad m_cref");
m_cref--;
if (!m_cref)
{
m_pgameoa->m_cp.Close(); // Make sure nobody's holding on to us because we're holding their sink
if (!m_cref && !m_pgameoa->m_cp.m_cref)
{
delete this;
return 0;
}
}
return m_cref;
}


//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Re-implement COleAuto's GetIDsOfNames(), so we can implement the dynamic
// properties we added for the $OBJECT=SpriteClassName lines.
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetIDsOfNames
(
REFIID iid,
OLECHAR** rgszNames,
UINT cNames,
LCID lcid,
DISPID* prgdispid
)
{
// Validate Args
if (iid != IID_NULL)
return E_INVALIDARG;

// See if a sub-object matches the name passed in.
for (int i=0; i<m_csubobj; i++)
if (!wcscmp(*rgszNames, m_rgpwszSubObjs[i]))
{
if (cNames != 1)
return DISP_E_NONAMEDARGS;
*prgdispid = 0x80010000 | i;
return S_OK;
}

return COleAuto::GetIDsOfNames(iid, rgszNames, cNames, lcid, prgdispid);
}


//---------------------------------------------------------------------------
// Re-implement COleAuto's Invoke(), so we can implement the dynamic
// properties we added for the $OBJECT=SpriteClassName lines.
//---------------------------------------------------------------------------
STDMETHODIMP CGame::Invoke
(
DISPID dispid,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr)
{
IDispatch *pdisp = NULL;

// Validate Args
if (pvarResult)
VariantInit(pvarResult);
if (puArgErr)
*puArgErr = 0;
SetErrorInfo(0L, NULL);

// If dispid isn't a dynamic property, call COleAuto's dispatch,
// and it will forward the request on to m_pdispBaseObject.
if ((dispid & 0x80010000) != 0x80010000)
return COleAuto::Invoke(dispid, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
// It's for one of our property objects...

// Make sure we have either a PropGet or a Method
if (!(wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET)))
return DISP_E_MEMBERNOTFOUND;

dispid &= 0xffff;
if (dispid >= m_csubobj)
return DISP_E_MEMBERNOTFOUND;

if (pdispparams->cArgs)
return DISP_E_BADPARAMCOUNT;

if (pdispparams->cNamedArgs)
return DISP_E_NONAMEDARGS;

// Get an addref'd IDispatch to the property object
pdisp = m_rgpdispSubObjs[dispid];
ASSERT(pdisp, "NULL m_rgpdispSubObjs[]");

if (pvarResult)
{
pvarResult->vt = VT_DISPATCH;
pvarResult->pdispVal = pdisp;
pdisp->AddRef();// We're returning this disp ptr
}

return S_OK;
}


//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CGame::GetTypeLibInfo
(
HINSTANCE *phinstOut,
const GUID **pplibidOut,
SHORT *pwMajLib,
SHORT *pwMinLib,
const CLSID **ppclsidOut,
const IID **ppiidOut,
ITypeLib ***ppptlOut
)
{
HRESULT hr = this->BuildTypeInfo();
if (hr)
return hr;
*phinstOut = NULL;
*pplibidOut = &m_libidSubObj;
*pwMajLib = 1;
*pwMinLib = 0;
*ppclsidOut = &m_clsidSubObj;
*ppiidOut = &m_iidSubObj;
*ppptlOut = &g_ptlGameSubObj;
return S_OK;
}


//***************************************************************************
// IProvideClassInfo Interfaces
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetClassInfo
(
ITypeInfo** pptinfoOut
)
{
HRESULT hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
if (hr)
return hr;
*pptinfoOut = g_ptinfoClsGame;
(*pptinfoOut)->AddRef();
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetGUID
(
DWORD dwGuidKind,
GUID* pGUID
)
{
if (pGUID)
return E_INVALIDARG;

if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
return E_INVALIDARG;

*pGUID = DIID_IGameEvents;
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetMultiTypeInfoCount
(
ULONG *pcti
)
{
if (!pcti)
return E_INVALIDARG;

*pcti = 2;
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetInfoOfIndex
(
ULONG iti,
DWORD dwFlags,
ITypeInfo** pptiCoClass,
DWORD* pdwTIFlags,
ULONG* pcdispidReserved,
IID* piidPrimary,
IID* piidSource
)
{
HRESULT hr = S_OK;
ITypeInfo *ptinfoOut = NULL;

if (iti > 1)
hr = E_FAIL;

// if (dwFlags & ~MULTICLASSINFO_ALL)
// hr = E_INVALIDARG;

if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
{
if (!pptiCoClass || !pdwTIFlags)
hr = E_INVALIDARG;
else
{
*pdwTIFlags = 0L;
*pptiCoClass = NULL;
if (iti == 0)
{
hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
if (hr)
return hr;
ptinfoOut = g_ptinfoClsGame;
}
else
{
hr = ((COleAuto *)m_pgameoa)->CheckTypeInfo(0, 0x0409);
if (hr)
return hr;
ptinfoOut = g_ptinfoClsGameOA;
}
}
}

if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
{
if (!pcdispidReserved)
hr = E_INVALIDARG;
else
*pcdispidReserved = 256;
}

if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
{
if (!piidPrimary)
hr = E_INVALIDARG;
else if (iti == 0)
*piidPrimary = IID_IGameSubObjects;
else
*piidPrimary = IID_IGame;
}

if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
{
if (!piidSource)
hr = E_INVALIDARG;
else if (iti == 0)
*piidSource = IID_NULL;
else
*piidSource = DIID_IGameEvents;
}

// Fill in ptinfo out param
if (hr)
{
if (pptiCoClass)
*pptiCoClass = NULL;
}
else if (pptiCoClass)
{
*pptiCoClass = ptinfoOut;
(*pptiCoClass)->AddRef();
}

return hr;
}



// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
//***************************************************************************
// IActiveScriptSite Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetLCID
(
LCID *plcid
)
{
return E_NOTIMPL; // Use system settings
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetItemInfo
(
LPCOLESTR pstrName,
DWORD dwReturnMask,
IUnknown** ppunkItemOut,
ITypeInfo** pptinfoOut
)
{
HRESULT hr;

if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
{
if (!pptinfoOut)
return E_INVALIDARG;
*pptinfoOut = NULL;
}

if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
{
if (!ppunkItemOut)
return E_INVALIDARG;
*ppunkItemOut = NULL;
}

if (!_wcsicmp(L"game", pstrName))
{
if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
{
hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
if (hr)
return hr;
*pptinfoOut = g_ptinfoClsGame;
(*pptinfoOut)->AddRef(); // because returning
}
if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
{
*ppunkItemOut = this->GetUnknown();
(*ppunkItemOut)->AddRef(); // because returning
}
return S_OK;
}

return TYPE_E_ELEMENTNOTFOUND;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetDocVersionString
(
BSTR *pbstrVersion
)
{
return E_NOTIMPL; // UNDONE: Implement this method
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::RequestItems
(
void
)
{
return m_ps->AddNamedItem(L"Game", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::RequestTypeLibs
(
void
)
{
return m_ps->AddTypeLib(LIBID_SPRUUIDS, 1, 0, 0);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnScriptTerminate
(
const VARIANT *pvarResult,
const EXCEPINFO *pexcepinfo
)
{
// UNDONE: Put up error dlg here
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnStateChange
(
SCRIPTSTATE ssScriptState
)
{
// Don't care about notification
return S_OK;
}


//---------------------------------------------------------------------------
// Display the error
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnScriptError
(
IActiveScriptError *pse
)
{
char szError[1024];
char *pszArrow = NULL;
BOOL fAlloc = FALSE;
EXCEPINFO ei;
DWORD dwSrcContext;
ULONG ulLine;
LONG ichError;
BSTR bstrLine = NULL;
HRESULT hr;

CHECK(pse->GetExceptionInfo(&ei));
CHECK(pse->GetSourcePosition(&dwSrcContext, &ulLine, &ichError));
ulLine += g_clineOffset; // Adjust for $ENGINE/$OBJECT/etc. lines
hr = pse->GetSourceLineText(&bstrLine);
if (hr)
hr = S_OK; // Ignore this error, there may not be source available

if (!hr)
{
MAKE_ANSIPTR_FROMWIDE(pszSrc, ei.bstrSource);
MAKE_ANSIPTR_FROMWIDE(pszDesc, ei.bstrDescription);
MAKE_ANSIPTR_FROMWIDE(pszLine, bstrLine);
if (ichError > 0 && ichError < 255)
{
pszArrow = new char[ichError+1];
memset(pszArrow, '-', ichError);
pszArrow[ichError-1] = 'v';
pszArrow[ichError] = 0;
fAlloc = TRUE;
}
else
pszArrow = "";

wsprintf(szError, "Source:'%s'\nFile:'%s' Line:%d Char:%d\nError:%d '%s'\n%s\n%s",
pszSrc, g_pszCodeFile, ulLine, ichError,
(int)ei.wCode, pszDesc, pszArrow, pszLine);
DisplayScriptError(g_hinst, m_hwndDlg, szError, ichError);
}

CleanUp:
if (fAlloc)
delete [] pszArrow;
if (bstrLine)
SysFreeString(bstrLine);

return hr;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnEnterScript
(
void
)
{
// No need to do anything
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnLeaveScript
(
void
)
{
// No need to do anything
return S_OK;
}



//***************************************************************************
// IActiveScriptSiteWindow Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetWindow
(
HWND *phwndOut
)
{
if (!phwndOut)
return E_INVALIDARG;
*phwndOut = m_hwndDlg;
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::EnableModeless
(
BOOL fEnable
)
{
if (fEnable)
m_pcm->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
else
m_pcm->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
return S_OK;
}
// ##### END ACTIVEX SCRIPTING SUPPORT #####


//***************************************************************************
// Misc WndProc-related Methods
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_DlgProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
if (!g_pgame)
return FALSE;

return g_pgame->DlgProc(hwnd, msg, wp, lp);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::DlgProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
switch (msg)
{
case WM_ERASEBKGND:
if (m_pscore)
{
m_pscore->Paint((HDC)wp);
return TRUE;
}
break;
}
return FALSE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT AppEvt_PSWndProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
if (!g_pgame)
return DefWindowProc(hwnd, msg, wp, lp);

return g_pgame->PSWndProc(hwnd, msg, wp, lp);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CGame::PSWndProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
switch (msg)
{
case WM_PAINT:
if (m_pdisp)
{
if (wp)
m_pdisp->Paint((HDC)wp);
else
{
PAINTSTRUCT ps;

BeginPaint(hwnd, &ps);
m_pdisp->Paint(ps.hdc);
EndPaint(hwnd, &ps);
}
return 1L;
}
break;

case WM_ERASEBKGND:
SelectObject((HDC)wp, (HGDIOBJ)(m_pdisp->m_hbrushBack ? (HBRUSH)(m_pdisp->m_hbrushBack) : (HBRUSH)g_hbrushStock));
PatBlt((HDC)wp, 0, 0, 32000, 32000, PATCOPY);
SelectObject((HDC)wp, g_hbrushStock);
return TRUE;

case WM_SIZE:
{
RECT rect;

GetClientRect(hwnd, &rect);
m_pdisp->m_cx = rect.right;
m_pdisp->m_cy = rect.bottom;
break;
}

case WM_TIMER:
{
if (m_fGameOver)
break;

m_pdisp->Timer();

if (m_fPaused)
break;

if (m_fShipRestart)
{
m_pdisp->Refresh();
break;
}

if (!m_fShipDead)
{
static cRefresh = 0;

m_pgameoa->FireTick();
if (++cRefresh > 5) // What's a reasonable number?
{
m_pdisp->Refresh();
cRefresh = 0;
}
}
else
{
m_pscore->ShipKilled();
InvalidateRect(m_hwndDlg, NULL, TRUE);
if (m_pscore->GetCShip() <= 0)
{
this->GameOver();
break;
}

m_fShipDead = FALSE;
m_fShipRestart = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
}
break;
}

case WM_KEYDOWN:
m_pgameoa->FireKeyDown(wp);
break;

case WM_CHAR:
m_pgameoa->FireKeyPress(wp);
break;

case WM_KEYUP:
m_pgameoa->FireKeyUp(wp);
break;

case WM_MOUSEMOVE:
m_pgameoa->FireMouseMove(LOWORD(lp), HIWORD(lp), wp);
break;

case WM_LBUTTONDOWN:
m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_LBUTTON);
break;

case WM_RBUTTONDOWN:
m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_RBUTTON);
break;

case WM_MBUTTONDOWN:
m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_MBUTTON);
break;

case WM_LBUTTONUP:
m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_LBUTTON);
break;

case WM_RBUTTONUP:
m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_RBUTTON);
break;

case WM_MBUTTONUP:
m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_MBUTTON);
break;
}

return DefWindowProc(hwnd, msg, wp, lp);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CALLBACK ScriptErrorDialog
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
LPCSTR pszError;

switch (msg)
{
case WM_INITDIALOG:
pszError = (LPCSTR)lp;
ShowWindow(hwnd, SW_HIDE);
SendDlgItemMessage(hwnd, ID_CTL_ERROR, WM_SETFONT,
(WPARAM)GetStockObject(ANSI_FIXED_FONT), MAKELPARAM(FALSE,0));
SendDlgItemMessage(hwnd, ID_CTL_ERROR, WM_SETTEXT, 0, (LPARAM)pszError);
ShowWindow(hwnd, SW_SHOW);
return (TRUE);

case WM_COMMAND:
switch(LOWORD(wp))
{
case IDOK:
case IDCANCEL:

goto Destroy; 
}
break;
}
return FALSE;

Destroy:
EndDialog(hwnd, 0);
return TRUE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
int DisplayScriptError
(
HINSTANCE hinst,
HWND hwndParent,
LPCSTR pszError,
int ichError
)
{
int result;
UINT len = lstrlen(pszError);
UINT newlen;
UINT newindex;
UINT i;
LPSTR pszNewError;

// Figure out how long the real string will be
newlen = 0;
for (i=0; i<len; i++)
{
CHAR c = pszError[i];

if (c == '\n')
newlen +=1; // Convert \n to \r\n
else if (c == '\r')
{
if (pszError[i+1] == '\n')
{
newlen++; // Leave \r\n alone
i++;
}
else
newlen++; // Convert \r to \r\n
}
newlen++;
}

// Create the real string:
pszNewError = new char[newlen+1];
if (!pszNewError)
return -1;

newindex = 0;
for (i=0; i<len; i++)
{
CHAR c = pszError[i];

if (c == '\n')
pszNewError[newindex++] = '\r'; // Convert \n to \r\n
else if (c == '\r')
{
if (pszError[i+1] == '\n')
{
pszNewError[newindex++] = pszError[i++]; // Leave \r\n alone
i++;
}
else
{
pszNewError[newindex++] = c; // Convert \r to \r\n
c = '\n';
}
}
pszNewError[newindex++] = c;
}
pszNewError[newindex] = '\0';

// Create the dialog box
g_pgame->m_pcm->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
g_pgame->Pause(1);
result = DialogBoxParam(
hinst, // Handle to application instance
MAKEINTRESOURCE(ID_DLG_ERROR), // Identifies dialog box template name
hwndParent, // Handle to owner window
(DLGPROC)ScriptErrorDialog, // Pointer to dialog box procedure
(DWORD)pszNewError); // Context
g_pgame->m_pcm->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
SetFocus(g_pgame->m_hwndPS);

delete[] pszNewError;

return result;
}


//***************************************************************************
// Misc General Methods
//***************************************************************************

//---------------------------------------------------------------------------
// New level
//---------------------------------------------------------------------------
void CGame::NewLevel
(
void
)
{
g_pgame->m_pdisp->DestroyAll();
m_pscore->NextLevel();
m_fShipRestart = TRUE;
InvalidateRect(m_hwndPS, NULL, TRUE);
InvalidateRect(m_hwndDlg, NULL, TRUE);
m_pgameoa->FireNewLevel();
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void AppEvt_NewGame
(
void
)
{
g_pgame->NewGame();
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGame::NewGame
(
void
)
{
m_pdisp->DestroyAll();
m_pscore->NewGame();
m_fGameOver = FALSE;
m_fShipRestart = TRUE;
m_pgameoa->FireNewGame();
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_Pause
(
int pause
)
{
return g_pgame->Pause(pause);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::Pause
(
int pause
)
{
if (pause == -1) // Toggle
pause = !m_fPaused;

switch (pause)
{
case 1: // Pause
if (m_fGameOver)
goto UnPause;
if (g_mode != MODE_PLAYBACK)
KillTimer(m_hwndPS, 0);
m_fPaused = TRUE;
break;

case 0: // UnPause
UnPause:
if (g_mode != MODE_PLAYBACK)
SetTimer(m_hwndPS, 0, 40, NULL);
m_fPaused = FALSE;
break;
}

if (m_fPaused)
m_pscore->SetStatusText("Game Paused. Press F3 to Continue.");
else
m_pscore->SetStatusText("");

return m_fPaused;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_FQueryTerminate
(
BOOL fPromptUser
)
{
return g_pgame->FQueryTerminate(fPromptUser);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::FQueryTerminate
(
BOOL fPromptUser
)
{
// UNDONE
return TRUE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGame::GameOver
(
void
)
{
m_fGameOver = TRUE;
InvalidateRect(m_hwndPS, NULL, TRUE);
m_pscore->SetStatusText("Game Over. Press F2 to Play a New Game.");
}


// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CGame::CreateScriptEngine
(
LPCOLESTR pstrItemName
)
{
HRESULT hr;

if (m_ps)
return S_FALSE; // Already created it

// Create the ActiveX Scripting Engine
hr = CoCreateInstance(m_clsidEngine, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void **)&m_ps);
if (hr)
{
s_pszError = "Creating the ActiveX Scripting engine failed. Scripting engine is probably not correctly registered or CLSID incorrect.";
return E_FAIL;
}
// Script Engine must support IActiveScriptParse for us to use it
hr = m_ps->QueryInterface(IID_IActiveScriptParse, (void **)&m_psp);
if (hr)
{
s_pszError = "ActiveX Scripting engine does not support IActiveScriptParse";
return hr;
}
hr = m_ps->SetScriptSite(this);
if (hr)
return hr;
// InitNew the object:
hr = m_psp->InitNew();
if (hr)
return hr;
hr = m_ps->AddNamedItem(L"Game", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
return hr;
}
// ##### END ACTIVEX SCRIPTING SUPPORT #####



//***************************************************************************
// Parsing support
//***************************************************************************

//---------------------------------------------------------------------------
// Skip white space, counting lines. Treat \r & \n as white space only if
// fSkipNewLines is TRUE.
//---------------------------------------------------------------------------
void SkipWhite
(
char **ppsz,
BOOL fSkipNewLines
)
{
// Skip white space
while (**ppsz && **ppsz<=' ')
{
if (**ppsz == '\r' || **ppsz == '\n')
{
if (!fSkipNewLines)
return;
if (**ppsz == '\r' && (*ppsz)[1] == '\n')
++*ppsz;
g_clineOffset++;
}
++*ppsz;
}
}


//---------------------------------------------------------------------------
// Finds $OBJECT, $ENGINE, $INCLUDE, etc, parses past '=' to argument. NULL
// terminates the argument, then returns a ptr to begining of arg & to beg
// of next line.
//---------------------------------------------------------------------------
struct
{
int cch;
char *psz;
} g_rgtoken[] =
{
{7, "$ENGINE"},
{7, "$OBJECT"},
{8, "$INCLUDE"},
{0, ""},
};

int FindToken
(
char *pszCur,
char **ppszBeginArg,
char **ppszNextLine,
HRESULT *phr,
char **ppszError
)
{
char chQuote = 0;
char ch;

*phr = S_OK;
SkipWhite(&pszCur, TRUE);
if (*pszCur != '$')
{
*ppszNextLine = pszCur;
return 0; // No token found
}

for (int i=0; g_rgtoken[i].cch; i++)
{
if (!strncmp(pszCur, g_rgtoken[i].psz, g_rgtoken[i].cch))
{
pszCur += g_rgtoken[i].cch;
SkipWhite(&pszCur, FALSE);
if (*pszCur != '=')
{
*ppszError = "Missing '=' on $ENGINE, $OBJECT, or $INCLUDE line";
*phr = E_FAIL;
return 0;
}
pszCur++;
SkipWhite(&pszCur, FALSE);
if (*pszCur <= ' ')
{
*ppszError = "Missing argument on $ENGINE, $OBJECT, or $INCLUDE line";
*phr = E_FAIL;
return 0;
}
if (*pszCur=='"' || *pszCur=='\'' || *pszCur=='<')
{
chQuote = *pszCur;
if (chQuote == '<')
chQuote = '>';
pszCur++;
}
*ppszBeginArg = pszCur;
if (chQuote)
{
// Look for End of String, EOL, or End of Quote
while (*pszCur && *pszCur!='\n' && *pszCur!='\r' && *pszCur!=chQuote)
pszCur++;
}
else
{
// Skip non-white space
while (*pszCur > ' ')
pszCur++;
}
ch = *pszCur;
*pszCur = 0;
NextChar:
switch (ch)
{
default:
// Look for EOL
pszCur++;
ch = *pszCur;
goto NextChar;

case '\r':
// Skip the cr we're at
g_clineOffset++;
pszCur++;
if (*pszCur == '\n')
pszCur++;
break;

case '\n':
// Skip the newline we're at
g_clineOffset++;
pszCur++;
break;

case 0:
break;
}

*ppszNextLine = pszCur;
return i+1;
}
}

*ppszError = "unrecognized token; must be $ENGINE, $OBJECT, or $INCLUDE";
*phr = E_FAIL;
return 0;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CGame::ParseFile
(
char *pszFileName,
LPCOLESTR pstrItemName
)
{
struct _stat stat;
size_t cch;
EXCEPINFO ei;
char *pszT;
char *pszAlloc;
FILE *pfile;
HRESULT hr = S_OK;
int tk = 0;
char *pszArg;

// Find the length of the file
if (_stat(pszFileName, &stat))
return E_FAIL;
cch = stat.st_size;

// Allocate a buffer to read the file
pszAlloc = new char[cch+1];
if (!pszAlloc)
return E_OUTOFMEMORY;
memset(pszAlloc, 0, cch); // HACK: Since cch may be too large, zero-fill string

// Open & read the file
pfile = fopen(pszFileName, "rb");
if (!pfile)
{
s_pszError = "Bad file name";
hr = E_FAIL;
goto Error;
}
fread(pszAlloc, cch, 1, pfile);
fclose(pfile);

// Parse lines begining w/$
pszAlloc[cch] = 0;
pszT = pszAlloc;
while (tk=FindToken(pszT, &pszArg, &pszT, &hr, &s_pszError))
{
switch (tk)
{
case 1: // $ENGINE
{
MAKE_WIDEPTR_FROMANSI(pwszCLSID, pszArg);
hr = CLSIDFromString(pwszCLSID, &m_clsidEngine);
if (hr)
{
s_pszError = "Bad $ENGINE={xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} syntax";
goto Error;
}
hr = this->CreateScriptEngine(pstrItemName);
if (hr == S_FALSE)
{
s_pszError = "$ENGINE must be first line; Script Engine already created.";
goto Error;
}
else if (hr)
goto Error;
break;
}

case 2: // $OBJECT
if (m_csubobj >= csubobjMAX)
{
s_pszError = "Too many $OBJECT lines";
goto Error;
}

m_rgpwszSubObjs[m_csubobj] = new WCHAR [strlen(pszArg)+1];
if (!m_rgpwszSubObjs[m_csubobj])
{
s_pszError = "Out Of Memory";
goto Error;
}
UNICODE_FROM_ANSI(m_rgpwszSubObjs[m_csubobj], pszArg, 2*strlen(pszArg));
m_rgpdispSubObjs[m_csubobj] = NULL;
m_rgpsc[m_csubobj] = NULL;
m_csubobj++;
break;

case 3: // $INCLUDE
hr = this->ParseFile(pszArg, pstrItemName);
if (hr)
goto Error;
break;
}
}

// If there's anything left, assume it's code
if (*pszT)
{
// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
// Make sure we've created the scripting engine
if (!m_ps)
{
hr = this->CreateScriptEngine(pstrItemName);
if (hr)
goto Error;
}

// Hand it the code
MAKE_WIDEPTR_FROMANSI(pwszCode, pszT);
hr = m_psp->ParseScriptText(pwszCode, pstrItemName, NULL, NULL, 0, 0, 0L, NULL, &ei);
hr = S_OK; // Ignore parse errors, so user can press F9 to view source...
// ##### END ACTIVEX SCRIPTING SUPPORT #####
}

Error:
delete [] pszAlloc;
return hr;
}



//***************************************************************************
// Dynamic object model support
//***************************************************************************

//---------------------------------------------------------------------------
// Builds the typeinfo for the container object from the list of property
// objects.
//---------------------------------------------------------------------------
HRESULT CGame::BuildTypeInfo
(
void
)
{
static GUID guidStdOle = {0x00020430,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46};
ITypeLib* ptlStdOle = NULL;
ITypeInfo* ptinfoIDisp = NULL;
ICreateTypeLib * pcreatetl = NULL;
ICreateTypeInfo* pctiCoClass = NULL;// For creating the CoClass
ICreateTypeInfo* pctiProps = NULL;// For creating the Primary (Property) Dispinterface
FUNCDESC funcdesc = {0};
TYPEDESC tdescUser = {0};
ELEMDESC edArg = {0};
HREFTYPE hreftype;
int index;
int i;
HRESULT hr;

if (g_ptinfoIntGame && g_ptinfoClsGame)
return S_OK;

// Create TypeLib
CHECKSZ(CreateTypeLib(SYS_WIN32, L"c:\\SprdSubO.tlb" /* szFile */, &pcreatetl), "CreateTypeLib FAILED");

// Misc. global props of the TypeLib
CHECKSZ(pcreatetl->SetGuid(m_libidSubObj), "TL SetGuid FAILED");
CHECKSZ(pcreatetl->SetVersion(1, 0), "TL SetVersion FAILED");
CHECKSZ(pcreatetl->SetName(L"Sprds"), "TL SetName FAILED");
CHECKSZ(pcreatetl->SetLcid(LOCALE_SYSTEM_DEFAULT), "TL SetLcid FAILED");

// Get Information on IDispatch
CHECKSZ(LoadRegTypeLib(guidStdOle, STDOLE_MAJORVERNUM, STDOLE_MINORVERNUM, STDOLE_LCID, &ptlStdOle), "LoadRegTypeLib FAILED");
CHECKSZ(ptlStdOle->GetTypeInfoOfGuid(IID_IDispatch, &ptinfoIDisp), "GetTypeInfoOfGuid (IID_IDispatch) FAILED");

// Create TypeInfo
CHECKSZ(pcreatetl->CreateTypeInfo(L"GameSubObjs", TKIND_DISPATCH, &pctiProps), "Create TI FAILED");
CHECKSZ(pctiProps->SetGuid(m_iidSubObj), "cti SetGuid FAILED");
CHECKSZ(pctiProps->SetVersion(1, 0), "cti SetVersion FAILED");
CHECKSZ(pctiProps->AddRefTypeInfo(ptinfoIDisp, &hreftype), "cti AddRefTypeInfoo FAILED"); // Reference StdOLE
CHECKSZ(pctiProps->AddImplType(0, hreftype), "cti AddImplType FAILED");

funcdesc.funckind = FUNC_DISPATCH;
funcdesc.invkind = INVOKE_PROPERTYGET;
funcdesc.callconv = CC_STDCALL;
funcdesc.cScodes = -1;
funcdesc.elemdescFunc.tdesc.vt = VT_PTR;
funcdesc.elemdescFunc.tdesc.lptdesc = &tdescUser;
tdescUser.vt = VT_USERDEFINED;

// Loop through all of the SpriteClasses ($OBJECTs) in this game, adding a
// property to pctiProps which corresponds to the control.
index = 0;
for (i=0; i<m_csubobj; i++)
{
// Need to make sure this starts out cleared
if (!g_ptinfoClsSpriteClass)
{
CHECKCL(LoadTypeInfo(g_hinst, 0, 1, 0, 0x0409,
LIBID_SPRUUIDS, CLSID_SpriteClass, IID_ISpriteClass, FALSE,
&g_ptlMain, &g_ptinfoClsSpriteClass, &g_ptinfoIntSpriteClass));
}
CHECKCL(pctiProps->AddRefTypeInfo(g_ptinfoClsSpriteClass, &tdescUser.hreftype));
funcdesc.memid = 0x80010000 | i;
funcdesc.wFuncFlags = FUNCFLAG_FSOURCE;

CHECKCL(pctiProps->AddFuncDesc(index, &funcdesc));
CHECKCL(pctiProps->SetFuncAndParamNames(index, &m_rgpwszSubObjs[i], 1));

index++;
continue;

CheckLoop:
// Do any clean up here

CHECKSZ(hr,"BuildTypeInfo FAILED");
break;
}

// Finish off the TI for the Props
CHECKSZ(pctiProps->LayOut(), "BuildTypeInfo FAILED");
CHECKSZ(pctiProps->QueryInterface(IID_ITypeInfo, (void **)&g_ptinfoIntGame), "BuildTypeInfo FAILED");

// Now we have to create a coclass for the interface
CHECKSZ(pcreatetl->CreateTypeInfo(L"Game", TKIND_COCLASS, &pctiCoClass), "BuildTypeInfo FAILED");
CHECKSZ(pctiCoClass->SetGuid(m_clsidSubObj), "BuildTypeInfo FAILED");
CHECKSZ(pctiCoClass->SetVersion(1, 0), "BuildTypeInfo FAILED");

// Add the Page Property dispinterface to coclass
CHECKSZ(pctiCoClass->AddRefTypeInfo(g_ptinfoIntGame, &hreftype), "BuildTypeInfo FAILED");
CHECKSZ(pctiCoClass->AddImplType(0, hreftype), "BuildTypeInfo FAILED");
CHECKSZ(pctiCoClass->SetImplTypeFlags(0, IMPLTYPEFLAG_FDEFAULT), "BuildTypeInfo FAILED");

// Finish off the CoClass
CHECKSZ(pctiCoClass->LayOut(), "BuildTypeInfo FAILED");
CHECKSZ(pctiCoClass->QueryInterface(IID_ITypeInfo, (void **)&g_ptinfoClsGame), "BuildTypeInfo FAILED");

// Finish off the Library
CHECKSZ(pcreatetl->QueryInterface(IID_ITypeLib, (void **)&g_ptlGameSubObj), "BuildTypeLib FAILED");

CleanUp:
// Code to release refs aquired during page build.
if (pctiCoClass)
pctiCoClass->Release();
if (pctiProps)
pctiProps->Release();
if (ptinfoIDisp)
ptinfoIDisp->Release();
if (ptlStdOle)
ptlStdOle->Release();
if (pcreatetl)
pcreatetl->Release();

return hr;
};

//--- EOF -------------------------------------------------------------------