SPR.CPP

//--------------------------------------------------------------------------- 
// Spr.cpp
//---------------------------------------------------------------------------
// Sprite handler
//---------------------------------------------------------------------------
// (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 "Spr.h"
#include <math.h>


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


//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------
ITypeInfo *g_ptinfoClsSpriteClass = NULL;
ITypeInfo *g_ptinfoIntSpriteClass = NULL;
ITypeInfo *g_ptinfoClsSprite = NULL;
ITypeInfo *g_ptinfoIntSprite = NULL;
HBITMAP g_hbmpStock = NULL;
HBRUSH g_hbrushStock = NULL;
HDC g_hdcMem = NULL;


//***************************************************************************
//***************************************************************************
//**
//** Display Class
//**
//***************************************************************************
//***************************************************************************

//---------------------------------------------------------------------------
// Initialize display surface for sprites
//---------------------------------------------------------------------------
CDisplay::CDisplay
(
HINSTANCE hinst,
HWND hwnd,
HDC hdc,
IUnknown *punkParent,
PFNCOLLIDE pfnCollide,
int cimg,
PIMG prgimg,
UINT idbmp
)
{
RECT rect;

INIT_SIGNATURE(SIG_Display);

// Create an HDC and get the stock bitmap
if (!g_hdcMem)
{
HBITMAP hbmp;

g_hdcMem = CreateCompatibleDC(NULL);
hbmp = CreateCompatibleBitmap(g_hdcMem, 1, 1);
g_hbmpStock = (HBITMAP)SelectObject(g_hdcMem, hbmp);
SelectObject(g_hdcMem, g_hbmpStock);
DeleteObject(hbmp);
g_hbrushStock = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
}

// Stash away values & init member variables
m_hinst = hinst;
m_hdc = hdc;
GetClientRect(hwnd, &rect);
m_cx = rect.right;
m_cy = rect.bottom;
m_pfnCollide = pfnCollide;
m_csc = 0;
m_cimg = cimg;
m_prgimg = prgimg;
m_idbmp = idbmp;
m_psprFirst = NULL;
m_psprLast = NULL;
m_punkParent = punkParent;
m_hbmp = NULL;
m_hbrushBack = NULL;
this->SetBackColor(0x00c0c0c0);
m_hwnd = hwnd;

// Compute animated images
for (int i=0; i<cimg; i++)
if (prgimg[i].iimgNext != iimgNONE &&
prgimg[i].iimgNext != iimgREMOVE)
prgimg[i].pimgNext = &prgimg[prgimg[i].iimgNext];

// Load in all hbmp
m_hbmp = LoadBitmap(m_hinst, MAKEINTRESOURCE(m_idbmp));
}


//---------------------------------------------------------------------------
// Terminate Sprite Sytem
//---------------------------------------------------------------------------
CDisplay::~CDisplay
(
void
)
{
CHECK_SIGNATURE(SIG_Display);

if (m_hbrushBack)
{
SelectObject(g_hdcMem, g_hbrushStock);
DeleteObject(m_hbrushBack);
m_hbrushBack = NULL;
}

if (g_hdcMem)
{
SelectObject(g_hdcMem, g_hbmpStock);
DeleteDC(g_hdcMem);
g_hdcMem = NULL;
}

if (m_hbmp)
{
// Delete all loaded bitmaps:
DeleteObject((HGDIOBJ)m_hbmp);
m_hbmp = (HBITMAP)NULL;
}

DESTROY_SIGNATURE(SIG_Display);
}


//---------------------------------------------------------------------------
// Free all sprites on Display.
//---------------------------------------------------------------------------
void CDisplay::DestroyAll
(
void
)
{
CSprite *pspr;

while (m_psprFirst)
{
pspr = m_psprFirst;
pspr->_Unlink(FALSE);
pspr->Release();
}
}


//---------------------------------------------------------------------------
// Move all sprites on Display.
//---------------------------------------------------------------------------
void CDisplay::Timer
(
void
)
{
CSprite *pspr;
CSprite *psprPrev;

for (pspr=m_psprLast; pspr; pspr=psprPrev)
{
psprPrev = pspr->m_psprPrev;

// Fire Tick event, if sprite exists
if (!pspr->m_fFree)
{
if (--pspr->m_tickEvt <= 0)
{
pspr->m_psc->FireTick(pspr);
pspr->m_tickEvt = pspr->m_tickEvtMax;
}
}

// Move sprite, if it still exits (FireTick might have killed it.)
if (!pspr->m_fFree)
{
if (--pspr->m_tickMove <= 0)
{
long xOldFull;
long yOldFull;

pspr->m_tickMove = pspr->m_tickMoveMax;

// Does this sprite have a "MoveAt" sprite attached?
if (pspr->m_psprAt)
pspr->DoMoveAt(pspr->m_psprAt, pspr->m_dMaxAt, pspr->m_scaleAt, pspr->m_flagsAt, TRUE);

xOldFull = pspr->m_xFull;
yOldFull = pspr->m_yFull;
if (HIWORD(xOldFull + pspr->m_vxFull) == HIWORD(xOldFull) &&
HIWORD(yOldFull + pspr->m_vyFull) == HIWORD(yOldFull))
{
// Movement was only fractional, so don't need to redraw sprite.
pspr->m_xFull += pspr->m_vxFull;
pspr->m_yFull += pspr->m_vyFull;
}
else
{
pspr->_Erase();
pspr->m_xFull += pspr->m_vxFull;
pspr->m_yFull += pspr->m_vyFull;
if (pspr->m_pimg->iimgNext == iimgREMOVE)
pspr->Remove();
else if (pspr->m_pimg->iimgNext != iimgNONE)
{
pspr->m_iimg = pspr->m_pimg->iimgNext;
pspr->m_pimg = pspr->m_pimg->pimgNext;
}
if (!pspr->_FDraw(TRUE))
{
pspr->m_xFull = xOldFull;
pspr->m_yFull = yOldFull;
pspr->_FDraw(FALSE);
}
}

// Calculate Friction
if (pspr->m_fFriction)
{
pspr->m_vxFull = (long)((FLOAT)pspr->m_vxFull * pspr->m_fx);
pspr->m_vyFull = (long)((FLOAT)pspr->m_vyFull * pspr->m_fy);
}

// Calculate AccelerationFriction
if (pspr->m_psc->m_fFrictionAccel)
{
pspr->m_axFull = (long)((FLOAT)pspr->m_axFull * pspr->m_psc->m_frictionAccel);
pspr->m_ayFull = (long)((FLOAT)pspr->m_ayFull * pspr->m_psc->m_frictionAccel);
}

// Calculate Acceleration
pspr->m_vxFull += pspr->m_axFull;
pspr->m_vyFull += pspr->m_ayFull;

// Ensure Vel is not over max allowed for SpriteClass
if (pspr->m_vxFull > pspr->m_psc->m_vFullMax)
pspr->m_vxFull = pspr->m_psc->m_vFullMax;
else if (pspr->m_vxFull < -pspr->m_psc->m_vFullMax)
pspr->m_vxFull = -pspr->m_psc->m_vFullMax;
if (pspr->m_vyFull > pspr->m_psc->m_vFullMax)
pspr->m_vyFull = pspr->m_psc->m_vFullMax;
else if (pspr->m_vyFull < -pspr->m_psc->m_vFullMax)
pspr->m_vyFull = -pspr->m_psc->m_vFullMax;
}
}

if (pspr->m_fFree)
{
pspr->_Unlink(TRUE);
pspr->Release();
}
}
}


//---------------------------------------------------------------------------
// Paint all sprites.
//---------------------------------------------------------------------------
void CDisplay::Paint
(
HDC hdc
)
{
CSprite *pspr;

if (hdc == NULL)
hdc = m_hdc;

for (pspr=m_psprLast; pspr; pspr=pspr->m_psprPrev)
{
if (!pspr->m_fFree && !pspr->m_fHidden)
{
SelectObject(g_hdcMem, m_hbmp);
BitBlt(hdc, pspr->m_x, pspr->m_y, pspr->m_pimg->cx, pspr->m_pimg->cy, g_hdcMem, pspr->m_pimg->x, pspr->m_pimg->y, SRCCOPY);
}
}
SelectObject(g_hdcMem, g_hbmpStock);
}


//---------------------------------------------------------------------------
// Paint any sprites with fNeedRefresh set.
//---------------------------------------------------------------------------
void CDisplay::Refresh
(
void
)
{
CSprite *pspr;
HDC hdc = m_hdc;

for (pspr=m_psprLast; pspr; pspr=pspr->m_psprPrev)
{
if (pspr->m_fNeedRefresh && !pspr->m_fHidden)
{
pspr->m_fNeedRefresh = FALSE;
SelectObject(g_hdcMem, m_hbmp);
BitBlt(hdc, pspr->m_x, pspr->m_y, pspr->m_pimg->cx, pspr->m_pimg->cy, g_hdcMem, pspr->m_pimg->x, pspr->m_pimg->y, SRCCOPY);
}
}
SelectObject(g_hdcMem, g_hbmpStock);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CDisplay::SetBackColor
(
COLORREF c
)
{
static LOGBRUSH lb = {BS_SOLID, 0x00c0c0c0, 0};

if (m_colorBack == c)
return;
m_colorBack = c;
lb.lbColor = c;
if (m_hbrushBack)
DeleteObject(m_hbrushBack);
m_hbrushBack = CreateBrushIndirect(&lb);
}


//---------------------------------------------------------------------------
// Send Collide event back to the fn passed into the constructor.
//---------------------------------------------------------------------------
void CDisplay::FireCollide
(
CSprite *psprLowId,
CSprite *psprHighId,
int maskCollide
)
{
(*m_pfnCollide)(psprLowId, psprHighId, maskCollide);
}


//***************************************************************************
//***************************************************************************
//**
//** SpriteClass Class
//**
//***************************************************************************
//***************************************************************************

HRESULT s_hr = S_OK;


//---------------------------------------------------------------------------
// Create function for SpriteClass. Use instead of "new CSpriteClass".
//---------------------------------------------------------------------------
HRESULT CSpriteClass::Create
(
CDisplay *pdisp,
CSpriteClass **ppscOut
)
{
if (!ppscOut)
return E_INVALIDARG;

*ppscOut = new CSpriteClass(pdisp);
if (!*ppscOut)
return E_OUTOFMEMORY;

return S_OK;
}


//---------------------------------------------------------------------------
// Constructor for SpriteClass
//---------------------------------------------------------------------------
CSpriteClass::CSpriteClass
(
CDisplay *pdisp
)
{
INIT_SIGNATURE(SIG_SpriteClass);

m_id = ++(pdisp->m_csc);
m_pdisp = pdisp;
m_brd = SPR_brdALL;
m_maskCollide = 0;
m_iimg = 0;
m_cspr = 0;
m_cref = 1;
m_pdispBaseObject = NULL;
VariantInit(&m_varTag);
m_friction = (FLOAT)1.0;
m_frictionAccel = (FLOAT)1.0;
m_fFrictionAccel = FALSE;
m_vFullMax = 0x000a0000;
m_aFullMax = 0x00010000;
}


//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
void CSpriteClass::Close
(
void
)
{
CHECK_SIGNATURE(SIG_SpriteClass);

CSprite *pspr;
CSprite *psprPrev;

// Destroy all sprites of this SpriteClass
for (pspr=m_pdisp->m_psprLast; pspr; pspr=psprPrev)
{
psprPrev = pspr->m_psprPrev;

if (pspr->m_psc == this)
{
pspr->_Unlink(FALSE);
pspr->m_psc = NULL;
pspr->Release();
}
}

// Unadvise all event sinks
m_cp.Close();

// Clear incase this is an object
VariantClear(&m_varTag);
}


//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
CSpriteClass::~CSpriteClass
(
void
)
{
CHECK_SIGNATURE(SIG_SpriteClass);
this->Close();
DESTROY_SIGNATURE(SIG_SpriteClass);
}


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

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::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_ISpriteClass)
*ppvObjOut = (ISpriteClass *)this;
else if (iid == IID_IConnectionPointContainer)
*ppvObjOut = (IConnectionPointContainer *)this;
else if (iid == IID_IProvideClassInfo)
*ppvObjOut = (IProvideClassInfo *)this;

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

return E_NOINTERFACE;
}


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


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::Release
(
void
)
{
ASSERT(m_cref, "bad m_cref");
m_cref--;
if (!m_cref)
{
this->Close();
if (!m_cref && !m_cp.m_cref)
{
delete this;
return 0;
}
}
return m_cref;
}


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

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CSpriteClass::GetTypeLibInfo
(
HINSTANCE *phinstOut,
const GUID **pplibidOut,
SHORT *pwMajLib,
SHORT *pwMinLib,
const CLSID **ppclsidOut,
const IID **ppiidOut,
ITypeLib ***ppptlOut
)
{
*phinstOut = g_hinst;
*pplibidOut = &LIBID_SPRUUIDS;
*pwMajLib = 1;
*pwMinLib = 0;
*ppclsidOut = &CLSID_SpriteClass;
*ppiidOut = &IID_ISpriteClass;
*ppptlOut = &g_ptlMain;
return S_OK;
}


//***************************************************************************
// IProvideClassInfo Interface
//***************************************************************************

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


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

if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
return E_INVALIDARG;

*pGUID = DIID_ISpriteClassEvents;
return S_OK;
}


//***************************************************************************
// IConnectionPointContainer Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::EnumConnectionPoints
(
LPENUMCONNECTIONPOINTS* ppEnum
)
{
return E_NOTIMPL; // UNDONE: Implement this method
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::FindConnectionPoint
(
REFIID iid,
LPCONNECTIONPOINT* ppCpOut
)
{
if (!ppCpOut)
return E_INVALIDARG;

if (iid == DIID_ISpriteClassEvents || iid == IID_IDispatch)
{
*ppCpOut = &m_cp;
(*ppCpOut)->AddRef();
return S_OK;
}

return E_NOINTERFACE;
}


//***************************************************************************
// Embedded IConnectionPoint Class
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CSpriteClass::XCP::XCP
(
void
)
{
m_cref = 1;
for (int i=0; i<SC_cADVISE; i++)
m_rgpdisp[i] = NULL;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::XCP::Close
(
void
)
{
for (int i=0; i<SC_cADVISE; i++)
if (m_rgpdisp[i])
{
m_rgpdisp[i]->Release();
m_rgpdisp[i] = NULL;
}
}


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

*ppvObjOut = NULL;

if (iid == IID_IUnknown)
*ppvObjOut = this->GetUnknown();
else if (iid == IID_IConnectionPoint)
*ppvObjOut = (IConnectionPoint *)this;

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

return E_NOINTERFACE;
}


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


#define SCOFCP(pcp) ((CSpriteClass *)((char *)pcp - (char *)&(((CSpriteClass *)0)->m_cp)))
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::XCP::Release
(
void
)
{
CSpriteClass *psc = SCOFCP(this);

ASSERT(m_cref, "bad m_cref");
m_cref--;
if (!m_cref && !psc->m_cref)
{
delete psc;
return 0;
}
return m_cref;
}


//***************************************************************************
// IConnectionPoint Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::GetConnectionInterface
(
IID* piid
)
{
if (!piid)
return E_INVALIDARG;

memcpy(piid, &DIID_ISpriteClassEvents, sizeof(IID));
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::GetConnectionPointContainer
(
IConnectionPointContainer** ppCpcOut
)
{
if (!ppCpcOut)
return E_INVALIDARG;

*ppCpcOut = SCOFCP(this);
(*ppCpcOut)->AddRef();
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::Advise
(
IUnknown* punkSink,
DWORD* pdwCookie
)
{
IDispatch *psce = NULL;
IDispatch **ppdisp;
HRESULT hr;

if (!punkSink || !pdwCookie)
return E_INVALIDARG;
*pdwCookie = 0;

// Look for empty slot
for (ppdisp=m_rgpdisp; *ppdisp && ppdisp<&m_rgpdisp[SC_cADVISE]; ppdisp++)
;
// Did we find one?
if (ppdisp >= &m_rgpdisp[SC_cADVISE])
return E_FAIL; // UNDONE: Error?

// Now see if sink supports correct interface
hr = punkSink->QueryInterface(DIID_ISpriteClassEvents, (void **)&psce);
if (hr == E_NOINTERFACE)
{
hr = punkSink->QueryInterface(IID_IDispatch, (void **)&psce);
if (hr)
return hr;
}
ASSERT(psce, "QI but no ptr");

// Finish advise by stashing punkSink QI'd to our interface
*ppdisp = psce; // AddRef'd from QI
*pdwCookie = (ppdisp - m_rgpdisp) + 1;

return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::Unadvise
(
DWORD dwCookie
)
{
if (!dwCookie || dwCookie > SC_cADVISE)
return E_INVALIDARG;

m_rgpdisp[dwCookie-1]->Release();
m_rgpdisp[dwCookie-1] = NULL;
return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::EnumConnections
(
LPENUMCONNECTIONS* ppEnum
)
{
return E_NOTIMPL; // UNDONE: Implement this method
}



//***************************************************************************
//***************************************************************************
//**
//** Sprite Class
//**
//***************************************************************************
//***************************************************************************

//---------------------------------------------------------------------------
// Constructor for Sprite
//---------------------------------------------------------------------------
CSprite::CSprite
(
CSpriteClass *psc,
int x,
int y,
CSprite *psprPrev, // NULL==1st, -1==Last
VARIANT *pvarUser
)
{
INIT_SIGNATURE(SIG_Sprite);

m_pdisp = psc->m_pdisp;
m_psprNext = NULL;
m_psprPrev = psprPrev;
m_xFull = 0;
m_x = x;
m_yFull = 0;
m_y = y;
m_vxFull = 0;
m_vyFull = 0;
m_axFull = 0;
m_ayFull = 0;
m_fx = psc->m_friction;
m_fy = psc->m_friction;
m_fFriction = (psc->m_friction != 1.0);
m_tickEvt = 0x7fffffff;
m_tickEvtMax = 0x7fffffff;
m_tickMove = 1;
m_tickMoveMax = 1;
m_iimg = psc->m_iimg;
m_pimg = &m_pdisp->m_prgimg[m_iimg];
m_psc = psc;
m_fNeedRefresh = TRUE;
m_fFree = FALSE;
m_fIgnoreMove = FALSE;
m_fLinked = FALSE;
m_fHidden = 2; // Set to 2 instead of TRUE to detect if user explicitly hides it during _Init event
m_cref = 1;
m_psprAt = NULL;
m_pdispBaseObject = NULL;
VariantInit(&m_varTag);
this->_Link();

m_psc->FireInit(this, pvarUser);
if (m_fHidden == 2)
m_fHidden = FALSE; // If not explicitly hidden in event, show it
}


//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
CSprite::~CSprite
(
void
)
{
CHECK_SIGNATURE(SIG_Sprite);
VariantClear(&m_varTag);
if (m_psprAt)
{
m_psprAt->Release();
m_psprAt = NULL;
}
this->_Unlink(TRUE);
DESTROY_SIGNATURE(SIG_Sprite);
}


//---------------------------------------------------------------------------
// Change the class of an existing pspr
//---------------------------------------------------------------------------
CSprite *CSprite::PsprMutate
(
CSpriteClass *psc,
VARIANT *pvarUser
)
{
m_x += m_pimg->cx >> 1;
m_y += m_pimg->cy >> 1;
m_iimg = psc->m_iimg;
m_pimg = &m_pdisp->m_prgimg[m_iimg];
m_x -= m_pimg->cx >> 1;
m_y -= m_pimg->cy >> 1;
m_psc->m_cspr--;
m_psc = psc;
m_psc->m_cspr++;
m_tickEvt = 0x7fffffff;
m_tickEvtMax = 0x7fffffff;
m_tickMove = 0;
m_tickMoveMax = 1;
m_fHidden = 2; // Set to 2 instead of TRUE to detect if user explicitly hides it during _Init event

m_psc->FireInit(this, pvarUser);
if (m_fHidden == 2)
m_fHidden = FALSE; // If not explicitly hidden in event, show it
return this;
}


//---------------------------------------------------------------------------
// Free a SPR structure
//---------------------------------------------------------------------------
void CSprite::MarkFree
(
void
)
{
if (m_psprAt)
{
m_psprAt->Release();
m_psprAt = NULL;
}
m_psc->FireTerm(this);
m_fFree = TRUE;
}


//---------------------------------------------------------------------------
// Link a sprite into the display list
//---------------------------------------------------------------------------
void CSprite::_Link
(
void
)
{
if (!m_psprPrev)
{
m_psprNext = m_pdisp->m_psprFirst;
m_pdisp->m_psprFirst = this;
if (m_psprNext)
m_psprNext->m_psprPrev = this;
if (!m_pdisp->m_psprLast)
m_pdisp->m_psprLast = this;
}
else if (m_psprPrev == (CSprite *)-1)
{
m_psprNext = NULL;
m_psprPrev = m_pdisp->m_psprLast;
if (m_pdisp->m_psprLast)
m_pdisp->m_psprLast->m_psprNext = this;
else
m_pdisp->m_psprFirst = this;
m_pdisp->m_psprLast = this;
}
else
{
// UNDONE: Fix this up.
m_psprNext = m_pdisp->m_psprLast->m_psprNext;
m_pdisp->m_psprLast->m_psprNext = this;
m_psprNext->m_psprPrev = this;
if (m_pdisp->m_psprLast == m_pdisp->m_psprLast)
m_pdisp->m_psprLast = this;
}

m_fLinked = TRUE;
m_psc->m_cspr++;
//this->_FDraw(FALSE); // Require "s.Refresh" to avoid spurious paint before img set
}


//---------------------------------------------------------------------------
// Unlink a sprite into the display list
//---------------------------------------------------------------------------
void CSprite::_Unlink
(
BOOL fFireEvt
)
{
if (!m_fLinked)
return;

this->_Erase();

if (m_pdisp->m_psprFirst == this)
m_pdisp->m_psprFirst = m_psprNext;
if (m_pdisp->m_psprLast == this)
m_pdisp->m_psprLast = m_psprPrev;

if (m_psprPrev)
m_psprPrev->m_psprNext = m_psprNext;
if (m_psprNext)
m_psprNext->m_psprPrev = m_psprPrev;

m_fLinked = FALSE;
m_fHidden = TRUE;

// Track # of sprites of this kind. If there are no more, then
// fire the _LastTerm event.
m_psc->m_cspr--;
if (fFireEvt && !m_psc->m_cspr)
m_psc->FireLastTerm();

if (m_psprAt)
{
m_psprAt->Release();
m_psprAt = NULL;
}
}


//---------------------------------------------------------------------------
// Draw the given sprite
//---------------------------------------------------------------------------
BOOL CSprite::_FDraw
(
BOOLfCollide
)
{
CSprite *psprT;
CSprite *psprNext;
int x = m_x;
int y = m_y;
int cx = m_pimg->cx;
int cy = m_pimg->cy;
HDC hdc = m_pdisp->m_hdc;

if (fCollide && !m_fFree)
{
int maskCollide = m_psc->m_maskCollide;

if (m_fHidden)
goto DoneWithColl;

for (psprT=m_pdisp->m_psprFirst; psprT; psprT=psprNext)
{
psprNext = psprT->m_psprNext;
if (psprT != this && // Never collide w/self
((psprT->m_psc != m_psc) || (maskCollide & 1)) && // Coll w/like kind only if bit 1 set

(maskCollide & psprT->m_psc->m_maskCollide & ~1) && // Coll w/other kinds only if masks overlap 
x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x && // Coll only if actually touch
y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
{
m_fIgnoreMove = FALSE;
if (m_psc->m_id <= psprT->m_psc->m_id)
m_pdisp->FireCollide(this, psprT, maskCollide & psprT->m_psc->m_maskCollide);
else
m_pdisp->FireCollide(psprT, this, maskCollide & psprT->m_psc->m_maskCollide);
if (m_fFree)
return TRUE;
if (m_fIgnoreMove)
return FALSE;
if (psprT->m_fFree)
goto DoneWithColl;
}
}
DoneWithColl:;
if ((m_psc->m_brd & SPR_brdALL))
{
BRD brd = 0;
if (m_psc->m_brd & SPR_brdIN)
{
if (x < 0) brd= SPR_brdINLEFT;
if (y < 0) brd |= SPR_brdINTOP;
if (x+cx > m_pdisp->m_cx) brd |= SPR_brdINRIGHT;
if (y+cy > m_pdisp->m_cy) brd |= SPR_brdINBOTTOM;
}
if (m_psc->m_brd & SPR_brdOUT)
{
if (x+cx < 0) brd = SPR_brdOUTLEFT;
if (y+cy < 0) brd |= SPR_brdOUTTOP;
if (x > m_pdisp->m_cx) brd |= SPR_brdOUTRIGHT;
if (y > m_pdisp->m_cy) brd |= SPR_brdOUTBOTTOM;
}
if (brd)
{
m_fIgnoreMove = FALSE;
m_psc->FireBorder(this, brd);

if (m_fFree)
return TRUE;
if (m_fIgnoreMove)
return FALSE;
}
}
}

// Don't draw if not visible
if (m_fHidden)
return TRUE;

SelectObject(g_hdcMem, m_pdisp->m_hbmp);
BitBlt(hdc, m_x, m_y, cx, cy, g_hdcMem, m_pimg->x, m_pimg->y, SRCCOPY);
m_fNeedRefresh = FALSE;
for (psprT=m_psprPrev; psprT; psprT=psprT->m_psprPrev)
{
if (!psprT->m_fNeedRefresh &&
x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x &&
y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
{
psprT->m_fNeedRefresh = TRUE;
}
}
SelectObject(g_hdcMem, g_hbmpStock);
return TRUE;
}


//---------------------------------------------------------------------------
// Erase the given sprite
//---------------------------------------------------------------------------
void CSprite::_Erase
(
void
)
{
int x = m_x;
int y = m_y;
int cx = m_pimg->cx;
int cy = m_pimg->cy;
HDC hdc = m_pdisp->m_hdc;
CSprite *psprT;

SelectObject(hdc, (HGDIOBJ)(m_pdisp->m_hbrushBack ? m_pdisp->m_hbrushBack : g_hbrushStock));
PatBlt(hdc, m_x, m_y, cx, cy, PATCOPY);
for (psprT=m_pdisp->m_psprLast; psprT; psprT=psprT->m_psprPrev)
{
if (psprT != this && !psprT->m_fNeedRefresh &&
x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x &&
y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
psprT->m_fNeedRefresh = TRUE;
}
SelectObject(g_hdcMem, g_hbrushStock);
}


//---------------------------------------------------------------------------
// Move the given sprite to new location
//---------------------------------------------------------------------------
BOOL CSprite::MoveToFull
(
longxFull,
longyFull,
BOOLfCollide
)
{
long xOldFull = m_xFull;
long yOldFull = m_yFull;

this->_Erase();
m_xFull = xFull;
m_yFull = yFull;
if (m_pimg->iimgNext == iimgREMOVE)
this->Remove();
else if (m_pimg->iimgNext != iimgNONE)
{
m_iimg = m_pimg->iimgNext;
m_pimg = m_pimg->pimgNext;
}
if (!this->_FDraw(fCollide))
{
m_xFull = xOldFull;
m_yFull = yOldFull;
this->_FDraw(FALSE);
return FALSE;
}
return TRUE;
}


//---------------------------------------------------------------------------
// Move the given sprite to new location
//---------------------------------------------------------------------------
BOOL CSprite::MoveByFull
(
longdxFull,
longdyFull,
BOOLfCollide
)
{
long xOldFull = m_xFull;
long yOldFull = m_yFull;

if (HIWORD(xOldFull + dxFull) == HIWORD(xOldFull) &&
HIWORD(yOldFull + dyFull) == HIWORD(yOldFull))
{
m_xFull += dxFull;
m_yFull += dyFull;
return TRUE;
}

this->_Erase();
m_xFull += dxFull;
m_yFull += dyFull;
if (m_pimg->iimgNext == iimgREMOVE)
this->Remove();
else if (m_pimg->iimgNext != iimgNONE)
{
m_iimg = m_pimg->iimgNext;
m_pimg = m_pimg->pimgNext;
}

if (!this->_FDraw(fCollide))
{
m_xFull = xOldFull;
m_yFull = yOldFull;
this->_FDraw(FALSE);
return FALSE;
}
return TRUE;
}


//---------------------------------------------------------------------------
// flags: 1=X, 2=Y;
// 4=Pos, 8=Vel, 16=Accel;
// 32=Less With Distance (default=More);
// 128=Remember
//---------------------------------------------------------------------------
BOOL CSprite::DoMoveAt
(
CSprite *psprAt,
float scale,
float dMax,
long flags,
BOOL fCollide
)
{
long dx = psprAt->m_xFull - m_xFull;
long dy = psprAt->m_yFull - m_yFull;

if (flags & MAS_RECIPDIST)
{
dx = FULLOFFLOAT((FLOAT)1.0 / FLOATOFFULL(dx | 0xffff));
dy = FULLOFFLOAT((FLOAT)1.0 / FLOATOFFULL(dy | 0xffff));
}

float d = FLOATOFFULL(dx) * scale;
if (d > dMax)
d = dMax;
else if (d < -dMax)
d = -dMax;
dx = FULLOFFLOAT(d);
d = FLOATOFFULL(dy) * scale;
if (d > dMax)
d = dMax;
else if (d < -dMax)
d = -dMax;
dy = FULLOFFLOAT(d);

if (flags & MAS_X)
{
if (flags & MAS_VEL)
{
m_vxFull += dx;
if (m_vxFull > m_psc->m_vFullMax)
m_vxFull = m_psc->m_vFullMax;
else if (m_vxFull < -m_psc->m_vFullMax)
m_vxFull = -m_psc->m_vFullMax;
}
if (flags & MAS_ACCEL)
{
m_axFull += dx;
if (m_axFull > m_psc->m_aFullMax)
m_axFull = m_psc->m_aFullMax;
else if (m_axFull < -m_psc->m_aFullMax)
m_axFull = -m_psc->m_aFullMax;
}
if (!(flags & MAS_POS))
dx = 0;
}
else
dx = 0;

if (flags & MAS_Y)
{
if (flags & MAS_VEL)
{
m_vyFull += dy;
if (m_vyFull > m_psc->m_vFullMax)
m_vyFull = m_psc->m_vFullMax;
else if (m_vyFull < -m_psc->m_vFullMax)
m_vyFull = -m_psc->m_vFullMax;
}
if (flags & MAS_ACCEL)
{
m_ayFull += dy;
if (m_ayFull > m_psc->m_aFullMax)
m_ayFull = m_psc->m_aFullMax;
else if (m_ayFull < -m_psc->m_aFullMax)
m_ayFull = -m_psc->m_aFullMax;
}
if (!(flags & MAS_POS))
dy = 0;
}
else
dy = 0;

if (dx || dy)
return this->MoveByFull(dx, dy, fCollide);

return TRUE;
}


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

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::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_ISprite)
*ppvObjOut = (ISprite *)this;

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

return E_NOINTERFACE;
}


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


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSprite::Release
(
void
)
{
ASSERT(m_cref, "bad m_cref");
m_cref--;
if (!m_cref)
{
delete this;
return 0;
}
return m_cref;
}


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

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CSprite::GetTypeLibInfo
(
HINSTANCE *phinstOut,
const GUID **pplibidOut,
SHORT *pwMajLib,
SHORT *pwMinLib,
const CLSID **ppclsidOut,
const IID **ppiidOut,
ITypeLib ***ppptlOut
)
{
*phinstOut = g_hinst;
*pplibidOut = &LIBID_SPRUUIDS;
*pwMajLib = 1;
*pwMinLib = 0;
*ppclsidOut = &CLSID_Sprite;
*ppiidOut = &IID_ISprite;
*ppptlOut = &g_ptlMain;
return S_OK;
}

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