Added Role Playing Game (at last)

It is what it is :D
master
Javidx9 6 years ago committed by GitHub
parent 1bf93cada4
commit 7920e0c6f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 175
      olcRolePlayingGame/RPGVideo.vcxproj
  2. 72
      olcRolePlayingGame/RPGVideo.vcxproj.filters
  3. 4
      olcRolePlayingGame/RPGVideo.vcxproj.user
  4. 137
      olcRolePlayingGame/RPG_Assets.cpp
  5. 122
      olcRolePlayingGame/RPG_Assets.h
  6. 201
      olcRolePlayingGame/RPG_Commands.cpp
  7. 165
      olcRolePlayingGame/RPG_Commands.h
  8. 373
      olcRolePlayingGame/RPG_Dynamics.cpp
  9. 204
      olcRolePlayingGame/RPG_Dynamics.h
  10. 693
      olcRolePlayingGame/RPG_Engine.cpp
  11. 159
      olcRolePlayingGame/RPG_Engine.h
  12. 207
      olcRolePlayingGame/RPG_Items.cpp
  13. 134
      olcRolePlayingGame/RPG_Items.h
  14. 269
      olcRolePlayingGame/RPG_Main.cpp
  15. 222
      olcRolePlayingGame/RPG_Maps.cpp
  16. 139
      olcRolePlayingGame/RPG_Maps.h
  17. 168
      olcRolePlayingGame/RPG_Quests.cpp
  18. 125
      olcRolePlayingGame/RPG_Quests.h
  19. 80
      olcRolePlayingGame/main.cpp
  20. 875
      olcRolePlayingGame/olcConsoleGameEngine.h
  21. 612
      olcRolePlayingGame/olcConsoleGameEngineOOP.cpp
  22. 352
      olcRolePlayingGame/olcConsoleGameEngineOOP.h
  23. BIN
      olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr
  24. BIN
      olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe
  25. BIN
      olcRolePlayingGame/rpgdata/gfx/Sword.spr
  26. BIN
      olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr
  27. BIN
      olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr
  28. BIN
      olcRolePlayingGame/rpgdata/gfx/item_health.spr
  29. BIN
      olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr
  30. BIN
      olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr
  31. BIN
      olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe
  32. BIN
      olcRolePlayingGame/rpgdata/gfx/sky1.png.spr
  33. BIN
      olcRolePlayingGame/rpgdata/gfx/sprites_items.png
  34. BIN
      olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr
  35. BIN
      olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png
  36. BIN
      olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr
  37. BIN
      olcRolePlayingGame/rpgdata/gfx/tiles002.png
  38. BIN
      olcRolePlayingGame/rpgdata/gfx/tiles002.spr
  39. BIN
      olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png
  40. BIN
      olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr
  41. BIN
      olcRolePlayingGame/rpgdata/gfx/title3.png.spr
  42. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr
  43. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr
  44. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr
  45. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr
  46. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_orange.spr
  47. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr
  48. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_purple.spr
  49. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr
  50. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr
  51. BIN
      olcRolePlayingGame/rpgdata/gfx/toml_village.spr
  52. BIN
      olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr
  53. BIN
      olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr
  54. 2
      olcRolePlayingGame/rpgdata/map/home.lvl
  55. 2
      olcRolePlayingGame/rpgdata/map/introduction.lvl
  56. 2
      olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl
  57. 2
      olcRolePlayingGame/rpgdata/map/village1.lvl

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="olcConsoleGameEngineOOP.cpp" />
<ClCompile Include="RPG_Assets.cpp" />
<ClCompile Include="RPG_Commands.cpp" />
<ClCompile Include="RPG_Dynamics.cpp" />
<ClCompile Include="RPG_Engine.cpp" />
<ClCompile Include="RPG_Items.cpp" />
<ClCompile Include="RPG_Maps.cpp" />
<ClCompile Include="RPG_Quests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="olcConsoleGameEngineOOP.h" />
<ClInclude Include="RPG_Assets.h" />
<ClInclude Include="RPG_Commands.h" />
<ClInclude Include="RPG_Dynamics.h" />
<ClInclude Include="RPG_Engine.h" />
<ClInclude Include="RPG_Items.h" />
<ClInclude Include="RPG_Maps.h" />
<ClInclude Include="RPG_Quests.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{73460557-14A0-4160-8A8F-BCBC4B271238}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>RPGVideo</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="RPG_Maps.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="olcConsoleGameEngineOOP.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Assets.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Dynamics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Commands.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Engine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Quests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RPG_Items.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="RPG_Maps.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="olcConsoleGameEngineOOP.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Assets.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Dynamics.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Commands.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Engine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Quests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RPG_Items.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

@ -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());
}

@ -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 <map>
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<string, olcSprite*> m_mapSprites;
map<string, cMap*> m_mapMaps;
map<string, cItem*> m_mapItems;
};

@ -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<string> 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;
}

@ -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 <list>
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<cCommand*> 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<string> line);
void Start() override;
private:
vector<string> 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;
};

@ -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;
}

@ -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;
};

@ -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<string> vecLines)
{
m_vecDialogToShow = vecLines;
m_bShowDialog = true;
}
void RPG_Engine::DisplayDialog(vector<string> 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<vecText.size(); l++)
DrawBigText(vecText[l], x, y + l * 8);
}
void RPG_Engine::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++;
}
}
void RPG_Engine::ChangeMap(string sMapName, float x, float y)
{
// Destroy all dynamics
m_vecDynamics.clear();
m_vecDynamics.push_back(m_pPlayer);
// Set current map
m_pCurrentMap = RPG_Assets::get().GetMap(sMapName);
// Update player location
m_pPlayer->px = 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;
}
}

@ -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 <iostream>
#include <string>
#include <memory.h>
#include <algorithm>
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<cDynamic*> m_vecDynamics; // Fixed
vector<cDynamic*> m_vecProjectiles; // Transient
cScriptProcessor m_script;
list<cQuest*> m_listQuests;
list<cItem*> 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<string> m_vecDialogToShow;
bool m_bShowDialog = false;
float m_fDialogX = 0.0f;
float m_fDialogY = 0.0f;
public:
void ShowDialog(vector<string> vecLines);
void DisplayDialog(vector<string> 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);
};

@ -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;
}

@ -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;
};

@ -0,0 +1,269 @@
#include <iostream>
#include <string>
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<cDynamic*> 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<string> m_vecDialogToShow;
bool m_bShowDialog = false;
float m_fDialogX = 0.0f;
float m_fDialogY = 0.0f;
public:
void ShowDialog(vector<string> vecLines)
{
m_vecDialogToShow = vecLines;
m_bShowDialog = true;
}
void DisplayDialog(vector<string> 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<vecText.size(); l++)
DrawBigText(vecText[l], x, y + l * 8);
}
};

@ -0,0 +1,222 @@
/*
+-------------------------------------------------------------+
| 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_Maps.h"
#include <fstream>
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<cDynamic*> &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<cDynamic*> &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<cDynamic*> &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<cDynamic*> &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;
}

@ -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<cDynamic*> &vecDyns)
{
return false;
}
virtual bool OnInteraction(vector<cDynamic*> &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<cDynamic*> &vecDyns) override;
bool OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature) override;
};
class cMap_Home1 : public cMap
{
public:
cMap_Home1();
bool PopulateDynamics(vector<cDynamic*> &vecDyns) override;
bool OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature) override;
};

@ -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<cDynamic*> &vecDyns, string sMap)
{
return true;
}
bool cQuest::OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature)
{
return true;
}
bool cQuest_MainQuest::PopulateDynamics(vector<cDynamic*> &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<cDynamic*> &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<cDynamic*> &vecDyns, string sMap)
{
return true;
}
bool cQuest_BobsQuest::OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature)
{
if (target->sName == "sarah")
{
X(ShowDialog({ "[Sarah]", "You are doing Bob's", "quest!" }));
return true;
}
return false;
}

@ -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<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature);
virtual bool PopulateDynamics(vector<cDynamic*> &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<cDynamic*> &vecDyns, string sMap) override;
bool OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature) override;
private:
int m_nPhase = 0;
};
class cQuest_BobsQuest : public cQuest
{
public:
bool PopulateDynamics(vector<cDynamic*> &vecDyns, string sMap) override;
bool OnInteraction(vector<cDynamic*> &vecDynobs, cDynamic *target, NATURE nature) override;
private:
int m_nPhase = 0;
};

@ -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;
}

@ -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 <iostream>
#include <chrono>
#include <vector>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
using namespace std;
#include <windows.h>
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; x<xe; i++)
{
x = x + 1;
if (px<0)
px = px + 2 * dy1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
y = y + 1;
else
y = y - 1;
px = px + 2 * (dy1 - dx1);
}
Draw(x, y, c, col);
}
}
else
{
if (dy >= 0)
{
x = x1;
y = y1;
ye = y2;
}
else
{
x = x2;
y = y2;
ye = y1;
}
Draw(x, y, c, col);
for (i = 0; y<ye; i++)
{
y = y + 1;
if (py <= 0)
py = py + 2 * dx1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
x = x + 1;
else
x = x - 1;
py = py + 2 * (dx1 - dy1);
}
Draw(x, y, c, col);
}
}
}
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<pair<float, float>> &vecModelCoordinates, float x, float y, float r = 0.0f, float s = 1.0f, short col = FG_WHITE)
{
// pair.first = x coordinate
// pair.second = y coordinate
// Create translated model vector of coordinate pairs
vector<pair<float, float>> vecTransformedCoordinates;
int verts = vecModelCoordinates.size();
vecTransformedCoordinates.resize(verts);
// Rotate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecModelCoordinates[i].first * cosf(r) - vecModelCoordinates[i].second * sinf(r);
vecTransformedCoordinates[i].second = vecModelCoordinates[i].first * sinf(r) + vecModelCoordinates[i].second * cosf(r);
}
// Scale
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first * s;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second * s;
}
// Translate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first + x;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second + y;
}
// Draw Closed Polygon
for (int i = 0; i < verts + 1; i++)
{
int j = (i + 1);
DrawLine((int)vecTransformedCoordinates[i % verts].first, (int)vecTransformedCoordinates[i % verts].second,
(int)vecTransformedCoordinates[j % verts].first, (int)vecTransformedCoordinates[j % verts].second, PIXEL_SOLID, col);
}
}
~olcConsoleGameEngine()
{
SetConsoleActiveScreenBuffer(m_hOriginalConsole);
delete[] m_bufScreen;
}
public:
void Start()
{
m_bAtomActive = true;
// Star the thread
thread t = thread(&olcConsoleGameEngine::GameThread, this);
// Wait for thread to be exited
t.join();
}
int ScreenWidth()
{
return m_nScreenWidth;
}
int ScreenHeight()
{
return m_nScreenHeight;
}
private:
void GameThread()
{
// Create user resources as part of this thread
if (!OnUserCreate())
m_bAtomActive = false;
auto tp1 = chrono::system_clock::now();
auto tp2 = chrono::system_clock::now();
while (m_bAtomActive)
{
// Run as fast as possible
while (m_bAtomActive)
{
// Handle Timing
tp2 = chrono::system_clock::now();
chrono::duration<float> elapsedTime = tp2 - tp1;
tp1 = tp2;
float fElapsedTime = elapsedTime.count();
// Handle Keyboard Input
for (int i = 0; i < 256; i++)
{
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<mutex> ul(m_muxGame);
m_cvGameFinished.wait(ul);
}
return true;
}
protected:
int m_nScreenWidth;
int m_nScreenHeight;
CHAR_INFO *m_bufScreen;
wstring m_sAppName;
HANDLE m_hOriginalConsole;
CONSOLE_SCREEN_BUFFER_INFO m_OriginalConsoleInfo;
HANDLE m_hConsole;
HANDLE m_hConsoleIn;
SMALL_RECT m_rectWindow;
short m_keyOldState[256] = { 0 };
short m_keyNewState[256] = { 0 };
bool m_mouseOldState[5] = { 0 };
bool m_mouseNewState[5] = { 0 };
bool m_bConsoleInFocus = true;
static atomic<bool> m_bAtomActive;
static condition_variable m_cvGameFinished;
static mutex m_muxGame;
};
atomic<bool> olcConsoleGameEngine::m_bAtomActive = false;
condition_variable olcConsoleGameEngine::m_cvGameFinished;
mutex olcConsoleGameEngine::m_muxGame;

@ -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; x<xe; i++)
{
x = x + 1;
if (px<0)
px = px + 2 * dy1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
y = y + 1;
else
y = y - 1;
px = px + 2 * (dy1 - dx1);
}
Draw(x, y, c, col);
}
}
else
{
if (dy >= 0)
{
x = x1;
y = y1;
ye = y2;
}
else
{
x = x2;
y = y2;
ye = y1;
}
Draw(x, y, c, col);
for (i = 0; y<ye; i++)
{
y = y + 1;
if (py <= 0)
py = py + 2 * dx1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0))
x = x + 1;
else
x = x - 1;
py = py + 2 * (dx1 - dy1);
}
Draw(x, y, c, col);
}
}
}
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<pair<float, float>> &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<pair<float, float>> vecTransformedCoordinates;
int verts = vecModelCoordinates.size();
vecTransformedCoordinates.resize(verts);
// Rotate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecModelCoordinates[i].first * cosf(r) - vecModelCoordinates[i].second * sinf(r);
vecTransformedCoordinates[i].second = vecModelCoordinates[i].first * sinf(r) + vecModelCoordinates[i].second * cosf(r);
}
// Scale
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first * s;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second * s;
}
// Translate
for (int i = 0; i < verts; i++)
{
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first + x;
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second + y;
}
// Draw Closed Polygon
for (int i = 0; i < verts + 1; i++)
{
int j = (i + 1);
DrawLine((int)vecTransformedCoordinates[i % verts].first, (int)vecTransformedCoordinates[i % verts].second,
(int)vecTransformedCoordinates[j % verts].first, (int)vecTransformedCoordinates[j % verts].second, PIXEL_SOLID, col);
}
}
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<float> 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<mutex> ul(m_muxGame);
m_cvGameFinished.wait(ul);
}
return true;
}
atomic<bool> olcConsoleGameEngineOOP::m_bAtomActive = false;
condition_variable olcConsoleGameEngineOOP::m_cvGameFinished;
mutex olcConsoleGameEngineOOP::m_muxGame;

@ -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 <iostream>
#include <chrono>
#include <vector>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
using namespace std;
#include <windows.h>
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<pair<float, float>> &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<bool> m_bAtomActive;
static condition_variable m_cvGameFinished;
static mutex m_muxGame;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

@ -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

@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save