The official distribution of olcConsoleGameEngine, a tool used in javidx9's YouTube videos and projects
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
videos/olcConsoleGameEngine.h

892 lines
22 KiB

/*
OneLoneCoder.com - Command Line Game Engine
"Who needs a frame buffer?" - @Javidx9
License
~~~~~~~
One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; See license for details.
Original works located at:
https://www.github.com/onelonecoder
https://www.onelonecoder.com
https://www.youtube.com/javidx9
GNU GPLv3
https://github.com/OneLoneCoder/videos/blob/master/LICENSE
From Javidx9 :)
~~~~~~~~~~~~~~~
Hello! Ultimately I don't care what you use this for. It's intended to be
educational, and perhaps to the oddly minded - a little bit of fun.
Please hack this, change it and use it in any way you see fit. You acknowledge
that I am not responsible for anything bad that happens as a result of
your actions. However this code is protected by GNU GPLv3, see the license in the
github repo. This means you must attribute me if you use it. You can view this
license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE
Cheers!
Background
~~~~~~~~~~
If you've seen any of my videos - I like to do things using the windows console. It's quick
and easy, and allows you to focus on just the code that matters - ideal when you're
experimenting. Thing is, I have to keep doing the same initialisation and display code
each time, so this class wraps that up.
Author
~~~~~~
Twitter: @javidx9 http://twitter.com/javidx9
Blog: http://www.onelonecoder.com
YouTube: http://www.youtube.com/javidx9
Videos:
~~~~~~
Original: https://youtu.be/cWc0hgYwZyc
Added mouse support: https://youtu.be/tdqc9hZhHxM
Beginners Guide: https://youtu.be/u5BhrA8ED0o
Shout Outs!
~~~~~~~~~~~
Thanks to cool people who helped with testing, bug-finding and fixing!
YouTube: wowLinh, JavaJack59, idkwid, kingtatgi, huhlig
Last Updated: 05/03/2018
Usage:
~~~~~~
This class is abstract, so you must inherit from it. Override the OnUserCreate() function
with all the stuff you need for your application (for thready reasons it's best to do
this in this function and not your class constructor). Override the OnUserUpdate(float fElapsedTime)
function with the good stuff, it gives you the elapsed time since the last call so you
can modify your stuff dynamically. Both functions should return true, unless you need
the application to close.
int main()
{
// Use olcConsoleGameEngine derived app
OneLoneCoder_Example game;
// Create a console with resolution 160x100 characters
// Each character occupies 8x8 pixels
game.ConstructConsole(160, 100, 8, 8);
// Start the engine!
game.Start();
return 0;
}
Input is also handled for you - interrogate the m_keys[] array with the virtual
keycode you want to know about. bPressed is set for the frame the key is pressed down
in, bHeld is set if the key is held down, bReleased is set for the frame the key
is released in. The same applies to mouse! m_mousePosX and Y can be used to get
the current cursor position, and m_mouse[1..5] returns the mouse buttons.
The draw routines treat characters like pixels. By default they are set to white solid
blocks - but you can draw any unicode character, using any of the colours listed below.
There may be bugs!
See my other videos for examples!
http://www.youtube.com/javidx9
Lots of programs to try:
http://www.github.com/OneLoneCoder/videos
Chat on the Discord server:
https://discord.gg/WhwHUMV
Be bored by Twitch:
http://www.twitch.tv/javidx9
*/
#pragma once
#ifndef UNICODE
#error Please enable UNICODE for your compiler! VS: Project Properties -> General -> \
7 years ago
Character Set -> Use Unicode. Thanks! - Javidx9
#endif
#include <iostream>
#include <chrono>
#include <vector>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
using namespace std;
#include <windows.h>
7 years ago
enum COLOUR
{
FG_BLACK = 0x0000,
FG_DARK_BLUE = 0x0001,
FG_DARK_GREEN = 0x0002,
FG_DARK_CYAN = 0x0003,
FG_DARK_RED = 0x0004,
FG_DARK_MAGENTA = 0x0005,
FG_DARK_YELLOW = 0x0006,
FG_GREY = 0x0007, // Thanks MS :-/
FG_DARK_GREY = 0x0008,
FG_BLUE = 0x0009,
FG_GREEN = 0x000A,
FG_CYAN = 0x000B,
FG_RED = 0x000C,
FG_MAGENTA = 0x000D,
FG_YELLOW = 0x000E,
FG_WHITE = 0x000F,
BG_BLACK = 0x0000,
BG_DARK_BLUE = 0x0010,
BG_DARK_GREEN = 0x0020,
BG_DARK_CYAN = 0x0030,
BG_DARK_RED = 0x0040,
BG_DARK_MAGENTA = 0x0050,
BG_DARK_YELLOW = 0x0060,
BG_GREY = 0x0070,
BG_DARK_GREY = 0x0080,
BG_BLUE = 0x0090,
BG_GREEN = 0x00A0,
BG_CYAN = 0x00B0,
BG_RED = 0x00C0,
BG_MAGENTA = 0x00D0,
BG_YELLOW = 0x00E0,
BG_WHITE = 0x00F0,
};
enum PIXEL_TYPE
{
PIXEL_SOLID = 0x2588,
PIXEL_THREEQUARTERS = 0x2593,
PIXEL_HALF = 0x2592,
PIXEL_QUARTER = 0x2591,
};
class olcSprite
{
public:
olcSprite()
{
}
olcSprite(int w, int h)
{
Create(w, h);
}
olcSprite(wstring sFile)
{
if (!Load(sFile))
Create(8, 8);
}
int nWidth = 0;
int nHeight = 0;
private:
wchar_t *m_Glyphs = nullptr;
short *m_Colours = nullptr;
void Create(int w, int h)
{
nWidth = w;
nHeight = h;
m_Glyphs = new wchar_t[w*h];
m_Colours = new short[w*h];
for (int i = 0; i < w*h; i++)
{
m_Glyphs[i] = L' ';
m_Colours[i] = FG_BLACK;
}
}
public:
void SetGlyph(int x, int y, wchar_t c)
{
7 years ago
if (x <0 || x >= nWidth || y < 0 || y >= nHeight)
return;
else
m_Glyphs[y * nWidth + x] = c;
}
void SetColour(int x, int y, short c)
{
7 years ago
if (x <0 || x >= nWidth || y < 0 || y >= nHeight)
return;
else
m_Colours[y * nWidth + x] = c;
}
wchar_t GetGlyph(int x, int y)
{
7 years ago
if (x <0 || x >= nWidth || y < 0 || y >= nHeight)
return L' ';
else
return m_Glyphs[y * nWidth + x];
}
short GetColour(int x, int y)
{
7 years ago
if (x <0 || x >= nWidth || y < 0 || y >= nHeight)
return FG_BLACK;
else
return m_Colours[y * nWidth + x];
}
wchar_t SampleGlyph(float x, float y)
{
int sx = (int)(x * (float)nWidth);
int sy = (int)(y * (float)nHeight-1.0f);
7 years ago
if (sx <0 || sx >= nWidth || sy < 0 || sy >= nHeight)
return L' ';
else
return m_Glyphs[sy * nWidth + sx];
}
short SampleColour(float x, float y)
{
int sx = (int)(x * (float)nWidth);
int sy = (int)(y * (float)nHeight-1.0f);
7 years ago
if (sx <0 || sx >= nWidth || sy < 0 || sy >= nHeight)
return FG_BLACK;
else
return m_Colours[sy * nWidth + sx];
}
bool Save(wstring sFile)
{
FILE *f = nullptr;
_wfopen_s(&f, sFile.c_str(), L"wb");
if (f == nullptr)
return false;
fwrite(&nWidth, sizeof(int), 1, f);
fwrite(&nHeight, sizeof(int), 1, f);
fwrite(m_Colours, sizeof(short), nWidth * nHeight, f);
fwrite(m_Glyphs, sizeof(wchar_t), nWidth * nHeight, f);
fclose(f);
return true;
}
bool Load(wstring sFile)
{
delete[] m_Glyphs;
delete[] m_Colours;
nWidth = 0;
nHeight = 0;
FILE *f = nullptr;
_wfopen_s(&f, sFile.c_str(), L"rb");
if (f == nullptr)
return false;
fread(&nWidth, sizeof(int), 1, f);
fread(&nHeight, sizeof(int), 1, f);
Create(nWidth, nHeight);
fread(m_Colours, sizeof(short), nWidth * nHeight, f);
fread(m_Glyphs, sizeof(wchar_t), nWidth * nHeight, f);
fclose(f);
return true;
}
};
class olcConsoleGameEngine
{
public:
olcConsoleGameEngine()
{
m_nScreenWidth = 80;
m_nScreenHeight = 30;
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
m_hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
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;
m_sAppName = L"Default";
}
int ConstructConsole(int width, int height, int fontw, int fonth)
{
if (m_hConsole == INVALID_HANDLE_VALUE)
return Error(L"Bad Handle");
m_nScreenWidth = width;
m_nScreenHeight = height;
// Update 13/09/2017 - It seems that the console behaves differently on some systems
// and I'm unsure why this is. It could be to do with windows default settings, or
// screen resolutions, or system languages. Unfortunately, MSDN does not offer much
// by way of useful information, and so the resulting sequence is the reult of experiment
// that seems to work in multiple cases.
//
// The problem seems to be that the SetConsoleXXX functions are somewhat circular and
// fail depending on the state of the current console properties, i.e. you can't set
// the buffer size until you set the screen size, but you can't change the screen size
// until the buffer size is correct. This coupled with a precise ordering of calls
// makes this procedure seem a little mystical :-P. Thanks to wowLinh for helping - Jx9
// Change console visual size to a minimum so ScreenBuffer can shrink
// below the actual visual size
m_rectWindow = { 0, 0, 1, 1 };
SetConsoleWindowInfo(m_hConsole, TRUE, &m_rectWindow);
// Set the size of the screen buffer
COORD coord = { (short)m_nScreenWidth, (short)m_nScreenHeight };
if (!SetConsoleScreenBufferSize(m_hConsole, coord))
Error(L"SetConsoleScreenBufferSize");
// Assign screen buffer to the console
if (!SetConsoleActiveScreenBuffer(m_hConsole))
return Error(L"SetConsoleActiveScreenBuffer");
// Set the font size now that the screen buffer has been assigned to the console
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
cfi.nFont = 0;
cfi.dwFontSize.X = fontw;
cfi.dwFontSize.Y = fonth;
cfi.FontFamily = FF_DONTCARE;
cfi.FontWeight = FW_NORMAL;
7 years ago
//wcscpy_s(cfi.FaceName, L"Liberation Mono");
wcscpy_s(cfi.FaceName, L"Consolas");
if (!SetCurrentConsoleFontEx(m_hConsole, false, &cfi))
return Error(L"SetCurrentConsoleFontEx");
// Get screen buffer info and check the maximum allowed window size. Return
// error if exceeded, so user knows their dimensions/fontsize are too large
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo(m_hConsole, &csbi))
return Error(L"GetConsoleScreenBufferInfo");
if (m_nScreenHeight > csbi.dwMaximumWindowSize.Y)
return Error(L"Screen Height / Font Height Too Big");
if (m_nScreenWidth > csbi.dwMaximumWindowSize.X)
return Error(L"Screen Width / Font Width Too Big");
// Set Physical Console Window Size
m_rectWindow = { 0, 0, (short)m_nScreenWidth - 1, (short)m_nScreenHeight - 1 };
if (!SetConsoleWindowInfo(m_hConsole, TRUE, &m_rectWindow))
return Error(L"SetConsoleWindowInfo");
// Set flags to allow mouse input
if (!SetConsoleMode(m_hConsoleIn, ENABLE_EXTENDED_FLAGS | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT))
return Error(L"SetConsoleMode");
// Allocate memory for screen buffer
m_bufScreen = new CHAR_INFO[m_nScreenWidth*m_nScreenHeight];
memset(m_bufScreen, 0, sizeof(CHAR_INFO) * m_nScreenWidth * m_nScreenHeight);
return 1;
}
virtual void Draw(int x, int y, wchar_t c = 0x2588, short col = 0x000F)
{
if (x >= 0 && x < m_nScreenWidth && y >= 0 && y < m_nScreenHeight)
{
m_bufScreen[y * m_nScreenWidth + x].Char.UnicodeChar = c;
m_bufScreen[y * m_nScreenWidth + x].Attributes = col;
}
}
void Fill(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F)
{
Clip(x1, y1);
Clip(x2, y2);
for (int x = x1; x < x2; x++)
for (int y = y1; y < y2; y++)
Draw(x, y, c, col);
}
void DrawString(int x, int y, wstring c, short col = 0x000F)
{
for (size_t i = 0; i < c.size(); i++)
{
m_bufScreen[y * m_nScreenWidth + x + i].Char.UnicodeChar = c[i];
m_bufScreen[y * m_nScreenWidth + x + i].Attributes = col;
}
}
void DrawStringAlpha(int x, int y, wstring c, short col = 0x000F)
{
for (size_t i = 0; i < c.size(); i++)
{
if (c[i] != L' ')
{
m_bufScreen[y * m_nScreenWidth + x + i].Char.UnicodeChar = c[i];
m_bufScreen[y * m_nScreenWidth + x + i].Attributes = col;
}
}
}
void Clip(int &x, int &y)
{
if (x < 0) x = 0;
if (x >= m_nScreenWidth) x = m_nScreenWidth;
if (y < 0) y = 0;
if (y >= m_nScreenHeight) y = m_nScreenHeight;
}
void DrawLine(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F)
{
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
dx = x2 - x1;
dy = y2 - y1;
dx1 = abs(dx);
dy1 = abs(dy);
px = 2 * dy1 - dx1;
py = 2 * dx1 - dy1;
if (dy1 <= dx1)
{
if (dx >= 0)
{
x = x1;
y = y1;
xe = x2;
}
else
{
x = x2;
y = y2;
xe = x1;
}
Draw(x, y, c, col);
for (i = 0; x<xe; i++)
{
x = x + 1;
if (px<0)
px = px + 2 * dy1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
y = y + 1;
else
y = y - 1;
px = px + 2 * (dy1 - dx1);
}
Draw(x, y, c, col);
}
}
else
{
if (dy >= 0)
{
x = x1;
y = y1;
ye = y2;
}
else
{
x = x2;
y = y2;
ye = y1;
}
Draw(x, y, c, col);
for (i = 0; y<ye; i++)
{
y = y + 1;
if (py <= 0)
py = py + 2 * dx1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
x = x + 1;
else
x = x - 1;
py = py + 2 * (dx1 - dy1);
}
Draw(x, y, c, col);
}
}
}
7 years ago
void DrawCircle(int xc, int yc, int r, wchar_t c = 0x2588, short col = 0x000F)
{
int x = 0;
int y = r;
int p = 3 - 2 * r;
if (!r) return;
while (y >= x) // only formulate 1/8 of circle
{
Draw(xc - x, yc - y, c, col);//upper left left
Draw(xc - y, yc - x, c, col);//upper upper left
Draw(xc + y, yc - x, c, col);//upper upper right
Draw(xc + x, yc - y, c, col);//upper right right
Draw(xc - x, yc + y, c, col);//lower left left
Draw(xc - y, yc + x, c, col);//lower lower left
Draw(xc + y, yc + x, c, col);//lower lower right
Draw(xc + x, yc + y, c, col);//lower right right
if (p < 0) p += 4 * x++ + 6;
else p += 4 * (x++ - y--) + 10;
}
}
7 years ago
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)
return;
for (int i = 0; i < sprite->nWidth; i++)
{
for (int j = 0; j < sprite->nHeight; j++)
{
if (sprite->GetGlyph(i, j) != L' ')
Draw(x + i, y + j, sprite->GetGlyph(i, j), sprite->GetColour(i, j));
}
}
}
void DrawPartialSprite(int x, int y, olcSprite *sprite, int ox, int oy, int w, int h)
{
if (sprite == nullptr)
return;
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
if (sprite->GetGlyph(i+ox, j+oy) != L' ')
Draw(x + i, y + j, sprite->GetGlyph(i+ox, j+oy), sprite->GetColour(i+ox, j+oy));
}
}
}
void DrawWireFrameModel(const vector<pair<float, float>> &vecModelCoordinates, float x, float y, float r = 0.0f, float s = 1.0f, short col = FG_WHITE)
{
// pair.first = x coordinate
// pair.second = y coordinate
// Create translated model vector of coordinate pairs
vector<pair<float, float>> vecTransformedCoordinates;
int verts = vecModelCoordinates.size();
vecTransformedCoordinates.resize(verts);
// Rotate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecModelCoordinates[i].first * cosf(r) - vecModelCoordinates[i].second * sinf(r);
vecTransformedCoordinates[i].second = vecModelCoordinates[i].first * sinf(r) + vecModelCoordinates[i].second * cosf(r);
}
// Scale
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first * s;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second * s;
}
// Translate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first + x;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second + y;
}
// Draw Closed Polygon
for (int i = 0; i < verts + 1; i++)
{
int j = (i + 1);
DrawLine((int)vecTransformedCoordinates[i % verts].first, (int)vecTransformedCoordinates[i % verts].second,
(int)vecTransformedCoordinates[j % verts].first, (int)vecTransformedCoordinates[j % verts].second, PIXEL_SOLID, col);
}
}
~olcConsoleGameEngine()
{
SetConsoleActiveScreenBuffer(m_hOriginalConsole);
delete[] m_bufScreen;
}
public:
void Start()
{
m_bAtomActive = true;
// Star the thread
thread t = thread(&olcConsoleGameEngine::GameThread, this);
// Wait for thread to be exited
t.join();
}
int ScreenWidth()
{
return m_nScreenWidth;
}
int ScreenHeight()
{
return m_nScreenHeight;
}
private:
void GameThread()
{
// Create user resources as part of this thread
if (!OnUserCreate())
m_bAtomActive = false;
auto tp1 = chrono::system_clock::now();
auto tp2 = chrono::system_clock::now();
while (m_bAtomActive)
7 years ago
{
// Run as fast as possible
while (m_bAtomActive)
{
7 years ago
// Handle Timing
tp2 = chrono::system_clock::now();
chrono::duration<float> elapsedTime = tp2 - tp1;
tp1 = tp2;
float fElapsedTime = elapsedTime.count();
// Handle Keyboard Input
for (int i = 0; i < 256; i++)
{
7 years ago
m_keyNewState[i] = GetAsyncKeyState(i);
m_keys[i].bPressed = false;
m_keys[i].bReleased = false;
if (m_keyNewState[i] != m_keyOldState[i])
{
7 years ago
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;
}
}
7 years ago
m_keyOldState[i] = m_keyNewState[i];
}
7 years ago
// 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);
7 years ago
// Handle events - we only care about mouse clicks and movement
// for now
for (DWORD i = 0; i < events; i++)
{
7 years ago
switch (inBuf[i].EventType)
{
case FOCUS_EVENT:
{
m_bConsoleInFocus = inBuf[i].Event.FocusEvent.bSetFocus;
}
break;
case MOUSE_EVENT:
{
switch (inBuf[i].Event.MouseEvent.dwEventFlags)
{
7 years ago
case MOUSE_MOVED:
{
m_mousePosX = inBuf[i].Event.MouseEvent.dwMousePosition.X;
m_mousePosY = inBuf[i].Event.MouseEvent.dwMousePosition.Y;
}
break;
7 years ago
case 0:
{
for (int m = 0; m < 5; m++)
m_mouseNewState[m] = (inBuf[i].Event.MouseEvent.dwButtonState & (1 << m)) > 0;
}
break;
7 years ago
default:
break;
}
}
break;
default:
break;
// We don't care just at the moment
7 years ago
}
}
7 years ago
for (int m = 0; m < 5; m++)
{
7 years ago
m_mouse[m].bPressed = false;
m_mouse[m].bReleased = false;
if (m_mouseNewState[m] != m_mouseOldState[m])
{
7 years ago
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;
}
}
7 years ago
m_mouseOldState[m] = m_mouseNewState[m];
}
7 years ago
// Handle Frame Update
if (!OnUserUpdate(fElapsedTime))
m_bAtomActive = false;
7 years ago
// 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);
}
7 years ago
if (OnUserDestroy())
{
// User has permitted destroy, so exit and clean up
7 years ago
delete[] m_bufScreen;
SetConsoleActiveScreenBuffer(m_hOriginalConsole);
m_cvGameFinished.notify_one();
}
else
{
// User denied destroy for some reason, so continue running
m_bAtomActive = true;
}
}
}
public:
// User MUST OVERRIDE THESE!!
virtual bool OnUserCreate() = 0;
virtual bool OnUserUpdate(float fElapsedTime) = 0;
7 years ago
// Optional for clean up
virtual bool OnUserDestroy()
{
return true;
}
protected:
7 years ago
struct sKeyState
{
bool bPressed;
bool bReleased;
bool bHeld;
} m_keys[256], m_mouse[5];
int m_mousePosX;
int m_mousePosY;
7 years ago
public:
7 years ago
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; }
7 years ago
protected:
int Error(const wchar_t *msg)
{
wchar_t buf[256];
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);
7 years ago
return 0;
}
7 years ago
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<mutex> 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;
7 years ago
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<bool> m_bAtomActive;
static condition_variable m_cvGameFinished;
static mutex m_muxGame;
};
atomic<bool> olcConsoleGameEngine::m_bAtomActive = false;
condition_variable olcConsoleGameEngine::m_cvGameFinished;
mutex olcConsoleGameEngine::m_muxGame;