Compare commits
No commits in common. 'sig' and '2.21' have entirely different histories.
@ -1,219 +0,0 @@ |
|||||||
/*
|
|
||||||
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; |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@ |
|||||||
--- 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 !! |
|
@ -1,258 +0,0 @@ |
|||||||
/*
|
|
||||||
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 }; |
|
||||||
}; |
|
||||||
} |
|
@ -1,158 +0,0 @@ |
|||||||
/*
|
|
||||||
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; |
|
||||||
}; |
|
||||||
} |
|
@ -1,435 +0,0 @@ |
|||||||
/*
|
|
||||||
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…
Reference in new issue