parent
1bf93cada4
commit
7920e0c6f6
@ -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,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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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…
Reference in new issue