diff --git a/olcRolePlayingGame/RPGVideo.vcxproj b/olcRolePlayingGame/RPGVideo.vcxproj new file mode 100644 index 0000000..6704d87 --- /dev/null +++ b/olcRolePlayingGame/RPGVideo.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + 15.0 + {73460557-14A0-4160-8A8F-BCBC4B271238} + Win32Proj + RPGVideo + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + + + true + + + true + + + false + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + Console + true + true + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/olcRolePlayingGame/RPGVideo.vcxproj.filters b/olcRolePlayingGame/RPGVideo.vcxproj.filters new file mode 100644 index 0000000..230e273 --- /dev/null +++ b/olcRolePlayingGame/RPGVideo.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/olcRolePlayingGame/RPGVideo.vcxproj.user b/olcRolePlayingGame/RPGVideo.vcxproj.user new file mode 100644 index 0000000..6e2aec7 --- /dev/null +++ b/olcRolePlayingGame/RPGVideo.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Assets.cpp b/olcRolePlayingGame/RPG_Assets.cpp new file mode 100644 index 0000000..ae5019a --- /dev/null +++ b/olcRolePlayingGame/RPG_Assets.cpp @@ -0,0 +1,137 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Assets.h" +#include "RPG_Maps.h" +#include "RPG_Items.h" + +RPG_Assets::RPG_Assets() +{ +} + + +RPG_Assets::~RPG_Assets() +{ +} + +void RPG_Assets::LoadSprites() +{ + auto load = [&](string sName, wstring sFileName) + { + olcSprite* s = new olcSprite(sFileName); + m_mapSprites[sName] = s; + }; + + load("village", L"rpgdata/gfx/toml_spritesheetdark.spr"); + + load("skelly", L"rpgdata/gfx/toml_Char001.png.spr"); + load("player", L"rpgdata/gfx/toml_CharacterSprites.spr"); + load("font", L"rpgdata/gfx/javidx9_nesfont8x8.spr"); + load("worldmap", L"rpgdata/gfx/worldmap1.png.spr"); + load("skymap", L"rpgdata/gfx/sky1.png.spr"); + load("title", L"rpgdata/gfx/title3.png.spr"); + load("balloon", L"rpgdata/gfx/balloon1.png.spr"); + load("sword", L"rpgdata/gfx/Sword.spr"); + load("hitech", L"rpgdata/gfx/toml_modernish.spr"); + + load("purple", L"rpgdata/gfx/toml_purple.spr"); + + load("health", L"rpgdata/gfx/item_health.spr"); + load("healthboost", L"rpgdata/gfx/item_healthboost.spr"); + + load("Basic Sword", L"rpgdata/gfx/weapon_basic_sword.spr"); +} + +void RPG_Assets::LoadMaps() +{ + auto load = [&](cMap* m) + { + m_mapMaps[m->sName] = m; + }; + + load(new cMap_Village1()); + load(new cMap_Home1()); + +} + + +void RPG_Assets::LoadItems() +{ + auto load = [&](cItem* i) + { + m_mapItems[i->sName] = i; + }; + + load(new cItem_Health()); + load(new cItem_HealthBoost()); + + load(new cWeapon_Sword()); +} diff --git a/olcRolePlayingGame/RPG_Assets.h b/olcRolePlayingGame/RPG_Assets.h new file mode 100644 index 0000000..2f63e4f --- /dev/null +++ b/olcRolePlayingGame/RPG_Assets.h @@ -0,0 +1,122 @@ +/* + +-------------------------------------------------------------+ + | 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 + +#include "olcConsoleGameEngineOOP.h" + +#include + +class cMap; +class cItem; + +class RPG_Assets +{ +public: + static RPG_Assets& get() + { + static RPG_Assets me; + return me; + } + + RPG_Assets(RPG_Assets const&) = delete; + void operator=(RPG_Assets const&) = delete; + + olcSprite* GetSprite(string name) + { + return m_mapSprites[name]; + } + + cMap* GetMap(string name) + { + return m_mapMaps[name]; + } + + cItem* GetItem(string name) + { + return m_mapItems[name]; + } + + void LoadSprites(); + void LoadMaps(); + void LoadItems(); + + +private: + RPG_Assets(); + ~RPG_Assets(); + + map m_mapSprites; + map m_mapMaps; + map m_mapItems; +}; + + diff --git a/olcRolePlayingGame/RPG_Commands.cpp b/olcRolePlayingGame/RPG_Commands.cpp new file mode 100644 index 0000000..e7195ae --- /dev/null +++ b/olcRolePlayingGame/RPG_Commands.cpp @@ -0,0 +1,201 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Commands.h" +#include "RPG_Engine.h" +#include "RPG_Quests.h" + +RPG_Engine* cCommand::g_engine = nullptr; + +cScriptProcessor::cScriptProcessor() +{ + bUserControlEnabled = true; +} + +void cScriptProcessor::AddCommand(cCommand *cmd) +{ + m_listCommands.push_back(cmd); +} + +void cScriptProcessor::ProcessCommands(float fElapsedTime) +{ + // If command are available, halt user control + bUserControlEnabled = m_listCommands.empty(); + + if (!m_listCommands.empty()) + { + // A command is available + if (!m_listCommands.front()->bCompleted) + { + // Command has not been started + if (!m_listCommands.front()->bStarted) + { + m_listCommands.front()->Start(); + m_listCommands.front()->bStarted = true; + } + else // Command has been started so process it + m_listCommands.front()->Update(fElapsedTime); + } + else + { + // Command has been completed + delete m_listCommands.front(); + m_listCommands.pop_front(); + } + } +} + +// Marks currently active command as complete, from external source +void cScriptProcessor::CompleteCommand() +{ + if (!m_listCommands.empty()) + { + m_listCommands.front()->bCompleted = true; + } +} + + + + +cCommand_MoveTo::cCommand_MoveTo(cDynamic* object, float x, float y, float duration) +{ + m_fTargetPosX = x; + m_fTargetPosY = y; + m_fTimeSoFar = 0.0f; + m_fDuration = max(duration, 0.001f); + m_pObject = object; +} + +void cCommand_MoveTo::Start() +{ + m_fStartPosX = m_pObject->px; + m_fStartPosY = m_pObject->py; +} + +void cCommand_MoveTo::Update(float fElapsedTime) +{ + m_fTimeSoFar += fElapsedTime; + float t = m_fTimeSoFar / m_fDuration; + if (t > 1.0f) t = 1.0f; + + + m_pObject->px = (m_fTargetPosX - m_fStartPosX) * t + m_fStartPosX; + m_pObject->py = (m_fTargetPosY - m_fStartPosY) * t + m_fStartPosY; + m_pObject->vx = (m_fTargetPosX - m_fStartPosX) / m_fDuration; + m_pObject->vy = (m_fTargetPosY - m_fStartPosY) / m_fDuration; + + if (m_fTimeSoFar >= m_fDuration) + { + // Object has reached destination, so stop + m_pObject->px = m_fTargetPosX; + m_pObject->py = m_fTargetPosY; + m_pObject->vx = 0.0f; + m_pObject->vy = 0.0f; + bCompleted = true; + } +} + + +cCommand_ShowDialog::cCommand_ShowDialog(vector line) +{ + vecLines = line; +} + +void cCommand_ShowDialog::Start() +{ + g_engine->ShowDialog(vecLines); +} + + +cCommand_ChangeMap::cCommand_ChangeMap(string mapName, float mapPosX, float mapPosY) +{ + m_sMapName = mapName; + m_fMapPosX = mapPosX; + m_fMapPosY = mapPosY; +} + + +void cCommand_ChangeMap::Start() +{ + g_engine->ChangeMap(m_sMapName, m_fMapPosX, m_fMapPosY); + bCompleted = true; +} + +cCommand_AddQuest::cCommand_AddQuest(cQuest* quest) +{ + m_quest = quest; +} + +void cCommand_AddQuest::Start() +{ + g_engine->AddQuest(m_quest); + bCompleted = true; +} \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Commands.h b/olcRolePlayingGame/RPG_Commands.h new file mode 100644 index 0000000..91dac8a --- /dev/null +++ b/olcRolePlayingGame/RPG_Commands.h @@ -0,0 +1,165 @@ +/* + +-------------------------------------------------------------+ + | 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 + +#include "RPG_Dynamics.h" +#include +using namespace std; + +class RPG_Engine; +class cQuest; + +class cCommand +{ +public: + cCommand() {} + virtual ~cCommand() {} + + bool bStarted = false; + bool bCompleted = false; + + virtual void Start() {} + virtual void Update(float fElapsedTime) {}; + + static RPG_Engine* g_engine; +}; + + +class cScriptProcessor +{ +public: + cScriptProcessor(); + +public: + void AddCommand(cCommand *cmd); + void ProcessCommands(float fElapsedTime); + void CompleteCommand(); + +public: + bool bUserControlEnabled; + +private: + list m_listCommands; +}; + + +class cCommand_MoveTo : public cCommand +{ +public: + cCommand_MoveTo(cDynamic* object, float x, float y, float duration = 0.0f); + void Start() override; + void Update(float fElapsedTime) override; + +private: + cDynamic * m_pObject; + float m_fStartPosX; + float m_fStartPosY; + float m_fTargetPosX; + float m_fTargetPosY; + float m_fDuration; + float m_fTimeSoFar; +}; + + +class cCommand_ShowDialog : public cCommand +{ +public: + cCommand_ShowDialog(vector line); + void Start() override; + +private: + vector vecLines; +}; + + +class cCommand_ChangeMap : public cCommand +{ +public: + cCommand_ChangeMap(string mapName, float mapPosX, float mapPosY); + void Start() override; + +private: + string m_sMapName; + float m_fMapPosX; + float m_fMapPosY; +}; + +class cCommand_AddQuest : public cCommand +{ +public: + cCommand_AddQuest(cQuest* quest); + void Start() override; + +private: + cQuest * m_quest; +}; + diff --git a/olcRolePlayingGame/RPG_Dynamics.cpp b/olcRolePlayingGame/RPG_Dynamics.cpp new file mode 100644 index 0000000..a4cac9b --- /dev/null +++ b/olcRolePlayingGame/RPG_Dynamics.cpp @@ -0,0 +1,373 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Dynamics.h" + +#include "RPG_Engine.h" + +RPG_Engine* cDynamic::g_engine = nullptr; + +cDynamic::cDynamic(string n) +{ + sName = n; + px = 0.0f; + py = 0.0f; + vx = 0.0f; + vy = 0.0f; + bSolidVsMap = true; + bSolidVsDyn = true; + bFriendly = true; + bRedundant = false; + bIsAttackable = false; + bIsProjectile = false; +} + +cDynamic::~cDynamic() +{ + +} + + +///////////////////////////////////////////////////////////////////// + +cDynamic_Creature::cDynamic_Creature(string name, olcSprite *sprite) : cDynamic(name) +{ + m_pSprite = sprite; + nHealth = 10; + nHealthMax = 10; + m_nFacingDirection = SOUTH; + m_nGraphicState = STANDING; + m_nGraphicCounter = 0; + m_fTimer = 0.0f; + bIsAttackable = true; +} + +void cDynamic_Creature::Update(float fElapsedTime, cDynamic* player) +{ + if (m_fKnockBackTimer > 0.0f) + { + vx = m_fKnockBackDX * 10.0f; + vy = m_fKnockBackDY * 10.0f; + bIsAttackable = false; + m_fKnockBackTimer -= fElapsedTime; + if (m_fKnockBackTimer <= 0.0f) + { + m_fStateTick = 0.0f; + bControllable = true; + bIsAttackable = true; + } + + } + else + { + bSolidVsDyn = true; + m_fTimer += fElapsedTime; + if (m_fTimer >= 0.2f) + { + m_fTimer -= 0.2f; + m_nGraphicCounter++; + m_nGraphicCounter %= 2; + } + + if (fabs(vx) > 0 || fabs(vy) > 0) + m_nGraphicState = WALKING; + else + m_nGraphicState = STANDING; + + if (nHealth <= 0) + m_nGraphicState = DEAD; + + if (vx < 0.0f) m_nFacingDirection = WEST; + if (vx > 0.0f) m_nFacingDirection = EAST; + if (vy < -0.0f) m_nFacingDirection = NORTH; + if (vy > 0.0f) m_nFacingDirection = SOUTH; + + Behaviour(fElapsedTime, player); + } +} + +void cDynamic_Creature::KnockBack(float dx, float dy, float dist) +{ + m_fKnockBackDX = dx; + m_fKnockBackDY = dy; + m_fKnockBackTimer = dist; + bSolidVsDyn = false; + bControllable = false; + bIsAttackable = false; +} + + +void cDynamic_Creature::DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) +{ + int nSheetOffsetX = 0; + int nSheetOffsetY = 0; + + switch (m_nGraphicState) + { + case STANDING: + nSheetOffsetX = m_nFacingDirection * 16; + break; + + case WALKING: + nSheetOffsetX = m_nFacingDirection * 16; + nSheetOffsetY = m_nGraphicCounter * 16; + break; + + case CELEBRATING: + nSheetOffsetX = 4 * 16; + break; + + case DEAD: + nSheetOffsetX = 4 * 16; + nSheetOffsetY = 1 * 16; + break; + + } + + gfx->DrawPartialSprite((px - ox) * 16.0f, (py - oy)*16.0f, m_pSprite, nSheetOffsetX, nSheetOffsetY, 16, 16); +} + +void cDynamic_Creature::Behaviour(float fElapsedTime, cDynamic* player) +{ + // No default behaviour +} + + + + +cDynamic_Creature_Witty::cDynamic_Creature_Witty() : cDynamic_Creature("witty", RPG_Assets::get().GetSprite("player")) +{ + bFriendly = true; + nHealth = 9; + nHealthMax = 10; + m_fStateTick = 2.0f; + pEquipedWeapon = (cWeapon*)RPG_Assets::get().GetItem("Basic Sword"); +} + +void cDynamic_Creature_Witty::PerformAttack() +{ + if (pEquipedWeapon == nullptr) + return; + + pEquipedWeapon->OnUse(this); +} + + + + +cDynamic_Creature_Skelly::cDynamic_Creature_Skelly() : cDynamic_Creature("Skelly", RPG_Assets::get().GetSprite("skelly")) +{ + bFriendly = false; + nHealth = 10; + nHealthMax = 10; + m_fStateTick = 2.0f; + + pEquipedWeapon = (cWeapon*)RPG_Assets::get().GetItem("Basic Sword"); +} + + +void cDynamic_Creature_Skelly::Behaviour(float fElapsedTime, cDynamic* player) +{ + if (nHealth <= 0) + { + vx = 0; + vy = 0; + bSolidVsDyn = false; + bIsAttackable = false; + return; + } + + // Check if player is nearby + float fTargetX = player->px - px; + float fTargetY = player->py - py; + float fDistance = sqrtf(fTargetX*fTargetX + fTargetY * fTargetY); + + m_fStateTick -= fElapsedTime; + + if (m_fStateTick <= 0.0f) + { + if (fDistance < 6.0f) + { + vx = (fTargetX / fDistance) * 2.0f; + vy = (fTargetY / fDistance) * 2.0f; + + if (fDistance < 1.5f) + PerformAttack(); + } + else + { + vx = 0; + vy = 0; + } + + m_fStateTick += 1.0f; + } +} + +void cDynamic_Creature_Skelly::PerformAttack() +{ + if (pEquipedWeapon == nullptr) + return; + + pEquipedWeapon->OnUse(this); +} + + + + + + + +cDynamic_Teleport::cDynamic_Teleport(float x, float y, string mapName, float tx, float ty) : cDynamic("Teleport") +{ + px = x; + py = y; + fMapPosX = tx; + fMapPosY = ty; + sMapName = mapName; + bSolidVsDyn = false; + bSolidVsMap = false; +} + +void cDynamic_Teleport::DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) +{ + // Does Nothing + gfx->DrawCircle(((px + 0.5f) - ox) * 16.0f, ((py + 0.5f) - oy) * 16.0f, 0.5f * 16.0f); // For debugging +} + +void cDynamic_Teleport::Update(float fElapsedTime, cDynamic* player) +{ + // Does Nothing +} + + +cDynamic_Item::cDynamic_Item(float x, float y, cItem* i) : cDynamic("pickup") +{ + px = x; + py = y; + bSolidVsDyn = false; + bSolidVsMap = false; + bFriendly = true; + bCollected = false; + item = i; +} + +void cDynamic_Item::DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) +{ + if (bCollected) + return; + + gfx->DrawPartialSprite((px - ox) * 16.0f, (py - oy)*16.0f, item->pSprite, 0, 0, 16, 16); +} + +void cDynamic_Item::OnInteract(cDynamic* player) +{ + if (bCollected) + return; + + if (item->OnInteract(player)) + { + // Add item to inventory + g_engine->GiveItem(item); + } + + bCollected = true; +} + +cDynamic_Projectile::cDynamic_Projectile(float ox, float oy, bool bFriend, float velx, float vely, float duration, olcSprite *sprite, float tx, float ty) : cDynamic("projectile") +{ + pSprite = sprite; + fSpriteX = tx; + fSpriteY = ty; + fDuration = duration; + px = ox; + py = oy; + vx = velx; + vy = vely; + bSolidVsDyn = false; + bSolidVsMap = true; + bIsProjectile = true; + bIsAttackable = false; + bFriendly = bFriend; +} + +void cDynamic_Projectile::DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) +{ + gfx->DrawPartialSprite((px - ox) * 16, (py - oy) * 16, pSprite, fSpriteX * 16, fSpriteY * 16, 16, 16); + +} + +void cDynamic_Projectile::Update(float fElapsedTime, cDynamic* player) +{ + fDuration -= fElapsedTime; + if (fDuration <= 0.0f) + bRedundant = true; +} diff --git a/olcRolePlayingGame/RPG_Dynamics.h b/olcRolePlayingGame/RPG_Dynamics.h new file mode 100644 index 0000000..cc277eb --- /dev/null +++ b/olcRolePlayingGame/RPG_Dynamics.h @@ -0,0 +1,204 @@ +/* + +-------------------------------------------------------------+ + | 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 +#include "RPG_Assets.h" +#include "olcConsoleGameEngineOOP.h" + +class RPG_Engine; +class cItem; +class cWeapon; + +class cDynamic +{ +public: + cDynamic(string n); + ~cDynamic(); + +public: + float px, py; + float vx, vy; + bool bSolidVsMap; + bool bSolidVsDyn; + bool bFriendly; + bool bRedundant; + bool bIsProjectile; + bool bIsAttackable; + string sName; + +public: + virtual void DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) {} + virtual void Update(float fElapsedTime, cDynamic* player = nullptr) {} + virtual void OnInteract(cDynamic* player = nullptr) {} + + static RPG_Engine* g_engine; +}; + + + +class cDynamic_Creature : public cDynamic +{ +public: + cDynamic_Creature(string n, olcSprite *sprite); + +protected: + olcSprite *m_pSprite; + float m_fTimer; + int m_nGraphicCounter; + enum { SOUTH = 0, WEST = 1, NORTH = 2, EAST = 3 } m_nFacingDirection; + enum { STANDING, WALKING, CELEBRATING, DEAD } m_nGraphicState; + +public: + int nHealth; + int nHealthMax; + int bControllable = true; + +public: + void DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) override; + void Update(float fElapsedTime, cDynamic* player = nullptr) override; + virtual void Behaviour(float fElapsedTime, cDynamic* player = nullptr); + int GetFacingDirection() { return m_nFacingDirection; }; + virtual void PerformAttack() {}; + void KnockBack(float dx, float dy, float dist); + + cWeapon *pEquipedWeapon = nullptr; + +protected: + float m_fStateTick; + float m_fKnockBackTimer = 0.0f; + float m_fKnockBackDX = 0.0f; + float m_fKnockBackDY = 0.0f; + +}; + + +class cDynamic_Creature_Skelly : public cDynamic_Creature +{ +public: + cDynamic_Creature_Skelly(); + + void Behaviour(float fElapsedTime, cDynamic* player = nullptr) override; + void PerformAttack() override; +}; + +class cDynamic_Creature_Witty : public cDynamic_Creature +{ +public: + cDynamic_Creature_Witty(); + +public: + void PerformAttack() override; +}; + + + + + +class cDynamic_Teleport : public cDynamic +{ +public: + cDynamic_Teleport(float x, float y, string sMapName, float tx, float ty); + void DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) override; + void Update(float fElapsedTime, cDynamic* player = nullptr) override; + +public: + string sMapName; + float fMapPosX; + float fMapPosY; +}; + +class cDynamic_Item : public cDynamic +{ +public: + cDynamic_Item(float x, float y, cItem* item); + void DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) override; + void OnInteract(cDynamic* player = nullptr) override; + +public: + cItem * item; + bool bCollected = false; +}; + +class cDynamic_Projectile : public cDynamic +{ +public: + cDynamic_Projectile(float ox, float oy, bool bFriend, float velx, float vely, float duration, olcSprite *sprite, float tx, float ty); + void DrawSelf(olcConsoleGameEngineOOP *gfx, float ox, float oy) override; + void Update(float fElapsedTime, cDynamic* player = nullptr) override; + +public: + olcSprite * pSprite = nullptr; + float fSpriteX; + float fSpriteY; + float fDuration; + bool bOneHit = true; + int nDamage = 0; +}; \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Engine.cpp b/olcRolePlayingGame/RPG_Engine.cpp new file mode 100644 index 0000000..49561e1 --- /dev/null +++ b/olcRolePlayingGame/RPG_Engine.cpp @@ -0,0 +1,693 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Engine.h" + +#define X(n) m_script.AddCommand(new cCommand_ ## n) + +RPG_Engine::RPG_Engine() +{ + m_sAppName = L"Top Down Role Playing Game"; +} + + + +bool RPG_Engine::OnUserCreate() +{ + cCommand::g_engine = this; + cMap::g_script = &m_script; + + cQuest::g_script = &m_script; + cQuest::g_engine = this; + + cDynamic::g_engine = this; + + cItem::g_engine = this; + + RPG_Assets::get().LoadSprites(); + RPG_Assets::get().LoadMaps(); + RPG_Assets::get().LoadItems(); + + m_sprFont = RPG_Assets::get().GetSprite("font"); + + m_listQuests.push_front(new cQuest_MainQuest()); + + m_pPlayer = new cDynamic_Creature_Witty(); + + m_listItems.push_back(RPG_Assets::get().GetItem("Basic Sword")); + + + ChangeMap("coder town", 5, 5); + + + + + + + return true; +} + +bool RPG_Engine::OnUserUpdate(float fElapsedTime) +{ + switch (m_nGameMode) + { + //case MODE_TITLE: + //return UpdateTitleScreen(fElapsedTime); + case MODE_LOCAL_MAP: + return UpdateLocalMap(fElapsedTime); + //case MODE_WORLD_MAP: + // return UpdateWorldMap(fElapsedTime); + case MODE_INVENTORY: + return UpdateInventory(fElapsedTime); + //case MODE_SHOP: + //return UpdateShop(fElapsedTime); + } + + return true; +} + +bool RPG_Engine::UpdateLocalMap(float fElapsedTime) +{ + + // Update script + m_script.ProcessCommands(fElapsedTime); + + // Erase and delete redundant projectiles + m_vecProjectiles.erase( + remove_if(m_vecProjectiles.begin(), m_vecProjectiles.end(), + [](const cDynamic* d) {return ((cDynamic_Projectile*)d)->bRedundant; }), m_vecProjectiles.end()); + + if (m_script.bUserControlEnabled) + { + + m_pPlayer->vx = 0.0f; + m_pPlayer->vy = 0.0f; + if (!m_bShowDialog && m_pPlayer->bControllable) + { + + // Handle Input + if (IsFocused()) + { + if (GetKey(VK_UP).bHeld) + { + m_pPlayer->vy = -4.0f; + } + + if (GetKey(VK_DOWN).bHeld) + { + m_pPlayer->vy = 4.0f; + } + + if (GetKey(VK_LEFT).bHeld) + { + m_pPlayer->vx = -4.0f; + } + + if (GetKey(VK_RIGHT).bHeld) + { + m_pPlayer->vx = 4.0f; + } + + if (GetKey(L'Z').bReleased) + m_nGameMode = MODE_INVENTORY; + + if (GetKey(VK_SPACE).bReleased) // Interaction requested + { + // Grab a point from the direction the player is facing and check for interactions + float fTestX, fTestY; + + if (m_pPlayer->GetFacingDirection() == 0) // South + { + fTestX = m_pPlayer->px + 0.5f; + fTestY = m_pPlayer->py + 1.5f; + } + + if (m_pPlayer->GetFacingDirection() == 1) // West + { + fTestX = m_pPlayer->px - 0.5f; + fTestY = m_pPlayer->py + 0.5f; + } + + if (m_pPlayer->GetFacingDirection() == 2) // North + { + fTestX = m_pPlayer->px + 0.5f; + fTestY = m_pPlayer->py - 0.5f; + } + + if (m_pPlayer->GetFacingDirection() == 3) // East + { + fTestX = m_pPlayer->px + 1.5f; + fTestY = m_pPlayer->py + 0.5f; + } + + // Check if test point has hit a dynamic object + bool bHitSomething = false; + for (auto dyns : m_vecDynamics) + { + if (fTestX > dyns->px && fTestX < (dyns->px + 1.0f) && fTestY > dyns->py && fTestY < (dyns->py + 1.0f)) + { + if (dyns->bFriendly) + { + bHitSomething = true; + + // Iterate through quest stack until something responds, the base quests should capture + // interactions that are not specfied in other quests + for (auto &quest : m_listQuests) + if (quest->OnInteraction(m_vecDynamics, dyns, cQuest::TALK)) + { + + break; + } + + // Some objects just do stuff when you interact with them + dyns->OnInteract(m_pPlayer); + + // Then check if it is map related + m_pCurrentMap->OnInteraction(m_vecDynamics, dyns, cMap::TALK); + } + else + { + // Interaction was with something not friendly - only enemies + // are not friendly, so perfrom attack + m_pPlayer->PerformAttack(); + } + } + } + + if (!bHitSomething) // Default action is attack + { + m_pPlayer->PerformAttack(); + } + + } + } + } + } + else + { + // Scripting system is in control + if (m_bShowDialog) + { + if (GetKey(VK_SPACE).bReleased) + { + m_bShowDialog = false; + m_script.CompleteCommand(); + } + } + } + + bool bWorkingWithProjectiles = false; + for (auto &source : { &m_vecDynamics, &m_vecProjectiles }) + { + for (auto &object : *source) + { + float fNewObjectPosX = object->px + object->vx * fElapsedTime; + float fNewObjectPosY = object->py + object->vy * fElapsedTime; + + // Collision + float fBorder = 0.1f; + bool bCollisionWithMap = false; + + if (object->vx <= 0) + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + fBorder, object->py + fBorder + 0.0f) || m_pCurrentMap->GetSolid(fNewObjectPosX + fBorder, object->py + (1.0f - fBorder))) + { + fNewObjectPosX = (int)fNewObjectPosX + 1; + object->vx = 0; + bCollisionWithMap = true; + } + } + else + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + (1.0f - fBorder), object->py + fBorder + 0.0f) || m_pCurrentMap->GetSolid(fNewObjectPosX + (1.0f - fBorder), object->py + (1.0f - fBorder))) + { + fNewObjectPosX = (int)fNewObjectPosX; + object->vx = 0; + bCollisionWithMap = true; + + } + } + + if (object->vy <= 0) + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + fBorder + 0.0f, fNewObjectPosY + fBorder) || m_pCurrentMap->GetSolid(fNewObjectPosX + (1.0f - fBorder), fNewObjectPosY + fBorder)) + { + fNewObjectPosY = (int)fNewObjectPosY + 1; + object->vy = 0; + bCollisionWithMap = true; + } + } + else + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + fBorder + 0.0f, fNewObjectPosY + (1.0f - fBorder)) || m_pCurrentMap->GetSolid(fNewObjectPosX + (1.0f - fBorder), fNewObjectPosY + (1.0f - fBorder))) + { + fNewObjectPosY = (int)fNewObjectPosY; + object->vy = 0; + bCollisionWithMap = true; + } + } + + if (object->bIsProjectile && bCollisionWithMap) + { + object->bRedundant = true; + } + + float fDynamicObjectPosX = fNewObjectPosX; + float fDynamicObjectPosY = fNewObjectPosY; + + // Object V Object collisions + for (auto &dyn : m_vecDynamics) + { + if (dyn != object) + { + // If the object is solid then the player must not overlap it + if (dyn->bSolidVsDyn && object->bSolidVsDyn) + { + // Check if bounding rectangles overlap + if (fDynamicObjectPosX < (dyn->px + 1.0f) && (fDynamicObjectPosX + 1.0f) > dyn->px && + object->py < (dyn->py + 1.0f) && (object->py + 1.0f) > dyn->py) + { + // First Check Horizontally - Check Left + if (object->vx <= 0) + fDynamicObjectPosX = dyn->px + 1.0f; + else + fDynamicObjectPosX = dyn->px - 1.0f; + } + + if (fDynamicObjectPosX < (dyn->px + 1.0f) && (fDynamicObjectPosX + 1.0f) > dyn->px && + fDynamicObjectPosY < (dyn->py + 1.0f) && (fDynamicObjectPosY + 1.0f) > dyn->py) + { + + // First Check Vertically - Check Left + if (object->vy <= 0) + fDynamicObjectPosY = dyn->py + 1.0f; + else + fDynamicObjectPosY = dyn->py - 1.0f; + } + + } + else + { + if (object == m_vecDynamics[0]) + { + // Object is player and can interact with things + if (fDynamicObjectPosX < (dyn->px + 1.0f) && (fDynamicObjectPosX + 1.0f) > dyn->px && + object->py < (dyn->py + 1.0f) && (object->py + 1.0f) > dyn->py) + { + // First check if object is part of a quest + for (auto &quest : m_listQuests) + if (quest->OnInteraction(m_vecDynamics, dyn, cQuest::WALK)) + break; + + // Then check if it is map related + m_pCurrentMap->OnInteraction(m_vecDynamics, dyn, cMap::WALK); + + // Finally just check the object + dyn->OnInteract(object); + } + } + else + { + if (bWorkingWithProjectiles) + { + if (fDynamicObjectPosX < (dyn->px + 1.0f) && (fDynamicObjectPosX + 1.0f) > dyn->px && + fDynamicObjectPosY < (dyn->py + 1.0f) && (fDynamicObjectPosY + 1.0f) > dyn->py) + { + if (dyn->bFriendly != object->bFriendly) + { + // We know object is a projectile, so dyn is something + // opposite that it has overlapped with + if (dyn->bIsAttackable) + { + // Dynamic object is a creature + Damage((cDynamic_Projectile*)object, (cDynamic_Creature*)dyn); + } + } + } + } + + + } + } + } + } + + + object->px = fDynamicObjectPosX; + object->py = fDynamicObjectPosY; + } + + bWorkingWithProjectiles = true; + } + + for (auto &source : { &m_vecDynamics, &m_vecProjectiles }) + for (auto &dyns : *source) + dyns->Update(fElapsedTime, m_pPlayer); + + // Remove quests that have been completed + auto i = remove_if(m_listQuests.begin(), m_listQuests.end(), [](const cQuest* d) {return d->bCompleted; }); + if (i != m_listQuests.end()) + m_listQuests.erase(i); + + + fCameraPosX = m_pPlayer->px; + fCameraPosY = m_pPlayer->py; + + // Draw Level + int nTileWidth = 16; + int nTileHeight = 16; + int nVisibleTilesX = ScreenWidth() / nTileWidth; + int nVisibleTilesY = ScreenHeight() / nTileHeight; + + // Calculate Top-Leftmost visible tile + float fOffsetX = fCameraPosX - (float)nVisibleTilesX / 2.0f; + float fOffsetY = fCameraPosY - (float)nVisibleTilesY / 2.0f; + + // Clamp camera to game boundaries + if (fOffsetX < 0) fOffsetX = 0; + if (fOffsetY < 0) fOffsetY = 0; + if (fOffsetX > m_pCurrentMap->nWidth - nVisibleTilesX) fOffsetX = m_pCurrentMap->nWidth - nVisibleTilesX; + if (fOffsetY > m_pCurrentMap->nHeight - nVisibleTilesY) fOffsetY = m_pCurrentMap->nHeight - nVisibleTilesY; + + // Get offsets for smooth movement + float fTileOffsetX = (fOffsetX - (int)fOffsetX) * nTileWidth; + float fTileOffsetY = (fOffsetY - (int)fOffsetY) * nTileHeight; + + // Draw visible tile map + for (int x = -1; x < nVisibleTilesX + 1; x++) + { + for (int y = -1; y < nVisibleTilesY + 1; y++) + { + int idx = m_pCurrentMap->GetIndex(x + fOffsetX, y + fOffsetY); + int sx = idx % 10; + int sy = idx / 10; + + DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, m_pCurrentMap->pSprite, sx * nTileWidth, sy * nTileHeight, nTileWidth, nTileHeight); + } + } + + // Draw Object + for (auto &source : { &m_vecDynamics, &m_vecProjectiles }) + for (auto &dyns : *source) + dyns->DrawSelf(this, fOffsetX, fOffsetY); + + m_pPlayer->DrawSelf(this, fOffsetX, fOffsetY); + + string sHealth = "HP: " + to_string(m_pPlayer->nHealth) + "/" + to_string(m_pPlayer->nHealthMax); + DisplayDialog({ sHealth }, 160, 10); + + + // Draw any dialog being displayed + if (m_bShowDialog) + DisplayDialog(m_vecDialogToShow, 20, 20); + + + return true; +} + +void RPG_Engine::ShowDialog(vector vecLines) +{ + m_vecDialogToShow = vecLines; + m_bShowDialog = true; +} + +void RPG_Engine::DisplayDialog(vector vecText, int x, int y) +{ + int nMaxLineLength = 0; + int nLines = vecText.size(); + + for (auto l : vecText) if (l.size() > nMaxLineLength) nMaxLineLength = l.size(); + + // Draw Box + Fill(x - 1, y - 1, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1, PIXEL_SOLID, FG_DARK_BLUE); + DrawLine(x - 2, y - 2, x - 2, y + nLines * 8 + 1); + DrawLine(x + nMaxLineLength * 8 + 1, y - 2, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1); + DrawLine(x - 2, y - 2, x + nMaxLineLength * 8 + 1, y - 2); + DrawLine(x - 2, y + nLines * 8 + 1, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1); + + for (int l = 0; lpx = x; + m_pPlayer->py = y; + + // Create new dynamics from map + m_pCurrentMap->PopulateDynamics(m_vecDynamics); + + // Create new dynamics from quests + for (auto q : m_listQuests) + q->PopulateDynamics(m_vecDynamics, m_pCurrentMap->sName); +} + +void RPG_Engine::AddQuest(cQuest* quest) +{ + m_listQuests.push_front(quest); +} + +bool RPG_Engine::GiveItem(cItem* item) +{ + //m_script.AddCommand(new cCommand_ShowDialog({ "You have found a" , item->sName })); + m_listItems.push_back(item); + return true; +} + +bool RPG_Engine::TakeItem(cItem* item) +{ + if (item != nullptr) + { + m_listItems.erase(find(m_listItems.begin(), m_listItems.end(), item)); + return true; + } + else + return false; +} + +bool RPG_Engine::HasItem(cItem* item) +{ + if (item != nullptr) + return find(m_listItems.begin(), m_listItems.end(), item) != m_listItems.end(); + else + return false; +} + +bool RPG_Engine::UpdateInventory(float fElapsedTime) +{ + Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); + DrawBigText("INVENTORY", 4, 4); + + + int i = 0; + cItem* highlighted = nullptr; + + // Draw Consumables + for (auto &item : m_listItems) + { + int x = i % 4; + int y = i / 4; + i++; + + DrawPartialSprite(8 + x * 20, 20 + y * 20, item->pSprite, 0, 0, 16, 16); + + if (m_nInvSelectX == x && m_nInvSelectY == y) + highlighted = item; + } + + // Draw selection reticule + DrawLine(6 + (m_nInvSelectX) * 20, 18 + (m_nInvSelectY) * 20, 6 + (m_nInvSelectX + 1) * 20, 18 + (m_nInvSelectY) * 20); + DrawLine(6 + (m_nInvSelectX) * 20, 18 + (m_nInvSelectY + 1) * 20, 6 + (m_nInvSelectX + 1) * 20, 18 + (m_nInvSelectY + 1) * 20); + DrawLine(6 + (m_nInvSelectX) * 20, 18 + (m_nInvSelectY) * 20, 6 + (m_nInvSelectX) * 20, 18 + (m_nInvSelectY + 1) * 20); + DrawLine(6 + (m_nInvSelectX + 1) * 20, 18 + (m_nInvSelectY) * 20, 6 + (m_nInvSelectX + 1) * 20, 18 + (m_nInvSelectY + 1) * 20); + + if (GetKey(VK_LEFT).bReleased) m_nInvSelectX--; + if (GetKey(VK_RIGHT).bReleased) m_nInvSelectX++; + if (GetKey(VK_UP).bReleased) m_nInvSelectY--; + if (GetKey(VK_DOWN).bReleased) m_nInvSelectY++; + if (m_nInvSelectX < 0) m_nInvSelectX = 3; + if (m_nInvSelectX >= 4) m_nInvSelectX = 0; + if (m_nInvSelectY < 0) m_nInvSelectY = 3; + if (m_nInvSelectY >= 4) m_nInvSelectY = 0; + + if (GetKey(L'Z').bReleased) + m_nGameMode = MODE_LOCAL_MAP; + + DrawBigText("SELECTED:", 8, 160); + + if (highlighted != nullptr) + { + DrawBigText("SELECTED:", 8, 160); + DrawBigText(highlighted->sName, 8, 170); + + DrawBigText("DESCRIPTION:", 8, 190); + DrawBigText(highlighted->sDescription, 8, 200); + + if (!highlighted->bKeyItem) + { + DrawBigText("(Press SPACE to use)", 80, 160); + } + + if (GetKey(VK_SPACE).bReleased) + { + // Use selected item + if (!highlighted->bKeyItem) + { + if (highlighted->OnUse(m_pPlayer)) + { + // Item has signalled it must be consumed, so remove it + TakeItem(highlighted); + } + } + else + { + + } + } + } + + DrawBigText("LOCATION:", 128, 8); + DrawBigText(m_pCurrentMap->sName, 128, 16); + + DrawBigText("HEALTH: " + to_string(m_pPlayer->nHealth), 128, 32); + DrawBigText("MAX HEALTH: " + to_string(m_pPlayer->nHealthMax), 128, 40); + return true; +} + +void RPG_Engine::Attack(cDynamic_Creature* aggressor, cWeapon *weapon) +{ + weapon->OnUse(aggressor); +} + +void RPG_Engine::AddProjectile(cDynamic_Projectile *proj) +{ + m_vecProjectiles.push_back(proj); +} + + +void RPG_Engine::Damage(cDynamic_Projectile* projectile, cDynamic_Creature* victim) +{ + if (victim != nullptr) + { + // Attack victim with damage + victim->nHealth -= projectile->nDamage; + + // Knock victim back + float tx = victim->px - projectile->px; + float ty = victim->py - projectile->py; + float d = sqrtf(tx*tx + ty * ty); + if (d < 1) d = 1.0f; + + // After a hit, they object experiences knock back, where it is temporarily + // under system control. This delivers two functions, the first being + // a visual indicator to the player that something has happened, and the second + // it stops the ability to spam attacks on a single creature + victim->KnockBack(tx / d, ty / d, 0.2f); + + if (victim != m_pPlayer) + { + victim->OnInteract(m_pPlayer); + } + else + { + // We must ensure the player is never pushed out of bounds by the physics engine. This + // is a bit of a hack, but it allows knockbacks to occur providing there is an exit + // point for the player to be knocked back into. If the player is "mobbed" then they + // become trapped, and must fight their way out + victim->bSolidVsDyn = true; + } + + + if (projectile->bOneHit) + projectile->bRedundant = true; + } +} \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Engine.h b/olcRolePlayingGame/RPG_Engine.h new file mode 100644 index 0000000..a31b284 --- /dev/null +++ b/olcRolePlayingGame/RPG_Engine.h @@ -0,0 +1,159 @@ +/* + +-------------------------------------------------------------+ + | 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 + + +#include +#include +#include +#include +using namespace std; + +#include "olcConsoleGameEngineOOP.h" + +#include "RPG_Assets.h" +#include "RPG_Maps.h" +#include "RPG_Dynamics.h" +#include "RPG_Commands.h" +#include "RPG_Quests.h" +#include "RPG_Items.h" + +#define X(n) m_script.AddCommand(new cCommand_ ## n) + +class RPG_Engine : public olcConsoleGameEngineOOP +{ +public: + RPG_Engine(); + +private: + cMap *m_pCurrentMap = nullptr; + cDynamic_Creature *m_pPlayer = nullptr; + vector m_vecDynamics; // Fixed + vector m_vecProjectiles; // Transient + cScriptProcessor m_script; + + list m_listQuests; + list m_listItems; + + float fCameraPosX = 0.0f; + float fCameraPosY = 0.0f; + + olcSprite *m_sprFont = nullptr; + + enum + { + MODE_TITLE, + MODE_LOCAL_MAP, + MODE_WORLD_MAP, + MODE_INVENTORY, + MODE_SHOP + }; + + int m_nGameMode = MODE_LOCAL_MAP; + + int m_nInvSelectX = 0; + int m_nInvSelectY = 0; + + + +protected: + bool OnUserCreate() override; + bool OnUserUpdate(float fElapsedTime) override; + + //bool UpdateTitleScreen(float fElapsedTime); + bool UpdateLocalMap(float fElapsedTime); + //bool UpdateWorldMap(float fElapsedTime); + bool UpdateInventory(float fElapsedTime); + //bool UpdateShop(float fElapsedTime); + +protected: + vector m_vecDialogToShow; + bool m_bShowDialog = false; + float m_fDialogX = 0.0f; + float m_fDialogY = 0.0f; + +public: + void ShowDialog(vector vecLines); + void DisplayDialog(vector vecText, int x, int y); + void DrawBigText(string sText, int x, int y); + void ChangeMap(string sMapName, float x, float y); + void AddQuest(cQuest* quest); + + void AddProjectile(cDynamic_Projectile *proj); + + bool GiveItem(cItem *item); + bool TakeItem(cItem* item); + bool HasItem(cItem* item); + + void Attack(cDynamic_Creature* aggressor, cWeapon *weapon); + void Damage(cDynamic_Projectile* projectile, cDynamic_Creature* victim); +}; diff --git a/olcRolePlayingGame/RPG_Items.cpp b/olcRolePlayingGame/RPG_Items.cpp new file mode 100644 index 0000000..66107fc --- /dev/null +++ b/olcRolePlayingGame/RPG_Items.cpp @@ -0,0 +1,207 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Items.h" +#include "RPG_Engine.h" + +RPG_Engine* cItem::g_engine = nullptr; + +cItem::cItem(string name, olcSprite* sprite, string desc) +{ + sName = name; pSprite = sprite; sDescription = desc; +} + + +cItem_Health::cItem_Health() : cItem("Small Health", RPG_Assets::get().GetSprite("health"), "Restores 10 health") +{ +} + + +bool cItem_Health::OnInteract(cDynamic *object) +{ + OnUse(object); + return false; // Just absorb +} + + +bool cItem_Health::OnUse(cDynamic *object) +{ + if (object != nullptr) + { + cDynamic_Creature* dyn = (cDynamic_Creature*)object; + dyn->nHealth = min(dyn->nHealth + 10, dyn->nHealthMax); + } + return true; +} + + +cItem_HealthBoost::cItem_HealthBoost() : + cItem("Health Boost", RPG_Assets::get().GetSprite("healthboost"), "Increases Max Health by 10") +{} + +bool cItem_HealthBoost::OnInteract(cDynamic *object) +{ + return true; // Add to inventory +} + +bool cItem_HealthBoost::OnUse(cDynamic *object) +{ + if (object != nullptr) + { + cDynamic_Creature* dyn = (cDynamic_Creature*)object; + dyn->nHealthMax += 10; + dyn->nHealth = dyn->nHealthMax; + } + + return true; // Remove from inventory +} + +cWeapon::cWeapon(string name, olcSprite* sprite, string desc, int dmg) : cItem(name, sprite, desc) +{ + nDamage = dmg; +} + +bool cWeapon::OnInteract(cDynamic *object) +{ + return false; +} + +bool cWeapon::OnUse(cDynamic *object) +{ + return false; +} + + +cWeapon_Sword::cWeapon_Sword() : + cWeapon("Basic Sword", RPG_Assets::get().GetSprite("Basic Sword"), "A wooden sword, 5 dmg", 5) +{ + +} + +bool cWeapon_Sword::OnUse(cDynamic *object) +{ + // When weapons are used, they are used on the object that owns the weapon, i.e. + // the attacker. However this does not imply the attacker attacks themselves + + // Get direction of attacker + cDynamic_Creature* aggressor = (cDynamic_Creature*)object; + + // Determine attack origin + float x, y, vx, vy; + if (aggressor->GetFacingDirection() == 0) // South + { + x = aggressor->px; + y = aggressor->py + 1.0f; + vx = 0.0f; vy = 1.0f; + } + + if (aggressor->GetFacingDirection() == 1) // East + { + x = aggressor->px - 1.0f; + y = aggressor->py; + vx = -1.0f; vy = 0.0f; + } + + if (aggressor->GetFacingDirection() == 2) // North + { + x = aggressor->px; + y = aggressor->py - 1.0f; + vx = 0.0f; vy = -1.0f; + } + + if (aggressor->GetFacingDirection() == 3) // West + { + x = aggressor->px + 1.0f; + y = aggressor->py; + vx = 1.0f; vy = 0.0f; + } + + if (aggressor->nHealth == aggressor->nHealthMax) + { + // Beam sword + cDynamic_Projectile *p = new cDynamic_Projectile(x, y, aggressor->bFriendly, vx * 15.0f, vy * 15.0f, 1.0f, RPG_Assets::get().GetSprite("Basic Sword"), (aggressor->GetFacingDirection() + 3) % 4 + 1, 1.0f); + p->bSolidVsMap = true; + p->bSolidVsDyn = false; + p->nDamage = 5; + p->bOneHit = false; + g_engine->AddProjectile(p); + } + + cDynamic_Projectile *p = new cDynamic_Projectile(x, y, aggressor->bFriendly, aggressor->vx, aggressor->vy, 0.1f, RPG_Assets::get().GetSprite("Basic Sword"), (aggressor->GetFacingDirection() + 3) % 4 + 1, 0.0f); + p->bSolidVsMap = false; + p->bSolidVsDyn = false; + p->nDamage = 5; + p->bOneHit = true; + + g_engine->AddProjectile(p); + + return false; +} \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Items.h b/olcRolePlayingGame/RPG_Items.h new file mode 100644 index 0000000..b0dd512 --- /dev/null +++ b/olcRolePlayingGame/RPG_Items.h @@ -0,0 +1,134 @@ +/* + +-------------------------------------------------------------+ + | 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 + +#include "RPG_Dynamics.h" + +class RPG_Engine; + +class cItem +{ +public: + cItem(string name, olcSprite* sprite, string desc); + + virtual bool OnInteract(cDynamic *object) {return false;} + virtual bool OnUse(cDynamic *object) {return false;} + +public: + string sName; + string sDescription; + olcSprite *pSprite; + bool bKeyItem = false; + bool bEquipable = false; + + static RPG_Engine *g_engine; +}; + + + +class cItem_Health : public cItem // give player 10hp +{ +public: + cItem_Health(); + bool OnInteract(cDynamic *object) override; + bool OnUse(cDynamic *object) override; +}; + +class cItem_HealthBoost : public cItem // raise max hp 10 +{ +public: + cItem_HealthBoost(); + bool OnInteract(cDynamic *object) override; + bool OnUse(cDynamic *object) override; +}; + + +class cWeapon : public cItem +{ +public: + cWeapon(string name, olcSprite* sprite, string desc, int dmg); + bool OnInteract(cDynamic *object) override; + bool OnUse(cDynamic *object) override; + +public: + int nDamage = 0; +}; + + +class cWeapon_Sword : public cWeapon +{ +public: + cWeapon_Sword(); + +public: + bool OnUse(cDynamic *object) override; +}; \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Main.cpp b/olcRolePlayingGame/RPG_Main.cpp new file mode 100644 index 0000000..1187b69 --- /dev/null +++ b/olcRolePlayingGame/RPG_Main.cpp @@ -0,0 +1,269 @@ +#include +#include +using namespace std; + +#include "olcConsoleGameEngineOOP.h" + +#include "RPG_Assets.h" +#include "RPG_Maps.h" +#include "RPG_Dynamics.h" +#include "RPG_Commands.h" + +#define X(n) m_script.AddCommand(new cCommand_ ## n) + +class OneLoneCoder_RPG : public olcConsoleGameEngineOOP +{ +public: + OneLoneCoder_RPG() + { + m_sAppName = L"Top Down Role Playing Game"; + } + +private: + cMap *m_pCurrentMap = nullptr; + + cDynamic *m_pPlayer = nullptr; + + vector m_vecDynamics; + + cScriptProcessor m_script; + + float fCameraPosX = 0.0f; + float fCameraPosY = 0.0f; + + olcSprite *spriteTiles = nullptr; + olcSprite *spriteMan = nullptr; + olcSprite *m_sprFont = nullptr; + + + void DrawBigText(string sText, int x, int y) + { + int i = 0; + for (auto c : sText) + { + int sx = ((c - 32) % 16) * 8; + int sy = ((c - 32) / 16) * 8; + DrawPartialSprite(x + i * 8, y, m_sprFont, sx, sy, 8, 8); + i++; + } + + } + +protected: + virtual bool OnUserCreate() + { + cCommand::g_engine = this; + RPG_Assets::get().LoadSprites(); + + m_sprFont = RPG_Assets::get().GetSprite("font"); + + m_pCurrentMap = new cMap_Village1(); + + m_pPlayer = new cDynamic_Creature("player", RPG_Assets::get().GetSprite("player")); + m_pPlayer->px = 5.0f; + m_pPlayer->py = 5.0f; + + cDynamic* ob1 = new cDynamic_Creature("skelly1", RPG_Assets::get().GetSprite("skelly")); + ob1->px = 12.0f; + ob1->py = 12.0f; + + cDynamic* ob2 = new cDynamic_Creature("skelly2", RPG_Assets::get().GetSprite("skelly")); + ob2->px = 5.0f; + ob2->py = 8.0f; + + m_vecDynamics.push_back(m_pPlayer); + m_vecDynamics.push_back(ob1); + m_vecDynamics.push_back(ob2); + + + return true; + } + + virtual bool OnUserUpdate(float fElapsedTime) + { + + // Update script + m_script.ProcessCommands(fElapsedTime); + + if (m_script.bUserControlEnabled) + { + + m_pPlayer->vx = 0.0f; + m_pPlayer->vy = 0.0f; + + // Handle Input + if (IsFocused()) + { + if (GetKey(VK_UP).bHeld) + { + m_pPlayer->vy = -4.0f; + } + + if (GetKey(VK_DOWN).bHeld) + { + m_pPlayer->vy = 4.0f; + } + + if (GetKey(VK_LEFT).bHeld) + { + m_pPlayer->vx = -4.0f; + } + + if (GetKey(VK_RIGHT).bHeld) + { + m_pPlayer->vx = 4.0f; + } + + if (GetKey(L'Z').bReleased) + { + X(MoveTo(m_pPlayer, 10, 10, 3.0f)); + X(MoveTo(m_pPlayer, 15, 10, 3.0f)); + X(MoveTo(m_vecDynamics[1], 15, 12, 2.0f)); + X(ShowDialog({ "Grrrrr!" })); + X(ShowDialog({ "I think OOP", "is really useful!" })); + X(MoveTo(m_pPlayer, 15, 15, 3.0f)); + X(MoveTo(m_pPlayer, 10, 10, 3.0f)); + } + } + } + else + { + // Scripting system is in control + if (m_bShowDialog) + { + if (GetKey(VK_SPACE).bReleased) + { + m_bShowDialog = false; + m_script.CompleteCommand(); + } + } + } + + for (auto &object : m_vecDynamics) + { + float fNewObjectPosX = object->px + object->vx * fElapsedTime; + float fNewObjectPosY = object->py + object->vy * fElapsedTime; + + // Collision + if (object->vx <= 0) + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + 0.0f, object->py + 0.0f) || m_pCurrentMap->GetSolid(fNewObjectPosX + 0.0f, object->py + 0.9f)) + { + fNewObjectPosX = (int)fNewObjectPosX + 1; + object->vx = 0; + } + } + else + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + 1.0f, object->py + 0.0f) || m_pCurrentMap->GetSolid(fNewObjectPosX + 1.0f, object->py + 0.9f)) + { + fNewObjectPosX = (int)fNewObjectPosX; + object->vx = 0; + + } + } + + if (object->vy <= 0) + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + 0.0f, fNewObjectPosY) || m_pCurrentMap->GetSolid(fNewObjectPosX + 0.9f, fNewObjectPosY)) + { + fNewObjectPosY = (int)fNewObjectPosY + 1; + object->vy = 0; + } + } + else + { + if (m_pCurrentMap->GetSolid(fNewObjectPosX + 0.0f, fNewObjectPosY + 1.0f) || m_pCurrentMap->GetSolid(fNewObjectPosX + 0.9f, fNewObjectPosY + 1.0f)) + { + fNewObjectPosY = (int)fNewObjectPosY; + object->vy = 0; + } + } + + object->px = fNewObjectPosX; + object->py = fNewObjectPosY; + + object->Update(fElapsedTime); + } + + + fCameraPosX = m_pPlayer->px; + fCameraPosY = m_pPlayer->py; + + // Draw Level + int nTileWidth = 16; + int nTileHeight = 16; + int nVisibleTilesX = ScreenWidth() / nTileWidth; + int nVisibleTilesY = ScreenHeight() / nTileHeight; + + // Calculate Top-Leftmost visible tile + float fOffsetX = fCameraPosX - (float)nVisibleTilesX / 2.0f; + float fOffsetY = fCameraPosY - (float)nVisibleTilesY / 2.0f; + + // Clamp camera to game boundaries + if (fOffsetX < 0) fOffsetX = 0; + if (fOffsetY < 0) fOffsetY = 0; + if (fOffsetX > m_pCurrentMap->nWidth - nVisibleTilesX) fOffsetX = m_pCurrentMap->nWidth - nVisibleTilesX; + if (fOffsetY > m_pCurrentMap->nHeight - nVisibleTilesY) fOffsetY = m_pCurrentMap->nHeight - nVisibleTilesY; + + // Get offsets for smooth movement + float fTileOffsetX = (fOffsetX - (int)fOffsetX) * nTileWidth; + float fTileOffsetY = (fOffsetY - (int)fOffsetY) * nTileHeight; + + // Draw visible tile map + for (int x = -1; x < nVisibleTilesX + 1; x++) + { + for (int y = -1; y < nVisibleTilesY + 1; y++) + { + int idx = m_pCurrentMap->GetIndex(x + fOffsetX, y + fOffsetY); + int sx = idx % 10; + int sy = idx / 10; + + DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, m_pCurrentMap->pSprite, sx * nTileWidth, sy * nTileHeight, nTileWidth, nTileHeight); + } + } + + // Draw Object + for (auto &object : m_vecDynamics) + object->DrawSelf(this, fOffsetX, fOffsetY); + + m_pPlayer->DrawSelf(this, fOffsetX, fOffsetY); + + // Draw any dialog being displayed + if (m_bShowDialog) + DisplayDialog(m_vecDialogToShow, 20, 20); + + + return true; + } + + vector m_vecDialogToShow; + bool m_bShowDialog = false; + float m_fDialogX = 0.0f; + float m_fDialogY = 0.0f; +public: + + void ShowDialog(vector vecLines) + { + m_vecDialogToShow = vecLines; + m_bShowDialog = true; + } + + void DisplayDialog(vector vecText, int x, int y) + { + int nMaxLineLength = 0; + int nLines = vecText.size(); + + for (auto l : vecText) if (l.size() > nMaxLineLength) nMaxLineLength = l.size(); + + // Draw Box + Fill(x - 1, y - 1, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1, PIXEL_SOLID, FG_DARK_BLUE); + DrawLine(x - 2, y - 2, x - 2, y + nLines * 8 + 1); + DrawLine(x + nMaxLineLength * 8 + 1, y - 2, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1); + DrawLine(x - 2, y - 2, x + nMaxLineLength * 8 + 1, y - 2); + DrawLine(x - 2, y + nLines * 8 + 1, x + nMaxLineLength * 8 + 1, y + nLines * 8 + 1); + + for (int l = 0; l + +cScriptProcessor* cMap::g_script = nullptr; + +#define X(n) g_script->AddCommand(new cCommand_ ## n) + +cMap::cMap() +{ + pSprite = nullptr; + nWidth = 0; + nHeight = 0; + m_solids = nullptr; + m_indices = nullptr; +} + + +cMap::~cMap() +{ + delete[] m_solids; + delete[] m_indices; +} + +int cMap::GetIndex(int x, int y) +{ + if (x >= 0 && x < nWidth && y >= 0 && y < nHeight) + return m_indices[y*nWidth + x]; + else + return 0; +} + +bool cMap::GetSolid(int x, int y) +{ + if (x >= 0 && x < nWidth && y >= 0 && y < nHeight) + return m_solids[y*nWidth + x]; + else + return true; +} + +bool cMap::Create(string fileData, olcSprite* sprite, string name) +{ + sName = name; + pSprite = sprite; + ifstream data(fileData, ios::in | ios::binary); + if (data.is_open()) + { + data >> nWidth >> nHeight; + m_solids = new bool[nWidth * nHeight]; + m_indices = new int[nWidth * nHeight]; + for (int i = 0; i < nWidth * nHeight; i++) + { + data >> m_indices[i]; + data >> m_solids[i]; + } + return true; + } + + return false; +} + + + + + + + + +cMap_Village1::cMap_Village1() +{ + Create("rpgdata/map/village1.lvl", RPG_Assets::get().GetSprite("village"), "coder town"); +} + +bool cMap_Village1::PopulateDynamics(vector &vecDyns) +{ + // Add Teleporters + vecDyns.push_back(new cDynamic_Teleport(12.0f, 6.0f, "home", 5.0f, 12.0f)); + + // Add Items + vecDyns.push_back(new cDynamic_Item(10, 10, RPG_Assets::get().GetItem("Small Health"))); + vecDyns.push_back(new cDynamic_Item(12, 10, RPG_Assets::get().GetItem("Health Boost"))); + + for (int i = 0; i < 3; i++) + { + cDynamic* g1 = new cDynamic_Creature_Skelly(); + vecDyns.push_back(g1); + g1->px = rand() % 10 + 5.0f; + g1->py = rand() % 10 + 5.0f; + } + + return true; +} + + +bool cMap_Village1::OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) +{ + if (target->sName == "Teleport") + g_script->AddCommand(new cCommand_ChangeMap( + ((cDynamic_Teleport*)target)->sMapName, + ((cDynamic_Teleport*)target)->fMapPosX, + ((cDynamic_Teleport*)target)->fMapPosY)); + + return false; +} + + + + + + + + + + + + +cMap_Home1::cMap_Home1() +{ + Create("rpgdata/map/home.lvl", RPG_Assets::get().GetSprite("hitech"), "home"); +} + +bool cMap_Home1::PopulateDynamics(vector &vecDyns) +{ + // Front door + vecDyns.push_back(new cDynamic_Teleport(5.0f, 13.0f, "coder town", 12.0f, 7.0f)); + vecDyns.push_back(new cDynamic_Teleport(4.0f, 13.0f, "coder town", 12.0f, 7.0f)); + + /*cDynamic_Creature* c1 = new cDynamic_Creature("bob", RPG_Assets::get().GetSprite("skelly")); + c1->px = 12.0f; + c1->py = 4.0f; + vecDyns.push_back(c1);*/ + + return true; +} + + +bool cMap_Home1::OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) +{ + if (target->sName == "Teleport") + g_script->AddCommand(new cCommand_ChangeMap( + ((cDynamic_Teleport*)target)->sMapName, + ((cDynamic_Teleport*)target)->fMapPosX, + ((cDynamic_Teleport*)target)->fMapPosY)); + + /*if (target->sName == "bob") + { + X(ShowDialog({ "Hello!", "I'm Bob!" })); + }*/ + + return false; +} \ No newline at end of file diff --git a/olcRolePlayingGame/RPG_Maps.h b/olcRolePlayingGame/RPG_Maps.h new file mode 100644 index 0000000..66ed00f --- /dev/null +++ b/olcRolePlayingGame/RPG_Maps.h @@ -0,0 +1,139 @@ +/* + +-------------------------------------------------------------+ + | 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 + +#include "olcConsoleGameEngineOOP.h" +#include "RPG_Assets.h" +#include "RPG_Dynamics.h" +#include "RPG_Commands.h" + +class cMap +{ + +public: + enum NATURE + { + TALK, + WALK + }; + +public: + cMap(); + ~cMap(); + +public: + int nWidth; + int nHeight; + string sName; + olcSprite* pSprite; + + int GetIndex(int x, int y); + bool GetSolid(int x, int y); + bool Create(string fileData, olcSprite* sprite, string name); + + virtual bool PopulateDynamics(vector &vecDyns) + { + return false; + } + + virtual bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) + { + return false; + } + +private: + int *m_indices = nullptr; + bool *m_solids = nullptr; + +public: + static cScriptProcessor* g_script; +}; + + + +class cMap_Village1 : public cMap +{ +public: + cMap_Village1(); + + bool PopulateDynamics(vector &vecDyns) override; + bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) override; +}; + +class cMap_Home1 : public cMap +{ +public: + cMap_Home1(); + + bool PopulateDynamics(vector &vecDyns) override; + bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) override; +}; diff --git a/olcRolePlayingGame/RPG_Quests.cpp b/olcRolePlayingGame/RPG_Quests.cpp new file mode 100644 index 0000000..62ae365 --- /dev/null +++ b/olcRolePlayingGame/RPG_Quests.cpp @@ -0,0 +1,168 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Quests.h" +#include "RPG_Engine.h" + +cScriptProcessor* cQuest::g_script = nullptr; +RPG_Engine* cQuest::g_engine = nullptr; + +#define X(n) g_script->AddCommand(new cCommand_ ## n) + +cQuest::cQuest() +{ +} + +bool cQuest::PopulateDynamics(vector &vecDyns, string sMap) +{ + return true; +} + +bool cQuest::OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) +{ + + + + + return true; +} + + + +bool cQuest_MainQuest::PopulateDynamics(vector &vecDyns, string sMap) +{ + if (sMap == "coder town") + { + cDynamic_Creature* c1 = new cDynamic_Creature("sarah", RPG_Assets::get().GetSprite("purple")); + c1->px = 6.0f; + c1->py = 4.0f; + c1->bFriendly = true; + vecDyns.push_back(c1); + } + + if (sMap == "home") + { + cDynamic_Creature* c1 = new cDynamic_Creature("bob", RPG_Assets::get().GetSprite("skelly")); + c1->px = 12.0f; + c1->py = 4.0f; + vecDyns.push_back(c1); + } + + + return true; +} + +bool cQuest_MainQuest::OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) +{ + if (target->sName == "sarah") + { + //X(ShowDialog({ "[Sarah]", "You have no additional", "quests!" })); + + if (g_engine->HasItem(RPG_Assets::get().GetItem("Health Boost"))) + { + X(ShowDialog({ "[Sarah]", "Woooooow! You have a health", "boost!" })); + + } + else + { + X(ShowDialog({ "[Sarah]", "Boooooo! You dont have a health", "boost!" })); + + } + + } + + + if (target->sName == "bob") + { + X(ShowDialog({ "[Bob]", "I need you to do", "something for me!" })); + X(ShowDialog({ "[Bob]", "Predictably, there are", "rats in my basement!"})); + X(AddQuest(new cQuest_BobsQuest())); + } + + return false; +} + + +bool cQuest_BobsQuest::PopulateDynamics(vector &vecDyns, string sMap) +{ + return true; +} + +bool cQuest_BobsQuest::OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) +{ + if (target->sName == "sarah") + { + X(ShowDialog({ "[Sarah]", "You are doing Bob's", "quest!" })); + return true; + } + + return false; +} diff --git a/olcRolePlayingGame/RPG_Quests.h b/olcRolePlayingGame/RPG_Quests.h new file mode 100644 index 0000000..6991cee --- /dev/null +++ b/olcRolePlayingGame/RPG_Quests.h @@ -0,0 +1,125 @@ +/* + +-------------------------------------------------------------+ + | 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 + +#include "RPG_Dynamics.h" +#include "RPG_Commands.h" + +class RPG_Engine; + +class cQuest +{ +public: + enum NATURE + { + TALK = 0, + ATTACK = 1, + KILL = 2, + WALK = 3 + }; + +public: + cQuest(); + +public: + virtual bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature); + virtual bool PopulateDynamics(vector &vecDyns, string sMap); + +public: + string sName; + bool bCompleted = false; + static cScriptProcessor* g_script; + static RPG_Engine* g_engine; +}; + + + +class cQuest_MainQuest : public cQuest +{ +public: + bool PopulateDynamics(vector &vecDyns, string sMap) override; + bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) override; + +private: + int m_nPhase = 0; +}; + + +class cQuest_BobsQuest : public cQuest +{ +public: + bool PopulateDynamics(vector &vecDyns, string sMap) override; + bool OnInteraction(vector &vecDynobs, cDynamic *target, NATURE nature) override; + +private: + int m_nPhase = 0; +}; \ No newline at end of file diff --git a/olcRolePlayingGame/main.cpp b/olcRolePlayingGame/main.cpp new file mode 100644 index 0000000..c744dcc --- /dev/null +++ b/olcRolePlayingGame/main.cpp @@ -0,0 +1,80 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "RPG_Engine.h" + +int main() +{ + RPG_Engine game; + if (game.ConstructConsole(256, 240, 4, 4)) + game.Start(); + return 0; +} \ No newline at end of file diff --git a/olcRolePlayingGame/olcConsoleGameEngine.h b/olcRolePlayingGame/olcConsoleGameEngine.h new file mode 100644 index 0000000..120954e --- /dev/null +++ b/olcRolePlayingGame/olcConsoleGameEngine.h @@ -0,0 +1,875 @@ +/* +OneLoneCoder.com - Command Line Game Engine +"Who needs a frame buffer?" - @Javidx9 + +Disclaimer +~~~~~~~~~~ +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. BUT, you acknowledge that I am not responsible for anything +bad that happens as a result of your actions. However, if good stuff happens, I +would appreciate a shout out, or at least give the blog some publicity for me. +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! +wowLinh, JavaJack59, idkwid, kingtatgi, Return Null + +Last Updated: 04/02/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 -> \ +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 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; + wcscpy_s(cfi.FaceName, L"Lucida Console"); + //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; x0 && 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; y0 && dy>0)) + x = x + 1; + else + x = x - 1; + py = py + 2 * (dx1 - dy1); + } + Draw(x, y, c, col); + } + } + } + + 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; + } + } + + 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> &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> 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) + { + // 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++) + { + m_keyNewState[i] = GetAsyncKeyState(i); + + m_keys[i].bPressed = false; + m_keys[i].bReleased = false; + + if (m_keyNewState[i] != m_keyOldState[i]) + { + 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]; + } + + // 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; + + // 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 + + 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; + + // Optional for clean up + virtual bool OnUserDestroy() + { + return true; + } + + +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 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); + return 0; + } + + 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[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 diff --git a/olcRolePlayingGame/olcConsoleGameEngineOOP.cpp b/olcRolePlayingGame/olcConsoleGameEngineOOP.cpp new file mode 100644 index 0000000..ac8f3b9 --- /dev/null +++ b/olcRolePlayingGame/olcConsoleGameEngineOOP.cpp @@ -0,0 +1,612 @@ +/* + +-------------------------------------------------------------+ + | 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 +*/ + +#include "olcConsoleGameEngineOOP.h" + +olcConsoleGameEngineOOP::olcConsoleGameEngineOOP() +{ + 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"; +} + +olcConsoleGameEngineOOP::~olcConsoleGameEngineOOP() +{ + SetConsoleActiveScreenBuffer(m_hOriginalConsole); + delete[] m_bufScreen; +} + +int olcConsoleGameEngineOOP::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; + //wcscpy_s(cfi.FaceName, L"Lucida Console"); + //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; +} + +void olcConsoleGameEngineOOP::Draw(int x, int y, wchar_t c, short col) +{ + 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 olcConsoleGameEngineOOP::Fill(int x1, int y1, int x2, int y2, wchar_t c, short col) +{ + 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 olcConsoleGameEngineOOP::DrawString(int x, int y, wstring c, short col) +{ + 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 olcConsoleGameEngineOOP::DrawStringAlpha(int x, int y, wstring c, short col) +{ + 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 olcConsoleGameEngineOOP::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 olcConsoleGameEngineOOP::DrawLine(int x1, int y1, int x2, int y2, wchar_t c, short col) +{ + 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; x0 && 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; y0 && dy>0)) + x = x + 1; + else + x = x - 1; + py = py + 2 * (dx1 - dy1); + } + Draw(x, y, c, col); + } + } +} + +void olcConsoleGameEngineOOP::DrawCircle(int xc, int yc, int r, wchar_t c, short col) +{ + 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; + } +} + +void olcConsoleGameEngineOOP::FillCircle(int xc, int yc, int r, wchar_t c, short col) +{ + // 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 olcConsoleGameEngineOOP::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 olcConsoleGameEngineOOP::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 olcConsoleGameEngineOOP::DrawWireFrameModel(const vector> &vecModelCoordinates, float x, float y, float r, float s, short col) +{ + // pair.first = x coordinate + // pair.second = y coordinate + + // Create translated model vector of coordinate pairs + vector> 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); + } +} + + + + +void olcConsoleGameEngineOOP::Start() +{ + m_bAtomActive = true; + + // Star the thread + thread t = thread(&olcConsoleGameEngineOOP::GameThread, this); + + // Wait for thread to be exited + t.join(); +} + +int olcConsoleGameEngineOOP::ScreenWidth() +{ + return m_nScreenWidth; +} + +int olcConsoleGameEngineOOP::ScreenHeight() +{ + return m_nScreenHeight; +} + +void olcConsoleGameEngineOOP::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) + { + // 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++) + { + m_keyNewState[i] = GetAsyncKeyState(i); + + m_keys[i].bPressed = false; + m_keys[i].bReleased = false; + + if (m_keyNewState[i] != m_keyOldState[i]) + { + 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]; + } + + // 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; + + // 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 + + delete[] m_bufScreen; + SetConsoleActiveScreenBuffer(m_hOriginalConsole); + m_cvGameFinished.notify_one(); + } + else + { + // User denied destroy for some reason, so continue running + m_bAtomActive = true; + } + } +} + + +// Optional for clean up +bool olcConsoleGameEngineOOP::OnUserDestroy() +{ + return true; +} + +int olcConsoleGameEngineOOP::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); + return 0; +} + +BOOL olcConsoleGameEngineOOP::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; +} + +atomic olcConsoleGameEngineOOP::m_bAtomActive = false; +condition_variable olcConsoleGameEngineOOP::m_cvGameFinished; +mutex olcConsoleGameEngineOOP::m_muxGame; \ No newline at end of file diff --git a/olcRolePlayingGame/olcConsoleGameEngineOOP.h b/olcRolePlayingGame/olcConsoleGameEngineOOP.h new file mode 100644 index 0000000..9ecbb8b --- /dev/null +++ b/olcRolePlayingGame/olcConsoleGameEngineOOP.h @@ -0,0 +1,352 @@ +/* + +-------------------------------------------------------------+ + | 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; +}; + diff --git a/olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr b/olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr new file mode 100644 index 0000000..28dbeb5 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe b/olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe new file mode 100644 index 0000000..e1b85d6 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe differ diff --git a/olcRolePlayingGame/rpgdata/gfx/Sword.spr b/olcRolePlayingGame/rpgdata/gfx/Sword.spr new file mode 100644 index 0000000..60e4138 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/Sword.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr b/olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr new file mode 100644 index 0000000..cb23f1f Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr b/olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr new file mode 100644 index 0000000..7c98f88 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/item_health.spr b/olcRolePlayingGame/rpgdata/gfx/item_health.spr new file mode 100644 index 0000000..c75b72a Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/item_health.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr b/olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr new file mode 100644 index 0000000..9ba8629 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr b/olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr new file mode 100644 index 0000000..79f5049 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe b/olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe new file mode 100644 index 0000000..36af2c9 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe differ diff --git a/olcRolePlayingGame/rpgdata/gfx/sky1.png.spr b/olcRolePlayingGame/rpgdata/gfx/sky1.png.spr new file mode 100644 index 0000000..7fb43ef Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/sky1.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/sprites_items.png b/olcRolePlayingGame/rpgdata/gfx/sprites_items.png new file mode 100644 index 0000000..debb651 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/sprites_items.png differ diff --git a/olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr b/olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr new file mode 100644 index 0000000..8b3bfa1 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png b/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png new file mode 100644 index 0000000..d97e5e4 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png differ diff --git a/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr b/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr new file mode 100644 index 0000000..6e82a5a Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/tiles002.png b/olcRolePlayingGame/rpgdata/gfx/tiles002.png new file mode 100644 index 0000000..48d0d46 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/tiles002.png differ diff --git a/olcRolePlayingGame/rpgdata/gfx/tiles002.spr b/olcRolePlayingGame/rpgdata/gfx/tiles002.spr new file mode 100644 index 0000000..f0ff720 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/tiles002.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png b/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png new file mode 100644 index 0000000..228a53a Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png differ diff --git a/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr b/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr new file mode 100644 index 0000000..ff97dc2 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/title3.png.spr b/olcRolePlayingGame/rpgdata/gfx/title3.png.spr new file mode 100644 index 0000000..13e213c Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/title3.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr b/olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr new file mode 100644 index 0000000..fca4267 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr b/olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr new file mode 100644 index 0000000..6b49846 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr b/olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr new file mode 100644 index 0000000..e887abf Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr b/olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr new file mode 100644 index 0000000..935d17c Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_orange.spr b/olcRolePlayingGame/rpgdata/gfx/toml_orange.spr new file mode 100644 index 0000000..db05845 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_orange.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr b/olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr new file mode 100644 index 0000000..2e47066 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_purple.spr b/olcRolePlayingGame/rpgdata/gfx/toml_purple.spr new file mode 100644 index 0000000..e966dab Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_purple.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr b/olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr new file mode 100644 index 0000000..f300e08 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr b/olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr new file mode 100644 index 0000000..5fd34c5 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/toml_village.spr b/olcRolePlayingGame/rpgdata/gfx/toml_village.spr new file mode 100644 index 0000000..f300e08 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/toml_village.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr b/olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr new file mode 100644 index 0000000..229433f Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr differ diff --git a/olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr b/olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr new file mode 100644 index 0000000..d3262c2 Binary files /dev/null and b/olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr differ diff --git a/olcRolePlayingGame/rpgdata/map/home.lvl b/olcRolePlayingGame/rpgdata/map/home.lvl new file mode 100644 index 0000000..704bd6b --- /dev/null +++ b/olcRolePlayingGame/rpgdata/map/home.lvl @@ -0,0 +1,2 @@ +16 16 +0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 56 1 56 1 56 1 55 1 55 1 13 1 14 1 51 1 56 1 56 1 11 1 54 1 11 1 11 1 0 1 0 1 20 1 63 1 65 1 65 1 65 1 23 1 24 1 20 1 63 1 66 1 66 1 63 1 66 1 66 1 0 1 0 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 0 1 0 1 30 0 18 0 58 1 58 1 58 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 0 1 0 1 30 0 30 0 58 1 58 1 58 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 0 1 0 1 30 0 30 0 58 1 58 1 12 1 30 0 30 0 30 0 0 1 0 1 30 0 30 0 0 1 0 1 0 1 0 1 30 0 30 0 10 1 10 1 30 0 30 0 30 0 30 0 0 1 53 1 30 0 30 0 56 1 56 1 0 1 0 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 0 1 63 1 30 0 30 0 66 1 66 1 0 1 0 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 84 0 0 1 31 0 31 0 31 0 31 0 31 0 0 1 0 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 84 0 0 1 31 0 31 0 31 0 31 0 31 0 0 1 0 1 30 0 30 0 30 0 30 0 30 0 30 0 30 0 30 0 0 1 31 0 31 0 31 0 31 0 31 0 0 1 0 1 0 1 0 1 0 1 30 0 30 0 0 1 0 1 0 1 0 1 31 0 18 0 31 0 31 0 31 0 0 1 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 0 1 31 0 31 0 31 0 31 0 31 0 0 1 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 0 1 31 0 31 0 31 0 31 0 31 0 0 1 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 \ No newline at end of file diff --git a/olcRolePlayingGame/rpgdata/map/introduction.lvl b/olcRolePlayingGame/rpgdata/map/introduction.lvl new file mode 100644 index 0000000..8dfec31 --- /dev/null +++ b/olcRolePlayingGame/rpgdata/map/introduction.lvl @@ -0,0 +1,2 @@ +64 15 +9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 19 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 14 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 19 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 19 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 3 0 25 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 15 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 13 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 19 0 19 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 14 0 14 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 14 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 19 0 9 0 9 0 9 0 19 0 19 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 19 0 9 0 9 0 9 0 19 0 9 0 19 0 9 0 9 0 19 0 9 0 9 0 9 0 19 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 13 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 \ No newline at end of file diff --git a/olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl b/olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl new file mode 100644 index 0000000..1c0839b --- /dev/null +++ b/olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl @@ -0,0 +1,2 @@ +64 64 +14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 73 0 14 0 14 1 14 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 0 1 1 1 2 1 3 1 4 1 5 1 6 1 35 1 35 1 9 1 10 1 14 1 14 1 13 1 14 1 15 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 16 1 17 1 18 1 19 1 20 1 21 1 22 1 51 0 52 0 25 1 26 1 27 1 28 1 29 1 30 1 31 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 32 1 33 1 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 46 1 47 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 48 1 49 1 34 0 34 0 50 1 50 1 50 1 50 1 50 1 50 1 50 1 50 1 58 0 34 0 62 1 63 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 64 1 65 1 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 50 1 58 0 34 0 90 1 92 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 80 1 39 1 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 50 1 58 0 34 0 105 0 108 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 96 1 97 1 34 0 34 0 34 0 50 1 42 0 34 0 34 0 34 0 41 0 50 1 58 1 34 0 123 1 124 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 112 1 113 1 34 0 34 0 34 0 50 1 50 1 50 1 50 1 50 1 50 1 50 1 58 1 34 0 126 1 127 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 128 1 129 1 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 34 0 142 1 143 1 73 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 73 0 73 0 144 1 145 1 146 1 147 1 148 1 149 1 150 1 151 1 152 1 153 1 154 1 155 1 156 1 157 1 158 1 159 1 73 0 144 0 145 0 146 0 147 0 148 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 160 1 161 1 162 1 163 1 164 1 165 1 166 1 161 1 161 1 169 1 170 1 171 1 171 1 173 1 174 1 175 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 \ No newline at end of file diff --git a/olcRolePlayingGame/rpgdata/map/village1.lvl b/olcRolePlayingGame/rpgdata/map/village1.lvl new file mode 100644 index 0000000..21f1724 --- /dev/null +++ b/olcRolePlayingGame/rpgdata/map/village1.lvl @@ -0,0 +1,2 @@ +64 32 +9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 1 9 1 9 1 9 1 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 14 0 9 1 9 1 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 1 14 0 19 1 14 0 14 0 14 0 6 1 7 1 7 1 7 1 7 1 7 1 7 1 8 1 14 0 14 0 14 0 19 1 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 16 1 17 1 17 1 17 1 17 1 17 1 6 1 8 1 14 0 14 0 14 0 14 0 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 26 1 26 1 26 1 27 0 26 1 26 1 6 1 8 1 14 0 14 0 14 0 14 0 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 1 14 0 14 0 9 1 14 0 14 0 14 0 14 0 14 0 19 1 29 0 19 1 14 0 16 1 18 1 14 0 14 0 14 0 14 0 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 29 0 14 0 14 0 26 1 26 1 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 19 1 29 0 19 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 29 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 9 1 14 0 14 0 14 0 14 0 29 0 14 0 14 0 14 0 9 1 14 0 14 0 14 0 14 0 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 29 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 29 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 1 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 29 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 1 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 1 9 1 9 1 9 1 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 14 0 14 0 9 1 9 1 9 1 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 1 9 1 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 9 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 14 0 \ No newline at end of file