Added Role Playing Game (at last)
It is what it is :D
This commit is contained in:
parent
1bf93cada4
commit
7920e0c6f6
175
olcRolePlayingGame/RPGVideo.vcxproj
Normal file
175
olcRolePlayingGame/RPGVideo.vcxproj
Normal file
@ -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>
|
72
olcRolePlayingGame/RPGVideo.vcxproj.filters
Normal file
72
olcRolePlayingGame/RPGVideo.vcxproj.filters
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="RPG_Maps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="olcConsoleGameEngineOOP.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Assets.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Dynamics.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Commands.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Engine.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Quests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RPG_Items.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="RPG_Maps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="olcConsoleGameEngineOOP.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Assets.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Dynamics.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Commands.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Engine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Quests.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RPG_Items.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
4
olcRolePlayingGame/RPGVideo.vcxproj.user
Normal file
4
olcRolePlayingGame/RPGVideo.vcxproj.user
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
137
olcRolePlayingGame/RPG_Assets.cpp
Normal file
137
olcRolePlayingGame/RPG_Assets.cpp
Normal file
@ -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());
|
||||
}
|
122
olcRolePlayingGame/RPG_Assets.h
Normal file
122
olcRolePlayingGame/RPG_Assets.h
Normal file
@ -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;
|
||||
};
|
||||
|
||||
|
201
olcRolePlayingGame/RPG_Commands.cpp
Normal file
201
olcRolePlayingGame/RPG_Commands.cpp
Normal file
@ -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;
|
||||
}
|
165
olcRolePlayingGame/RPG_Commands.h
Normal file
165
olcRolePlayingGame/RPG_Commands.h
Normal file
@ -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;
|
||||
};
|
||||
|
373
olcRolePlayingGame/RPG_Dynamics.cpp
Normal file
373
olcRolePlayingGame/RPG_Dynamics.cpp
Normal file
@ -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;
|
||||
}
|
204
olcRolePlayingGame/RPG_Dynamics.h
Normal file
204
olcRolePlayingGame/RPG_Dynamics.h
Normal file
@ -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;
|
||||
};
|
693
olcRolePlayingGame/RPG_Engine.cpp
Normal file
693
olcRolePlayingGame/RPG_Engine.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
159
olcRolePlayingGame/RPG_Engine.h
Normal file
159
olcRolePlayingGame/RPG_Engine.h
Normal file
@ -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);
|
||||
};
|
207
olcRolePlayingGame/RPG_Items.cpp
Normal file
207
olcRolePlayingGame/RPG_Items.cpp
Normal file
@ -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;
|
||||
}
|
134
olcRolePlayingGame/RPG_Items.h
Normal file
134
olcRolePlayingGame/RPG_Items.h
Normal file
@ -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;
|
||||
};
|
269
olcRolePlayingGame/RPG_Main.cpp
Normal file
269
olcRolePlayingGame/RPG_Main.cpp
Normal file
@ -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);
|
||||
}
|
||||
};
|
222
olcRolePlayingGame/RPG_Maps.cpp
Normal file
222
olcRolePlayingGame/RPG_Maps.cpp
Normal file
@ -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;
|
||||
}
|
139
olcRolePlayingGame/RPG_Maps.h
Normal file
139
olcRolePlayingGame/RPG_Maps.h
Normal file
@ -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;
|
||||
};
|
168
olcRolePlayingGame/RPG_Quests.cpp
Normal file
168
olcRolePlayingGame/RPG_Quests.cpp
Normal file
@ -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;
|
||||
}
|
125
olcRolePlayingGame/RPG_Quests.h
Normal file
125
olcRolePlayingGame/RPG_Quests.h
Normal file
@ -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;
|
||||
};
|
80
olcRolePlayingGame/main.cpp
Normal file
80
olcRolePlayingGame/main.cpp
Normal file
@ -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;
|
||||
}
|
875
olcRolePlayingGame/olcConsoleGameEngine.h
Normal file
875
olcRolePlayingGame/olcConsoleGameEngine.h
Normal file
@ -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;
|
612
olcRolePlayingGame/olcConsoleGameEngineOOP.cpp
Normal file
612
olcRolePlayingGame/olcConsoleGameEngineOOP.cpp
Normal file
@ -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;
|
352
olcRolePlayingGame/olcConsoleGameEngineOOP.h
Normal file
352
olcRolePlayingGame/olcConsoleGameEngineOOP.h
Normal file
@ -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;
|
||||
};
|
||||
|
BIN
olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/SeditSlimeTransparent.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/SpriteEditor.exe
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/Sword.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/Sword.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/balloon1.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/cpp_guy.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/item_health.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/item_health.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/item_healthboost.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/javidx9_nesfont8x8.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/olc_sprite.exe
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/sky1.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/sky1.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_items.png
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_items.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_items.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/sprites_weapons.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/tiles002.png
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/tiles002.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
olcRolePlayingGame/rpgdata/gfx/tiles002.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/tiles002.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
BIN
olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/tileset_modernish.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/title3.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/title3.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_CharacterSprites.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_castleinterior.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_char001.png.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_modernish.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_orange.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_orange.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_orange_glasses.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_purple.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_purple.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_spritesheetdark.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_treasurechest.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/toml_village.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/toml_village.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/weapon_basic_sword.spr
Normal file
Binary file not shown.
BIN
olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr
Normal file
BIN
olcRolePlayingGame/rpgdata/gfx/worldmap1.png.spr
Normal file
Binary file not shown.
2
olcRolePlayingGame/rpgdata/map/home.lvl
Normal file
2
olcRolePlayingGame/rpgdata/map/home.lvl
Normal file
@ -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
|
2
olcRolePlayingGame/rpgdata/map/introduction.lvl
Normal file
2
olcRolePlayingGame/rpgdata/map/introduction.lvl
Normal file
@ -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
|
2
olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl
Normal file
2
olcRolePlayingGame/rpgdata/map/loz_dungeon.lvl
Normal file
File diff suppressed because one or more lines are too long
2
olcRolePlayingGame/rpgdata/map/village1.lvl
Normal file
2
olcRolePlayingGame/rpgdata/map/village1.lvl
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user