commit
a81f98983e
@ -6,9 +6,6 @@ For the *nix and Mac users - like most Linux software, this
|
||||
may not be fully compatible or fully featured, and development
|
||||
will invariably lag behind the rest - sorry.
|
||||
|
||||
NOTE: DOES NOT SUPPORT SOUND YET
|
||||
NOTE: DOES NOT SUUPORT MOUSE INPUT YET
|
||||
|
||||
I recommend compiling as follows, of course you need SDL from your vendor
|
||||
|
||||
g++ thing.cpp -DUNICODE -I/usr/include/SDL2 -lSDL2 -lpthread -std=C++11
|
||||
@ -18,7 +15,7 @@ some awesome individuals, this would not be a thing!
|
||||
|
||||
=========== IMPORTANT!!! PLEASE READ ==========================================
|
||||
|
||||
Firstly, Thanks to Jack Clarke (JackOJC on Discord and Github). He had the
|
||||
Firstly, Thanks to Jack Clarke (JackOJC on Discord and Github). He had the
|
||||
idea of porting over the olcConsoleGameEngine to non-windows operating systems.
|
||||
|
||||
He developed a prototype which can be found here:
|
||||
@ -27,14 +24,14 @@ which really proved that it could be possible.
|
||||
|
||||
I then added functionality to ensure that this header is 99% compatible with
|
||||
my existing code base, so almost all the videos I've made should be compatible
|
||||
with this version of the olcConsoleGameEngine, without modificatcion.
|
||||
with this version of the olcConsoleGameEngine, without modification.
|
||||
|
||||
Finally I'd also like to thank MrBadNews on Discord for testing and finding
|
||||
many bugs, and creating workarounds, and Slavka on Discord for help debug SDL
|
||||
double buffering issues - stupid SDL :P
|
||||
|
||||
Please remember I, javidx9, am not a Linux or SDL specialist. Therfore please
|
||||
report bugs, or ask for help on the Discord Server, as I probably don't use
|
||||
Please remember I, javidx9, am not a Linux or SDL specialist. Therefore please
|
||||
report bugs, or ask for help on the Discord Server, as I probably don't use
|
||||
the same non-windows operating systems as you, and each have their own methods
|
||||
for building and installing.
|
||||
|
||||
@ -137,6 +134,8 @@ http://www.twitch.tv/javidx9
|
||||
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef UNICODE
|
||||
@ -202,7 +201,7 @@ Character Set -> Use Unicode. Thanks! For now, Ill try enabling it for you - Jav
|
||||
#define VK_F11 SDL_SCANCODE_F11 + KEY_OFFSET
|
||||
#define VK_F12 SDL_SCANCODE_F12 + KEY_OFFSET
|
||||
|
||||
struct CHAR_INFO
|
||||
struct CHAR_INFO
|
||||
{
|
||||
unsigned short glyph;
|
||||
short colour;
|
||||
@ -313,7 +312,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;
|
||||
@ -321,7 +320,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;
|
||||
@ -329,7 +328,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];
|
||||
@ -337,7 +336,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];
|
||||
@ -347,7 +346,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];
|
||||
@ -357,7 +356,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];
|
||||
@ -374,7 +373,7 @@ public:
|
||||
#else
|
||||
wcstombs(buff, sFile.c_str(), 256);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
fopen_s(&f, buff, "wb");
|
||||
#else
|
||||
@ -430,6 +429,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
int len = 0, done = 0, bits = 0, which = 0,
|
||||
sample_size = 0, position = 0, rate = 0;
|
||||
Sint16 *stream[2];
|
||||
|
||||
class olcConsoleGameEngine
|
||||
{
|
||||
@ -439,7 +441,7 @@ public:
|
||||
m_nScreenWidth = 80;
|
||||
m_nScreenHeight = 30;
|
||||
|
||||
|
||||
|
||||
memset(m_keyNewState, 0, 512 * sizeof(bool));
|
||||
memset(m_keyOldState, 0, 512 * sizeof(bool));
|
||||
memset(m_keys._state, 0, 512 * sizeof(sKeyState));
|
||||
@ -456,15 +458,15 @@ public:
|
||||
m_nFontWidth = fontw;
|
||||
m_nFontHeight = fonth;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Allocate memory for screen buffer
|
||||
m_bufScreen[0] = new CHAR_INFO[m_nScreenWidth*m_nScreenHeight];
|
||||
m_bufScreen[1] = new CHAR_INFO[m_nScreenWidth*m_nScreenHeight];
|
||||
// NOTE(MrBadNewS): set buffers to zero, old way
|
||||
memset(m_bufScreen[0], 0, m_nScreenWidth*m_nScreenHeight*sizeof(CHAR_INFO));
|
||||
memset(m_bufScreen[1], 0, m_nScreenWidth*m_nScreenHeight*sizeof(CHAR_INFO));
|
||||
memset(m_bufScreen[0], 0, m_nScreenWidth*m_nScreenHeight * sizeof(CHAR_INFO));
|
||||
memset(m_bufScreen[1], 0, m_nScreenWidth*m_nScreenHeight * sizeof(CHAR_INFO));
|
||||
m_nCurrentBuffer = 0;
|
||||
|
||||
return 1;
|
||||
@ -491,7 +493,7 @@ public:
|
||||
void DrawString(int x, int y, std::wstring c, short col = 0x000F)
|
||||
{
|
||||
for (size_t i = 0; i < c.size(); i++)
|
||||
{
|
||||
{
|
||||
m_bufScreen[m_nCurrentBuffer][y * m_nScreenWidth + x + i].glyph = c[i];
|
||||
m_bufScreen[m_nCurrentBuffer][y * m_nScreenWidth + x + i].colour = col;
|
||||
}
|
||||
@ -541,14 +543,14 @@ public:
|
||||
xe = x1;
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
for (i = 0; x<xe; i++)
|
||||
for (i = 0; x < xe; i++)
|
||||
{
|
||||
x = x + 1;
|
||||
if (px<0)
|
||||
if (px < 0)
|
||||
px = px + 2 * dy1;
|
||||
else
|
||||
{
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0))
|
||||
if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))
|
||||
y = y + 1;
|
||||
else
|
||||
y = y - 1;
|
||||
@ -572,14 +574,14 @@ public:
|
||||
ye = y1;
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
for (i = 0; y<ye; i++)
|
||||
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))
|
||||
if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))
|
||||
x = x + 1;
|
||||
else
|
||||
x = x - 1;
|
||||
@ -710,7 +712,7 @@ public:
|
||||
|
||||
~olcConsoleGameEngine()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
@ -718,7 +720,7 @@ public:
|
||||
{
|
||||
m_bAtomActive = true;
|
||||
|
||||
// Star the thread
|
||||
// Start the thread
|
||||
std::thread t = std::thread(&olcConsoleGameEngine::GameThread, this);
|
||||
|
||||
// Wait for thread to be exited
|
||||
@ -736,10 +738,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void EnableSound() {
|
||||
m_bEnableSound = true;
|
||||
}
|
||||
|
||||
void GameThread()
|
||||
{
|
||||
// Start SDL
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO);
|
||||
|
||||
char bufAppName[256];
|
||||
#ifdef _WIN32
|
||||
@ -798,30 +804,84 @@ private:
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
m_bAtomActive = false;
|
||||
case SDL_QUIT:
|
||||
m_bAtomActive = false;
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
int k = e.key.keysym.scancode; // SDL Scancode
|
||||
m_keyNewState[k] = true;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
int k = e.key.keysym.scancode; // SDL Scancode
|
||||
m_keyNewState[k] = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYUP:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
int k = e.key.keysym.scancode; // SDL Scancode
|
||||
m_keyNewState[k] = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
m_mousePosX = e.motion.x / m_nFontWidth;
|
||||
m_mousePosY = e.motion.y / m_nFontHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
switch (e.button.button)
|
||||
{
|
||||
int k = e.key.keysym.scancode; // SDL Scancode
|
||||
m_keyNewState[k] = false;
|
||||
case SDL_BUTTON_LEFT:
|
||||
m_mouseNewState[0] = 1;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
m_mouseNewState[1] = 1;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
m_mouseNewState[2] = 1;
|
||||
break;
|
||||
case SDL_BUTTON_X1:
|
||||
m_mouseNewState[3] = 1;
|
||||
break;
|
||||
case SDL_BUTTON_X2:
|
||||
m_mouseNewState[4] = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
|
||||
switch (e.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
m_mouseNewState[0] = 0;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
m_mouseNewState[1] = 0;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
m_mouseNewState[2] = 0;
|
||||
break;
|
||||
case SDL_BUTTON_X1:
|
||||
m_mouseNewState[3] = 0;
|
||||
break;
|
||||
case SDL_BUTTON_X2:
|
||||
m_mouseNewState[4] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Keyboard Input
|
||||
for (int k = 0; k < 512; k++)
|
||||
{
|
||||
{
|
||||
m_keys._state[k].bPressed = false;
|
||||
m_keys._state[k].bReleased = false;
|
||||
|
||||
@ -842,57 +902,7 @@ private:
|
||||
m_keyOldState[k] = m_keyNewState[k];
|
||||
}
|
||||
|
||||
// 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)
|
||||
// {
|
||||
// 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 0:
|
||||
// {
|
||||
// for (int m = 0; m < 5; m++)
|
||||
// m_mouseNewState[m] = (inBuf[i].Event.MouseEvent.dwButtonState & (1 << m)) > 0;
|
||||
|
||||
// }
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// break;
|
||||
// // We don't care just at the moment
|
||||
// }
|
||||
//}
|
||||
|
||||
/*for (int m = 0; m < 5; m++)
|
||||
for (int m = 0; m < 5; m++)
|
||||
{
|
||||
m_mouse[m].bPressed = false;
|
||||
m_mouse[m].bReleased = false;
|
||||
@ -913,9 +923,81 @@ private:
|
||||
|
||||
m_mouseOldState[m] = m_mouseNewState[m];
|
||||
}
|
||||
*/
|
||||
|
||||
// Handle Frame Update
|
||||
// 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)
|
||||
// {
|
||||
// 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 0:
|
||||
// {
|
||||
// for (int m = 0; m < 5; m++)
|
||||
// m_mouseNewState[m] = (inBuf[i].Event.MouseEvent.dwButtonState & (1 << m)) > 0;
|
||||
|
||||
// }
|
||||
// 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])
|
||||
{
|
||||
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];
|
||||
}
|
||||
*/
|
||||
|
||||
// Handle Frame Update
|
||||
if (!OnUserUpdate(fElapsedTime))
|
||||
m_bAtomActive = false;
|
||||
|
||||
@ -927,7 +1009,7 @@ private:
|
||||
// Render differences
|
||||
CHAR_INFO *buff_old, *buff_new;
|
||||
buff_new = m_bufScreen[m_nCurrentBuffer];
|
||||
buff_old = m_bufScreen[(m_nCurrentBuffer+1) % 2];
|
||||
buff_old = m_bufScreen[(m_nCurrentBuffer + 1) % 2];
|
||||
|
||||
SDL_SetRenderTarget(m_render, m_screen);
|
||||
|
||||
@ -939,13 +1021,13 @@ private:
|
||||
if (buff_new[i].colour != buff_old[i].colour || buff_new[i].glyph != buff_old[i].glyph)
|
||||
{
|
||||
// Draw Cell
|
||||
int cx = (buff_new[i].glyph-0) % 64;
|
||||
int cy = (buff_new[i].glyph-0) / 64;
|
||||
int cx = (buff_new[i].glyph - 0) % 64;
|
||||
int cy = (buff_new[i].glyph - 0) / 64;
|
||||
|
||||
// Draw Foreground
|
||||
int cidx = (buff_new[i].colour & 0x00F0) >> 4;
|
||||
//SDL_Rect src_bg = { (cidx+45) * 8, 88, 8, 8 };
|
||||
SDL_Rect src_bg = { (cidx+0) * 16, 0, 16, 16 };
|
||||
SDL_Rect src_bg = { (cidx + 0) * 16, 0, 16, 16 };
|
||||
SDL_Rect dst = { x * m_nFontWidth, y * m_nFontHeight, m_nFontWidth, m_nFontHeight };
|
||||
SDL_SetTextureColorMod(m_fontFile, 255, 255, 255);
|
||||
SDL_SetTextureAlphaMod(m_fontFile, 255);
|
||||
@ -967,7 +1049,7 @@ private:
|
||||
SDL_SetRenderTarget(m_render, nullptr);
|
||||
SDL_RenderCopy(m_render, m_screen, nullptr, nullptr);
|
||||
SDL_RenderPresent(m_render);
|
||||
|
||||
|
||||
|
||||
// Flip buffers
|
||||
m_nCurrentBuffer = (m_nCurrentBuffer + 1) % 2;
|
||||
@ -1004,7 +1086,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
struct sKeyState
|
||||
@ -1027,8 +1108,8 @@ protected:
|
||||
{
|
||||
int scode = (SDL_Scancode)nKeyID - KEY_OFFSET;
|
||||
return _state[scode];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (nKeyID)
|
||||
{
|
||||
case L'A': scode = SDL_SCANCODE_A; break;
|
||||
@ -1068,16 +1149,16 @@ protected:
|
||||
case L'8': scode = SDL_SCANCODE_8; break;
|
||||
case L'9': scode = SDL_SCANCODE_9; break;
|
||||
|
||||
/*case L'\'': scode = SDL_SCANCODE_APOSTROPHE; break;
|
||||
case L'\\': scode = SDL_SCANCODE_BACKSLASH; break;
|
||||
case L',': scode = SDL_SCANCODE_COMMA; break;
|
||||
case L'=': scode = SDL_SCANCODE_EQUALS; break;
|
||||
case L'[': scode = SDL_SCANCODE_LEFTBRACKET; break;
|
||||
case L']': scode = SDL_SCANCODE_RIGHTBRACKET; break;
|
||||
case L'-': scode = SDL_SCANCODE_MINUS; break;
|
||||
case L'.': scode = SDL_SCANCODE_PERIOD; break;
|
||||
case L';': scode = SDL_SCANCODE_SEMICOLON; break;
|
||||
case L'/': scode = SDL_SCANCODE_SLASH; break;*/
|
||||
/*case L'\'': scode = SDL_SCANCODE_APOSTROPHE; break;
|
||||
case L'\\': scode = SDL_SCANCODE_BACKSLASH; break;
|
||||
case L',': scode = SDL_SCANCODE_COMMA; break;
|
||||
case L'=': scode = SDL_SCANCODE_EQUALS; break;
|
||||
case L'[': scode = SDL_SCANCODE_LEFTBRACKET; break;
|
||||
case L']': scode = SDL_SCANCODE_RIGHTBRACKET; break;
|
||||
case L'-': scode = SDL_SCANCODE_MINUS; break;
|
||||
case L'.': scode = SDL_SCANCODE_PERIOD; break;
|
||||
case L';': scode = SDL_SCANCODE_SEMICOLON; break;
|
||||
case L'/': scode = SDL_SCANCODE_SLASH; break;*/
|
||||
|
||||
default: scode = (SDL_Scancode)nKeyID;
|
||||
}
|
||||
@ -1085,21 +1166,23 @@ protected:
|
||||
return _state[scode];
|
||||
}
|
||||
|
||||
} m_keys;
|
||||
} m_keys;
|
||||
|
||||
sKeyState m_mouse[5];
|
||||
|
||||
int m_mousePosX;
|
||||
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]; }
|
||||
sKeyState GetMouse(int nMouseButtonID) { return m_mouse[nMouseButtonID]; }
|
||||
bool IsFocused() { return m_bConsoleInFocus; }
|
||||
|
||||
|
||||
@ -1115,14 +1198,19 @@ protected:
|
||||
{
|
||||
// Load image.
|
||||
SDL_Surface* temp = SDL_LoadBMP(fname.c_str());
|
||||
|
||||
|
||||
if (temp == nullptr) {
|
||||
std::wcout << L"Please download the necessary bmp file too!\n";
|
||||
throw 1;
|
||||
}
|
||||
|
||||
// set color key to 255,0,255; this basically makes
|
||||
// it transparent.
|
||||
SDL_SetColorKey(temp, SDL_TRUE, SDL_MapRGB(temp->format, 255, 0, 255));
|
||||
|
||||
|
||||
// Convert to texture.
|
||||
m_fontFile = SDL_CreateTextureFromSurface(m_render, temp);
|
||||
|
||||
m_fontFile = SDL_CreateTextureFromSurface(m_render, temp);
|
||||
|
||||
// Cleanup and return.
|
||||
SDL_FreeSurface(temp);
|
||||
}
|
||||
@ -1156,6 +1244,7 @@ protected:
|
||||
bool m_mouseOldState[5] = { 0 };
|
||||
bool m_mouseNewState[5] = { 0 };
|
||||
bool m_bConsoleInFocus = true;
|
||||
bool m_bEnableSound = false;
|
||||
static std::atomic<bool> m_bAtomActive;
|
||||
static std::condition_variable m_cvGameFinished;
|
||||
static std::mutex m_muxGame;
|
||||
@ -1166,10 +1255,253 @@ private:
|
||||
SDL_Texture *m_screen;
|
||||
SDL_Texture* m_fontFile;
|
||||
|
||||
protected: // Audio Engine =====================================================================
|
||||
|
||||
class olcAudioSample
|
||||
{
|
||||
public:
|
||||
olcAudioSample()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
olcAudioSample(std::string sWavFile, olcConsoleGameEngine &cge)
|
||||
{
|
||||
uint8_t *wavData;
|
||||
SDL_AudioSpec fileSpec;
|
||||
uint32_t streamLen = 0;
|
||||
if (!SDL_LoadWAV(sWavFile.c_str(), &fileSpec, (uint8_t **)&wavData, &streamLen)) {
|
||||
std::cout << "Couldn't load audio file!\n" << SDL_GetError() << '\n';
|
||||
bSampleValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioCVT cvt;
|
||||
if (!SDL_BuildAudioCVT(&cvt, fileSpec.format, fileSpec.channels, fileSpec.freq,
|
||||
cge.sampleSpec.format, cge.sampleSpec.channels, cge.sampleSpec.freq)) {
|
||||
std::cout << "Failed to build cvt!\n" << SDL_GetError() << '\n';
|
||||
bSampleValid = false;
|
||||
return;
|
||||
}
|
||||
cvt.buf = (uint8_t *)malloc(streamLen * cvt.len_mult);
|
||||
cvt.len = streamLen;
|
||||
memcpy(cvt.buf, wavData, streamLen);
|
||||
SDL_FreeWAV((uint8_t *)wavData);
|
||||
if (SDL_ConvertAudio(&cvt) == -1) {
|
||||
std::cout << "Failed to convert audio!\n" << SDL_GetError() << '\n';
|
||||
bSampleValid = false;
|
||||
return;
|
||||
}
|
||||
fSample = (float *)cvt.buf;
|
||||
nSamples = cvt.len_cvt / sizeof(float) / cge.spec.channels;
|
||||
bSampleValid = true;
|
||||
}
|
||||
|
||||
~olcAudioSample() {
|
||||
SDL_FreeWAV((uint8_t *)fSample);
|
||||
}
|
||||
|
||||
float *fSample;
|
||||
uint32_t nSamples = 0;
|
||||
bool bSampleValid = false;
|
||||
};
|
||||
|
||||
// This vector holds all loaded sound samples in memory
|
||||
std::vector<olcAudioSample> vecAudioSamples;
|
||||
|
||||
// This structure represents a sound that is currently playing. It only
|
||||
// holds the sound ID and where this instance of it is up to for its
|
||||
// current playback
|
||||
struct sCurrentlyPlayingSample
|
||||
{
|
||||
int nAudioSampleID = 0;
|
||||
long nSamplePosition = 0;
|
||||
bool bFinished = false;
|
||||
bool bLoop = false;
|
||||
};
|
||||
std::list<sCurrentlyPlayingSample> listActiveSamples;
|
||||
|
||||
// Load a 16-bit WAVE file @ 44100Hz ONLY into memory. A sample ID
|
||||
// number is returned if successful, otherwise -1
|
||||
unsigned int LoadAudioSample(std::string sWavFile)
|
||||
{
|
||||
if (!m_bEnableSound)
|
||||
return -1;
|
||||
|
||||
olcAudioSample a(sWavFile, *this);
|
||||
if (a.bSampleValid)
|
||||
{
|
||||
vecAudioSamples.push_back(a);
|
||||
return vecAudioSamples.size();
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add sample 'id' to the mixers sounds to play list
|
||||
void PlaySample(int id, bool bLoop = false)
|
||||
{
|
||||
sCurrentlyPlayingSample a;
|
||||
a.nAudioSampleID = id;
|
||||
a.nSamplePosition = 0;
|
||||
a.bFinished = false;
|
||||
a.bLoop = bLoop;
|
||||
listActiveSamples.push_back(a);
|
||||
}
|
||||
|
||||
void StopSample(int id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// The audio system uses by default a specific wave format
|
||||
bool CreateAudio(unsigned int nSampleRate = 44100, unsigned int nChannels = 1,
|
||||
unsigned int nBlocks = 8, unsigned int nBlockSamples = 512)
|
||||
{
|
||||
SDL_AudioSpec wanted;
|
||||
SDL_zero(wanted);
|
||||
wanted.channels = nChannels;
|
||||
wanted.format = AUDIO_S16;
|
||||
wanted.freq = nSampleRate;
|
||||
wanted.samples = nBlockSamples;
|
||||
wanted.userdata = this;
|
||||
wanted.callback = forwardCallback;
|
||||
SDL_zero(sampleSpec);
|
||||
sampleSpec.channels = nChannels;
|
||||
sampleSpec.format = AUDIO_F32;
|
||||
sampleSpec.freq = nSampleRate;
|
||||
sampleSpec.userdata = this;
|
||||
|
||||
deviceID = SDL_OpenAudioDevice(NULL, 0, &wanted, &spec, 0);
|
||||
|
||||
if (deviceID == 0) {
|
||||
std::cout << "Failed to open audio device!\n" << SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(deviceID, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop and clean up audio system
|
||||
bool DestroyAudio()
|
||||
{
|
||||
SDL_CloseAudioDevice(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void forwardCallback(void *userdata, uint8_t *byteStream, int len) {
|
||||
static_cast<olcConsoleGameEngine *>(userdata)->AudioThread(userdata, byteStream, len);
|
||||
}
|
||||
|
||||
// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
|
||||
// with audio data. If no requests are available it goes dormant until the sound
|
||||
// card is ready for more data. The block is fille by the "user" in some manner
|
||||
// and then issued to the soundcard.
|
||||
void AudioThread(void *userdata, uint8_t *byteStream, int len)
|
||||
{
|
||||
m_fGlobalTime = 0.0f;
|
||||
float fTimeStep = 1.0f / (float)spec.freq;
|
||||
|
||||
// Goofy hack to get maximum integer for a type at run-time
|
||||
short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
|
||||
float fMaxSample = (float)nMaxSample;
|
||||
short nPreviousSample = 0;
|
||||
|
||||
// Prepare block for processing
|
||||
memset(byteStream, 0, len);
|
||||
|
||||
int16_t *buf = (int16_t *)byteStream;
|
||||
|
||||
auto clip = [](float fSample, float fMax)
|
||||
{
|
||||
if (fSample >= 0.0)
|
||||
return fmin(fSample, fMax);
|
||||
else
|
||||
return fmax(fSample, -fMax);
|
||||
};
|
||||
|
||||
uint32_t i = 0;
|
||||
for (unsigned int n = 0; n < len / sizeof(int16_t); n += spec.channels)
|
||||
{
|
||||
// User Process
|
||||
for (unsigned int c = 0; c < spec.channels; c++)
|
||||
{
|
||||
int16_t sample = (int16_t)(clip(GetMixerOutput(c, fTimeStep * i, fTimeStep), 1.0) * fMaxSample);
|
||||
buf[i] = sample;
|
||||
i++;
|
||||
}
|
||||
|
||||
m_fGlobalTime = m_fGlobalTime + fTimeStep;
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden by user if they want to generate sound in real-time
|
||||
virtual float onUserSoundSample(int nChannel, float fGlobalTime, float fTimeStep)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Overriden by user if they want to manipulate the sound before it is played
|
||||
virtual float onUserSoundFilter(int nChannel, float fGlobalTime, float fSample)
|
||||
{
|
||||
return fSample;
|
||||
}
|
||||
|
||||
// The Sound Mixer - If the user wants to play many sounds simultaneously, and
|
||||
// perhaps the same sound overlapping itself, then you need a mixer, which
|
||||
// takes input from all sound sources for that audio frame. This mixer maintains
|
||||
// a list of sound locations for all concurrently playing audio samples. Instead
|
||||
// of duplicating audio data, we simply store the fact that a sound sample is in
|
||||
// use and an offset into its sample data. As time progresses we update this offset
|
||||
// until it is beyound the length of the sound sample it is attached to. At this
|
||||
// point we remove the playing souind from the list.
|
||||
//
|
||||
// Additionally, the users application may want to generate sound instead of just
|
||||
// playing audio clips (think a synthesizer for example) in which case we also
|
||||
// provide an "onUser..." event to allow the user to return a sound for that point
|
||||
// in time.
|
||||
//
|
||||
// Finally, before the sound is issued to the operating system for performing, the
|
||||
// user gets one final chance to "filter" the sound, perhaps changing the volume
|
||||
// or adding funky effects
|
||||
float GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep)
|
||||
{
|
||||
// Accumulate sample for this channel
|
||||
float fMixerSample = 0.0f;
|
||||
|
||||
for (auto &s : listActiveSamples)
|
||||
{
|
||||
// Calculate sample position
|
||||
if (nChannel == 0)
|
||||
s.nSamplePosition += (long)((float)spec.freq * fTimeStep);
|
||||
|
||||
// If sample position is valid add to the mix
|
||||
if (s.nSamplePosition < vecAudioSamples[s.nAudioSampleID - 1].nSamples)
|
||||
fMixerSample += vecAudioSamples[s.nAudioSampleID - 1].fSample[(s.nSamplePosition * spec.channels) + nChannel];
|
||||
else
|
||||
s.bFinished = true; // Else sound has completed
|
||||
}
|
||||
|
||||
// If sounds have completed then remove them
|
||||
listActiveSamples.remove_if([](const sCurrentlyPlayingSample &s) {return s.bFinished; });
|
||||
|
||||
// The users application might be generating sound, so grab that if it exists
|
||||
fMixerSample += onUserSoundSample(nChannel, fGlobalTime, fTimeStep);
|
||||
|
||||
// Return the sample via an optional user override to filter the sound
|
||||
return onUserSoundFilter(nChannel, fGlobalTime, fMixerSample);
|
||||
return fMixerSample;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID deviceID;
|
||||
SDL_AudioSpec spec, sampleSpec;
|
||||
|
||||
//thx J. Random Programmer
|
||||
std::atomic<float> m_fGlobalTime = {0.0f};
|
||||
};
|
||||
|
||||
std::atomic<bool> olcConsoleGameEngine::m_bAtomActive(false);
|
||||
std::condition_variable olcConsoleGameEngine::m_cvGameFinished;
|
||||
std::mutex olcConsoleGameEngine::m_muxGame;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user