/* +-------------------------------------------------------------+ | OneLoneCoder RPG Game Engine | | "The Legend of WittyBit, Fantasy Quest VI" - javidx9 | +-------------------------------------------------------------+ What is this? ~~~~~~~~~~~~~ This is the code I created as part of my "Code-It-Yourself! Role Playing Game" series on YouTube. This is NOT a game. The project will compile and demonstrate several systems developed as part of that series. My original intention was to develop a small yet complete RPG, alas real life got in the way. After several months, I've decided to just open the source "as is", so it will contain bugs, be confusing and all round not up to the usual "quality" I strive for. Part 1: https://youtu.be/xXXt3htgDok Part 2: https://youtu.be/AWY_ITpldRk Part 3: https://youtu.be/UcNSb-m4YQU Part 4: https://youtu.be/AnyoUfeNZ1Y License (OLC-3) ~~~~~~~~~~~~~~~ Copyright 2018, 2019 OneLoneCoder.com Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions or derivations of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions or derivative works in binary form must reproduce the above copyright notice. This list of conditions and the following disclaimer must be reproduced in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Links ~~~~~ YouTube: https://www.youtube.com/javidx9 Discord: https://discord.gg/WhwHUMV Twitter: https://www.twitter.com/javidx9 Twitch: https://www.twitch.tv/javidx9 GitHub: https://www.github.com/onelonecoder Homepage: https://www.onelonecoder.com Patreon: https://www.patreon.com/javidx9 Author ~~~~~~ David Barr, aka javidx9, ŠOneLoneCoder 2018, 2019 */ #pragma once #ifndef UNICODE #error Please enable UNICODE for your compiler! VS: Project Properties -> General -> \ Character Set -> Use Unicode. Thanks! - Javidx9 #endif #include #include #include #include #include #include #include using namespace std; #include 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) { 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) { if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return; else m_Colours[y * nWidth + x] = c; } wchar_t GetGlyph(int x, int y) { if (x <0 || x >= nWidth || y < 0 || y >= nHeight) return L' '; else return m_Glyphs[y * nWidth + x]; } short GetColour(int x, int y) { 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); 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); 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 olcConsoleGameEngineOOP { public: olcConsoleGameEngineOOP(); ~olcConsoleGameEngineOOP(); public: int ConstructConsole(int width, int height, int fontw, int fonth); void Start(); public: virtual void Draw(int x, int y, wchar_t c = 0x2588, short col = 0x000F); void Fill(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F); void DrawString(int x, int y, wstring c, short col = 0x000F); void DrawStringAlpha(int x, int y, wstring c, short col = 0x000F); void Clip(int &x, int &y); void DrawLine(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F); void DrawCircle(int xc, int yc, int r, wchar_t c = 0x2588, short col = 0x000F); void FillCircle(int xc, int yc, int r, wchar_t c = 0x2588, short col = 0x000F); void DrawSprite(int x, int y, olcSprite *sprite); void DrawPartialSprite(int x, int y, olcSprite *sprite, int ox, int oy, int w, int h); void DrawWireFrameModel(const vector> &vecModelCoordinates, float x, float y, float r = 0.0f, float s = 1.0f, short col = FG_WHITE); int ScreenWidth(); int ScreenHeight(); private: void GameThread(); protected: // User MUST OVERRIDE THESE!! virtual bool OnUserCreate() = 0; virtual bool OnUserUpdate(float fElapsedTime) = 0; // Optional for clean up virtual bool OnUserDestroy(); int Error(const wchar_t *msg); static BOOL CloseHandler(DWORD evt); protected: struct sKeyState { bool bPressed; bool bReleased; bool bHeld; } m_keys[256], m_mouse[5]; int m_mousePosX; int m_mousePosY; public: 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: 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[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; };