Game Programming – Part 02

 
  typedef TScene inherited;
 
 
 
private:
 
  Classes::TList* FSpriteList;
 
 
 
protected:
 
  __fastcall virtual ~TSpriteScene(void);
 
  virtual void __fastcall SetupSurfaces(System::TObject* Sender);
 
 
 
public:
 
  __fastcall virtual TSpriteScene(Classes::TComponent* AOwner);
 
  virtual void __fastcall DestroyObjects(void);
 
  void __fastcall AddSprite(TSprite* Sprite);
 
  virtual void __fastcall DrawScene(void);
 
  virtual long __fastcall RestoreSurfaces(void);
 
  __property Classes::TList* SpriteList = {read=FSpriteList, write=FSpriteList,nodefault};
 
 
 
__published:
 
  __property TransparentColor;
 
};
 
 
 
////////////////////////////////////////
 
// TSceneEditor ////////////////////////
 
////////////////////////////////////////
 
class TSceneEditor: public TComponentEditor
 
{
 
protected:
 
  virtual __fastcall void Edit(void);
 
public:
 
  virtual __fastcall TSceneEditor(TComponent *AOwner, TFormDesigner *Designer)
 
    : TComponentEditor(AOwner, Designer) {}
 
};
 
 
 
 
 
//-- var, const, procedure --------------------------------------------------
 
#define Timer1 (Byte)(1)
 
extern void __fastcall Register(void);
 
 
 
}/* namespace Mercury1 */
 
#if !defined(NO_IMPLICIT_NAMESPACE_USE)
 
using namespace Mercury2;
 
#endif
 
//-- end unit ----------------------------------------------------------------

#endif // Mercury2

Listing 28.14. The main source file for the Mercury2 module.

 
 
///////////////////////////////////////
 
// Mercury2.cpp
 
// DirectX Graphics
 
// Copyright (c) 1997 by Charlie Calvert
 
// Thanks to John Thomas, Stuart Fullmar and Jeff Cottingham
 
///////////////////////////////////////
 
#include <vcl\vcl.h>
 
#pragma hdrstop
 
#include "Mercury2.h"
 
#include "errors1.h"
 
#include "SceneEditor1.h"
 
#pragma link "Errors1.obj"
 
#pragma link "SceneEditor1.obj"
 
 
 
/*{ ------------------------ }
 
  { --- THermes ------------ }
 
  { ------------------------ } */
 
 
 
THermes *AHermes;
 
 
 
void Timer2Timer(HWND H, UINT Msg, UINT Event, DWORD Time)
 
{
 
  if (AHermes->Active)
 
    AHermes->Flip();
 
}
 
 
 
namespace Mercury2
 
{
 
  // Slightly modified version of code from DDUtils.cpp
 
  HRESULT DDCopyBitmap(IDirectDrawSurface *pdds, 
 
    HBITMAP hbm, int x, int y, int dx, int dy)
 
  {
 
    HDC                 hdcImage;
 
    HDC                 hdc;
 
    BITMAP              bm;
 
    DDSURFACEDESC      ddsd;
 
    HRESULT             hr;
 
 
 
    if (hbm == NULL || pdds == NULL)
 
        return E_FAIL;
 
 
 
    //
 
    // make sure this surface is restored.
 
    //
 
    pdds->Restore();
 
 
 
    //
 
    //  select bitmap into a memoryDC so we can use it.
 
    //
 
    hdcImage = CreateCompatibleDC(NULL);
 
    if (!hdcImage)
 
        OutputDebugString("createcompatible dc failed\n");
 
    SelectObject(hdcImage, hbm);
 
 
 
    //
 
    // get size of the bitmap
 
    //
 
    GetObject(hbm, sizeof(bm), &bm);    // get size of bitmap
 
    dx = dx == 0 ? bm.bmWidth  : dx;    // use the passed size, unless zero
 
    dy = dy == 0 ? bm.bmHeight : dy;
 
 
 
    //
 
    // get size of surface.
 
    //
 
    ddsd.dwSize = sizeof(ddsd);
 
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
 
    pdds->GetSurfaceDesc(&ddsd);
 
 
 
    if ((hr = pdds->GetDC(&hdc)) == DD_OK)
 
    {
 
        StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y, dx, dy, SRCCOPY);
 
        pdds->ReleaseDC(hdc);
 
    }
 
 
 
    DeleteDC(hdcImage);
 
 
 
    return hr;
 
  }
 
 
 
  // Slightly modified version of code from DDUtils.cpp
 
  void DDReloadBitmapLib(HANDLE Lib, IDirectDrawSurface *Surface,
 
    const AnsiString BitmapName)
 
  {
 
    HBITMAP Bitmap;
 
 
 
    if (Lib)
 
      Bitmap = LoadImage(Lib, BitmapName.c_str(),
 
        IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
 
    else
 
      Bitmap = LoadImage(GetModuleHandle(NULL), BitmapName.c_str(),
 
        IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
 
 
 
    if (!Bitmap)
 
      Bitmap = LoadImage(0, BitmapName.c_str(), IMAGE_BITMAP,
 
        0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
 
 
 
    if (!Bitmap)
 
      throw Exception("Unable to load bitmap %s",
 
        OPENARRAY(TVarRec, (BitmapName)));
 
 
 
    DDCopyBitmap(Surface, Bitmap, 0, 0, 0, 0);
 
    DeleteObject(Bitmap);
 
  }
 
 
 
 
 
  // Slightly modified version of code from DDUtils.cpp
 
  IDirectDrawSurface *DDCreateSurface(IDirectDraw *DD, DWORD Width, DWORD Height,
 
        bool SysMem, bool Trans, DWORD dwColorKey)
 
  {
 
    DDSURFACEDESC SurfaceDesc;
 
    HRESULT hr;
 
    DDCOLORKEY ColorKey;
 
    IDirectDrawSurface *Surface;
 
 
 
    // fill in surface desc
 
    memset(&SurfaceDesc, 0, sizeof(DDSURFACEDESC));
 
    SurfaceDesc.dwSize = sizeof(SurfaceDesc);
 
    SurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
 
 
 
    SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
 
 
 
    SurfaceDesc.dwHeight = Height;
 
    SurfaceDesc.dwWidth = Width;
 
 
 
    hr = DD->CreateSurface(&SurfaceDesc, &Surface, NULL);
 
 
 
    // set the color key for this bitmap
 
    if (hr == DD_OK)
 
    {
 
      if (Trans)
 
      {
 
        ColorKey.dwColorSpaceLowValue = dwColorKey;
 
        ColorKey.dwColorSpaceHighValue = dwColorKey;
 
        Surface->SetColorKey(DDCKEY_SRCBLT, &ColorKey);
 
      }
 
    }
 
    else
 
      throw EDDError("CreateSurface Failed in DDCreateSurface");
 
 
 
    return Surface;
 
  } // DDCreateSurface
 
 
 
  IDirectDrawClipper *CreateClipper(IDirectDraw *DD, HWND Handle)
 
  {
 
    HRESULT hr;
 
    IDirectDrawClipper *lpClipper;
 
 
 
    hr = DD->CreateClipper(0, &lpClipper, NULL);
 
 
 
    if (hr != DD_OK)
 
    {
 
      throw EDDError("No Clipper");
 
    }
 
 
 
    hr = lpClipper->SetHWnd(0, Handle);
 
 
 
    if (hr != DD_OK)
 
    {
 
      throw EDDError("Can''t set clipper window handle");
 
    }
 
 
 
    return lpClipper;
 
  }
 
}
 
 
 
 
 
 
 
__fastcall THermes::THermes(TComponent* AOwner)
 
:TComponent(AOwner)
 
{
 
  FHandle = ((TWinControl *)(AOwner))->Handle;
 
  FFirstTime = true;
 
}// THermes
 
 
 
 
 
void __fastcall THermes::EndExclusive()
 
{
 
  FDirectDraw->FlipToGDISurface();
 
  if (FExclusive)
 
    FDirectDraw->SetCooperativeLevel(FHandle, DDSCL_NORMAL);
 
}// EndExclusive
 
 
 
void __fastcall THermes::ErrorEvent(String S)
 
{
 
  FActive = false;
 
  EndExclusive();
 
  throw EDDError(S);
 
}// ErrorEvent
 
 
 
void __fastcall THermes::Run()
 
{
 
  InitObjects();
 
  Flip();
 
}// Run
 
 
 
void __fastcall THermes::DDTest(long hr,  System::AnsiString S)
 
{
 
  if (!Windows::Succeeded(hr))
 
    throw EDDError("DDTest Error: %s $%x %s",
 
      OPENARRAY(TVarRec, (S, int(hr), AnsiString(GetOleError(hr)))));
 
 
 
}// DDTest
 
 
 
/////////////////////////////////////////////////
 
// Create the primary surface
 
/////////////////////////////////////////////////
 
bool __fastcall THermes::CreatePrimary(void)
 
{
 
  DDSURFACEDESC SurfaceDesc;
 
  HResult hr;
 
  bool Result = true;
 
 
 
  memset(&SurfaceDesc, 0, sizeof(SurfaceDesc));
 
  SurfaceDesc.dwSize = sizeof(SurfaceDesc);
 
 
 
  if (!FExclusive){
 
    SurfaceDesc.dwFlags = DDSD_CAPS;
 
    SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
 
  }
 
  else{
 
    SurfaceDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
 
    SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
 
                                  DDSCAPS_FLIP |
 
                                  DDSCAPS_COMPLEX;
 
    SurfaceDesc.dwBackBufferCount = 1;
 
  };
 
 
 
  hr = FDirectDraw->CreateSurface(&SurfaceDesc, &FPrimarySurface, NULL);
 
  if (hr != DD_OK)
 
    throw EDDError("THermes.CreatePrimary: %d %s",
 
      OPENARRAY(TVarRec, (int(hr), GetOleError(hr))));
 
  else
 
    return Result;
 
}// CreatePrimary
 
 
 
void __fastcall THermes::SetScene(TScene *Scene)
 
{
 
  if (Scene)
 
    Scene->DestroyObjects();
 
 
 
  FScene = Scene;
 
}// SetScene
 
 
 
bool __fastcall THermes::SetUpBack(void)
 
{
 
  bool Result = false;
 
  HResult hr;
 
  DDSCAPS DDSCaps;
 
  if (!FExclusive)
 
  {
 
    FBackSurface =
 
      Mercury2::DDCreateSurface(FDirectDraw, 640, 480, false, false, 0);
 
    if (FBackSurface == NULL)
 
      throw EDDError("Can''t set up back surface");
 
 
 
    FClipper = Mercury2::CreateClipper(FDirectDraw, FHandle);
 
    hr = FPrimarySurface->SetClipper(FClipper);
 
    if( hr != DD_OK )
 
      throw EDDError("Can''t attach clipper to front buffer");
 
  }
 
  else
 
  {
 
    memset(&DDSCaps, 0, sizeof(DDSCaps));
 
    DDSCaps.dwCaps = DDSCAPS_BACKBUFFER;
 
      hr = FPrimarySurface->GetAttachedSurface(&DDSCaps, &FBackSurface);
 
    if (hr != DD_OK)
 
      throw EDDError("TSpeedDraw.SetUpBack: %d %s",
 
        OPENARRAY(TVarRec, (int(hr), GetOleError(hr))));
 
    else
 
      Result = true;
 
  };
 
  return Result;
 
}// SetUpBack
 
 
 
void __fastcall  THermes::InitBaseObjects()
 
{
 
  DWORD Flags;
 
  HResult hr;
 
  DDTest(DirectDrawCreate(NULL, &FDirectDraw, NULL), "InitObjects1");
 
 
 
  if (!FExclusive)
 
    Flags = DDSCL_NORMAL;
 
  else
 
    Flags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
 
 
 
  DDTest(FDirectDraw->SetCooperativeLevel(FHandle, Flags),"SetCooperativeLevel");
 
  if (FExclusive)
 
  {
 
    hr = FDirectDraw->SetDisplayMode(640, 480, 8);
 
    if(hr != DD_OK)
 
      throw EDDError("TSpeedDraw.InitObjects: %d %s",
 
        OPENARRAY(TVarRec, (int(hr), GetOleError(hr))));
 
  };
 
 
 
  CreatePrimary();
 
  SetUpBack();
 
  if (FCreatureList != NULL) FCreatureList->ReadFiles();
 
}// InitBaseObjects
 
 
 
 
 
/* Here are the steps in the initialization:
 
    Create DirectDraw Object
 
    SetCooperativeLevel
 
    if Exclusive then SetDisplayMode
 
    CreatePrimary
 
    SetupBack
 
    Create the work surface
 
    Set Active to true */
 
void __stdcall THermes::InitObjects(void)
 
{
 
  AHermes = this;
 
 
 
  if (FFirstTime)
 
  {
 
    InitBaseObjects();
 
    FFirstTime = false;
 
  };
 
 
 
  if ((Scene) && (Scene->FBackgroundBitmap != ""))
 
    Scene->SetupSurfaces(this);
 
  if (FUseTimer)
 
    SetTimer(FHandle, Timer1, FTimerInterval, (FARPROC)Timer2Timer);
 
 
 
  FActive = true;
 
}// InitObjects
 
 
 
 
 
void __fastcall THermes::DrawBitmaps()
 
{
 
  if (Scene) Scene->DrawScene();
 
}
 
 
 
bool __fastcall THermes::MakeItSo(HResult DDResult)
 
{
 
  bool Result;
 
  
 
  switch(DDResult)
 
  {
 
    case DD_OK:
 
      Result = true;
 
      break;
 
    case DDERR_SURFACELOST:
 
      Result = (RestoreSurfaces() == DD_OK);
 
      break;
 
    default:
 
      Result = DDResult != DDERR_WASSTILLDRAWING;
 
      break;
 
  };
 
  return Result;
 
} // MakeItSo
 
 
 
long __fastcall THermes::RestoreSurfaces()
 
{
 
  HRESULT Result;
 
  Result = FPrimarySurface->Restore();
 
  if ((Result == DD_OK) && (Scene))
 
    Result = Scene->RestoreSurfaces();
 
  return Result;
 
}// RestoreSurfaces
 
 
 
void __fastcall THermes::Flip(void)
 
{
 
  RECT R1, R;
 
  FTimerOdd = !FTimerOdd;
 
  
 
  if (!FActive)
 
    return;
 
    
 
  if (!FExclusive)
 
  {
 
    try
 
    {
 
      DrawBitmaps();
 
      GetWindowRect(FHandle, &R);
 
      R1 = Rect(0, 0, 640, 480);
 
      DDTest(FPrimarySurface->Blt(&R, FBackSurface, &R1, 0, NULL), "Flip");
 
    }
 
    catch(Exception &E)
 
    {
 
      ErrorEvent("Flipping");
 
    };
 
  }
 
  else
 
  {
 
    try
 
    {
 
      if (FActive)
 
      {
 
        do
 
        {
 
           Application->ProcessMessages();
 
        } while(!MakeItSo(FPrimarySurface->Flip(NULL, DDFLIP_WAIT)));
 
        
 
        DrawBitmaps();
 
      }
 
    }
 
    catch(...)
 
    {
 
      ErrorEvent("Flipping");
 
    }
 
  }
 
}// Flip
 
 
 
 
 
///////////////////////////////////////
 
// TDraw //////////////////////////////
 
///////////////////////////////////////
 
 
 
__fastcall TDraw::TDraw(Classes::TComponent* AOwner)
 
  : TComponent(AOwner)
 
{
 
}
 
 
 
__fastcall TDraw::~TDraw(void)
 
{
 
  if (FLib != NULL)
 
    FreeLibrary(FLib);
 
}
 
 
 
bool __fastcall TDraw::CreateDDSurface(IDirectDrawSurface* &DDS,
 
  System::AnsiString BitmapName, bool UsePalette)
 
{
 
  bool Result;
 
  DDCOLORKEY ColorKey;
 
  HRESULT hr;
 
  HANDLE Dib;
 
 
 
  if (UsePalette)
 
  {
 
    FPalette = LoadPalette(FLib, BitmapName.c_str());
 
    if (!FPalette)
 
      throw EDDError("LoadPalette Failed");
 
 
 
    FHermes->FPrimarySurface->SetPalette(FPalette);
 
  }
 
 
 
  Dib = GetDib(FLib, BitmapName);
 
  DDS = CreateSurface(Dib);
 
 
 
  if (!DDS)
 
    throw EDDError("CreateSurface Failed");
 
 
 
  ColorKey.dwColorSpaceLowValue = FTransparentColor;
 
  ColorKey.dwColorSpaceHighValue = FTransparentColor;
 
  hr = DDS->SetColorKey(DDCKEY_SRCBLT, &ColorKey);
 
  if (hr != DD_OK)
 
    throw EDDError("TSpeedDraw.CreateDDSurface: %d %s",
 
      OPENARRAY(TVarRec, (int(hr), GetOleError(hr))));
 
  else
 
    Result = True;
 
    
 
  return Result;
 
}
 
 
 
IDirectDrawSurface* __fastcall TDraw::CreateSurface(HANDLE Bitmap)
 
{
 
  DDSURFACEDESC SurfaceDesc;
 
  Windows::TBitmap BM;
 
  IDirectDrawSurface *Result;
 
 
 
  if (Bitmap == 0)
 
    throw Exception("No Bitmap in CreateSurface");
 
 
 
  try
 
  {
 
    GetObject(Bitmap, sizeof(BM), &BM);
 
 
 
    memset(&SurfaceDesc, 0, sizeof(SurfaceDesc));
 
    SurfaceDesc.dwSize = sizeof(SurfaceDesc);
 
    SurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
 
    SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
 
    SurfaceDesc.dwWidth = BM.bmWidth;
 
    SurfaceDesc.dwHeight = BM.bmHeight;
 
 
 
    if (FHermes->FDirectDraw->CreateSurface(&SurfaceDesc, &Result, NULL) != DD_OK)
 
      throw Exception("CreateSurface failed");
 
 
 
    DDCopyBitmap(Result, Bitmap, 0, 0, 0, 0);
 
  }
 
  catch(Exception &E)
 
  {
 
    FHermes->ErrorEvent("TSpeedDraw.CreateSurface: " + E.Message);
 
  }
 
 
 
  return Result;
 
}
 
 
 
HANDLE __fastcall TDraw::GetDib(HANDLE Instance,  System::AnsiString S)
 
{
 
  HANDLE Result;
 
  UINT Flags;
 
  
 
  if (Instance != 0)
 
    Flags = LR_CREATEDIBSECTION;
 
  else
 
    Flags = LR_LOADFROMFILE | LR_CREATEDIBSECTION;
 
 
 
  Result = LoadImage((HANDLE)Instance, S.c_str(), IMAGE_BITMAP,
 
              0, 0, Flags);
 
  if (Result == 0)
 
    FHermes->ErrorEvent("TSpeedDraw.GetDib: Could not load bitmap");
 
 
 
  return Result;
 
}
 
 
 
IDirectDrawPalette* __fastcall TDraw::LoadPalette(HANDLE Instance,
 
    const System::AnsiString BitmapName)
 
{
 
    IDirectDrawPalette* ddpal;
 
    int                 i;
 
    int                 n;
 
    int                 fh;
 
    HRSRC               h;
 
    LPBITMAPINFO        BitmapInfo;
 
    PALETTEENTRY        ape[256];
 
    RGBQUAD *           RGBQuad;
 
 
 
    //
 
    // build a 332 palette as the default.
 
    //
 
    for (i=0; i<256; i++)
 
    {
 
        ape[i].peRed   = (BYTE)(((i >> 5) & 0x07) * 255 / 7);
 
        ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7);
 
        ape[i].peBlue  = (BYTE)(((i >> 0) & 0x03) * 255 / 3);
 
        ape[i].peFlags = (BYTE)0;
 
    }
 
 
 
    if (BitmapName == "")
 
      FHermes->ErrorEvent("No bitmapname in LoadPalette");
 
 
 
    //
 
    // get a pointer to the bitmap resource.
 
    //
 
    if (Instance)
 
    {
 
      h = FindResource(Instance, BitmapName.c_str(), RT_BITMAP);
 
      if (h)
 
      {
 
        BitmapInfo = (LPBITMAPINFO)LockResource(LoadResource(Instance, h));
 
        if (!BitmapInfo)
 
          throw EDDError("No LockResouce: " + this->ClassName());
 
 
 
        RGBQuad = (RGBQUAD*)BitmapInfo->bmiColors;
 
 
 
        if (BitmapInfo == NULL || BitmapInfo->bmiHeader.biSize <sizeof(BITMAPINFOHEADER))
 
            n = 0;
 
        else if (BitmapInfo->bmiHeader.biBitCount > 8)
 
            n = 0;
 
        else if (BitmapInfo->bmiHeader.biClrUsed == 0)
 
            n = 1 << BitmapInfo->bmiHeader.biBitCount;
 
        else
 
            n = BitmapInfo->bmiHeader.biClrUsed;
 
 
 
        //
 
        //  a DIB color table has its colors stored BGR not RGB
 
        //  so flip them around.
 
        //
 
        for(i=0; i<n; i++ )
 
        {
 
          ape[i].peRed   = RGBQuad[i].rgbRed;
 
          ape[i].peGreen = RGBQuad[i].rgbGreen;
 
          ape[i].peBlue  = RGBQuad[i].rgbBlue;
 
          ape[i].peFlags = 0;
 
        }
 
      }
 
    }
 
    else
 
    {
 
      fh = _lopen(BitmapName.c_str(), OF_READ);
 
      if (fh != -1)
 
      {
 
        BITMAPFILEHEADER bf;
 
        BITMAPINFOHEADER bi;
 
 
 
       _lread(fh, &bf, sizeof(bf));
 
       _lread(fh, &bi, sizeof(bi));
 
       _lread(fh, ape, sizeof(ape));
 
       _lclose(fh);
 
 
 
        if (bi.biSize != sizeof(BITMAPINFOHEADER))
 
          n = 0;
 
        else if (bi.biBitCount > 8)
 
          n = 0;
 
        else if (bi.biClrUsed == 0)
 
          n = 1 << bi.biBitCount;
 
        else
 
          n = bi.biClrUsed;
 
 
 
        //
 
        //  a DIB color table has its colors stored BGR not RGB
 
        //  so flip them around.
 
        //
 
        for(i=0; i<n; i++ )
 
        {
 
            BYTE r = ape[i].peRed;
 
            ape[i].peRed  = ape[i].peBlue;
 
            ape[i].peBlue = r;
 
        }
 
      }
 
    }
 
 
 
    FHermes->FDirectDraw->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
 
 
 
    return ddpal;
 
}
 
 
 
void __fastcall TDraw::WriteXY(int X, int Y,  System::AnsiString S)
 
{
 
  HDC DC;
 
 
 
  FHermes->BackSurface->GetDC(&DC);
 
  SetBkMode(DC, TRANSPARENT);
 
  TextOut(DC, X, Y, S.c_str(), S.Length());
 
  FHermes->BackSurface->ReleaseDC(DC);
 
}
 
 
 
 
 
// ------------------------ 
 
// -- TScene --------------
 
// ------------------------
 
 
 
__fastcall TScene::TScene(TComponent *AOwner) : TDraw(AOwner)
 
{
 
  ShowBitmap = true;
 
}
 
 
 
void __fastcall TScene::DestroyObjects(void)
 
{
 
  if(FWorkSurface != NULL)
 
    FWorkSurface->Release();
 
 
 
  FWorkSurface = NULL;
 
}
 
 
 
long _fastcall TScene::RestoreSurfaces(void)
 
{
 
  long Result;
 
  Result = FWorkSurface->Restore();
 
  if(Result == DD_OK)
 
    DDReloadBitmapLib(FLib, FWorkSurface, FBackgroundBitmap);
 
 
 
  return(Result);
 
}
 
 
 
void __fastcall TScene::SetupSurfaces(System::TObject *Sender)
 
{
 
  AnsiString ErrStr = "TSpeedDraw.SetupWorkSurface: No Surface Desc %d %s";
 
  DDSURFACEDESC SurfaceDesc;
 
  HRESULT hr;
 
 
 
  FHermes = dynamic_cast <THermes *>(Sender);
 
  if(FDLLName != "")
 
  {
 
    if(!FLib)
 
      FLib = LoadLibrary(FDLLName.c_str());
 
    if(int(FLib) < 32)
 
    throw EDDError("No Library");
 
  }
 
 
 
  if(!CreateDDSurface(FWorkSurface, FBackgroundBitmap, True))
 
    throw EDDError("TSpeedDraw.SetupWorkSurface: No WorkSurface: " +
 
      FBackgroundBitmap);
 
     else
 
  {
 
    SurfaceDesc.dwSize = sizeof(SurfaceDesc);
 
    hr = FWorkSurface->GetSurfaceDesc(&SurfaceDesc);
 
    if(hr != DD_OK)
 
      throw EDDError(ErrStr, OPENARRAY(TVarRec, (int(hr), GetOleError(hr))));
 
    FBackRect = Rect(0, 0, SurfaceDesc.dwWidth, SurfaceDesc.dwHeight);
 
  }
 
 
 
  if(FOnSetupSurfaces)
 
  FOnSetupSurfaces(this);
 
}
 
 
 
void _fastcall TScene::DrawScene()
 
{
 
  AnsiString ErrStr = "TSpeedDraw.BackGroundBlits: $%x \r %s \r %s";
 
  HRESULT hr;
 
  HDC DC;
 
  HBRUSH OldBrush, Brush;
 
 
 
  if (FBlankScene)
 
  {
 
    FHermes->BackSurface->GetDC(&DC); // Don't step through!!
 
    Brush = CreateSolidBrush(FBackColor);
 
    OldBrush = SelectObject(DC, Brush);
 
    Rectangle(DC, 0, 0, 640, 480);
 
    SelectObject(DC, OldBrush);
 
    DeleteObject(Brush);
 
    FHermes->BackSurface->ReleaseDC(DC);
 
  }
 
  if (FShowBitmap)
 
  {
 
    hr = AHermes->BackSurface->BltFast(FBackOrigin.x, FBackOrigin.y,
 
          FWorkSurface, &FBackRect,
 
          DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY);
 
    if (!Windows::Succeeded(hr))
 
      throw EDDError(ErrStr, OPENARRAY(TVarRec,
 
        (int(hr), GetOleError(hr), "Check BackRect, BackOrigin???")));
 
  }
 
  if (FOnDrawScene)
 
    FOnDrawScene(this);
 
}
 
 
 
 
 
///////////////////////////////////////
 
// THermesTiler ///////////////////////
 
///////////////////////////////////////
 
__fastcall THermesTiler::THermesTiler(Classes::TComponent* AOwner)
 
 :TScene(AOwner)
 
{
 
  FBitmapWidth = 32;
 
  FBitmapHeight = 32;
 
  FMaxMapRows = 12;
 
  FMaxMapCols = 20;
 
}
 
 
 
__fastcall THermesTiler::~THermesTiler(void)
 
{
 
  DestroyObjects();
 
}
 
 
 
void __fastcall THermesTiler::DrawScene(void)
 
{
 
  TScene::DrawScene();
 
 
 
  int i, j, k;
 
  TSpecialRect SpR;
 
  
 
  if (FTileSurface == NULL)
 
    return;
 
    
 
  k = 1;
 
  for (j = 1; j <= FMaxMapRows; j++)
 
    for (i = 1; i <= FMaxMapCols; i++)
 
    {
 
      SpR = GetRect(i + FHermes->CreatureList->MapCol, j + FHermes->CreatureList->MapRow);
 
      FHermes->BackSurface->BltFast(FBitmapWidth * (k - 1),
 
        FBitmapHeight * (j - 1), FTileSurface, &SpR.R1, DDBLTFAST_SRCCOLORKEY);
 
      if (SpR.IsCreature) // There's a character or building here
 
        FHermes->BackSurface->BltFast(FBitmapWidth * (k - 1),
 
          FBitmapHeight * (j - 1), FTileSurface, &SpR.R2, DDBLTFAST_SRCCOLORKEY);
 
 
 
      if ((i % 20) == 0)
 
        k = 1;
 
      else
 
        k++;
 
    }
 
}
 
 
 
void __fastcall THermesTiler::SetupSurfaces(System::TObject* Sender)
 
{
 
  TScene::SetupSurfaces(Sender);
 
  if (FTileMap != "")
 
    if (!CreateDDSurface(FTileSurface, FTileMap, True))
 
      throw Exception("THermesTiler.InitObjects");
 
}
 
 
 
void __fastcall THermesTiler::DestroyObjects(void)
 
{
 
  if (FTileMap != "")
 
    if (FTileSurface != NULL)
 
      FTileSurface->Release();
 
  FTileSurface = NULL;
 
}
 
 
 
Windows::TRect __fastcall THermesTiler::MapTypeToTileRect(int MapType)
 
{
 
  int X, Y = 0;
 
 
 
  if ((MapType > 19) && (MapType < 41))
 
  {
 
    Y = FBitmapHeight;
 
    X = (MapType - 21) * FBitmapWidth;
 
  }
 
  else if (MapType > 39)
 
  {
 
    Y = 64;
 
    X = (MapType - 41) * FBitmapWidth;
 
  }
 
  else
 
  {
 
    X = MapType * FBitmapWidth;
 
  }
 
 
 
  return Rect(X, Y, X + FBitmapWidth, FBitmapHeight + Y);
 
}
 
 
 
long __fastcall THermesTiler::RestoreSurfaces(void)
 
{
 
  long Result = TScene::RestoreSurfaces();
 
  if (Result == DD_OK)
 
  {
 
    Result = FTileSurface->Restore();
 
    if (Result == DD_OK)
 
      if (FTileMap != "")
 
        DDReloadBitmapLib(FLib, FTileSurface, FTileMap);
 
  }
 
  return Result;
 
}
 
 
 
///////////////////////////////////////
 
// THermesChart ///////////////////////
 
///////////////////////////////////////
 
 
 
bool __fastcall THermesChart::CheckHeroPos(Creatures1::TCreatureList* HeroList,
 
                                           int Col, int Row)
 
{
 
  int NewPosType;
 
  TPoint NewPos;
 
  bool MoveOk = True;
 
 
 
  if (OnHeroMove != NULL)
 
  {
 
    NewPos.x = FHero->TrueCol + Col;
 
    NewPos.y = FHero->TrueRow + Row;
 
    NewPosType = HeroList->GetMapType(NewPos.x, NewPos.y);
 
    OnHeroMove(this, NewPos, NewPosType, MoveOk);
 
  }
 
  return MoveOk;
 
}
 
 
 
Windows::TRect __fastcall THermesChart::MapTypeToCreature(int Col, int Row)
 
{
 
  TCreature *Creature = FHermes->CreatureList->CreatureFromLocation(Col, Row);
 
  
 
  if (Creature->TypeStr == "Hero")
 
    return MapTypeToTileRect(3);
 
  else
 
    return MapTypeToTileRect(StrToInt(Creature->ID));
 
}
 
 
 
TSpecialRect __fastcall THermesChart::GetRect(int Col, int Row)
 
{
 
  Byte MapType;
 
  TSpecialRect Result;
 
 
 
  MapType = FHermes->CreatureList->Map[Col][Row];
 
  if ((FHermes->FTimerOdd == True) && (MapType == 1 /*mtWater*/))
 
    MapType = 4; // mtWater2;
 
 
 
  if (BitOn(7, MapType))
 
  {
 
    Result.IsCreature = True;
 
    SetBit(7, 0, MapType);
 
    Result.R1 = MapTypeToTileRect(MapType);
 
    Result.R2 = MapTypeToCreature(Col, Row);
 
  }
 
  else
 
  {
 
    Result.IsCreature = False;
 
    Result.R1 = MapTypeToTileRect(MapType);
 
  }
 
  return Result;
 
}
 
 
 
bool __fastcall THermesChart::MoveGrid(int Col, int Row, bool CallFlip)
 
{
 
  TPoint P;
 
 
 
  if (CheckHeroPos(FHermes->CreatureList, Col, Row))
 
  {
 
    P.x = FHermes->CreatureList->MapCol + Col;
 
    P.y = FHermes->CreatureList->MapRow + Row;
 
    if ((P.x < 0) ||
 
        (P.x > (FHermes->CreatureList->MaxCols - FMaxMapCols)) ||
 
        (P.y < 0) ||
 
        (P.y > (FHermes->CreatureList->MaxRows - FMaxMapRows)))
 
      return False;
 
    FHermes->CreatureList->MapCol = P.x;
 
    FHermes->CreatureList->MapRow = P.y;
 
    FHermes->CreatureList->MoveCreature(FHero, Col, Row);
 
    FHermes->Flip();
 
    return True;
 
  }
 
  return False;
 
}
 
 
 
__fastcall THermesChart::~THermesChart(void)
 
{
 
}
 
 
 
void __fastcall THermesChart::MoveHero(int NewCol, int NewRow)
 
{
 
  TPoint P;
 
 
 
  if (!CheckHeroPos(FHermes->CreatureList, NewCol, NewRow))
 
    return;
 
 
 
  P.x = FHero->ScreenCol + NewCol;
 
  P.y = FHero->ScreenRow + NewRow;
 
  if ((P.x <= 0) || (P.x > (FMaxMapCols)) ||
 
     (P.y <= 1) || (P.y > FMaxMapRows))
 
    return;
 
 
 
  FHero->ScreenCol = P.x;
 
  FHero->ScreenRow = P.y;
 
 
 
  FHermes->CreatureList->MoveCreature(FHero, NewCol, NewRow);
 
  FHermes->Flip();
 
}
 
 
 
void __fastcall THermesChart::SetupSurfaces(System::TObject* Sender)
 
{
 
  THermesTiler::SetupSurfaces(Sender);
 
 
 
  if (FHermes->CreatureList == NULL)
 
    throw EDDError("CreatureList cannot be blank.");
 
  FHeroActive = True;
 
  FHero = FHermes->CreatureList->CreatureFromName("Hero");
 
}
 
 
 
void __fastcall THermesChart::Move(int Value)
 
{
 
  if (Value == VK_INSERT)
 
    HeroActive = !HeroActive;
 
  else if (HeroActive)
 
    switch (Value)
 
    {
 
      case VK_RIGHT: MoveHero(1, 0); break;
 
      case VK_LEFT: MoveHero(-1, 0); break;
 
      case VK_DOWN: MoveHero(0, 1); break;
 
      case VK_UP: MoveHero(0, -1); break;
 
    }
 
  else
 
    switch(Value)
 
    {
 
      case VK_RIGHT: MoveGrid(1, 0, False); break;
 
      case VK_LEFT: MoveGrid(-1, 0, False); break;
 
      case VK_DOWN: MoveGrid(0, 1, False); break;
 
      case VK_UP: MoveGrid(0, -1, False); break;
 
    }
 
}
 
 
 
/* ------------------------ */
 
/* -- TSprite ------------- */
 
/* ------------------------ */
 
 
 
bool __fastcall TSprite::IsHit(int X, int Y)
 
{
 
  X = X - XPos;
 
  Y = Y - YPos;
 
 
 
  if ((X >= 0) && (Y >= 0) && (X <= Rect.right) && (Y <= Rect.bottom))
 
    return true;
 
  else
 
    return false;
 
}
 
 
 
/* ------------------------ */
 
/* -- TSpriteScene -------- */
 
/* ------------------------ */
 
 
 
__fastcall TSpriteScene::TSpriteScene(TComponent *AOwner)
 
  : TScene(AOwner)
 
{
 
  FSpriteList = new TList();
 
}
 
 
 
__fastcall TSpriteScene::~TSpriteScene()
 
{
 
  DestroyObjects();
 
    delete FSpriteList;
 
}
 
 
 
void __fastcall TSpriteScene::DestroyObjects(void)
 
{
 
  if(FSpriteList != NULL)
 
  {
 
    for(int i = 0; i < FSpriteList->Count; i++)
 
      ((TSprite*)(FSpriteList->Items[i]))->Surface->Release();
 
 
 
    FSpriteList->Clear();
 
  }
 
}
 
 
 
void __fastcall TSpriteScene::AddSprite(TSprite *Sprite)
 
{
 
  SpriteList->Add(Sprite);
 
}
 
 
 
void __fastcall TSpriteScene::DrawScene(void)
 
{
 
  const char ErrStr[] = "TSpriteScene.DrawScene";
 
  HResult hr;
 
  TSprite *Sprite;
 
 
 
  TScene::DrawScene();
 
  for(int i = 0; i < FSpriteList->Count; i++)
 
  {
 
    Sprite = (TSprite *)FSpriteList->Items[i];
 
    hr = FHermes->BackSurface->BltFast(Sprite->XPos, Sprite->YPos,
 
      Sprite->Surface, &Sprite->FRect, DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY);
 
    if(!Windows::Succeeded(hr))
 
      throw(EDDError(ErrStr, OPENARRAY(TVarRec, (int(hr), GetOleError(hr),
 
        "Check BackRect, BackOrigin???"))));
 
  }
 
}
 
 
 
long __fastcall TSpriteScene::RestoreSurfaces(void)
 
{
 
  TSprite *Sprite;
 
  HRESULT Result;
 
 
 
  Result = TScene::RestoreSurfaces();
 
  if(Result == DD_OK)
 
  {
 
        for(int i = 0; i < FSpriteList->Count; i++)
 
    {
 
      Sprite = (TSprite *)FSpriteList->Items[i];
 
      Result = Sprite->Surface->Restore();
 
      if(Result == DD_OK)
 
        DDReloadBitmapLib(FLib, Sprite->Surface, Sprite->Bitmap);
 
      else
 
        break; // Exit on error
 
    }
 
  }
 
 
 
  return((long)Result);
 
}
 
 
 
void __fastcall TSpriteScene::SetupSurfaces(TObject *Sender)
 
{
 
  IDirectDrawSurface *Surface;
 
  TSprite *Sprite;
 
  DDSURFACEDESC SurfaceDesc;
 
  HRESULT hr;
 
 
 
  TScene::SetupSurfaces(Sender);
 
  if(SpriteList == NULL)
 
    return;
 
  for(int i = 0; i < SpriteList->Count; i++)
 
  {
 
    Sprite = (TSprite *)SpriteList->Items[i];
 
    if(!CreateDDSurface(Surface, Sprite->Bitmap, False))
 
      throw EDDError("Could not create surface");
 
    SurfaceDesc.dwSize = sizeof(SurfaceDesc);
 
    hr = Surface->GetSurfaceDesc(&SurfaceDesc);
 
    if(hr == DD_OK)
 
      Sprite->Rect = Rect(0, 0, SurfaceDesc.dwWidth, SurfaceDesc.dwHeight);
 
    else
 
      throw EDDError("No SurfaceDesc");
 
    Sprite->Surface = Surface;
 
  }
 
}
 
 
 
void __fastcall TSceneEditor::Edit(void)
 
{
 
  RunSceneEditorDlg((TScene *)Component);
 
}
 
 
 
namespace Mercury2
 
{
 
  void __fastcall Register()
 
  {
 
    TComponentClass classes[5] = {__classid(THermes), __classid(TScene),
 
      __classid(THermesChart), __classid(TSpriteScene), __classid(TSprite)};
 
    RegisterComponents("Unleash", classes, 4);
 
 
 
    RegisterComponentEditor(__classid(TScene), __classid(TSceneEditor));
 
  }
 

}

Summary

In this chapter, you learned how to create a simple graphics engine that gives you access to DirectX. In particular, you learned how to do the following:

  1. Get into and out of Exclusive mode

 

  1. Restore surfaces when the user switches away from a DirectX application

 

  1. Create backgrounds and sprites

 

  1. Work with a tiled world that expands far beyond the reaches of the screen

In the next chapter, I add a simple game engine to this graphics engine and then give a simple example of how to use these tools to create the elements of a strategy game.

 

<<Back (Part 01)  

 

VMS Desenvolvimentos

Diversas Dicas, Apostilas, Arquivos Fontes, Tutoriais, Vídeo Aula, Download de Arquivos Relacionado a Programação em C++ Builder.