Patch to v2.23
This commit is contained in:
parent
e9ba22354c
commit
0e33d3fffb
219
TEST_Camera2D.cpp
Normal file
219
TEST_Camera2D.cpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
Example file for olcUTIL_Camera2D.h
|
||||||
|
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2018 - 2022 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
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION
|
||||||
|
#include "olcPixelGameEngine.h"
|
||||||
|
|
||||||
|
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||||
|
#include "extensions/olcPGEX_TransformedView.h"
|
||||||
|
|
||||||
|
#include "utilities/olcUTIL_Camera2D.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
To demonstrate the camera, we need a world. In this case its a simle tile
|
||||||
|
world of 80x75 tiles, and each tile is 32x32 screen pixels.
|
||||||
|
|
||||||
|
A transformed view is used to navigate the world manually via the middle
|
||||||
|
mouse button in "free roam" mode, or controlled by the camera.
|
||||||
|
|
||||||
|
Specifically a Tile Transformed View is used, which means all units for
|
||||||
|
drawing and for the camera are specified in tile space, i.e. 1 tile is
|
||||||
|
1x1 units (regardless of pixel size)
|
||||||
|
|
||||||
|
No assets are used for this application, so the world is constructed
|
||||||
|
out of coloured squares so you can see you are moving through it.
|
||||||
|
|
||||||
|
Pressing "TAB" key will swap between "free roam" and "play" modes. In
|
||||||
|
free roam mode, you can use middle mouse button to pan and zoom around
|
||||||
|
the world. The camera's visible area to the player is highlighted in red.
|
||||||
|
In play mode, the camera behaves as it would in a 2D game, depending upon
|
||||||
|
the selected mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TEST_Camera2D : public olc::PixelGameEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TEST_Camera2D()
|
||||||
|
{
|
||||||
|
sAppName = "Camera2D Utility Test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transformed view object to make world offsetting simple
|
||||||
|
olc::TileTransformedView tv;
|
||||||
|
|
||||||
|
// Conveninet constants to define tile map world
|
||||||
|
olc::vi2d m_vWorldSize = { 80, 75 };
|
||||||
|
olc::vi2d m_vTileSize = { 32, 32 };
|
||||||
|
|
||||||
|
// The camera!
|
||||||
|
olc::utils::Camera2D camera;
|
||||||
|
|
||||||
|
// The point that represents the player, it is "tracked"
|
||||||
|
// by the camera
|
||||||
|
olc::vf2d vTrackedPoint;
|
||||||
|
|
||||||
|
// Flag whether we are in "free roam" or "play" mode
|
||||||
|
bool bFreeRoam = false;
|
||||||
|
|
||||||
|
// The world map, stored as a 1D array
|
||||||
|
std::vector<uint8_t> vWorldMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool OnUserCreate() override
|
||||||
|
{
|
||||||
|
// Construct transform view
|
||||||
|
tv = olc::TileTransformedView(GetScreenSize(), m_vTileSize);
|
||||||
|
|
||||||
|
// Construct Camera
|
||||||
|
vTrackedPoint = { 20.0f, 20.0f };
|
||||||
|
camera = olc::utils::Camera2D(GetScreenSize() / m_vTileSize, vTrackedPoint);
|
||||||
|
|
||||||
|
// Configure Camera
|
||||||
|
camera.SetTarget(vTrackedPoint);
|
||||||
|
camera.SetMode(olc::utils::Camera2D::Mode::Simple);
|
||||||
|
camera.SetWorldBoundary({ 0.0f, 0.0f }, m_vWorldSize);
|
||||||
|
camera.EnableWorldBoundary(true);
|
||||||
|
|
||||||
|
// Create "tile map" world with just two tiles
|
||||||
|
vWorldMap.resize(m_vWorldSize.x * m_vWorldSize.y);
|
||||||
|
for (int i = 0; i < vWorldMap.size(); i++)
|
||||||
|
vWorldMap[i] = ((rand() % 20) == 1) ? 1 : 0;
|
||||||
|
|
||||||
|
// Set background colour
|
||||||
|
Clear(olc::CYAN);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override
|
||||||
|
{
|
||||||
|
// In free roam, middle mouse button pans & zooms
|
||||||
|
if (bFreeRoam)
|
||||||
|
tv.HandlePanAndZoom();
|
||||||
|
|
||||||
|
// Handle player "physics" in response to key presses
|
||||||
|
olc::vf2d vVel = { 0.0f, 0.0f };
|
||||||
|
if (GetKey(olc::Key::W).bHeld) vVel += {0, -1};
|
||||||
|
if (GetKey(olc::Key::S).bHeld) vVel += {0, +1};
|
||||||
|
if (GetKey(olc::Key::A).bHeld) vVel += {-1, 0};
|
||||||
|
if (GetKey(olc::Key::D).bHeld) vVel += {+1, 0};
|
||||||
|
vTrackedPoint += vVel * 8.0f * fElapsedTime;
|
||||||
|
|
||||||
|
// Switch between "free roam" and "play" mode with TAB key
|
||||||
|
if (GetKey(olc::Key::TAB).bPressed)
|
||||||
|
{
|
||||||
|
// Always setup camera to play mode when tab key pressed
|
||||||
|
tv.SetWorldOffset(camera.GetViewPosition());
|
||||||
|
tv.SetWorldScale(m_vTileSize);
|
||||||
|
bFreeRoam = !bFreeRoam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch camera mode in operation
|
||||||
|
if (GetKey(olc::Key::K1).bPressed)
|
||||||
|
camera.SetMode(olc::utils::Camera2D::Mode::Simple);
|
||||||
|
if (GetKey(olc::Key::K2).bPressed)
|
||||||
|
camera.SetMode(olc::utils::Camera2D::Mode::EdgeMove);
|
||||||
|
if (GetKey(olc::Key::K3).bPressed)
|
||||||
|
camera.SetMode(olc::utils::Camera2D::Mode::LazyFollow);
|
||||||
|
if (GetKey(olc::Key::K4).bPressed)
|
||||||
|
camera.SetMode(olc::utils::Camera2D::Mode::FixedScreens);
|
||||||
|
|
||||||
|
// Update the camera, if teh tracked object remains visible,
|
||||||
|
// true is returned
|
||||||
|
bool bOnScreen = camera.Update(fElapsedTime);
|
||||||
|
|
||||||
|
// In "play" mode, set the transformed view to that required by
|
||||||
|
// the camera
|
||||||
|
if (!bFreeRoam)
|
||||||
|
tv.SetWorldOffset(camera.GetViewPosition());
|
||||||
|
|
||||||
|
// Render "tile map", by getting visible tiles
|
||||||
|
olc::vi2d vTileTL = tv.GetTopLeftTile().max({ 0,0 });
|
||||||
|
olc::vi2d vTileBR = tv.GetBottomRightTile().min(m_vWorldSize);
|
||||||
|
olc::vi2d vTile;
|
||||||
|
// Then looping through them and drawing them
|
||||||
|
for (vTile.y = vTileTL.y; vTile.y < vTileBR.y; vTile.y++)
|
||||||
|
for (vTile.x = vTileTL.x; vTile.x < vTileBR.x; vTile.x++)
|
||||||
|
{
|
||||||
|
int idx = vTile.y * m_vWorldSize.x + vTile.x;
|
||||||
|
|
||||||
|
if (vWorldMap[idx] == 0)
|
||||||
|
tv.FillRectDecal(vTile, { 1.0f, 1.0f }, olc::Pixel(40, 40, 40));
|
||||||
|
|
||||||
|
if (vWorldMap[idx] == 1)
|
||||||
|
tv.FillRectDecal(vTile, { 1.0f, 1.0f }, olc::Pixel(60, 60, 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the "player" as a 1x1 cell
|
||||||
|
tv.FillRectDecal(vTrackedPoint - olc::vf2d(0.5f, 0.5f), {1.0f, 1.0f}, olc::BLUE);
|
||||||
|
|
||||||
|
// Overlay with information
|
||||||
|
if (bFreeRoam)
|
||||||
|
{
|
||||||
|
tv.FillRectDecal(camera.GetViewPosition(), camera.GetViewSize(), olc::PixelF(1.0f, 0.0f, 0.0f, 0.5f));
|
||||||
|
DrawStringPropDecal({ 2, 2 }, "TAB: Free Mode, M-Btn to Pan & Zoom", olc::YELLOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DrawStringPropDecal({ 2,2 }, "TAB: Play Mode", olc::YELLOW);
|
||||||
|
|
||||||
|
DrawStringPropDecal({ 2,12 }, "WASD : Move", olc::YELLOW);
|
||||||
|
DrawStringPropDecal({ 2,22 }, "CAMERA: 1) Simple 2) EdgeMove 3) LazyFollow 4) Screens", olc::YELLOW);
|
||||||
|
DrawStringPropDecal({ 2,42 }, vTrackedPoint.str(), olc::YELLOW);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TEST_Camera2D demo;
|
||||||
|
if (demo.Construct(512, 480, 2, 2))
|
||||||
|
demo.Start();
|
||||||
|
return 0;
|
||||||
|
}
|
197
diff
Normal file
197
diff
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
diff --git a/.gitignore b/.gitignore
|
||||||
|
index e915029..dd400cf 100644
|
||||||
|
--- a/.gitignore
|
||||||
|
+++ b/.gitignore
|
||||||
|
@@ -3,3 +3,5 @@
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
/.vs
|
||||||
|
+/utilities/olcUTIL_AffineView.h
|
||||||
|
+/utilities/olcUTIL_Maths.h
|
||||||
|
diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h
|
||||||
|
index 5480a15..92dee60 100644
|
||||||
|
--- a/olcPixelGameEngine.h
|
||||||
|
+++ b/olcPixelGameEngine.h
|
||||||
|
@@ -3,7 +3,7 @@
|
||||||
|
olcPixelGameEngine.h
|
||||||
|
|
||||||
|
+-------------------------------------------------------------+
|
||||||
|
- | OneLoneCoder Pixel Game Engine v2.21 |
|
||||||
|
+ | OneLoneCoder Pixel Game Engine v2.23 |
|
||||||
|
| "What do you need? Pixels... Lots of Pixels..." - javidx9 |
|
||||||
|
+-------------------------------------------------------------+
|
||||||
|
|
||||||
|
@@ -197,7 +197,7 @@
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
- David Barr, aka javidx9, <20>OneLoneCoder 2018, 2019, 2020, 2021, 2022
|
||||||
|
+ David Barr, aka javidx9, (c) OneLoneCoder 2018, 2019, 2020, 2021, 2022
|
||||||
|
*/
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
@@ -315,6 +315,9 @@
|
||||||
|
+FillTexturedTriangle() - Software rasterizes a textured, coloured, triangle
|
||||||
|
+FillTexturedPolygon() - Hijacks DecalStructure for configuration
|
||||||
|
+olc::vf2d arguments for Sprite::Sample() functions
|
||||||
|
+ 2.22: = Fix typo on dragged file buffers for unicode builds
|
||||||
|
+ 2.23: Fixed Emscripten host sizing errors - Thanks Moros
|
||||||
|
+ Fixed v2d_generic.clamp() function
|
||||||
|
|
||||||
|
!! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !!
|
||||||
|
!! Volunteers willing to help appreciated, though PRs are manually integrated with credit !!
|
||||||
|
@@ -394,7 +397,7 @@ int main()
|
||||||
|
#include <cstring>
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
-#define PGE_VER 221
|
||||||
|
+#define PGE_VER 223
|
||||||
|
|
||||||
|
// O------------------------------------------------------------------------------O
|
||||||
|
// | COMPILER CONFIGURATION ODDITIES |
|
||||||
|
@@ -682,7 +685,7 @@ namespace olc
|
||||||
|
v2d_generic min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); }
|
||||||
|
v2d_generic cart() { return { std::cos(y) * x, std::sin(y) * x }; }
|
||||||
|
v2d_generic polar() { return { mag(), std::atan2(y, x) }; }
|
||||||
|
- v2d_generic clamp(const v2d_generic& v1, const v2d_generic& v2) const { return this->max(v1)->min(v2); }
|
||||||
|
+ v2d_generic clamp(const v2d_generic& v1, const v2d_generic& v2) const { return this->max(v1).min(v2); }
|
||||||
|
v2d_generic lerp(const v2d_generic& v1, const double t) { return this->operator*(T(1.0 - t)) + (v1 * T(t)); }
|
||||||
|
T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; }
|
||||||
|
T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; }
|
||||||
|
@@ -1357,8 +1360,9 @@ namespace olc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OLC_PLATFORM_X11)
|
||||||
|
- namespace X11
|
||||||
|
- {#include <GL/glx.h>}
|
||||||
|
+ namespace X11 {
|
||||||
|
+ #include <GL/glx.h>
|
||||||
|
+ }
|
||||||
|
#define CALLSTYLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -4594,17 +4598,17 @@ namespace olc
|
||||||
|
// #include <OpenGL/glu.h>
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
-//#if defined(OLC_PLATFORM_EMSCRIPTEN)
|
||||||
|
-// #include <EGL/egl.h>
|
||||||
|
-// #include <GLES2/gl2.h>
|
||||||
|
-// #define GL_GLEXT_PROTOTYPES
|
||||||
|
-// #include <GLES2/gl2ext.h>
|
||||||
|
-// #include <emscripten/emscripten.h>
|
||||||
|
-// #define CALLSTYLE
|
||||||
|
-// typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
|
||||||
|
-// #define GL_CLAMP GL_CLAMP_TO_EDGE
|
||||||
|
-// #define OGL_LOAD(t, n) n;
|
||||||
|
-//#endif
|
||||||
|
+#if defined(OLC_PLATFORM_EMSCRIPTEN)
|
||||||
|
+ #include <EGL/egl.h>
|
||||||
|
+ #include <GLES2/gl2.h>
|
||||||
|
+ #define GL_GLEXT_PROTOTYPES
|
||||||
|
+ #include <GLES2/gl2ext.h>
|
||||||
|
+ #include <emscripten/emscripten.h>
|
||||||
|
+ #define CALLSTYLE
|
||||||
|
+ typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
|
||||||
|
+ #define GL_CLAMP GL_CLAMP_TO_EDGE
|
||||||
|
+ #define OGL_LOAD(t, n) n;
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
namespace olc
|
||||||
|
{
|
||||||
|
@@ -5574,7 +5578,7 @@ namespace olc
|
||||||
|
vFiles.push_back(std::string(buffer));
|
||||||
|
delete[] buffer;
|
||||||
|
#else
|
||||||
|
- vFiles.push_back(std::string(dbuffer));
|
||||||
|
+ vFiles.push_back(std::string(dfbuffer));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -6318,8 +6322,8 @@ namespace olc
|
||||||
|
let isFullscreen = (document.fullscreenElement != null);
|
||||||
|
|
||||||
|
// get the width of the containing element
|
||||||
|
- let width = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerWidth : Module.canvas.parentNode.clientWidth;
|
||||||
|
- let height = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerHeight : Module.canvas.parentNode.clientHeight;
|
||||||
|
+ let width = (isFullscreen) ? window.innerWidth : Module.canvas.parentNode.clientWidth;
|
||||||
|
+ let height = (isFullscreen) ? window.innerHeight : Module.canvas.parentNode.clientHeight;
|
||||||
|
|
||||||
|
// calculate the expected viewport size
|
||||||
|
let viewWidth = width;
|
||||||
|
diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h
|
||||||
|
index 801c1d3..3b8f363 100644
|
||||||
|
--- a/utilities/olcUTIL_Geometry2D.h
|
||||||
|
+++ b/utilities/olcUTIL_Geometry2D.h
|
||||||
|
@@ -277,43 +277,60 @@ namespace olc::utils::geom2d
|
||||||
|
|
||||||
|
// Returns closest point to point
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
- inline olc::v2d_generic<T2> closest(const olc::v2d_generic<T1>& p1, const olc::v2d_generic<T2>& p2)
|
||||||
|
+ inline olc::v2d_generic<T1> closest(const olc::v2d_generic<T1>& p1, const olc::v2d_generic<T2>& p2)
|
||||||
|
{
|
||||||
|
return p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns closest point on line to point
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
- inline olc::v2d_generic<T2> closest(const line<T1>& l, const olc::v2d_generic<T2>& p)
|
||||||
|
+ inline olc::v2d_generic<T1> closest(const line<T1>& l, const olc::v2d_generic<T2>& p)
|
||||||
|
{
|
||||||
|
auto d = l.vector();
|
||||||
|
- double u = std::clamp(double(d.dot(p - l.start) / d.mag2()), 0.0, 1.0);
|
||||||
|
- return l.start + d * u;
|
||||||
|
+ double u = std::clamp(double(d.dot(p - l.start)) / d.mag2(), 0.0, 1.0);
|
||||||
|
+ return l.start + u * d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns closest point on circle to point
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
- inline olc::v2d_generic<T2> closest(const circle<T1>& c, const olc::v2d_generic<T2>& p)
|
||||||
|
+ inline olc::v2d_generic<T1> closest(const circle<T1>& c, const olc::v2d_generic<T2>& p)
|
||||||
|
{
|
||||||
|
- return c.pos + (p - c.pos).norm() * c.radius;
|
||||||
|
+ return c.pos + olc::vd2d(p - c.pos).norm() * c.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns closest point on rectangle to point
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
- inline olc::v2d_generic<T2> closest(const rect<T1>& r, const olc::v2d_generic<T2>& p)
|
||||||
|
+ inline olc::v2d_generic<T1> closest(const rect<T1>& r, const olc::v2d_generic<T2>& p)
|
||||||
|
{
|
||||||
|
// This could be a "constrain" function hmmmm
|
||||||
|
// TODO: Not quite what i wanted, should restrain to boundary
|
||||||
|
- return olc::v2d_generic<T2>{ std::clamp(p.x, r.pos.x, r.pos.x + r.size.x), std::clamp(p.y, r.pos.y, r.pos.y + r.size.y) };
|
||||||
|
+ return olc::v2d_generic<T1>{ std::clamp(p.x, r.pos.x, r.pos.x + r.size.x), std::clamp(p.y, r.pos.y, r.pos.y + r.size.y) };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns closest point on triangle to point
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
- inline olc::v2d_generic<T2> closest(const triangle<T1>& t, const olc::v2d_generic<T2>& p)
|
||||||
|
+ inline olc::v2d_generic<T1> closest(const triangle<T1>& t, const olc::v2d_generic<T2>& p)
|
||||||
|
{
|
||||||
|
- // TODO:
|
||||||
|
- return olc::v2d_generic<T2>();
|
||||||
|
+ olc::utils::geom2d::line<T1> l{t.pos[0], t.pos[1]};
|
||||||
|
+ auto p0 = closest(l, p);
|
||||||
|
+ auto d0 = (p0 - p).mag2();
|
||||||
|
+
|
||||||
|
+ l.end = t.pos[2];
|
||||||
|
+ auto p1 = closest(l, p);
|
||||||
|
+ auto d1 = (p1 - p).mag2();
|
||||||
|
+
|
||||||
|
+ l.start = t.pos[1];
|
||||||
|
+ auto p2 = closest(l, p);
|
||||||
|
+ auto d2 = (p2 - p).mag2();
|
||||||
|
+
|
||||||
|
+ if((d0 <= d1) && (d0 <= d2)) {
|
||||||
|
+ return p0;
|
||||||
|
+ } else if((d1 <= d0) && (d1 <= d2)) {
|
||||||
|
+ return p1;
|
||||||
|
+ } else {
|
||||||
|
+ return p2;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
|||||||
olcPixelGameEngine.h
|
olcPixelGameEngine.h
|
||||||
|
|
||||||
+-------------------------------------------------------------+
|
+-------------------------------------------------------------+
|
||||||
| OneLoneCoder Pixel Game Engine v2.21 |
|
| OneLoneCoder Pixel Game Engine v2.23 |
|
||||||
| "What do you need? Pixels... Lots of Pixels..." - javidx9 |
|
| "What do you need? Pixels... Lots of Pixels..." - javidx9 |
|
||||||
+-------------------------------------------------------------+
|
+-------------------------------------------------------------+
|
||||||
|
|
||||||
@ -197,7 +197,7 @@
|
|||||||
|
|
||||||
Author
|
Author
|
||||||
~~~~~~
|
~~~~~~
|
||||||
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2018, 2019, 2020, 2021, 2022
|
David Barr, aka javidx9, (c) OneLoneCoder 2018, 2019, 2020, 2021, 2022
|
||||||
*/
|
*/
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
@ -399,7 +399,7 @@ int main()
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#define PGE_VER 221
|
#define PGE_VER 223
|
||||||
|
|
||||||
// O------------------------------------------------------------------------------O
|
// O------------------------------------------------------------------------------O
|
||||||
// | COMPILER CONFIGURATION ODDITIES |
|
// | COMPILER CONFIGURATION ODDITIES |
|
||||||
@ -687,7 +687,7 @@ namespace olc
|
|||||||
v2d_generic min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); }
|
v2d_generic min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); }
|
||||||
v2d_generic cart() { return { std::cos(y) * x, std::sin(y) * x }; }
|
v2d_generic cart() { return { std::cos(y) * x, std::sin(y) * x }; }
|
||||||
v2d_generic polar() { return { mag(), std::atan2(y, x) }; }
|
v2d_generic polar() { return { mag(), std::atan2(y, x) }; }
|
||||||
v2d_generic clamp(const v2d_generic& v1, const v2d_generic& v2) const { return this->max(v1)->min(v2); }
|
v2d_generic clamp(const v2d_generic& v1, const v2d_generic& v2) const { return this->max(v1).min(v2); }
|
||||||
v2d_generic lerp(const v2d_generic& v1, const double t) { return this->operator*(T(1.0 - t)) + (v1 * T(t)); }
|
v2d_generic lerp(const v2d_generic& v1, const double t) { return this->operator*(T(1.0 - t)) + (v1 * T(t)); }
|
||||||
T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; }
|
T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; }
|
||||||
T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; }
|
T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; }
|
||||||
@ -1364,8 +1364,9 @@ namespace olc
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(OLC_PLATFORM_X11)
|
#if defined(OLC_PLATFORM_X11)
|
||||||
namespace X11
|
namespace X11 {
|
||||||
{#include <GL/glx.h>}
|
#include <GL/glx.h>
|
||||||
|
}
|
||||||
#define CALLSTYLE
|
#define CALLSTYLE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -4613,17 +4614,17 @@ namespace olc
|
|||||||
// #include <OpenGL/glu.h>
|
// #include <OpenGL/glu.h>
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
//#if defined(OLC_PLATFORM_EMSCRIPTEN)
|
#if defined(OLC_PLATFORM_EMSCRIPTEN)
|
||||||
// #include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
// #include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
// #define GL_GLEXT_PROTOTYPES
|
#define GL_GLEXT_PROTOTYPES
|
||||||
// #include <GLES2/gl2ext.h>
|
#include <GLES2/gl2ext.h>
|
||||||
// #include <emscripten/emscripten.h>
|
#include <emscripten/emscripten.h>
|
||||||
// #define CALLSTYLE
|
#define CALLSTYLE
|
||||||
// typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
|
typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
|
||||||
// #define GL_CLAMP GL_CLAMP_TO_EDGE
|
#define GL_CLAMP GL_CLAMP_TO_EDGE
|
||||||
// #define OGL_LOAD(t, n) n;
|
#define OGL_LOAD(t, n) n;
|
||||||
//#endif
|
#endif
|
||||||
|
|
||||||
namespace olc
|
namespace olc
|
||||||
{
|
{
|
||||||
@ -5593,7 +5594,7 @@ namespace olc
|
|||||||
vFiles.push_back(std::string(buffer));
|
vFiles.push_back(std::string(buffer));
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
#else
|
#else
|
||||||
vFiles.push_back(std::string(dbuffer));
|
vFiles.push_back(std::string(dfbuffer));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6337,8 +6338,8 @@ namespace olc
|
|||||||
let isFullscreen = (document.fullscreenElement != null);
|
let isFullscreen = (document.fullscreenElement != null);
|
||||||
|
|
||||||
// get the width of the containing element
|
// get the width of the containing element
|
||||||
let width = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerWidth : Module.canvas.parentNode.clientWidth;
|
let width = (isFullscreen) ? window.innerWidth : Module.canvas.parentNode.clientWidth;
|
||||||
let height = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerHeight : Module.canvas.parentNode.clientHeight;
|
let height = (isFullscreen) ? window.innerHeight : Module.canvas.parentNode.clientHeight;
|
||||||
|
|
||||||
// calculate the expected viewport size
|
// calculate the expected viewport size
|
||||||
let viewWidth = width;
|
let viewWidth = width;
|
||||||
|
6711
olcPixelGameEngine.h.orig
Normal file
6711
olcPixelGameEngine.h.orig
Normal file
File diff suppressed because it is too large
Load Diff
12
olcPixelGameEngine.h.rej
Normal file
12
olcPixelGameEngine.h.rej
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
--- olcPixelGameEngine.h
|
||||||
|
+++ olcPixelGameEngine.h
|
||||||
|
@@ -315,6 +315,9 @@
|
||||||
|
+FillTexturedTriangle() - Software rasterizes a textured, coloured, triangle
|
||||||
|
+FillTexturedPolygon() - Hijacks DecalStructure for configuration
|
||||||
|
+olc::vf2d arguments for Sprite::Sample() functions
|
||||||
|
+ 2.22: = Fix typo on dragged file buffers for unicode builds
|
||||||
|
+ 2.23: Fixed Emscripten host sizing errors - Thanks Moros
|
||||||
|
+ Fixed v2d_generic.clamp() function
|
||||||
|
|
||||||
|
!! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !!
|
||||||
|
!! Volunteers willing to help appreciated, though PRs are manually integrated with credit !!
|
258
olcUTIL_Camera2D.h
Normal file
258
olcUTIL_Camera2D.h
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
OneLoneCoder - Camera2D v1.00
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
A 2D world camera with various modes
|
||||||
|
|
||||||
|
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2018 - 2022 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
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "olcPixelGameEngine.h"
|
||||||
|
|
||||||
|
namespace olc::utils
|
||||||
|
{
|
||||||
|
class Camera2D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Mode : uint8_t
|
||||||
|
{
|
||||||
|
Simple, // No motion, just directly settable
|
||||||
|
EdgeMove, // Moves as target crosses boundary
|
||||||
|
LazyFollow, // Lazily follows the target
|
||||||
|
FixedScreens, // Moves statically between "screens"
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Camera2D() : m_pTarget(&m_vLocalTarget) {}
|
||||||
|
|
||||||
|
// Construct a camera with a viewable area size, and an optional starting position
|
||||||
|
inline Camera2D(const olc::vf2d& vViewSize, const olc::vf2d& vViewPos = { 0.0f, 0.0f }) : m_pTarget(&m_vLocalTarget)
|
||||||
|
{
|
||||||
|
m_vViewSize = vViewSize;
|
||||||
|
m_vViewPos = vViewPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the operational mode of this camera
|
||||||
|
inline void SetMode(const Mode t)
|
||||||
|
{
|
||||||
|
m_nMode = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the operational mode of this camera
|
||||||
|
inline Mode GetMode() const
|
||||||
|
{
|
||||||
|
return m_nMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the position of the target being tracked by this camera
|
||||||
|
inline const olc::vf2d& GetTarget() const
|
||||||
|
{
|
||||||
|
return *m_pTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the position of the cameras focus point
|
||||||
|
inline const olc::vf2d& GetPosition() const
|
||||||
|
{
|
||||||
|
return m_vPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the top left of teh cameras visible area in world space
|
||||||
|
inline const olc::vf2d& GetViewPosition() const
|
||||||
|
{
|
||||||
|
return m_vViewPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the camera's visible area
|
||||||
|
inline const olc::vf2d& GetViewSize() const
|
||||||
|
{
|
||||||
|
return m_vViewSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set tracked point via pointer
|
||||||
|
inline void SetTarget(olc::vf2d& vTarget)
|
||||||
|
{
|
||||||
|
m_pTarget = &vTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set tracked point via const ref - {10, 35} for example
|
||||||
|
inline void SetTarget(const olc::vf2d&& vTarget)
|
||||||
|
{
|
||||||
|
m_vLocalTarget = vTarget;
|
||||||
|
m_pTarget = &m_vLocalTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set world boundary rectangle
|
||||||
|
inline void SetWorldBoundary(const olc::vf2d& vPos, const olc::vf2d& vSize)
|
||||||
|
{
|
||||||
|
m_vWorldBoundaryPos = vPos;
|
||||||
|
m_vWorldBoundarySize = vSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instruct camera to respect world boundaries
|
||||||
|
inline void EnableWorldBoundary(const bool bEnable)
|
||||||
|
{
|
||||||
|
m_bWorldBoundary = bEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we using a world boundary?
|
||||||
|
inline bool IsWorldBoundaryEnabled() const
|
||||||
|
{
|
||||||
|
return m_bWorldBoundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the world boundary rectangle position
|
||||||
|
inline const olc::vf2d& GetWorldBoundaryPosition() const
|
||||||
|
{
|
||||||
|
return m_vWorldBoundaryPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the world boundary rectangle size
|
||||||
|
inline const olc::vf2d& GetWorldBoundarySize() const
|
||||||
|
{
|
||||||
|
return m_vWorldBoundarySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the velocity at which the lazy follower reaches tracked point
|
||||||
|
inline void SetLazyFollowRate(const float fRate)
|
||||||
|
{
|
||||||
|
m_fLazyFollowRate = fRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the velocity at which the lazy follower reaches tracked point
|
||||||
|
inline float GetLazyFollowRate() const
|
||||||
|
{
|
||||||
|
return m_fLazyFollowRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set distance from tracked point to start nudging screen
|
||||||
|
inline void SetEdgeTriggerDistance(const olc::vf2d& vEdge)
|
||||||
|
{
|
||||||
|
m_vEdgeTriggerDistance = vEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return disance from tracked point that screen will nudge
|
||||||
|
inline const olc::vf2d& GetEdgeTriggerDistance() const
|
||||||
|
{
|
||||||
|
return m_vEdgeTriggerDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update camera, animating if necessary, obeying world boundary rules
|
||||||
|
// returns true if target is visible
|
||||||
|
inline virtual bool Update(const float fElapsedTime)
|
||||||
|
{
|
||||||
|
switch (m_nMode)
|
||||||
|
{
|
||||||
|
case Mode::Simple:
|
||||||
|
{
|
||||||
|
m_vPosition = GetTarget();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode::EdgeMove:
|
||||||
|
{
|
||||||
|
olc::vf2d vOverlap = GetTarget() - m_vPosition;
|
||||||
|
if (vOverlap.x > m_vEdgeTriggerDistance.x) m_vPosition.x += vOverlap.x - m_vEdgeTriggerDistance.x;
|
||||||
|
if (vOverlap.x < -m_vEdgeTriggerDistance.x) m_vPosition.x += vOverlap.x + m_vEdgeTriggerDistance.x;
|
||||||
|
if (vOverlap.y > m_vEdgeTriggerDistance.y) m_vPosition.y += vOverlap.y - m_vEdgeTriggerDistance.y;
|
||||||
|
if (vOverlap.y < -m_vEdgeTriggerDistance.y) m_vPosition.y += vOverlap.y + m_vEdgeTriggerDistance.y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode::LazyFollow:
|
||||||
|
{
|
||||||
|
m_vPosition += (GetTarget() - m_vPosition) * m_fLazyFollowRate * fElapsedTime;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode::FixedScreens:
|
||||||
|
{
|
||||||
|
m_vPosition = olc::vf2d(olc::vi2d(GetTarget() / m_vScreenSize) * olc::vi2d(m_vScreenSize)) + (m_vViewSize * 0.5f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make camera target the middle of the view
|
||||||
|
m_vViewPos = m_vPosition - (m_vViewSize * 0.5f);
|
||||||
|
|
||||||
|
// Clamp to World Boundary (if in place)
|
||||||
|
if (m_bWorldBoundary)
|
||||||
|
{
|
||||||
|
m_vViewPos = m_vViewPos.max(m_vWorldBoundaryPos).min(m_vWorldBoundaryPos + m_vWorldBoundarySize - m_vViewSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetTarget().x >= m_vViewPos.x && GetTarget().x < (m_vViewPos.x + m_vViewSize.x) &&
|
||||||
|
GetTarget().y >= m_vViewPos.y && GetTarget().y < (m_vViewPos.y + m_vViewSize.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Position of camera focus point in the world
|
||||||
|
olc::vf2d m_vPosition;
|
||||||
|
// Rectangular size of camera viewing area
|
||||||
|
olc::vf2d m_vViewSize;
|
||||||
|
// Top left coordinate of camera viewing area
|
||||||
|
olc::vf2d m_vViewPos;
|
||||||
|
// Camera movement mode
|
||||||
|
Mode m_nMode = Mode::Simple;
|
||||||
|
|
||||||
|
// Target Vector2D object camera should follow (either ref or ptr)
|
||||||
|
olc::vf2d* m_pTarget = nullptr;
|
||||||
|
olc::vf2d m_vLocalTarget;
|
||||||
|
|
||||||
|
// World Boundary
|
||||||
|
bool m_bWorldBoundary = false;
|
||||||
|
olc::vf2d m_vWorldBoundaryPos = { 0.0f, 0.0f };
|
||||||
|
olc::vf2d m_vWorldBoundarySize = { 256.0f, 240.0f };
|
||||||
|
|
||||||
|
// Mode specific
|
||||||
|
olc::vf2d m_vEdgeTriggerDistance = { 1.0f, 1.0f };
|
||||||
|
float m_fLazyFollowRate = 4.0f;
|
||||||
|
olc::vi2d m_vScreenSize = { 16,15 };
|
||||||
|
};
|
||||||
|
}
|
158
olcUTIL_Container.h
Normal file
158
olcUTIL_Container.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
OneLoneCoder - Container v1.00
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Assortment of std::container like objects with access specific mechanisms
|
||||||
|
|
||||||
|
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2018 - 2022 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
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
SingleSelection
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
This container behaves like a std::vector in all circumstances but features
|
||||||
|
additional methods that allow it to surve as a list of items, one of which
|
||||||
|
can be selected, and the items can be manipulated.
|
||||||
|
|
||||||
|
An example of this would be the image layers in Photoshop.
|
||||||
|
|
||||||
|
For convenience, all container operations operate on an item index rather
|
||||||
|
than an iterator
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace olc::utils::Container
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
class SingleSelection : public std::vector<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
SingleSelection() : std::vector<T>()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
SingleSelection(std::initializer_list<T> list) : std::vector<T>(list)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Returns selected item in container
|
||||||
|
size_t selection() const
|
||||||
|
{
|
||||||
|
return m_nSelectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the item actually selected
|
||||||
|
const T& selected() const
|
||||||
|
{
|
||||||
|
return this->operator[](m_nSelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the item actually selected
|
||||||
|
T& selected()
|
||||||
|
{
|
||||||
|
return this->operator[](m_nSelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select item in container
|
||||||
|
void select(const size_t item)
|
||||||
|
{
|
||||||
|
m_nSelectedItem = std::clamp(item, size_t(0), this->size() - size_t(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move selected item positively
|
||||||
|
void move_up()
|
||||||
|
{
|
||||||
|
if(move_up(m_nSelectedItem))
|
||||||
|
m_nSelectedItem++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move selected item negatively
|
||||||
|
void move_down()
|
||||||
|
{
|
||||||
|
if(move_down(m_nSelectedItem))
|
||||||
|
m_nSelectedItem--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move specified item negatively
|
||||||
|
bool move_down(const size_t item)
|
||||||
|
{
|
||||||
|
// Are there at least two items and not first one selected?
|
||||||
|
if (this->size() >= 2 && item > 0)
|
||||||
|
{
|
||||||
|
std::swap(this->operator[](item - 1), this->operator[](item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move specified item positively
|
||||||
|
bool move_up(const size_t item)
|
||||||
|
{
|
||||||
|
// Are there at least two items and not last one selected?
|
||||||
|
if (this->size() >= 2 && item < this->size() - 1)
|
||||||
|
{
|
||||||
|
std::swap(this->operator[](item + 1), this->operator[](item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert_after(const size_t idx, const T& value)
|
||||||
|
{
|
||||||
|
this->insert(idx, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t m_nSelectedItem = 0;
|
||||||
|
};
|
||||||
|
}
|
435
olcUTIL_DataFile.h
Normal file
435
olcUTIL_DataFile.h
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
/*
|
||||||
|
OneLoneCoder - DataFile v1.00
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
An "easy to use" serialisation/deserialisation class that yields
|
||||||
|
human readable hierachical files.
|
||||||
|
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2018 - 2022 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
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stack>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace olc::utils
|
||||||
|
{
|
||||||
|
class datafile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline datafile() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Sets the String Value of a Property (for a given index)
|
||||||
|
inline void SetString(const std::string& sString, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
if (nItem >= m_vContent.size())
|
||||||
|
m_vContent.resize(nItem + 1);
|
||||||
|
|
||||||
|
m_vContent[nItem] = sString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the String Value of a Property (for a given index) or ""
|
||||||
|
inline const std::string GetString(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
if (nItem >= m_vContent.size())
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return m_vContent[nItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the Real Value of a Property (for a given index) or 0.0
|
||||||
|
inline const double GetReal(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
return std::atof(GetString(nItem).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the Real Value of a Property (for a given index)
|
||||||
|
inline void SetReal(const double d, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
SetString(std::to_string(d), nItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the Integer Value of a Property (for a given index) or 0
|
||||||
|
inline const int32_t GetInt(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
return std::atoi(GetString(nItem).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the Integer Value of a Property (for a given index)
|
||||||
|
inline void SetInt(const int32_t n, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
SetString(std::to_string(n), nItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of Values a property consists of
|
||||||
|
inline size_t GetValueCount() const
|
||||||
|
{
|
||||||
|
return m_vContent.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a property exists - useful to avoid creating properties
|
||||||
|
// via reading them, though non-essential
|
||||||
|
inline bool HasProperty(const std::string& sName) const
|
||||||
|
{
|
||||||
|
return m_mapObjects.count(sName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access a datafile via a convenient name - "root.node.something.property"
|
||||||
|
inline datafile& GetProperty(const std::string& name)
|
||||||
|
{
|
||||||
|
size_t x = name.find_first_of('.');
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string sProperty = name.substr(0, x);
|
||||||
|
if (HasProperty(sProperty))
|
||||||
|
return operator[](sProperty).GetProperty(name.substr(x + 1, name.size()));
|
||||||
|
else
|
||||||
|
return operator[](sProperty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return operator[](name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access a numbered element - "node[23]", or "root[56].node"
|
||||||
|
inline datafile& GetIndexedProperty(const std::string& name, const size_t nIndex)
|
||||||
|
{
|
||||||
|
return GetProperty(name + "[" + std::to_string(nIndex) + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Writes a "datafile" node (and all of its child nodes and properties) recursively
|
||||||
|
// to a file.
|
||||||
|
inline static bool Write(const datafile& n, const std::string& sFileName, const std::string& sIndent = "\t", const char sListSep = ',')
|
||||||
|
{
|
||||||
|
// Cache indentation level
|
||||||
|
size_t nIndentCount = 0;
|
||||||
|
// Cache sperator string for convenience
|
||||||
|
std::string sSeperator = std::string(1, sListSep) + " ";
|
||||||
|
|
||||||
|
// Fully specified lambda, because this lambda is recursive!
|
||||||
|
std::function<void(const datafile&, std::ofstream&)> write = [&](const datafile& n, std::ofstream& file)
|
||||||
|
{
|
||||||
|
// Lambda creates string given indentation preferences
|
||||||
|
auto indent = [&](const std::string& sString, const size_t nCount)
|
||||||
|
{
|
||||||
|
std::string sOut;
|
||||||
|
for (size_t n = 0; n < nCount; n++) sOut += sString;
|
||||||
|
return sOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through each property of this node
|
||||||
|
for (auto const& property : n.m_vecObjects)
|
||||||
|
{
|
||||||
|
// Does property contain any sub objects?
|
||||||
|
if (property.second.m_vecObjects.empty())
|
||||||
|
{
|
||||||
|
// No, so it's an assigned field and should just be written. If the property
|
||||||
|
// is flagged as comment, it has no assignment potential. First write the
|
||||||
|
// property name
|
||||||
|
file << indent(sIndent, nIndentCount) << property.first << (property.second.m_bIsComment ? "" : " = ");
|
||||||
|
|
||||||
|
// Second, write the property value (or values, seperated by provided
|
||||||
|
// separation charater
|
||||||
|
size_t nItems = property.second.GetValueCount();
|
||||||
|
for (size_t i = 0; i < property.second.GetValueCount(); i++)
|
||||||
|
{
|
||||||
|
// If the Value being written, in string form, contains the separation
|
||||||
|
// character, then the value must be written inside quotation marks. Note,
|
||||||
|
// that if the Value is the last of a list of Values for a property, it is
|
||||||
|
// not suffixed with the separator
|
||||||
|
size_t x = property.second.GetString(i).find_first_of(sListSep);
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
// Value contains separator, so wrap in quotes
|
||||||
|
file << "\"" << property.second.GetString(i) << "\"" << ((nItems > 1) ? sSeperator : "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Value does not contain separator, so just write out
|
||||||
|
file << property.second.GetString(i) << ((nItems > 1) ? sSeperator : "");
|
||||||
|
}
|
||||||
|
nItems--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property written, move to next line
|
||||||
|
file << "\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Yes, property has properties of its own, so it's a node
|
||||||
|
// Force a new line and write out the node's name
|
||||||
|
file << "\n" << indent(sIndent, nIndentCount) << property.first << "\n";
|
||||||
|
// Open braces, and update indentation
|
||||||
|
file << indent(sIndent, nIndentCount) << "{\n";
|
||||||
|
nIndentCount++;
|
||||||
|
// Recursively write that node
|
||||||
|
write(property.second, file);
|
||||||
|
// Node written, so close braces
|
||||||
|
file << indent(sIndent, nIndentCount) << "}\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've finished writing out a node, regardless of state, our indentation
|
||||||
|
// must decrease, unless we're top level
|
||||||
|
if (nIndentCount > 0) nIndentCount--;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start Here! Open the file for writing
|
||||||
|
std::ofstream file(sFileName);
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
// Write the file starting form the supplied node
|
||||||
|
write(n, file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',')
|
||||||
|
{
|
||||||
|
// Open the file!
|
||||||
|
std::ifstream file(sFileName);
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
// These variables are outside of the read loop, as we will
|
||||||
|
// need to refer to previous iteration values in certain conditions
|
||||||
|
std::string sPropName = "";
|
||||||
|
std::string sPropValue = "";
|
||||||
|
|
||||||
|
// The file is fundamentally structured as a stack, so we will read it
|
||||||
|
// in a such, but note the data structure in memory is not explicitly
|
||||||
|
// stored in a stack, but one is constructed implicitly via the nodes
|
||||||
|
// owning other nodes (aka a tree)
|
||||||
|
|
||||||
|
// I dont want to accidentally create copies all over the place, nor do
|
||||||
|
// I want to use pointer syntax, so being a bit different and stupidly
|
||||||
|
// using std::reference_wrapper, so I can store references to datafile
|
||||||
|
// nodes in a std::container.
|
||||||
|
std::stack<std::reference_wrapper<datafile>> stkPath;
|
||||||
|
stkPath.push(n);
|
||||||
|
|
||||||
|
|
||||||
|
// Read file line by line and process
|
||||||
|
while (!file.eof())
|
||||||
|
{
|
||||||
|
// Read line
|
||||||
|
std::string line;
|
||||||
|
std::getline(file, line);
|
||||||
|
|
||||||
|
// This little lambda removes whitespace from
|
||||||
|
// beginning and end of supplied string
|
||||||
|
auto trim = [](std::string& s)
|
||||||
|
{
|
||||||
|
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
||||||
|
s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
trim(line);
|
||||||
|
|
||||||
|
// If line has content
|
||||||
|
if (!line.empty())
|
||||||
|
{
|
||||||
|
// Test if its a comment...
|
||||||
|
if (line[0] == '#')
|
||||||
|
{
|
||||||
|
// ...it is a comment, so ignore
|
||||||
|
datafile comment;
|
||||||
|
comment.m_bIsComment = true;
|
||||||
|
stkPath.top().get().m_vecObjects.push_back({ line, comment });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...it is content, so parse. Firstly, find if the line
|
||||||
|
// contains an assignment. If it does then it's a property...
|
||||||
|
size_t x = line.find_first_of('=');
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
// ...so split up the property into a name, and its values!
|
||||||
|
|
||||||
|
// Extract the property name, which is all characters up to
|
||||||
|
// first assignment, trim any whitespace from ends
|
||||||
|
sPropName = line.substr(0, x);
|
||||||
|
trim(sPropName);
|
||||||
|
|
||||||
|
// Extract the property value, which is all characters after
|
||||||
|
// the first assignment operator, trim any whitespace from ends
|
||||||
|
sPropValue = line.substr(x + 1, line.size());
|
||||||
|
trim(sPropValue);
|
||||||
|
|
||||||
|
// The value may be in list form: a, b, c, d, e, f etc and some of those
|
||||||
|
// elements may exist in quotes a, b, c, "d, e", f. So we need to iterate
|
||||||
|
// character by character and break up the value
|
||||||
|
bool bInQuotes = false;
|
||||||
|
std::string sToken;
|
||||||
|
size_t nTokenCount = 0;
|
||||||
|
for (const auto c : sPropValue)
|
||||||
|
{
|
||||||
|
// Is character a quote...
|
||||||
|
if (c == '\"')
|
||||||
|
{
|
||||||
|
// ...yes, so toggle quote state
|
||||||
|
bInQuotes = !bInQuotes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...no, so proceed creating token. If we are in quote state
|
||||||
|
// then just append characters until we exit quote state.
|
||||||
|
if (bInQuotes)
|
||||||
|
{
|
||||||
|
sToken.append(1, c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Is the character our seperator? If it is
|
||||||
|
if (c == sListSep)
|
||||||
|
{
|
||||||
|
// Clean up the token
|
||||||
|
trim(sToken);
|
||||||
|
// Add it to the vector of values for this property
|
||||||
|
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||||
|
// Reset our token state
|
||||||
|
sToken.clear();
|
||||||
|
nTokenCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It isnt, so just append to token
|
||||||
|
sToken.append(1, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any residual characters at this point just make up the final token,
|
||||||
|
// so clean it up and add it to the vector of values
|
||||||
|
if (!sToken.empty())
|
||||||
|
{
|
||||||
|
trim(sToken);
|
||||||
|
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...but if it doesnt, then it's something structural
|
||||||
|
if (line[0] == '{')
|
||||||
|
{
|
||||||
|
// Open brace, so push this node to stack, subsequent properties
|
||||||
|
// will belong to the new node
|
||||||
|
stkPath.push(stkPath.top().get()[sPropName]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (line[0] == '}')
|
||||||
|
{
|
||||||
|
// Close brace, so this node has been defined, pop it from the
|
||||||
|
// stack
|
||||||
|
stkPath.pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Line is a property with no assignment. Who knows whether this is useful,
|
||||||
|
// but we can simply add it as a valueless property...
|
||||||
|
sPropName = line;
|
||||||
|
// ...actually it is useful, as valuless properties are typically
|
||||||
|
// going to be the names of new datafile nodes on the next iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close and exit!
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File not found, so fail
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline datafile& operator[](const std::string& name)
|
||||||
|
{
|
||||||
|
// Check if this "node"'s map already contains an object with this name...
|
||||||
|
if (m_mapObjects.count(name) == 0)
|
||||||
|
{
|
||||||
|
// ...it did not! So create this object in the map. First get a vector id
|
||||||
|
// and link it with the name in the unordered_map
|
||||||
|
m_mapObjects[name] = m_vecObjects.size();
|
||||||
|
// then creating the new, blank object in the vector of objects
|
||||||
|
m_vecObjects.push_back({ name, datafile() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...it exists! so return the object, by getting its index from the map, and using that
|
||||||
|
// index to look up a vector element.
|
||||||
|
return m_vecObjects[m_mapObjects[name]].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The "list of strings" that make up a property value
|
||||||
|
std::vector<std::string> m_vContent;
|
||||||
|
|
||||||
|
// Linkage to create "ordered" unordered_map. We have a vector of
|
||||||
|
// "properties", and the index to a specific element is mapped.
|
||||||
|
std::vector<std::pair<std::string, datafile>> m_vecObjects;
|
||||||
|
std::unordered_map<std::string, size_t> m_mapObjects;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Used to identify if a property is a comment or not, not user facing
|
||||||
|
bool m_bIsComment = false;
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user