diff --git a/olcConsoleGameEngine.h b/olcConsoleGameEngine.h index 889cfc7..aa73dc0 100644 --- a/olcConsoleGameEngine.h +++ b/olcConsoleGameEngine.h @@ -33,9 +33,9 @@ Beginners Guide: https://youtu.be/u5BhrA8ED0o Shout Outs! ~~~~~~~~~~~ Thanks to cool people who helped with testing, bug-finding and fixing! - YouTube: wowLinh, JavaJack59, idkwid + YouTube: wowLinh, JavaJack59, idkwid, kingtatgi -Last Updated: 07/12/2017 +Last Updated: 04/02/2018 Usage: ~~~~~~ @@ -191,7 +191,7 @@ private: public: void SetGlyph(int x, int y, wchar_t c) { - if (x <0 || x > nWidth || y < 0 || y > nHeight) + if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return; else m_Glyphs[y * nWidth + x] = c; @@ -199,7 +199,7 @@ public: void SetColour(int x, int y, short c) { - if (x <0 || x > nWidth || y < 0 || y > nHeight) + if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return; else m_Colours[y * nWidth + x] = c; @@ -207,7 +207,7 @@ public: wchar_t GetGlyph(int x, int y) { - if (x <0 || x > nWidth || y < 0 || y > nHeight) + if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return L' '; else return m_Glyphs[y * nWidth + x]; @@ -215,7 +215,7 @@ public: short GetColour(int x, int y) { - if (x <0 || x > nWidth || y < 0 || y > nHeight) + if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return FG_BLACK; else return m_Colours[y * nWidth + x]; @@ -225,7 +225,7 @@ public: { int sx = (int)(x * (float)nWidth); int sy = (int)(y * (float)nHeight-1.0f); - if (sx <0 || sx > nWidth || sy < 0 || sy > nHeight) + if (sx <0 || sx >= nWidth || sy < 0 || sy >= nHeight) return L' '; else return m_Glyphs[sy * nWidth + sx]; @@ -235,7 +235,7 @@ public: { int sx = (int)(x * (float)nWidth); int sy = (int)(y * (float)nHeight-1.0f); - if (sx <0 || sx > nWidth || sy < 0 || sy > nHeight) + if (sx <0 || sx >= nWidth || sy < 0 || sy >= nHeight) return FG_BLACK; else return m_Colours[sy * nWidth + sx]; @@ -296,12 +296,9 @@ public: m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE); m_hConsoleIn = GetStdHandle(STD_INPUT_HANDLE); - m_keyNewState = new short[256]; - m_keyOldState = new short[256]; memset(m_keyNewState, 0, 256 * sizeof(short)); memset(m_keyOldState, 0, 256 * sizeof(short)); memset(m_keys, 0, 256 * sizeof(sKeyState)); - m_mousePosX = 0; m_mousePosY = 0; @@ -350,6 +347,7 @@ public: cfi.dwFontSize.Y = fonth; cfi.FontFamily = FF_DONTCARE; cfi.FontWeight = FW_NORMAL; + //wcscpy_s(cfi.FaceName, L"Liberation Mono"); wcscpy_s(cfi.FaceName, L"Consolas"); if (!SetCurrentConsoleFontEx(m_hConsole, false, &cfi)) return Error(L"SetCurrentConsoleFontEx"); @@ -522,6 +520,32 @@ public: } } + void FillCircle(int xc, int yc, int r, wchar_t c = 0x2588, short col = 0x000F) + { + // Taken from wikipedia + int x = 0; + int y = r; + int p = 3 - 2 * r; + if (!r) return; + + auto drawline = [&](int sx, int ex, int ny) + { + for (int i = sx; i < ex; i++) + Draw(i, ny, c, col); + }; + + while (y >= x) + { + // Modified to draw scan-lines instead of edges + drawline(xc - x, xc + x, yc - y); + drawline(xc - y, xc + y, yc - x); + drawline(xc - x, xc + x, yc + y); + drawline(xc - y, xc + y, yc + x); + if (p < 0) p += 4 * x++ + 6; + else p += 4 * (x++ - y--) + 10; + } + }; + void DrawSprite(int x, int y, olcSprite *sprite) { if (sprite == nullptr) @@ -607,10 +631,6 @@ public: thread t = thread(&olcConsoleGameEngine::GameThread, this); // Wait for thread to be exited - unique_lock ul(m_muxGame); - m_cvGameFinished.wait(ul); - - // Tidy up t.join(); } @@ -634,119 +654,140 @@ private: auto tp1 = chrono::system_clock::now(); auto tp2 = chrono::system_clock::now(); - // Run as fast as possible while (m_bAtomActive) - { - // Handle Timing - tp2 = chrono::system_clock::now(); - chrono::duration elapsedTime = tp2 - tp1; - tp1 = tp2; - float fElapsedTime = elapsedTime.count(); - - // Handle Keyboard Input - for (int i = 0; i < 256; i++) + { + // Run as fast as possible + while (m_bAtomActive) { - m_keyNewState[i] = GetAsyncKeyState(i); - - m_keys[i].bPressed = false; - m_keys[i].bReleased = false; - - if (m_keyNewState[i] != m_keyOldState[i]) + // Handle Timing + tp2 = chrono::system_clock::now(); + chrono::duration elapsedTime = tp2 - tp1; + tp1 = tp2; + float fElapsedTime = elapsedTime.count(); + + // Handle Keyboard Input + for (int i = 0; i < 256; i++) { - if (m_keyNewState[i] & 0x8000) - { - m_keys[i].bPressed = !m_keys[i].bHeld; - m_keys[i].bHeld = true; - } - else + m_keyNewState[i] = GetAsyncKeyState(i); + + m_keys[i].bPressed = false; + m_keys[i].bReleased = false; + + if (m_keyNewState[i] != m_keyOldState[i]) { - m_keys[i].bReleased = true; - m_keys[i].bHeld = false; + if (m_keyNewState[i] & 0x8000) + { + m_keys[i].bPressed = !m_keys[i].bHeld; + m_keys[i].bHeld = true; + } + else + { + m_keys[i].bReleased = true; + m_keys[i].bHeld = false; + } } - } - m_keyOldState[i] = m_keyNewState[i]; - } + m_keyOldState[i] = m_keyNewState[i]; + } - // Handle Mouse Input - Check for window events - INPUT_RECORD inBuf[32]; - DWORD events = 0; - GetNumberOfConsoleInputEvents(m_hConsoleIn, &events); - if(events > 0) - ReadConsoleInput(m_hConsoleIn, inBuf, events, &events); + // Handle Mouse Input - Check for window events + INPUT_RECORD inBuf[32]; + DWORD events = 0; + GetNumberOfConsoleInputEvents(m_hConsoleIn, &events); + if (events > 0) + ReadConsoleInput(m_hConsoleIn, inBuf, events, &events); - // Handle events - we only care about mouse clicks and movement - // for now - for (DWORD i = 0; i < events; i++) - { - switch (inBuf[i].EventType) + // Handle events - we only care about mouse clicks and movement + // for now + for (DWORD i = 0; i < events; i++) { + switch (inBuf[i].EventType) + { + case FOCUS_EVENT: + { + m_bConsoleInFocus = inBuf[i].Event.FocusEvent.bSetFocus; + } + break; + case MOUSE_EVENT: { switch (inBuf[i].Event.MouseEvent.dwEventFlags) { - case MOUSE_MOVED: - { - m_mousePosX = inBuf[i].Event.MouseEvent.dwMousePosition.X; - m_mousePosY = inBuf[i].Event.MouseEvent.dwMousePosition.Y; - } - break; + case MOUSE_MOVED: + { + m_mousePosX = inBuf[i].Event.MouseEvent.dwMousePosition.X; + m_mousePosY = inBuf[i].Event.MouseEvent.dwMousePosition.Y; + } + break; - case 0: - { - for (int m = 0; m < 5; m++) - m_mouseNewState[m] = (inBuf[i].Event.MouseEvent.dwButtonState & (1 << m)) > 0; - - } - break; + case 0: + { + for (int m = 0; m < 5; m++) + m_mouseNewState[m] = (inBuf[i].Event.MouseEvent.dwButtonState & (1 << m)) > 0; + + } + break; - default: - break; - } + default: + break; + } } break; default: break; // We don't care just at the moment + } } - } - - for (int m = 0; m < 5; m++) - { - m_mouse[m].bPressed = false; - m_mouse[m].bReleased = false; - if (m_mouseNewState[m] != m_mouseOldState[m]) + for (int m = 0; m < 5; m++) { - if (m_mouseNewState[m]) - { - m_mouse[m].bPressed = true; - m_mouse[m].bHeld = true; - } - else + m_mouse[m].bPressed = false; + m_mouse[m].bReleased = false; + + if (m_mouseNewState[m] != m_mouseOldState[m]) { - m_mouse[m].bReleased = true; - m_mouse[m].bHeld = false; + if (m_mouseNewState[m]) + { + m_mouse[m].bPressed = true; + m_mouse[m].bHeld = true; + } + else + { + m_mouse[m].bReleased = true; + m_mouse[m].bHeld = false; + } } + + m_mouseOldState[m] = m_mouseNewState[m]; } - m_mouseOldState[m] = m_mouseNewState[m]; - } + // Handle Frame Update + if (!OnUserUpdate(fElapsedTime)) + m_bAtomActive = false; - // Handle Frame Update - if (!OnUserUpdate(fElapsedTime)) - m_bAtomActive = false; + // Update Title & Present Screen Buffer + wchar_t s[256]; + swprintf_s(s, 256, L"OneLoneCoder.com - Console Game Engine - %s - FPS: %3.2f - %d ", m_sAppName.c_str(), 1.0f / fElapsedTime, events); + SetConsoleTitle(s); + WriteConsoleOutput(m_hConsole, m_bufScreen, { (short)m_nScreenWidth, (short)m_nScreenHeight }, { 0,0 }, &m_rectWindow); + } - // Update Title & Present Screen Buffer - wchar_t s[256]; - swprintf_s(s, 256, L"OneLoneCoder.com - Console Game Engine - %s - FPS: %3.2f - %d ", m_sAppName.c_str(), 1.0f / fElapsedTime, events); - SetConsoleTitle(s); - WriteConsoleOutput(m_hConsole, m_bufScreen, { (short)m_nScreenWidth, (short)m_nScreenHeight }, { 0,0 }, &m_rectWindow); - } + if (OnUserDestroy()) + { + // User has permitted destroy, so exit and clean up - m_cvGameFinished.notify_one(); + delete[] m_bufScreen; + SetConsoleActiveScreenBuffer(m_hOriginalConsole); + m_cvGameFinished.notify_one(); + } + else + { + // User denied destroy for some reason, so continue running + m_bAtomActive = true; + } + } } public: @@ -754,15 +795,15 @@ public: virtual bool OnUserCreate() = 0; virtual bool OnUserUpdate(float fElapsedTime) = 0; + // Optional for clean up + virtual bool OnUserDestroy() + { + return true; + } + protected: - int m_nScreenWidth; - int m_nScreenHeight; - CHAR_INFO *m_bufScreen; - atomic m_bAtomActive; - condition_variable m_cvGameFinished; - mutex m_muxGame; - wstring m_sAppName; + struct sKeyState { @@ -775,11 +816,11 @@ protected: int m_mousePosY; public: - sKeyState GetKey(int nKeyID) - { - return m_keys[nKeyID]; - } - + sKeyState GetKey(int nKeyID){ return m_keys[nKeyID]; } + int GetMouseX() { return m_mousePosX; } + int GetMouseY() { return m_mousePosY; } + sKeyState GetMouse(int nMouseButtonID) { return m_mouse[nMouseButtonID]; } + bool IsFocused() { return m_bConsoleInFocus; } protected: @@ -789,17 +830,45 @@ protected: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); SetConsoleActiveScreenBuffer(m_hOriginalConsole); wprintf(L"ERROR: %s\n\t%s\n", msg, buf); - return -1; + return 0; } -private: + static BOOL CloseHandler(DWORD evt) + { + // Note this gets called in a seperate OS thread, so it must + // only exit when the game has finished cleaning up, or else + // the process will be killed before OnUserDestroy() has finished + if (evt == CTRL_CLOSE_EVENT) + { + m_bAtomActive = false; + + // Wait for thread to be exited + unique_lock ul(m_muxGame); + m_cvGameFinished.wait(ul); + } + return true; + } + +protected: + int m_nScreenWidth; + int m_nScreenHeight; + CHAR_INFO *m_bufScreen; + wstring m_sAppName; HANDLE m_hOriginalConsole; CONSOLE_SCREEN_BUFFER_INFO m_OriginalConsoleInfo; HANDLE m_hConsole; HANDLE m_hConsoleIn; SMALL_RECT m_rectWindow; - short *m_keyOldState; - short *m_keyNewState; - bool m_mouseOldState[5]; - bool m_mouseNewState[5]; -}; \ No newline at end of file + short m_keyOldState[256] = { 0 }; + short m_keyNewState[256] = { 0 }; + bool m_mouseOldState[5] = { 0 }; + bool m_mouseNewState[5] = { 0 }; + bool m_bConsoleInFocus = true; + static atomic m_bAtomActive; + static condition_variable m_cvGameFinished; + static mutex m_muxGame; +}; + +atomic olcConsoleGameEngine::m_bAtomActive = false; +condition_variable olcConsoleGameEngine::m_cvGameFinished; +mutex olcConsoleGameEngine::m_muxGame; \ No newline at end of file