root/week1oplossing/GameEngine.cpp
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers//-----------------------------------------------------------------
// Game Engine Object
// C++ Source - GameEngine.cpp - version 2008 v3_02
// Copyright Kevin Hoefman - kevin.hoefman@howest.be
// http://www.digitalartsandentertainment.be/
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "GameEngine.h"
#define _USE_MATH_DEFINES // necessary for including (among other values) PI - see math.h
#include <math.h> // used in various draw methods
#include <stdio.h>
#include <tchar.h> // used for unicode strings
#include <memory.h>
#include <vector> // using std::vector for tab control logic
//-----------------------------------------------------------------
// Static Variable Initialization
//-----------------------------------------------------------------
GameEngine* GameEngine::m_GameEnginePtr = NULL;
//-----------------------------------------------------------------
// Windows Functions
//-----------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route all Windows messages to the game engine
return GameEngine::GetSingleton()->HandleEvent(hWindow, msg, wParam, lParam);
}
DWORD WINAPI KeybThreadProc(GameEngine* gamePtr)
{
return gamePtr->KeybThreadProc();
}
//-----------------------------------------------------------------
// GameEngine Constructor(s)/Destructor
//-----------------------------------------------------------------
GameEngine::GameEngine() : m_hInstance(NULL),
m_hWindow(NULL),
m_TitlePtr(0),
m_iFrameDelay(50), // 20 FPS default
m_bSleep(true),
m_bRunGameLoop(false),
m_bKeybRunning(true), // create the keyboard monitoring thread
m_KeyListPtr(NULL),
m_KeybMonitor(0x0), // binary ; 0 = key not pressed, 1 = key pressed
m_isPainting(false),
m_isDoublebuffering(false),
m_colDraw(RGB(0,0,0)),
m_FontDraw(0),
m_GamePtr(0),
m_PaintDoublebuffered(false),
m_Fullscreen(false),
m_WindowRegionPtr(0)
{
m_hKeybThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ::KeybThreadProc, this, NULL, &m_dKeybThreadID);
}
GameEngine::~GameEngine()
{
// Clean up the keyboard monitoring thread
m_bKeybRunning = false;
WaitForSingleObject( m_hKeybThread, INFINITE );
CloseHandle( m_hKeybThread );
// clean up keyboard monitor buffer after the thread that uses it is closed
if (m_KeyListPtr != 0)
{
delete m_KeyListPtr;
m_KeyListPtr = 0;
}
// clean up the font
if (m_FontDraw != 0)
{
DeleteObject(m_FontDraw);
m_FontDraw = 0;
}
// clean up the AbstractGame object
delete m_GamePtr;
}
//-----------------------------------------------------------------
// Game Engine Static Methods
//-----------------------------------------------------------------
GameEngine* GameEngine::GetSingleton()
{
if ( m_GameEnginePtr == NULL) m_GameEnginePtr = new GameEngine();
return m_GameEnginePtr;
}
void GameEngine::SetGame(AbstractGame* gamePtr)
{
m_GamePtr = gamePtr;
}
DWORD GameEngine::KeybThreadProc()
{
while (m_bKeybRunning)
{
if (m_KeyListPtr != NULL)
{
int count = 0;
int key = m_KeyListPtr[0];
while (key != '\0' && count < (8 * sizeof(unsigned int)))
{
if ( !(GetAsyncKeyState(key)<0) ) // key is not pressed
{
if (m_KeybMonitor & (0x1 << count)) {
m_GamePtr->KeyPressed(key); // als de bit op 1 stond, dan firet dit een keypress
}
m_KeybMonitor &= ~(0x1 << count); // de bit wordt op 0 gezet: key is not pressed
}
else m_KeybMonitor |= (0x1 << count); // de bit wordt op 1 gezet: key is pressed
key = m_KeyListPtr[++count]; // increase count and get next key
}
}
Sleep(1000 / KEYBCHECKRATE);
}
return 0;
}
//-----------------------------------------------------------------
// Game Engine General Methods
//-----------------------------------------------------------------
void GameEngine::SetTitle(const tstring& titleRef)
{
delete m_TitlePtr; // delete the title string if it already exists
m_TitlePtr = new tstring(titleRef);
}
bool GameEngine::Run(HINSTANCE hInstance, int iCmdShow)
{
MSG msg;
int iTickTrigger = 0;
int iTickCount;
// create the game engine object, exit if failure
if (GameEngine::GetSingleton() == NULL) return false;
// set the instance member variable of the game engine
GameEngine::GetSingleton()->SetInstance(hInstance);
// Game Initialization
m_GamePtr->GameInitialize(hInstance);
// Initialize the game engine
if (!GameEngine::GetSingleton()->ClassRegister(iCmdShow)) return false;
// Attach the keyboard thread to the main thread. This gives the keyboard events access to the window state
// In plain English: this allows a KeyPressed() event to hide the cursor of the window.
AttachThreadInput(m_dKeybThreadID, GetCurrentThreadId(), true);
// Enter the main message loop
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Make sure the game engine isn't sleeping
if (!GameEngine::GetSingleton()->GetSleep() && m_bRunGameLoop)
{
// Check the tick count to see if a game cycle has elapsed
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount + GameEngine::GetSingleton()->GetFrameDelay();
// Do user defined keyboard check
m_GamePtr->CheckKeyboard();
// Get window, rectangle and HDC
HWND hWindow = GetWindow();
RECT rect;
HDC hDC = GetDC(hWindow);
GetClientRect(hWindow, &rect);
// Double buffering code
HDC hBufferDC = CreateCompatibleDC(hDC);
int iWidth = GetWidth();
int iHeight = GetHeight();
// Create the buffer
HBITMAP hBufferBmp = CreateCompatibleBitmap(hDC, iWidth, iHeight);
HBITMAP hOldBmp = (HBITMAP) SelectObject(hBufferDC, hBufferBmp);
// Do user defined drawing functions on the buffer, parameters added
// for ease of drawing
m_HdcDraw = hBufferDC;
m_RectDraw = rect;
m_isDoublebuffering = true;
m_GamePtr->GameCycle(rect);
m_isDoublebuffering = false;
// As a last step copy the memdc to the hdc
BitBlt(hDC, 0, 0, iWidth, iHeight, hBufferDC, 0, 0, SRCCOPY);
// Reset the old bmp of the buffer, mainly for show since we kill it anyway
SelectObject(hBufferDC, hOldBmp);
// Kill the buffer
DeleteObject(hBufferBmp);
DeleteDC(hBufferDC);
// Release HDC
ReleaseDC(hWindow, hDC);
}
else Sleep(1);//Sleep for one ms te bring cpu load from 100% to 1%. if removed this loops like roadrunner
}
else WaitMessage(); // if the engine is sleeping or the game loop isn't supposed to run, wait for the next windows message.
}
}
return msg.wParam?true:false;
}
bool GameEngine::SetGameValues(const tstring& titleRef, WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480)
{
SetTitle(titleRef);
SetIcon(wIcon);
SetSmallIcon(wSmallIcon);
SetWidth(iWidth);
SetHeight(iHeight);
return true;
}
void GameEngine::ShowMousePointer(bool value) const
{
// set the value
ShowCursor(value);
// redraw the screen
InvalidateRect(m_hWindow, 0, true);
}
bool GameEngine::SetWindowRegion(const HitRegion* regionPtr)
{
if (m_Fullscreen) return false;
if (regionPtr == 0)
{
// turn off window region
SetWindowRgn(m_hWindow, 0, true);
// delete the buffered window region (if it exists)
delete m_WindowRegionPtr;
m_WindowRegionPtr = 0;
}
else
{
// if there is already a window region set, release the buffered region object
if (m_WindowRegionPtr != 0)
{
// turn off window region for safety
SetWindowRgn(m_hWindow, 0, true);
// delete the buffered window region
delete m_WindowRegionPtr;
m_WindowRegionPtr = 0;
}
// create a copy of the submitted region (windows will lock the region handle that it receives)
m_WindowRegionPtr = regionPtr->Clone();
// translate region coordinates in the client field to window coordinates, taking title bar and frame into account
m_WindowRegionPtr->Move(GetSystemMetrics(SM_CXFIXEDFRAME), GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION));
// set the window region
SetWindowRgn(m_hWindow, m_WindowRegionPtr->GetHandle(), true);
}
return true;
}
bool GameEngine::HasWindowRegion() const
{
return (m_WindowRegionPtr?true:false);
}
bool GameEngine::GoFullscreen()
{
// exit if already in fullscreen mode
if (m_Fullscreen) return false;
// turn off window region without redraw
SetWindowRgn(m_hWindow, 0, false);
DEVMODE newSettings;
// request current screen settings
EnumDisplaySettings(0, 0, &newSettings);
// set desired screen size/res
newSettings.dmPelsWidth = GetWidth();
newSettings.dmPelsHeight = GetHeight();
newSettings.dmBitsPerPel = 32;
//specify which aspects of the screen settings we wish to change
newSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// attempt to apply the new settings
long result = ChangeDisplaySettings(&newSettings, CDS_FULLSCREEN);
// exit if failure, else set datamember to fullscreen and return true
if ( result != DISP_CHANGE_SUCCESSFUL ) return false;
else
{
// store the location of the window
m_oldLoc = GetLocation();
// switch off the title bar
DWORD dwStyle = GetWindowLong(m_hWindow, GWL_STYLE);
dwStyle &= ~WS_CAPTION;
SetWindowLong(m_hWindow, GWL_STYLE, dwStyle);
// move the window to (0,0)
SetWindowPos(m_hWindow, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
InvalidateRect(m_hWindow, 0, true);
m_Fullscreen = true;
return true;
}
}
bool GameEngine::GoWindowedMode()
{
// exit if already in windowed mode
if (!m_Fullscreen) return false;
// this resets the screen to the registry-stored values
ChangeDisplaySettings(0, 0);
// replace the title bar
DWORD dwStyle = GetWindowLong(m_hWindow, GWL_STYLE);
dwStyle = dwStyle | WS_CAPTION;
SetWindowLong(m_hWindow, GWL_STYLE, dwStyle);
// move the window back to its old position
SetWindowPos(m_hWindow, 0, m_oldLoc.x, m_oldLoc.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
InvalidateRect(m_hWindow, 0, true);
m_Fullscreen = false;
return true;
}
bool GameEngine::IsFullscreen() const
{
return m_Fullscreen;
}
bool GameEngine::ClassRegister(int iCmdShow)
{
WNDCLASSEX wndclass;
// Create the window class for the main window
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = m_hInstance;
wndclass.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = m_PaintDoublebuffered?NULL:(HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = m_TitlePtr->c_str();
// Register the window class
if (!RegisterClassEx(&wndclass))
return false;
// Calculate the window size and position based upon the game size
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
// Create the window
m_hWindow = CreateWindow( m_TitlePtr->c_str(),
m_TitlePtr->c_str(),
WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | WS_CLIPCHILDREN,
iXWindowPos,
iYWindowPos,
iWindowWidth,
iWindowHeight,
NULL,
NULL,
m_hInstance,
NULL);
if (!m_hWindow)
return false;
// Show and update the window
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);
return true;
}
bool GameEngine::IsKeyDown(int vKey) const
{
if (GetAsyncKeyState(vKey) < 0) return true;
else return false;
}
void GameEngine::SetKeyList(const tstring& keyListRef)
{
if (keyListRef.c_str() != NULL) delete m_KeyListPtr; // clear lijst als er al een lijst bestaat
/*
int iLength = 0;
while (keyListRef.c_str()[iLength] != '\0') iLength++; // tellen hoeveel tekens er zijn
*/
m_KeyListPtr = (TCHAR*) malloc((keyListRef.size() + 1) * sizeof(TCHAR)); // maak plaats voor zoveel tekens + 1
for (int count = 0; count < (int) keyListRef.size() + 1; ++count)
{
TCHAR key = keyListRef.c_str()[count];
m_KeyListPtr[count] = (key > 96 && key < 123)? key-32 : key; // vul het teken in, in uppercase indien kleine letter
}
}
void GameEngine::QuitGame() const
{
PostMessage(GameEngine::GetWindow(), WM_DESTROY, 0, 0);
}
void GameEngine::MessageBox(const tstring& textRef) const
{
#ifdef UNICODE // MessageBox define is undef'd at begin of GameEngine.h
#define MessageBox MessageBoxW // but is needed here
#else
#define MessageBox MessageBoxA
#endif
MessageBox(GetWindow(), textRef.c_str(), m_TitlePtr->c_str(), MB_ICONEXCLAMATION | MB_OK);
#undef MessageBox // undefining it again
}
void GameEngine::MessageBox(int value) const
{
tstringstream buffer;
buffer << value;
MessageBox(buffer.str());
}
void GameEngine::MessageBox(size_t value) const
{
tstringstream buffer;
buffer << value;
MessageBox(buffer.str());
}
void GameEngine::MessageBox(double value) const
{
tstringstream buffer;
buffer << value;
MessageBox(buffer.str());
}
static bool CALLBACK EnumInsertChildrenProc(HWND hwnd, LPARAM lParam)
{
std::vector<HWND> *row = (std::vector<HWND> *) lParam; // std::vector niet te kennen 1e jaar DAE
row->push_back(hwnd); // elk element invullen in de vector
return true;
}
void GameEngine::TabNext(HWND ChildWindow) const
{
std::vector<HWND> childWindows; // std::vector niet te kennen 1e jaar DAE
EnumChildWindows(m_hWindow, (WNDENUMPROC) EnumInsertChildrenProc, (LPARAM) &childWindows);
int position = 0;
HWND temp = childWindows[position];
while(temp != ChildWindow) temp = childWindows[++position]; // positie van childWindow in de vector opzoeken
if (position == childWindows.size() - 1) SetFocus(childWindows[0]);
else SetFocus(childWindows[position + 1]);
}
void GameEngine::TabPrevious(HWND ChildWindow) const
{
std::vector<HWND> childWindows;
EnumChildWindows(m_hWindow, (WNDENUMPROC) EnumInsertChildrenProc, (LPARAM) &childWindows);
int position = (int) childWindows.size() - 1;
HWND temp = childWindows[position];
while(temp != ChildWindow) temp = childWindows[--position]; // positie van childWindow in de vector opzoeken
if (position == 0) SetFocus(childWindows[childWindows.size() - 1]);
else SetFocus(childWindows[position - 1]);
}
bool GameEngine::DrawLine(int x1, int y1, int x2, int y2, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
MoveToEx(hDC, x1, y1, NULL);
LineTo(hDC, x2, y2);
MoveToEx(hDC, 0, 0, NULL); // reset van de positie - zorgt ervoor dat bvb AngleArc vanaf 0,0 gaat tekenen ipv de laatste positie van de DrawLine
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
return true;
}
bool GameEngine::DrawLine(int x1, int y1, int x2, int y2) const
{
if (m_isDoublebuffering || m_isPainting) return DrawLine(x1, y1, x2, y2, m_HdcDraw);
else return false;
}
bool GameEngine::DrawPolygon(const POINT ptsArr[], int count, bool close, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
FormPolygon(ptsArr, count, close, hDC);
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
return true;
}
bool GameEngine::DrawPolygon(const POINT ptsArr[], int count, bool close) const
{
if (m_isDoublebuffering || m_isPainting) return DrawPolygon(ptsArr, count, close, m_HdcDraw);
else return false;
}
bool GameEngine::DrawPolygon(const POINT ptsArr[], int count) const
{
if (m_isDoublebuffering || m_isPainting) return DrawPolygon(ptsArr, count, false, m_HdcDraw);
else return false;
}
bool GameEngine::FillPolygon(const POINT ptsArr[], int count, bool close, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
HBRUSH hOldBrush, hNewBrush = CreateSolidBrush(m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
hOldBrush = (HBRUSH) SelectObject(hDC, hNewBrush);
BeginPath(hDC);
FormPolygon(ptsArr, count, close, hDC);
EndPath(hDC);
StrokeAndFillPath(hDC);
SelectObject(hDC, hOldPen);
SelectObject(hDC, hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
return true;
}
bool GameEngine::FillPolygon(const POINT ptsArr[], int count, bool close) const
{
if (m_isDoublebuffering || m_isPainting) return FillPolygon(ptsArr, count, close, m_HdcDraw);
else return false;
}
bool GameEngine::FillPolygon(const POINT ptsArr[], int count) const
{
if (m_isDoublebuffering || m_isPainting) return FillPolygon(ptsArr, count, false, m_HdcDraw);
else return false;
}
void GameEngine::FormPolygon(const POINT ptsArr[], int count, bool close, HDC hDC) const
{
if (!close) Polyline(hDC, ptsArr, count);
else
{
POINT* newPtsArr= new POINT[count+1]; // interessant geval: deze code werkt niet met memory allocation at compile time => demo case for dynamic memory use
for (int i = 0; i < count; i++) newPtsArr[i] = ptsArr[i];
newPtsArr[count] = ptsArr[0];
Polyline(hDC, newPtsArr, count+1);
delete[] newPtsArr;
}
}
bool GameEngine::DrawRect(int x, int y, int width, int height, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
POINT pts[4] = {x, y, x + width -1, y, x + width-1, y + height-1, x, y + height-1};
DrawPolygon(pts, 4, true, hDC);
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
return true;
}
bool GameEngine::DrawRect(int x, int y, int width, int height) const
{
if (m_isDoublebuffering || m_isPainting) return DrawRect(x, y, width, height, m_HdcDraw);
else return false;
}
bool GameEngine::FillRect(int x, int y, int width, int height, HDC hDC) const
{
HBRUSH hOldBrush, hNewBrush = CreateSolidBrush(m_colDraw);
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldBrush = (HBRUSH) SelectObject(hDC, hNewBrush);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
Rectangle(hDC, x, y, x + width, y + height);
SelectObject(hDC, hOldPen);
SelectObject(hDC, hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
return true;
}
bool GameEngine::FillRect(int x, int y, int width, int height) const
{
if (m_isDoublebuffering || m_isPainting) return FillRect(x, y, width, height, m_HdcDraw);
else return false;
}
bool GameEngine::DrawRoundRect(int x, int y, int width, int height, int radius, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
BeginPath(hDC);
RoundRect(hDC, x, y, x + width, y + height, radius, radius);
EndPath(hDC);
StrokePath(hDC);
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
return true;
}
bool GameEngine::DrawRoundRect(int x, int y, int width, int height, int radius) const
{
if (m_isDoublebuffering || m_isPainting) return DrawRoundRect(x, y, width, height, radius, m_HdcDraw);
else return false;
}
bool GameEngine::FillRoundRect(int x, int y, int width, int height, int radius, HDC hDC) const
{
HBRUSH hOldBrush, hNewBrush = CreateSolidBrush(m_colDraw);
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldBrush = (HBRUSH) SelectObject(hDC, hNewBrush);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
RoundRect(hDC, x, y, x + width, y + height, radius, radius);
SelectObject(hDC, hOldPen);
SelectObject(hDC, hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
return true;
}
bool GameEngine::FillRoundRect(int x, int y, int width, int height, int radius) const
{
if (m_isDoublebuffering || m_isPainting) return FillRoundRect(x, y, width, height, radius, m_HdcDraw);
else return false;
}
bool GameEngine::DrawOval(int x, int y, int width, int height, HDC hDC) const
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
Arc(hDC, x, y, x + width, y + height, x, y + height/2, x, y + height/2);
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
return true;
}
bool GameEngine::DrawOval(int x, int y, int width, int height) const
{
if (m_isDoublebuffering || m_isPainting) return DrawOval(x, y, width, height, m_HdcDraw);
else return false;
}
bool GameEngine::FillOval(int x, int y, int width, int height, HDC hDC) const
{
HBRUSH hOldBrush, hNewBrush = CreateSolidBrush(m_colDraw);
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldBrush = (HBRUSH) SelectObject(hDC, hNewBrush);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
Ellipse(hDC, x, y, x + width, y + height);
SelectObject(hDC, hOldPen);
SelectObject(hDC, hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
return true;
}
bool GameEngine::FillOval(int x, int y, int width, int height) const
{
if (m_isDoublebuffering || m_isPainting) return FillOval(x, y, width, height, m_HdcDraw);
else return false;
}
bool GameEngine::DrawArc(int x, int y, int width, int height, int startDegree, int angle, HDC hDC) const
{
if (angle == 0) return false;
if (angle > 360) { DrawOval(x, y, width, height, hDC); }
else
{
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
POINT ptStart = AngleToPoint(x, y, width, height, startDegree);
POINT ptEnd = AngleToPoint(x, y, width, height, startDegree + angle);
if (angle > 0) Arc(hDC, x, y, x + width, y + height, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
else Arc(hDC, x, y, x + width, y + height, ptEnd.x, ptEnd.y, ptStart.x, ptStart.y);
SelectObject(hDC, hOldPen);
DeleteObject(hNewPen);
}
return true;
}
bool GameEngine::DrawArc(int x, int y, int width, int height, int startDegree, int angle) const
{
if (m_isDoublebuffering || m_isPainting) return DrawArc(x, y, width, height, startDegree, angle, m_HdcDraw);
else return false;
}
bool GameEngine::FillArc(int x, int y, int width, int height, int startDegree, int angle, HDC hDC) const
{
if (angle == 0) return false;
if (angle > 360) { FillOval(x, y, width, height, hDC); }
else
{
HBRUSH hOldBrush, hNewBrush = CreateSolidBrush(m_colDraw);
HPEN hOldPen, hNewPen = CreatePen(PS_SOLID, 1, m_colDraw);
hOldBrush = (HBRUSH) SelectObject(hDC, hNewBrush);
hOldPen = (HPEN) SelectObject(hDC, hNewPen);
POINT ptStart = AngleToPoint(x, y, width, height, startDegree);
POINT ptEnd = AngleToPoint(x, y, width, height, startDegree + angle);
if (angle >0) Pie(hDC, x, y, x + width, y + height, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
else Pie(hDC, x, y, x + width, y + height, ptEnd.x, ptEnd.y, ptStart.x, ptStart.y);
SelectObject(hDC, hOldPen);
SelectObject(hDC, hOldBrush);
DeleteObject(hNewPen);
DeleteObject(hNewBrush);
}
return true;
}
bool GameEngine::FillArc(int x, int y, int width, int height, int startDegree, int angle) const
{
if (m_isDoublebuffering || m_isPainting) return FillArc(x, y, width, height, startDegree, angle, m_HdcDraw);
else return false;
}
POINT GameEngine::AngleToPoint(int x, int y, int width, int height, int angle) const
{
POINT pt;
// if necessary adjust angle so that it has a value between 0 and 360 degrees
if (angle > 360 || angle < -360) angle = angle % 360;
if (angle < 0) angle += 360;
// default values for standard angles
if (angle == 0) { pt.x = x + width; pt.y = y + (int) (height / 2); }
else if (angle == 90) { pt.x = x + (int) (width / 2); pt.y = y; }
else if (angle == 180) { pt.x = x; pt.y = y + (int) (height / 2); }
else if (angle == 270) { pt.x = x + (int) (width / 2); pt.y = y + height; }
// else calculate non-default values
else
{
// point on the ellipse = "stelsel" of the cartesian equation of the ellipse combined with y = tg(alpha) * x
// using the equation for ellipse with 0,0 in the center of the ellipse
double aSquare = pow(width/2.0, 2);
double bSquare = pow(height/2.0, 2);
double tangens = tan(angle * M_PI / 180);
double tanSquare = pow(tangens, 2);
// calculate x
pt.x = (long) sqrt( aSquare * bSquare / (bSquare + tanSquare * aSquare));
if (angle > 90 && angle < 270) pt.x *= -1; // sqrt returns the positive value of the square, take the negative value if necessary
// calculate y
pt.y = (long) (tangens * pt.x);
pt.y *= -1; // reverse the sign because of inverted y-axis
// offset the ellipse into the screen
pt.x += x + (int)(width / 2);
pt.y += y + (int)(height / 2);
}
return pt;
}
int GameEngine::DrawString(const tstring& textRef, int x, int y, int width, int height, HDC hDC) const
{
HFONT hOldFont;
COLORREF oldColor;
if (m_FontDraw != 0) hOldFont = (HFONT) SelectObject(hDC, m_FontDraw);
oldColor = SetTextColor(hDC, m_colDraw);
SetBkMode(hDC, TRANSPARENT);
RECT rc = {x, y, x + width - 1, y + height - 1};
int result = DrawText(hDC, textRef.c_str(), -1, &rc, DT_WORDBREAK);
SetBkMode(hDC, OPAQUE);
SetTextColor(hDC, oldColor);
if (m_FontDraw != 0) SelectObject(hDC, hOldFont);
return result;
}
int GameEngine::DrawString(const tstring& textRef, int x, int y, int width, int height) const
{
if (m_isDoublebuffering || m_isPainting) return DrawString(textRef, x, y, width, height, m_HdcDraw);
else return -1;
}
int GameEngine::DrawString(const tstring& textRef, int x, int y, HDC hDC) const
{
HFONT hOldFont;
COLORREF oldColor;
if (m_FontDraw != 0) hOldFont = (HFONT) SelectObject(hDC, m_FontDraw);
oldColor = SetTextColor(hDC, m_colDraw);
SetBkMode(hDC, TRANSPARENT);
/*
int count = 0;
while (textRef.c_str()[count] != '\0') count++;
*/
int result = TextOut(hDC, x, y, textRef.c_str(), (int) textRef.size());
SetBkMode(hDC, OPAQUE);
SetTextColor(hDC, oldColor);
if (m_FontDraw != 0) SelectObject(hDC, hOldFont);
return result;
}
int GameEngine::DrawString(const tstring& textRef, int x, int y) const
{
if (m_isDoublebuffering || m_isPainting) return DrawString(textRef, x, y, m_HdcDraw);
else return -1;
}
bool GameEngine::DrawBitmap(Bitmap* bitmapPtr, int x, int y, RECT rect, HDC hDC) const
{
if (!bitmapPtr->Exists()) return false;
int opacity = bitmapPtr->GetOpacity();
if (opacity == 0 && bitmapPtr->HasAlphaChannel()) return true; // don't draw if opacity == 0 and opacity is used
HDC hdcMem = CreateCompatibleDC(hDC);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, bitmapPtr->GetHandle());
if (bitmapPtr->HasAlphaChannel())
{
BLENDFUNCTION blender={AC_SRC_OVER, 0, (int) (2.55 * opacity), AC_SRC_ALPHA}; // blend function combines opacity and pixel based transparency
AlphaBlend(hDC, x, y, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, blender);
}
else TransparentBlt(hDC, x, y, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bitmapPtr->GetTransparencyColor());
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
return true;
}
bool GameEngine::DrawBitmap(Bitmap* bitmapPtr, int x, int y, RECT rect) const
{
if (m_isDoublebuffering || m_isPainting) return DrawBitmap(bitmapPtr, x, y, rect, m_HdcDraw);
else return false;
}
bool GameEngine::DrawBitmap(Bitmap* bitmapPtr, int x, int y, HDC hDC) const
{
if (!bitmapPtr->Exists()) return false;
BITMAP bm;
GetObject(bitmapPtr->GetHandle(), sizeof(bm), &bm);
RECT rect = {0, 0, bm.bmWidth, bm.bmHeight};
return DrawBitmap(bitmapPtr, x, y, rect, hDC);
}
bool GameEngine::DrawBitmap(Bitmap* bitmapPtr, int x, int y) const
{
if (m_isDoublebuffering || m_isPainting) return DrawBitmap(bitmapPtr, x, y, m_HdcDraw);
else return false;
}
bool GameEngine::DrawSolidBackground(COLORREF color, HDC hDC, RECT rect)
{
COLORREF oldColor = GetDrawColor();
SetColor(color);
FillRect(0, 0, rect.right, rect.bottom, hDC);
SetColor(oldColor);
return true;
}
bool GameEngine::DrawSolidBackground(COLORREF color)
{
if (m_isDoublebuffering || m_isPainting) return DrawSolidBackground(color, m_HdcDraw, m_RectDraw);
else return false;
}
bool GameEngine::Repaint() const
{
return InvalidateRect(m_hWindow, NULL, true)?true:false;
}
POINT GameEngine::GetLocation() const
{
RECT info;
POINT pos;
GetWindowRect(m_hWindow, &info);
pos.x = info.left;
pos.y = info.top;
return pos;
}
void GameEngine::SetLocation(int x, int y)
{
SetWindowPos(m_hWindow, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
InvalidateRect(m_hWindow, 0, TRUE);
}
void GameEngine::SetFont(const tstring& fontNameRef, bool bold, bool italic, bool underline, int size)
{
if (m_FontDraw != 0) DeleteObject(m_FontDraw);
LOGFONT ft;
ZeroMemory(&ft, sizeof(ft));
//_tcscpy_s(ft.lfFaceName, sizeof(ft.lfFaceName) / sizeof(TCHAR), fontName.c_str());
for (int teller = 0; teller < (int) fontNameRef.size() && teller < LF_FACESIZE; ++teller)
{
ft.lfFaceName[teller] = fontNameRef[teller];
}
ft.lfStrikeOut = 0;
ft.lfUnderline = underline?1:0;
ft.lfHeight = size;
ft.lfEscapement = 0;
ft.lfWeight = bold?FW_BOLD:0;
ft.lfItalic = italic?1:0;
m_FontDraw = CreateFontIndirect(&ft);
}
void GameEngine::RunGameLoop(bool value)
{
m_bRunGameLoop = value;
}
LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
RECT rect;
HDC hDC;
PAINTSTRUCT ps;
// HWND hWindow;
// RECT rect;
// HDC hDC;
// Double buffering code
HDC hBufferDC;
HBITMAP hBufferBmp;
HBITMAP hOldBmp;
int iWidth, iHeight;
static int count = 0;
// Route Windows messages to game engine member functions
switch (msg)
{
case WM_CREATE:
// Set the game window and start the game
SetWindow(hWindow);
// User defined functions for start of the game
m_GamePtr->GameStart();
// Seed the random number generator
srand(GetTickCount());
return 0;
case WM_ACTIVATE:
// Activate/deactivate the game and update the Sleep status
if (wParam != WA_INACTIVE)
{
// Get window rectangle and HDC
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
// Do user defined drawing functions
m_GamePtr->GameActivate(hDC, rect);
// Release HDC
ReleaseDC(hWindow, hDC);
SetSleep(false);
}
else
{
// Get window rectangle and HDC
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
// Do user defined drawing functions
m_GamePtr->GameDeactivate(hDC, rect);
// Release HDC
ReleaseDC(hWindow, hDC);
}
return 0;
case WM_PAINT:
if (m_PaintDoublebuffered)
{
// Get window, rectangle and HDC
hDC = BeginPaint(hWindow, &ps);
GetClientRect(hWindow, &rect);
// Double buffering code
hBufferDC = CreateCompatibleDC(hDC);
iWidth = GameEngine::GetSingleton()->GetWidth();
iHeight = GameEngine::GetSingleton()->GetHeight();
// Create the buffer
hBufferBmp = CreateCompatibleBitmap(hDC, iWidth, iHeight);
hOldBmp = (HBITMAP) SelectObject(hBufferDC, hBufferBmp);
// Do user defined drawing functions on the buffer, parameters added
// for ease of drawing
m_HdcDraw = hBufferDC;
m_RectDraw = rect;
m_isPainting = true;
m_GamePtr->GamePaint(rect);
m_isPainting = false;
// As a last step copy the memdc to the hdc
BitBlt(hDC, 0, 0, iWidth, iHeight, hBufferDC, 0, 0, SRCCOPY);
// Reset the old bmp of the buffer, mainly for show since we kill it anyway
SelectObject(hBufferDC, hOldBmp);
// Kill the buffer
DeleteObject(hBufferBmp);
DeleteDC(hBufferDC);
// end paint
EndPaint(hWindow, &ps);
}
else
{
m_HdcDraw = BeginPaint(hWindow, &ps);
GetClientRect(hWindow, &m_RectDraw);
m_isPainting = true;
m_GamePtr->GamePaint(m_RectDraw);
m_isPainting = false;
EndPaint(hWindow, &ps);
}
return 0;
case WM_CTLCOLOREDIT:
return SendMessage((HWND) lParam, WM_CTLCOLOREDIT, wParam, lParam); // delegate this message to the child window
case WM_CTLCOLORBTN:
return SendMessage((HWND) lParam, WM_CTLCOLOREDIT, wParam, lParam); // delegate this message to the child window
case WM_LBUTTONDOWN:
m_GamePtr->MouseButtonAction(true, true, LOWORD(lParam), HIWORD(lParam), wParam);
return 0;
case WM_LBUTTONUP:
m_GamePtr->MouseButtonAction(true, false, LOWORD(lParam), HIWORD(lParam), wParam);
return 0;
case WM_RBUTTONDOWN:
m_GamePtr->MouseButtonAction(false, true, LOWORD(lParam), HIWORD(lParam), wParam);
return 0;
case WM_RBUTTONUP:
m_GamePtr->MouseButtonAction(false, false, LOWORD(lParam), HIWORD(lParam), wParam);
return 0;
case WM_MOUSEMOVE:
m_GamePtr->MouseMove(LOWORD(lParam), HIWORD(lParam), wParam);
return 0;
case WM_SYSCOMMAND: // trapping this message prevents a freeze after the ALT key is released
if (wParam == SC_KEYMENU) return 0; // see win32 API : WM_KEYDOWN
else break;
case WM_DESTROY:
// User defined code for exiting the game
m_GamePtr->GameEnd();
// Delete the game engine
delete GameEngine::GetSingleton();
// End the game and exit the application
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}
//-----------------------------------------------------------------
// Caller methods
//-----------------------------------------------------------------
bool Caller::AddActionListener(Callable* targetPtr)
{
return AddListenerObject(targetPtr);
}
bool Caller::RemoveActionListener(const Callable* targetPtr)
{
return RemoveListenerObject(targetPtr);
}
class CallAllActions
{
public:
CallAllActions(Caller* callerPtr) : m_CallerPtr(callerPtr)
{}
void operator()(Callable* callablePtr)
{
callablePtr->CallAction(m_CallerPtr);
}
private:
Caller* m_CallerPtr;
};
bool Caller::CallListeners()
{
/*
for (vector<Callable*>::iterator it = m_TargetList.begin(); it != m_TargetList.end(); ++it)
{
(*it)->CallAction(this);
}
*/
for_each(m_TargetList.begin(), m_TargetList.end(), CallAllActions(this));
return (m_TargetList.size() > 0);
}
bool Caller::AddListenerObject(Callable* targetPtr)
{
/*
for (vector<Callable*>::iterator it = m_TargetList.begin(); it != m_TargetList.end(); ++it)
{
if ((*it) == target) return false;
}*/
vector<Callable*>::iterator pos = find(m_TargetList.begin(), m_TargetList.end(), targetPtr);
if (pos != m_TargetList.end()) return false;
m_TargetList.push_back(targetPtr);
return true;
}
bool Caller::RemoveListenerObject(const Callable* targetPtr)
{
vector<Callable*>::iterator pos = find(m_TargetList.begin(), m_TargetList.end(), targetPtr); // find algorithm from STL
if (pos == m_TargetList.end()) return false;
m_TargetList.erase(pos);
return true;
}
//-----------------------------------------------------------------
// Bitmap methods
//-----------------------------------------------------------------
// set static datamember to zero
int Bitmap::m_nr = 0;
Bitmap::Bitmap(const tstring& nameRef, bool createAlphaChannel): m_Handle(0), m_TransparencyKey(-1), m_Opacity(100), m_PixelsPtr(0), m_Exists(false)
{
// check if the file to load is a targa
size_t len = nameRef.length();
if (len > 4 && nameRef.substr(len-4) == _T(".tga"))
{
m_IsTarga = true;
m_HasAlphaChannel = true;
TargaLoader* targa = new TargaLoader();
if (targa->Load((TCHAR*) nameRef.c_str()) == 1)
{
m_Handle = CreateBitmap(targa->GetWidth(), targa->GetHeight(), 1, targa->GetBPP(), (void*)targa->GetImg());
if (m_Handle != 0) m_Exists = true;
}
delete targa;
}
// else load as bitmap
else
{
m_IsTarga = false;
m_HasAlphaChannel = createAlphaChannel;
m_Handle = (HBITMAP) LoadImage(GameEngine::GetSingleton()->GetInstance(), nameRef.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (m_Handle != 0) m_Exists = true;
}
if (m_IsTarga || createAlphaChannel) LoadBitInfo();
}
Bitmap::Bitmap(int IDBitmap, const tstring& typeRef, bool createAlphaChannel): m_TransparencyKey(-1), m_Opacity(100), m_Exists(false)
{
if (typeRef == _T("BITMAP"))
{
m_IsTarga = false;
m_HasAlphaChannel = createAlphaChannel;
m_Handle = LoadBitmap(GameEngine::GetSingleton()->GetInstance(), MAKEINTRESOURCE(IDBitmap));
if (m_Handle != 0) m_Exists = true;
if (createAlphaChannel) LoadBitInfo();
}
else if (typeRef == _T("TGA"))
{
m_IsTarga = true;
m_HasAlphaChannel = true;
tstringstream buffer;
buffer << "temp\\targa";
buffer << m_nr++;
buffer << ".tga";
tstring fileName = buffer.str();
Extract(IDBitmap, _T("TGA"), fileName);
TargaLoader* targa = new TargaLoader();
if (targa->Load((TCHAR*) fileName.c_str()) == 1)
{
m_Handle = CreateBitmap(targa->GetWidth(), targa->GetHeight(), 1, targa->GetBPP(), (void*)targa->GetImg());
if (m_Handle != 0) m_Exists = true;
}
delete targa;
LoadBitInfo();
}
}
void Bitmap::LoadBitInfo()
{
BITMAPINFOHEADER bminfoheader;
::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
bminfoheader.biSize = sizeof(BITMAPINFOHEADER);
bminfoheader.biWidth = GetWidth();
bminfoheader.biHeight = GetHeight();
bminfoheader.biPlanes = 1;
bminfoheader.biBitCount = 32;
bminfoheader.biCompression = BI_RGB;
HDC windowDC = GetWindowDC(GameEngine::GetSingleton()->GetWindow());
m_PixelsPtr = new unsigned char[this->GetWidth() * this->GetHeight() * 4];
GetDIBits(windowDC, m_Handle, 0, GetHeight(), m_PixelsPtr, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // load pixel info
// premultiply if it's a targa
if (m_IsTarga)
{
for (int count = 0; count < GetWidth() * GetHeight(); count++)
{
if (m_PixelsPtr[count * 4 + 3] < 255)
{
m_PixelsPtr[count * 4 + 2] = (unsigned char)((int) m_PixelsPtr[count * 4 + 2] * (int) m_PixelsPtr[count * 4 + 3] / 0xff);
m_PixelsPtr[count * 4 + 1] = (unsigned char)((int) m_PixelsPtr[count * 4 + 1] * (int) m_PixelsPtr[count * 4 + 3] / 0xff);
m_PixelsPtr[count * 4] = (unsigned char)((int) m_PixelsPtr[count * 4] * (int) m_PixelsPtr[count * 4 + 3] / 0xff);
}
}
SetDIBits(windowDC, m_Handle, 0, GetHeight(), m_PixelsPtr, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // save the pixel info for later manipulation
}
// add alpha channel values of 255 for every pixel if bmp
else
{
for (int count = 0; count < GetWidth() * GetHeight(); count++)
{
m_PixelsPtr[count * 4 + 3] = 255;
}
}
SetDIBits(windowDC, m_Handle, 0, GetHeight(), m_PixelsPtr, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // save the pixel info for later manipulation
}
/*
void Bitmap::Premultiply() // Multiply R, G and B with Alpha
{
//Note that the APIs use premultiplied alpha, which means that the red,
//green and blue channel values in the bitmap must be premultiplied with
//the alpha channel value. For example, if the alpha channel value is x,
//the red, green and blue channels must be multiplied by x and divided by
//0xff prior to the call.
unsigned long Index,nPixels;
unsigned char *bCur;
short iPixelSize;
// Set ptr to start of image
bCur=pImage;
// Calc number of pixels
nPixels=iWidth*iHeight;
// Get pixel size in bytes
iPixelSize=iBPP/8;
for(Index=0;Index!=nPixels;Index++) // For each pixel
{
*bCur=(unsigned char)((int)*bCur* (int)*(bCur+3)/0xff);
*(bCur+1)=(unsigned char)((int)*(bCur+1)* (int)*(bCur+3)/0xff);
*(bCur+2)=(unsigned char)((int)*(bCur+2)* (int)*(bCur+3)/0xff);
bCur+=iPixelSize; // Jump to next pixel
}
}
*/
Bitmap::~Bitmap()
{
if (HasAlphaChannel())
{
delete[] m_PixelsPtr;
m_PixelsPtr = 0;
}
DeleteObject(m_Handle);
}
bool Bitmap::Exists() const
{
return m_Exists;
}
void Bitmap::Extract(WORD id, tstring sType, tstring fileName) const
{
CreateDirectory(_T("temp\\"), NULL);
HRSRC hrsrc = FindResource(NULL, MAKEINTRESOURCE(id), sType.c_str());
HGLOBAL hLoaded = LoadResource( NULL, hrsrc);
LPVOID lpLock = LockResource(hLoaded);
DWORD dwSize = SizeofResource(NULL, hrsrc);
HANDLE hFile = CreateFile(fileName.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwByteWritten;
WriteFile(hFile, lpLock , dwSize , &dwByteWritten , NULL);
CloseHandle(hFile);
FreeResource(hLoaded);
}
HBITMAP Bitmap::GetHandle() const
{
return m_Handle;
}
int Bitmap::GetWidth() const
{
if (!Exists()) return 0;
BITMAP bm;
GetObject(m_Handle, sizeof(bm), &bm);
return bm.bmWidth;
}
int Bitmap::GetHeight() const
{
if (!Exists()) return 0;
BITMAP bm;
GetObject(m_Handle, sizeof(bm), &bm);
return bm.bmHeight;
}
void Bitmap::SetTransparencyColor(COLORREF color) // converts transparency value to pixel-based alpha
{
m_TransparencyKey = color;
if (HasAlphaChannel())
{
BITMAPINFOHEADER bminfoheader;
::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
bminfoheader.biSize = sizeof(BITMAPINFOHEADER);
bminfoheader.biWidth = GetWidth();
bminfoheader.biHeight = GetHeight();
bminfoheader.biPlanes = 1;
bminfoheader.biBitCount = 32;
bminfoheader.biCompression = BI_RGB;
HDC windowDC = GetWindowDC(GameEngine::GetSingleton()->GetWindow());
unsigned char* NewPixels = new unsigned char[this->GetWidth() * this->GetHeight() * 4]; // create 32 bit buffer
for (int count = 0; count < this->GetWidth() * this->GetHeight(); ++count)
{
if (RGB(m_PixelsPtr[count * 4 + 2], m_PixelsPtr[count * 4 + 1], m_PixelsPtr[count * 4]) == color) // if the color of this pixel == transparency color
{
((int*) NewPixels)[count] = 0; // set all four values to zero, this assumes sizeof(int) == 4 on this system
// setting values to zero means premultiplying the RGB values to an alpha of 0
}
else ((int*) NewPixels)[count] = ((int*) m_PixelsPtr)[count]; // copy all four values from m_PixelsPtr to NewPixels
}
SetDIBits(windowDC, m_Handle, 0, GetHeight(), NewPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // insert pixels into bitmap
delete[] NewPixels; //destroy buffer
ReleaseDC(GameEngine::GetSingleton()->GetWindow(), windowDC); // release DC
}
}
COLORREF Bitmap::GetTransparencyColor() const
{
return m_TransparencyKey;
}
void Bitmap::SetOpacity(int opacity)
{
if (HasAlphaChannel())
{
if (opacity > 100) m_Opacity = 100;
else if (opacity < 0) m_Opacity = 0;
else m_Opacity = opacity;
}
}
int Bitmap::GetOpacity() const
{
return m_Opacity;
}
bool Bitmap::IsTarga() const
{
return m_IsTarga;
}
bool Bitmap::HasAlphaChannel() const
{
return m_HasAlphaChannel;
}
//-----------------------------------------------------------------
// Audio methods
//-----------------------------------------------------------------
// set static datamember to zero
int Audio::m_nr = 0;
#pragma warning(disable:4311)
#pragma warning(disable:4312)
Audio::Audio(const tstring& nameRef) : m_Playing(false), m_Paused(false), m_MustRepeat(false), m_hWnd(0), m_Volume(100)
{
size_t len = nameRef.length();
ASSERT(len >= 4, _T("Audio name length must be longer than 4 characters!"));
if (len < 4) return;
tstring end = nameRef.substr(len - 4);
if (end == _T(".mp3") || end == _T(".wav") || end == _T(".mid"))
{
tstringstream buffer;
buffer << _T("audio");
buffer << m_nr++;
m_Alias = buffer.str();
m_FileName = nameRef;
Create(nameRef);
}
}
Audio::Audio(int IDAudio, const tstring& typeRef) : m_Playing(false), m_Paused(false), m_MustRepeat(false), m_hWnd(0), m_Volume(100)
{
if (typeRef == _T("MP3") || typeRef == _T("WAV") || typeRef == _T("MID"))
{
tstringstream buffer;
buffer << _T("audio");
buffer << m_nr++;
m_Alias = buffer.str();
m_FileName = tstring(_T("temp\\")) + m_Alias;
if (typeRef == _T("MP3")) m_FileName += _T(".mp3");
else if (typeRef == _T("WAV")) m_FileName += _T(".wav");
else m_FileName += _T(".mid");
Extract(IDAudio, typeRef, m_FileName);
Create(m_FileName);
}
}
void Audio::Create(const tstring& nameRef)
{
TCHAR response[100];
tstringstream buffer;
size_t len = nameRef.length();
ASSERT(len >= 4, _T("Audio name length must be longer than 4 characters!"));
if (len < 4) return;
tstring end = nameRef.substr(len - 4);
if (end == _T(".mp3"))
{
buffer << _T("open \"") + m_FileName + _T("\" type mpegvideo alias ");
buffer << m_Alias;
}
else if (end == _T(".wav"))
{
buffer << _T("open \"") + m_FileName + _T("\" type waveaudio alias ");
buffer << m_Alias;
}
else if (end == _T(".mid"))
{
buffer << _T("open \"") + m_FileName + _T("\" type sequencer alias ");
buffer << m_Alias;
}
int result = mciSendString(buffer.str().c_str(), 0, 0, 0);
if (result != 0) return;
buffer.str(_T(""));
buffer << _T("set ") + m_Alias + _T(" time format milliseconds");
mciSendString(buffer.str().c_str(), 0, 0, 0);
buffer.str(_T(""));
buffer << _T("status ") + m_Alias + _T(" length");
mciSendString(buffer.str().c_str(), response, 100, 0);
buffer.str(_T(""));
buffer << response;
buffer >> m_Duration;
// Create a window to catch the MM_MCINOTIFY message with
m_hWnd = CreateWindow(TEXT("STATIC"), TEXT(""), 0, 0, 0, 0, 0, 0, 0, GameEngine::GetSingleton()->GetInstance(), 0);
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG) AudioProcStatic); // set the custom message loop (subclassing)
SetWindowLong(m_hWnd, GWL_USERDATA, (LONG) this); // set this object as the parameter for the Proc
}
void Audio::Extract(WORD id , const tstring& typeRef, const tstring& fileNameRef) const
{
CreateDirectory(TEXT("temp\\"), NULL);
HRSRC hrsrc = FindResource(NULL, MAKEINTRESOURCE(id), typeRef.c_str());
HGLOBAL hLoaded = LoadResource( NULL, hrsrc);
LPVOID lpLock = LockResource(hLoaded);
DWORD dwSize = SizeofResource(NULL, hrsrc);
HANDLE hFile = CreateFile(fileNameRef.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwByteWritten;
WriteFile(hFile, lpLock , dwSize , &dwByteWritten , NULL);
CloseHandle(hFile);
FreeResource(hLoaded);
}
#pragma warning(default:4311)
#pragma warning(default:4312)
Audio::~Audio()
{
Stop();
tstring sendString = tstring(_T("close")) + m_Alias;
mciSendString(sendString.c_str(), 0, 0, 0);
// release the window resources if necessary
if (m_hWnd)
{
DestroyWindow(m_hWnd);
m_hWnd = 0;
}
}
void Audio::Play(int msecStart, int msecStop)
{
if (!m_Playing)
{
m_Playing = true;
m_Paused = false;
if (msecStop == -1) QueuePlayCommand(msecStart);
else QueuePlayCommand(msecStart, msecStop);
}
else if (m_Paused)
{
m_Paused = false;
QueueResumeCommand();
}
}
void Audio::Pause()
{
if (m_Playing && !m_Paused)
{
m_Paused = true;
QueuePauseCommand();
}
}
void Audio::Stop()
{
if (m_Playing)
{
m_Playing = false;
m_Paused = false;
QueueStopCommand();
}
}
void Audio::QueuePlayCommand(int msecStart)
{
tstringstream buffer;
buffer << _T("play ") + m_Alias + _T(" from ");
buffer << msecStart;
buffer << _T(" notify");
QueueCommand(buffer.str());
}
void Audio::QueuePlayCommand(int msecStart, int msecStop)
{
tstringstream buffer;
buffer << _T("play ") + m_Alias + _T(" from ");
buffer << msecStart;
buffer << _T(" to ");
buffer << msecStop;
buffer << _T(" notify");
QueueCommand(buffer.str());
}
void Audio::QueuePauseCommand()
{
QueueCommand(_T("pause ") + m_Alias);
}
void Audio::QueueResumeCommand()
{
QueueCommand(_T("resume ") + m_Alias);
}
void Audio::QueueStopCommand()
{
QueueCommand(_T("stop ") + m_Alias);
}
void Audio::QueueVolumeCommand(int volume)
{
tstringstream buffer;
buffer << _T("setaudio ") + m_Alias + _T(" volume to ");
buffer << volume * 10;
QueueCommand(buffer.str());
}
void Audio::QueueCommand(const tstring& commandRef)
{
m_CommandQueue.push(commandRef);
}
void Audio::Tick()
{
if (!m_CommandQueue.empty())
{
SendMCICommand(m_CommandQueue.front());
m_CommandQueue.pop();
}
}
void Audio::SendMCICommand(const tstring& commandRef) const
{
int result = mciSendString(commandRef.c_str(), 0, 0, m_hWnd);
}
const tstring& Audio::GetName() const
{
return m_FileName;
}
const tstring& Audio::GetAlias() const
{
return m_Alias;
}
bool Audio::IsPlaying() const
{
return m_Playing;
}
bool Audio::IsPaused() const
{
return m_Paused;
}
void Audio::SwitchPlayingOff()
{
m_Playing = false;
m_Paused = false;
}
void Audio::SetRepeat(bool repeat)
{
m_MustRepeat = repeat;
}
bool Audio::GetRepeat() const
{
return m_MustRepeat;
}
int Audio::GetDuration() const
{
return m_Duration;
}
void Audio::SetVolume(int volume)
{
m_Volume = min(100, max(0, volume)); // values below 0 and above 100 are trimmed to 0 and 100, respectively
QueueVolumeCommand(volume);
}
int Audio::GetVolume() const
{
return m_Volume;
}
bool Audio::Exists() const
{
return m_hWnd?true:false;
}
int Audio::GetType() const
{
return Caller::Audio;
}
LRESULT Audio::AudioProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#pragma warning(disable: 4312)
Audio* audio = reinterpret_cast<Audio*>(GetWindowLong(hWnd, GWL_USERDATA));
#pragma warning(default: 4312)
switch (msg)
{
case MM_MCINOTIFY: // message received when an audio file has finished playing - used for repeat function
if (wParam == MCI_NOTIFY_SUCCESSFUL && audio->IsPlaying())
{
audio->SwitchPlayingOff();
if (audio->GetRepeat()) audio->Play(); // repeat the audio
else audio->CallListeners(); // notify listeners that the audio file has come to an end
}
}
return 0;
}
//-----------------------------------------------------------------
// TextBox methods
//-----------------------------------------------------------------
#pragma warning(disable:4311)
#pragma warning(disable:4312)
TextBox::TextBox(const tstring& textRef) : m_x(0), m_y(0), m_BgColor(RGB(255, 255, 255)), m_ForeColor(RGB(0, 0, 0)), m_BgColorBrush(0), m_Font(0), m_OldFont(0)
{
// Create the edit box
m_hWndEdit = CreateWindow(_T("EDIT"), textRef.c_str(), WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL, 0, 0, 0, 0, GameEngine::GetSingleton()->GetWindow(), NULL, GameEngine::GetSingleton()->GetInstance(), NULL);
// Set de nieuwe WNDPROC voor de edit box, en houd de oude bij
m_procOldEdit = (WNDPROC) SetWindowLong(m_hWndEdit, GWL_WNDPROC, (LONG) EditProcStatic);
// Stel dit object in als userdata voor de statische wndproc functie van de edit box zodat deze members kan aanroepen
SetWindowLong(m_hWndEdit, GWL_USERDATA, (LONG) this);
}
TextBox::TextBox() : m_x(0), m_y(0), m_BgColor(RGB(255, 255, 255)), m_ForeColor(RGB(0, 0, 0)), m_BgColorBrush(0), m_Font(0), m_OldFont(0)
{
// Create the edit box
m_hWndEdit = CreateWindow(_T("EDIT"), _T(""), WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL, 0, 0, 0, 0, GameEngine::GetSingleton()->GetWindow(), NULL, GameEngine::GetSingleton()->GetInstance(), NULL);
// Set de nieuwe WNDPROC voor de edit box, en houd de oude bij
m_procOldEdit = (WNDPROC) SetWindowLong(m_hWndEdit, GWL_WNDPROC, (LONG) EditProcStatic);
// Stel dit object in als userdata voor de statische wndproc functie van de edit box zodat deze members kan aanroepen
SetWindowLong(m_hWndEdit, GWL_USERDATA, (LONG) this);
}
#pragma warning(default:4311)
#pragma warning(default:4312)
TextBox::~TextBox()
{
// release the background brush if necessary
if (m_BgColorBrush != 0)
{
DeleteObject(m_BgColorBrush);
m_BgColorBrush = 0;
}
// release the font if necessary
if (m_Font != 0)
{
SelectObject(GetDC(m_hWndEdit), m_OldFont);
DeleteObject(m_Font);
m_Font = m_OldFont = 0;
}
// release the window resources
DestroyWindow(m_hWndEdit);
m_hWndEdit = NULL;
}
void TextBox::SetBounds(int x, int y, int width, int height)
{
m_x = x;
m_y = y;
MoveWindow(m_hWndEdit, x, y, width, height, true);
}
RECT TextBox::GetRect() const
{
RECT rc;
GetClientRect(m_hWndEdit, &rc);
rc.left += m_x;
rc.right += m_x;
rc.top += m_y;
rc.bottom += m_y;
return rc;
}
void TextBox::SetEnabled(bool bEnable)
{
EnableWindow(m_hWndEdit, bEnable);
}
void TextBox::Update() const
{
UpdateWindow(m_hWndEdit);
}
void TextBox::Show() const
{
// Show and update the edit box
ShowWindow(m_hWndEdit, SW_SHOW);
UpdateWindow(m_hWndEdit);
}
void TextBox::Hide() const
{
// Show and update the edit box
ShowWindow(m_hWndEdit, SW_HIDE);
UpdateWindow(m_hWndEdit);
}
tstring TextBox::GetText() const
{
int textLength = (int) SendMessage(m_hWndEdit, (UINT) WM_GETTEXTLENGTH, 0, 0);
TCHAR* buffer = new TCHAR[textLength + 1];
SendMessage(m_hWndEdit, (UINT) WM_GETTEXT, (WPARAM) textLength + 1, (LPARAM) buffer);
tstring newString(buffer);
delete buffer;
return newString;
}
void TextBox::SetText(const tstring& textRef)
{
SendMessage(m_hWndEdit, WM_SETTEXT, 0, (LPARAM) textRef.c_str());
}
void TextBox::SetFont(const tstring& fontNameRef, bool bold, bool italic, bool underline, int size)
{
LOGFONT ft;
//_tcscpy_s(ft.lfFaceName, sizeof(ft.lfFaceName) / sizeof(TCHAR), fontName.c_str());
for (int teller = 0; teller < (int) fontNameRef.size() && teller < LF_FACESIZE; ++teller)
{
ft.lfFaceName[teller] = fontNameRef[teller];
}
ft.lfStrikeOut = 0;
ft.lfUnderline = underline?1:0;
ft.lfHeight = size;
ft.lfEscapement = 0;
ft.lfWeight = bold?FW_BOLD:0;
ft.lfItalic = italic?1:0;
// clean up if another custom font was already in place
if (m_Font != 0) { DeleteObject(m_Font); }
// create the new font. The WM_CTLCOLOREDIT message will set the font when the textbox is about to redraw
m_Font = CreateFontIndirect(&ft);
// redraw the textbox
InvalidateRect(m_hWndEdit, NULL, true);
}
void TextBox::SetForecolor( COLORREF color )
{
m_ForeColor = color;
// redraw the textbox
InvalidateRect(m_hWndEdit, NULL, true);
}
void TextBox::SetBackcolor( COLORREF color )
{
m_BgColor = color;
if (m_BgColorBrush != 0) DeleteObject(m_BgColorBrush);
m_BgColorBrush = CreateSolidBrush( color );
// redraw the textbox
InvalidateRect(m_hWndEdit, NULL, true);
}
COLORREF TextBox::GetForecolor() const
{
return m_ForeColor;
}
COLORREF TextBox::GetBackcolor() const
{
return m_BgColor;
}
HBRUSH TextBox::GetBackcolorBrush() const
{
return m_BgColorBrush;
}
LRESULT TextBox::EditProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#pragma warning(disable: 4312)
return reinterpret_cast<TextBox*>(GetWindowLong(hWnd, GWL_USERDATA))->EditProc(hWnd, msg, wParam, lParam);
#pragma warning(default: 4312)
}
LRESULT TextBox::EditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CTLCOLOREDIT:
SetBkColor((HDC) wParam, GetBackcolor() );
SetTextColor((HDC) wParam, GetForecolor() );
if (m_Font != 0)
{
if (m_OldFont == 0) m_OldFont = (HFONT) SelectObject((HDC) wParam, m_Font);
else SelectObject((HDC) wParam, m_Font);
}
return (LRESULT) GetBackcolorBrush();
case WM_CHAR:
if (wParam == VK_TAB) return 0;
if (wParam == VK_RETURN) return 0;
break;
case WM_KEYDOWN :
switch (wParam)
{
case VK_TAB:
if (GameEngine::GetSingleton()->IsKeyDown(VK_SHIFT)) GameEngine::GetSingleton()->TabPrevious(hWnd);
else GameEngine::GetSingleton()->TabNext(hWnd);
return 0;
case VK_ESCAPE:
SetFocus(GetParent(hWnd));
return 0;
case VK_RETURN:
//if (m_Target) result = m_Target->CallAction(this);
CallListeners();
break;
}
}
return CallWindowProc(m_procOldEdit, hWnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------
// Button methods
//-----------------------------------------------------------------
#pragma warning(disable:4311)
#pragma warning(disable:4312)
Button::Button(const tstring& textRef) : m_x(0), m_y(0), m_Armed(false), m_Font(0), m_OldFont(0)
{
// Create the button object
m_hWndButton = CreateWindow(_T("BUTTON"), textRef.c_str(), WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | BS_PUSHBUTTON, 0, 0, 0, 0, GameEngine::GetSingleton()->GetWindow(), NULL, GameEngine::GetSingleton()->GetInstance(), NULL);
// Set de new WNDPROC for the button, and store the old one
m_procOldButton = (WNDPROC) SetWindowLong(m_hWndButton, GWL_WNDPROC, (LONG) ButtonProcStatic);
// Store 'this' as data for the Button object so that the static PROC can call the member proc
SetWindowLong(m_hWndButton, GWL_USERDATA, (LONG) this);
}
Button::Button() : m_x(0), m_y(0), m_Armed(false), m_Font(0), m_OldFont(0)
{
// Create the button object
m_hWndButton = CreateWindow(_T("BUTTON"), _T(""), WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | BS_PUSHBUTTON, 0, 0, 0, 0, GameEngine::GetSingleton()->GetWindow(), NULL, GameEngine::GetSingleton()->GetInstance(), NULL);
// Set de new WNDPROC for the button, and store the old one
m_procOldButton = (WNDPROC) SetWindowLong(m_hWndButton, GWL_WNDPROC, (LONG) ButtonProcStatic);
// Store 'this' as data for the Button object so that the static PROC can call the member proc
SetWindowLong(m_hWndButton, GWL_USERDATA, (LONG) this);
}
#pragma warning(default:4311)
#pragma warning(default:4312)
Button::~Button()
{
// release the font if necessary
if (m_Font != 0)
{
SelectObject(GetDC(m_hWndButton), m_OldFont);
DeleteObject(m_Font);
m_Font = m_OldFont = 0;
}
// release the window resource
DestroyWindow(m_hWndButton);
m_hWndButton = NULL;
}
void Button::SetBounds(int x, int y, int width, int height)
{
m_x = x;
m_y = y;
MoveWindow(m_hWndButton, x, y, width, height, true);
}
RECT Button::GetRect() const
{
RECT rc;
GetClientRect(m_hWndButton, &rc);
rc.left += m_x;
rc.right += m_x;
rc.top += m_y;
rc.bottom += m_y;
return rc;
}
void Button::SetEnabled(bool bEnable)
{
EnableWindow(m_hWndButton, bEnable);
}
void Button::Update() const
{
UpdateWindow(m_hWndButton);
}
void Button::Show() const
{
// Show and update the button
ShowWindow(m_hWndButton, SW_SHOW);
UpdateWindow(m_hWndButton);
}
void Button::Hide() const
{
// Show and update the button
ShowWindow(m_hWndButton, SW_HIDE);
UpdateWindow(m_hWndButton);
}
tstring Button::GetText() const
{
int textLength = (int) SendMessage(m_hWndButton, (UINT) WM_GETTEXTLENGTH, 0, 0);
TCHAR* buffer = new TCHAR[textLength + 1];
SendMessage(m_hWndButton, (UINT) WM_GETTEXT, (WPARAM) textLength + 1, (LPARAM) buffer);
tstring newString(buffer);
delete buffer;
return newString;
}
void Button::SetText(const tstring& textRef)
{
SendMessage(m_hWndButton, WM_SETTEXT, 0, (LPARAM) textRef.c_str());
}
void Button::SetFont(const tstring& fontNameRef, bool bold, bool italic, bool underline, int size)
{
LOGFONT ft;
//_tcscpy_s(ft.lfFaceName, sizeof(ft.lfFaceName) / sizeof(TCHAR), fontName.c_str());
for (int teller = 0; teller < (int) fontNameRef.size() && teller < LF_FACESIZE; ++teller)
{
ft.lfFaceName[teller] = fontNameRef[teller];
}
ft.lfStrikeOut = 0;
ft.lfUnderline = underline?1:0;
ft.lfHeight = size;
ft.lfEscapement = 0;
ft.lfWeight = bold?FW_BOLD:0;
ft.lfItalic = italic?1:0;
// clean up if another custom font was already in place
if (m_Font != 0) { DeleteObject(m_Font); }
// create the new font. The WM_CTLCOLOREDIT message will set the font when the button is about to redraw
m_Font = CreateFontIndirect(&ft);
// redraw the button
InvalidateRect(m_hWndButton, NULL, true);
}
LRESULT Button::ButtonProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#pragma warning(disable: 4312)
return reinterpret_cast<Button*>(GetWindowLong(hWnd, GWL_USERDATA))->ButtonProc(hWnd, msg, wParam, lParam);
#pragma warning(default: 4312)
}
LRESULT Button::ButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CTLCOLOREDIT:
if (m_Font != 0)
{
if (m_OldFont == 0) m_OldFont = (HFONT) SelectObject((HDC) wParam, m_Font);
else SelectObject((HDC) wParam, m_Font);
}
return 0;
case WM_CHAR:
if (wParam == VK_TAB) return 0;
if (wParam == VK_RETURN) return 0;
break;
case WM_KEYDOWN :
switch (wParam)
{
case VK_TAB:
if (GameEngine::GetSingleton()->IsKeyDown(VK_SHIFT)) GameEngine::GetSingleton()->TabPrevious(hWnd);
else GameEngine::GetSingleton()->TabNext(hWnd);
return 0;
case VK_ESCAPE:
SetFocus(GetParent(hWnd));
return 0;
case VK_SPACE:
//if (m_Target) result = m_Target->CallAction(this);
CallListeners();
break;
}
break;
case WM_LBUTTONDOWN :
case WM_LBUTTONDBLCLK: // clicking fast will throw LBUTTONDBLCLK's as well as LBUTTONDOWN's, you need to capture both to catch all button clicks
m_Armed = true;
break;
case WM_LBUTTONUP :
if (m_Armed)
{
RECT rc;
POINT pt;
GetWindowRect(hWnd, &rc);
GetCursorPos(&pt);
//if (PtInRect(&rc, pt) && m_Target) result = m_Target->CallAction(this);
if (PtInRect(&rc, pt)) CallListeners();
m_Armed = false;
}
}
return CallWindowProc(m_procOldButton, hWnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------
// Timer methods
//-----------------------------------------------------------------
Timer::Timer(int msec, Callable* targetPtr) : m_IsRunning(false)
{
m_Delay = msec;
AddActionListener(targetPtr);
}
Timer::~Timer()
{
if (m_IsRunning) Stop(); // stop closes the handle
// no objects to delete
}
void Timer::Start()
{
if (m_IsRunning == false)
{
CreateTimerQueueTimer(&m_TimerHandle, NULL, TimerProcStatic, (void*) this, m_Delay, m_Delay, WT_EXECUTEINTIMERTHREAD);
m_IsRunning = true;
}
}
void Timer::Stop()
{
if (m_IsRunning == true)
{
DeleteTimerQueueTimer(NULL, m_TimerHandle, NULL);
//CloseHandle (m_TimerHandle); DeleteTimerQueueTimer automatically closes the handle? MSDN Documentation seems to suggest this
m_IsRunning = false;
}
}
bool Timer::IsRunning() const
{
return m_IsRunning;
}
void Timer::SetDelay(int msec)
{
m_Delay = max(msec, 1); // timer will not accept values less than 1 msec
if (m_IsRunning)
{
Stop();
Start();
}
}
int Timer::GetDelay() const
{
return m_Delay;
}
void CALLBACK Timer::TimerProcStatic(void* lpParameter, BOOLEAN TimerOrWaitFired)
{
Timer* timer = reinterpret_cast<Timer*>(lpParameter);
//if (timer->m_IsRunning) timer->m_Target->CallAction(timer);
if (timer->m_IsRunning) timer->CallListeners();
}
//-----------------------------------------------------------------
// Targa loader code
//-----------------------------------------------------------------
#define IMG_OK 0x1
#define IMG_ERR_NO_FILE 0x2
#define IMG_ERR_MEM_FAIL 0x4
#define IMG_ERR_BAD_FORMAT 0x8
#define IMG_ERR_UNSUPPORTED 0x40
TargaLoader::TargaLoader()
{
pImage=pPalette=pData=NULL;
iWidth=iHeight=iBPP=bEnc=0;
lImageSize=0;
}
TargaLoader::~TargaLoader()
{
if(pImage)
{
delete [] pImage;
pImage=NULL;
}
if(pPalette)
{
delete [] pPalette;
pPalette=NULL;
}
if(pData)
{
delete [] pData;
pData=NULL;
}
}
int TargaLoader::Load(const TCHAR* fileNamePtr)
{
using namespace std;
ifstream fIn;
unsigned long ulSize;
int iRet;
// Clear out any existing image and palette
if(pImage)
{
delete [] pImage;
pImage=NULL;
}
if(pPalette)
{
delete [] pPalette;
pPalette=NULL;
}
// Open the specified file
//fIn.open(szFilename,ios::binary);
fIn.open(fileNamePtr,ios::binary);
if(fIn==NULL) return IMG_ERR_NO_FILE;
// Get file size
fIn.seekg(0,ios_base::end);
ulSize=fIn.tellg();
fIn.seekg(0,ios_base::beg);
// Allocate some space
// Check and clear pDat, just in case
if(pData) delete [] pData;
pData=new unsigned char[ulSize];
if(pData==NULL)
{
fIn.close();
return IMG_ERR_MEM_FAIL;
}
// Read the file into memory
fIn.read((char*)pData,ulSize);
fIn.close();
// Process the header
iRet=ReadHeader();
if(iRet!=IMG_OK) return iRet;
switch(bEnc)
{
case 1: // Raw Indexed
// Check filesize against header values
if((lImageSize+18+pData[0]+768)>ulSize) return IMG_ERR_BAD_FORMAT;
// Double check image type field
if(pData[1]!=1) return IMG_ERR_BAD_FORMAT;
// Load image data
iRet=LoadRawData();
if(iRet!=IMG_OK) return iRet;
// Load palette
iRet=LoadTgaPalette();
if(iRet!=IMG_OK) return iRet;
break;
case 2: // Raw RGB
// Check filesize against header values
if((lImageSize+18+pData[0])>ulSize) return IMG_ERR_BAD_FORMAT;
// Double check image type field
if(pData[1]!=0) return IMG_ERR_BAD_FORMAT;
// Load image data
iRet=LoadRawData();
if(iRet!=IMG_OK) return iRet;
//BGRtoRGB(); // Convert to RGB
break;
case 9: // RLE Indexed
// Double check image type field
if(pData[1]!=1) return IMG_ERR_BAD_FORMAT;
// Load image data
iRet=LoadTgaRLEData();
if(iRet!=IMG_OK) return iRet;
// Load palette
iRet=LoadTgaPalette();
if(iRet!=IMG_OK) return iRet;
break;
case 10: // RLE RGB
// Double check image type field
if(pData[1]!=0) return IMG_ERR_BAD_FORMAT;
// Load image data
iRet=LoadTgaRLEData();
if(iRet!=IMG_OK) return iRet;
//BGRtoRGB(); // Convert to RGB
break;
default:
return IMG_ERR_UNSUPPORTED;
}
// Check flip bit
if((pData[17] & 0x20)==0) FlipImg();
// Release file memory
delete [] pData;
pData=NULL;
return IMG_OK;
}
int TargaLoader::ReadHeader() // Examine the header and populate our class attributes
{
short ColMapStart,ColMapLen;
short x1,y1,x2,y2;
if(pData==NULL)
return IMG_ERR_NO_FILE;
if(pData[1]>1) // 0 (RGB) and 1 (Indexed) are the only types we know about
return IMG_ERR_UNSUPPORTED;
bEnc=pData[2]; // Encoding flag 1 = Raw indexed image
// 2 = Raw RGB
// 3 = Raw greyscale
// 9 = RLE indexed
// 10 = RLE RGB
// 11 = RLE greyscale
// 32 & 33 Other compression, indexed
if(bEnc>11) // We don't want 32 or 33
return IMG_ERR_UNSUPPORTED;
// Get palette info
memcpy(&ColMapStart,&pData[3],2);
memcpy(&ColMapLen,&pData[5],2);
// Reject indexed images if not a VGA palette (256 entries with 24 bits per entry)
if(pData[1]==1) // Indexed
{
if(ColMapStart!=0 || ColMapLen!=256 || pData[7]!=24) return IMG_ERR_UNSUPPORTED;
}
// Get image window and produce width & height values
memcpy(&x1,&pData[8],2);
memcpy(&y1,&pData[10],2);
memcpy(&x2,&pData[12],2);
memcpy(&y2,&pData[14],2);
iWidth=(x2-x1);
iHeight=(y2-y1);
if(iWidth<1 || iHeight<1) return IMG_ERR_BAD_FORMAT;
// Bits per Pixel
iBPP=pData[16];
// Check flip / interleave byte
if(pData[17]>32) // Interleaved data
return IMG_ERR_UNSUPPORTED;
// Calculate image size
lImageSize=(iWidth * iHeight * (iBPP/8));
return IMG_OK;
}
int TargaLoader::LoadRawData() // Load uncompressed image data
{
short iOffset;
if(pImage) // Clear old data if present
delete [] pImage;
pImage=new unsigned char[lImageSize];
if(pImage==NULL) return IMG_ERR_MEM_FAIL;
iOffset=pData[0]+18; // Add header to ident field size
if(pData[1]==1) // Indexed images
iOffset+=768; // Add palette offset
memcpy(pImage,&pData[iOffset],lImageSize);
return IMG_OK;
}
int TargaLoader::LoadTgaRLEData() // Load RLE compressed image data
{
short iOffset,iPixelSize;
unsigned char *pCur;
unsigned long Index=0;
unsigned char bLength,bLoop;
// Calculate offset to image data
iOffset=pData[0]+18;
// Add palette offset for indexed images
if(pData[1]==1) iOffset+=768;
// Get pixel size in bytes
iPixelSize=iBPP/8;
// Set our pointer to the beginning of the image data
pCur=&pData[iOffset];
// Allocate space for the image data
if(pImage!=NULL) delete [] pImage;
pImage=new unsigned char[lImageSize];
if(pImage==NULL) return IMG_ERR_MEM_FAIL;
// Decode
while(Index<lImageSize)
{
if(*pCur & 0x80) // Run length chunk (High bit = 1)
{
bLength=*pCur-127; // Get run length
pCur++; // Move to pixel data
// Repeat the next pixel bLength times
for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize)
memcpy(&pImage[Index],pCur,iPixelSize);
pCur+=iPixelSize; // Move to the next descriptor chunk
}
else // Raw chunk
{
bLength=*pCur+1; // Get run length
pCur++; // Move to pixel data
// Write the next bLength pixels directly
for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize,pCur+=iPixelSize)
memcpy(&pImage[Index],pCur,iPixelSize);
}
}
return IMG_OK;
}
int TargaLoader::LoadTgaPalette() // Load a 256 color palette
{
unsigned char bTemp;
short iIndex,iPalPtr;
// Delete old palette if present
if(pPalette)
{
delete [] pPalette;
pPalette=NULL;
}
// Create space for new palette
pPalette=new unsigned char[768];
if(pPalette==NULL) return IMG_ERR_MEM_FAIL;
// VGA palette is the 768 bytes following the header
memcpy(pPalette,&pData[pData[0]+18],768);
// Palette entries are BGR ordered so we have to convert to RGB
for(iIndex=0,iPalPtr=0;iIndex!=256;++iIndex,iPalPtr+=3)
{
bTemp=pPalette[iPalPtr]; // Get Blue value
pPalette[iPalPtr]=pPalette[iPalPtr+2]; // Copy Red to Blue
pPalette[iPalPtr+2]=bTemp; // Replace Blue at the end
}
return IMG_OK;
}
void TargaLoader::BGRtoRGB() // Convert BGR to RGB (or back again)
{
unsigned long Index,nPixels;
unsigned char *bCur;
unsigned char bTemp;
short iPixelSize;
// Set ptr to start of image
bCur=pImage;
// Calc number of pixels
nPixels=iWidth*iHeight;
// Get pixel size in bytes
iPixelSize=iBPP/8;
for(Index=0;Index!=nPixels;Index++) // For each pixel
{
bTemp=*bCur; // Get Blue value
*bCur=*(bCur+2); // Swap red value into first position
*(bCur+2)=bTemp; // Write back blue to last position
bCur+=iPixelSize; // Jump to next pixel
}
}
void TargaLoader::FlipImg() // Flips the image vertically (Why store images upside down?)
{
unsigned char bTemp;
unsigned char *pLine1, *pLine2;
int iLineLen,iIndex;
iLineLen=iWidth*(iBPP/8);
pLine1=pImage;
pLine2=&pImage[iLineLen * (iHeight - 1)];
for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
{
for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
{
bTemp=*pLine1;
*pLine1=*pLine2;
*pLine2=bTemp;
}
}
}
int TargaLoader::GetBPP() const
{
return iBPP;
}
int TargaLoader::GetWidth() const
{
return iWidth;
}
int TargaLoader::GetHeight() const
{
return iHeight;
}
const unsigned char* TargaLoader::GetImg() const
{
return pImage;
}
const unsigned char* TargaLoader::GetPalette() const
{
return pPalette;
}
//-----------------------------------------------------------------
// OutputDebugString functions
//-----------------------------------------------------------------
void OutputDebugString(const tstring& textRef)
{
OutputDebugString(textRef.c_str());
}
//---------------------------
// HitRegion methods
//---------------------------
HitRegion::HitRegion() : m_HitRegion(0)
{
// nothing to create
}
HitRegion::~HitRegion()
{
if (m_HitRegion)
DeleteObject(m_HitRegion);
}
bool HitRegion::Create(int type, int x, int y, int width, int height)
{
if (m_HitRegion) DeleteObject(m_HitRegion);
if (type == HitRegion::Ellipse)
m_HitRegion = CreateEllipticRgn(x, y, x + width, y + height);
else
m_HitRegion = CreateRectRgn(x, y, x + width, y + height);
return true;
}
bool HitRegion::Create(int type, const POINT* pointsArr, int numberOfPoints)
{
if (m_HitRegion) DeleteObject(m_HitRegion);
m_HitRegion = CreatePolygonRgn(pointsArr, numberOfPoints, WINDING);
return true;
}
bool HitRegion::Create(int type, const Bitmap* bmpPtr, COLORREF cTransparent, COLORREF cTolerance)
{
if (!bmpPtr->Exists()) return false;
HBITMAP hBitmap = bmpPtr->GetHandle();
if (!hBitmap) return false;
if (m_HitRegion) DeleteObject(m_HitRegion);
// for some reason, the BitmapToRegion function has R and B switched. Flipping the colors to get the right result.
COLORREF flippedTransparent = RGB(GetBValue(cTransparent), GetGValue(cTransparent), GetRValue(cTransparent));
COLORREF flippedTolerance = RGB(GetBValue(cTolerance), GetGValue(cTolerance), GetRValue(cTolerance));
m_HitRegion = BitmapToRegion(hBitmap, flippedTransparent, flippedTolerance);
return (m_HitRegion?true:false);
}
// BitmapToRegion : Create a region from the "non-transparent" pixels of a bitmap
// Author : Jean-Edouard Lachand-Robert (http://www.geocities.com/Paris/LeftBank/1160/resume.htm), June 1998
// Some modifications: Kevin Hoefman, Febr 2007
HRGN HitRegion::BitmapToRegion(HBITMAP hBmp, COLORREF cTransparentColor, COLORREF cTolerance) const
{
HRGN hRgn = NULL;
if (hBmp)
{
// Create a memory DC inside which we will scan the bitmap content
HDC hMemDC = CreateCompatibleDC(NULL);
if (hMemDC)
{
// Get bitmap siz
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
// Create a 32 bits depth bitmap and select it into the memory DC
BITMAPINFOHEADER RGB32BITSBITMAPINFO = {
sizeof(BITMAPINFOHEADER), // biSize
bm.bmWidth, // biWidth;
bm.bmHeight, // biHeight;
1, // biPlanes;
32, // biBitCount
BI_RGB, // biCompression;
0, // biSizeImage;
0, // biXPelsPerMeter;
0, // biYPelsPerMeter;
0, // biClrUsed;
0 // biClrImportant;
};
VOID * pbits32;
HBITMAP hbm32 = CreateDIBSection(hMemDC, (BITMAPINFO *)&RGB32BITSBITMAPINFO, DIB_RGB_COLORS, &pbits32, NULL, 0);
if (hbm32)
{
HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);
// Create a DC just to copy the bitmap into the memory D
HDC hDC = CreateCompatibleDC(hMemDC);
if (hDC)
{
// Get how many bytes per row we have for the bitmap bits (rounded up to 32 bits
BITMAP bm32;
GetObject(hbm32, sizeof(bm32), &bm32);
while (bm32.bmWidthBytes % 4)
bm32.bmWidthBytes++;
// Copy the bitmap into the memory D
HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);
// For better performances, we will use the ExtCreateRegion() function to create the
// region. This function take a RGNDATA structure on entry. We will add rectangles b
// amount of ALLOC_UNIT number in this structure
#define ALLOC_UNIT 100
DWORD maxRects = ALLOC_UNIT;
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
// Keep on hand highest and lowest values for the "transparent" pixel
BYTE lr = GetRValue(cTransparentColor);
BYTE lg = GetGValue(cTransparentColor);
BYTE lb = GetBValue(cTransparentColor);
BYTE hr = min(0xff, lr + GetRValue(cTolerance));
BYTE hg = min(0xff, lg + GetGValue(cTolerance));
BYTE hb = min(0xff, lb + GetBValue(cTolerance));
// Scan each bitmap row from bottom to top (the bitmap is inverted vertically
BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;
for (int y = 0; y < bm.bmHeight; y++)
{
// Scan each bitmap pixel from left to righ
for (int x = 0; x < bm.bmWidth; x++)
{
// Search for a continuous range of "non transparent pixels"
int x0 = x;
LONG *p = (LONG *)p32 + x;
while (x < bm.bmWidth)
{
BYTE b = GetRValue(*p);
if (b >= lr && b <= hr)
{
b = GetGValue(*p);
if (b >= lg && b <= hg)
{
b = GetBValue(*p);
if (b >= lb && b <= hb)
// This pixel is "transparent"
break;
}
}
p++;
x++;
}
if (x > x0)
{
// Add the pixels (x0, y) to (x, y+1) as a new rectangle in the regio
if (pData->rdh.nCount >= maxRects)
{
GlobalUnlock(hData);
maxRects += ALLOC_UNIT;
hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE);
pData = (RGNDATA *)GlobalLock(hData);
}
RECT *pr = (RECT *)&pData->Buffer;
SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
if (x0 < pData->rdh.rcBound.left)
pData->rdh.rcBound.left = x0;
if (y < pData->rdh.rcBound.top)
pData->rdh.rcBound.top = y;
if (x > pData->rdh.rcBound.right)
pData->rdh.rcBound.right = x;
if (y+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = y+1;
pData->rdh.nCount++;
/*
// On Windows98, ExtCreateRegion() may fail if the number of rectangles is to
// large (ie: > 4000). Therefore, we have to create the region by multiple steps
if (pData->rdh.nCount == 2000)
{
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
// Free the data
GlobalFree(hData);
if (hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
else
hRgn = h;
pData->rdh.nCount = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
}
*/
}
}
// Go to next row (remember, the bitmap is inverted vertically
p32 -= bm32.bmWidthBytes;
}
// Create or extend the region with the remaining rectangle
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
if (hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
else
hRgn = h;
// Clean u
SelectObject(hDC, holdBmp);
DeleteDC(hDC);
}
DeleteObject(SelectObject(hMemDC, holdBmp));
}
DeleteDC(hMemDC);
}
}
return hRgn;
}
HitRegion* HitRegion::Clone() const
{
HitRegion* temp = new HitRegion();
temp->m_HitRegion = CreateRectRgn(0, 0, 10, 10); // create dummy region
CombineRgn(temp->m_HitRegion, m_HitRegion, 0, RGN_COPY);
return temp;
}
void HitRegion::Move(int x, int y)
{
OffsetRgn(m_HitRegion, x, y);
}
RECT HitRegion::GetDimension() const
{
RECT boundingbox;
GetRgnBox(m_HitRegion, &boundingbox);
return boundingbox;
}
HRGN HitRegion::GetHandle() const
{
return m_HitRegion;
}
bool HitRegion::HitTest(HitRegion* regPtr) const
{
HRGN temp = CreateRectRgn(0, 0, 10, 10); // dummy region
bool result = (CombineRgn(temp, m_HitRegion, regPtr->m_HitRegion, RGN_AND) != NULLREGION);
DeleteObject(temp);
return result;
}
bool HitRegion::HitTest(int x, int y) const
{
return PtInRegion(m_HitRegion, x, y)?true:false;
}
POINT HitRegion::CollisionTest(HitRegion* regPtr) const
{
POINT result;
HRGN temp = CreateRectRgn(0, 0, 10, 10); // dummy region
int overlap = CombineRgn(temp, m_HitRegion, regPtr->m_HitRegion, RGN_AND);
if (overlap == NULLREGION)
{
result.x = -1000000;
result.y = -1000000;
}
else
{
RECT boundingbox;
GetRgnBox(temp, &boundingbox);
result.x = boundingbox.left + (boundingbox.right - boundingbox.left)/2;
result.y = boundingbox.top + (boundingbox.bottom - boundingbox.top)/2;
}
DeleteObject(temp);
return result;
} |