From 6662e68da009ffc668a200493fab53f9bcf94e21 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 26 Jul 2020 17:27:25 +0100 Subject: [PATCH] Added SlidingBlockPuzzle stream --- .../OneLoneCoder_PGE_SlidingBlockPuzzle.cpp | 431 ++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 Videos/OneLoneCoder_PGE_SlidingBlockPuzzle.cpp diff --git a/Videos/OneLoneCoder_PGE_SlidingBlockPuzzle.cpp b/Videos/OneLoneCoder_PGE_SlidingBlockPuzzle.cpp new file mode 100644 index 00000000..35863722 --- /dev/null +++ b/Videos/OneLoneCoder_PGE_SlidingBlockPuzzle.cpp @@ -0,0 +1,431 @@ +/* + Mechanics of Sliding Puzzle Game + "Just let me go home... please!!!" - javidx9 + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018-2020 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. + + Relevant Video: https://www.youtube.com/watch?v=l7YEaa2otVE + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + https://www.youtube.com/javidx9extra + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Patreon: https://www.patreon.com/javidx9 + Homepage: https://www.onelonecoder.com + + Community Blog: https://community.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2018, 2019, 2020 +*/ + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + + +constexpr int NORTH = 0; +constexpr int EAST = 1; +constexpr int SOUTH = 2; +constexpr int WEST = 3; + + +class BlockPuzzle : public olc::PixelGameEngine +{ +public: + BlockPuzzle() + { + sAppName = "BlockPuzzle"; + } + + std::string sLevel = + "################" + "#..............#" + "#...+....+.....#" + "#........+..|..#" + "#........+..|..#" + "#....P......|..#" + "#..5....---....#" + "#..............#" + "#..+++....#..@.#" + "#.........#..@.#" + "#..-.........@.#" + "#.....+....|...#" + "#........+.....#" + "#..............#" + "################"; + + olc::vf2d vLevelSize = { 16, 15 }; + olc::vf2d vBlockSize = { 16, 16 }; + + std::vector vGoals; + + olc::Renderable gfxTiles; + + struct block + { + block() + { + + } + + virtual void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) + { + + } + + virtual bool Push(const int from) + { + return true; + } + + virtual void Move() + { + + } + + }; + + struct block_solid : public block + { + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + //pge->FillRect(pos * size, size, olc::BLUE); + pge->DrawPartialSprite(pos * size, skin.Sprite(), olc::vi2d(0, 0) * size, size); + + } + + bool Push(const int from) override + { + return false; + } + + }; + + + struct block_player : public block + { + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + pge->FillRect(pos * size, size, olc::WHITE); + } + + bool Push(const int from) override + { + return true; + } + }; + + + struct block_simple : public block + { + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + //pge->FillRect(pos * size, size, olc::RED); + pge->DrawPartialSprite(pos * size, skin.Sprite(), olc::vi2d(1, 0) * size, size); + } + + bool Push(const int from) override + { + return true; + } + }; + + + struct block_horizontal : public block + { + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + //pge->FillRect(pos * size, size, olc::GREEN); + pge->DrawPartialSprite(pos * size, skin.Sprite(), olc::vi2d(3, 0) * size, size); + } + + bool Push(const int from) override + { + return from == EAST || from == WEST; + } + }; + + struct block_vertical : public block + { + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + //pge->FillRect(pos * size, size, olc::YELLOW); + pge->DrawPartialSprite(pos * size, skin.Sprite(), olc::vi2d(2, 0) * size, size); + } + + bool Push(const int from) override + { + return from == NORTH || from == SOUTH; + } + }; + + struct block_countdown : public block + { + block_countdown(int m) + { + nMoves = m; + } + + void DrawSelf(olc::PixelGameEngine* pge, const olc::vi2d& pos, const olc::vi2d& size, const olc::Renderable& skin) override + { + //pge->FillRect(pos * size, size, olc::CYAN); + pge->DrawPartialSprite(pos * size, skin.Sprite(), olc::vi2d(4, 0) * size, size); + pge->DrawString(pos * size + olc::vi2d(4, 4), std::to_string(nMoves), olc::BLACK); + } + + bool Push(const int from) override + { + return nMoves > 0; + } + + void Move() override + { + nMoves--; + } + + int nMoves = 0; + }; + + + olc::vi2d vPlayer; + std::vector> vLevel; + + +public: + void LoadLevel(int n) + { + vLevel.clear(); + vGoals.clear(); + + + for (int y = 0; y < vLevelSize.y; y++) + { + for (int x = 0; x < vLevelSize.x; x++) + { + switch (sLevel[y * vLevelSize.x + x]) + { + case '#': + vLevel.emplace_back(std::make_unique()); + break; + + case 'P': + vLevel.emplace_back(std::make_unique()); + vPlayer = { x, y }; + break; + + case '+': + vLevel.emplace_back(std::make_unique()); + break; + + case '-': + vLevel.emplace_back(std::make_unique()); + break; + + case '|': + vLevel.emplace_back(std::make_unique()); + break; + + case '5': + vLevel.emplace_back(std::make_unique(5)); + break; + + case '@': + vGoals.push_back({ x, y }); + vLevel.emplace_back(nullptr); + break; + + + default: + vLevel.emplace_back(nullptr); + } + } + } + } + + bool OnUserCreate() override + { + gfxTiles.Load("./gfx/blocks.png"); + LoadLevel(0); + return true; + } + + bool OnUserUpdate(float fElapsedTime) override + { + + bool bPushing = false; + int dirPush = 0; + + if (GetKey(olc::Key::W).bPressed) + { + dirPush = NORTH; + bPushing = true; + } + + if (GetKey(olc::Key::S).bPressed) + { + dirPush = SOUTH; + bPushing = true; + } + + if (GetKey(olc::Key::A).bPressed) + { + dirPush = WEST; + bPushing = true; + } + + if (GetKey(olc::Key::D).bPressed) + { + dirPush = EAST; + bPushing = true; + } + + if (GetKey(olc::Key::R).bPressed) + { + LoadLevel(0); + } + + auto id = [&](olc::vi2d& pos) { return pos.y * vLevelSize.x + pos.x; }; + + if (bPushing) + { + olc::vi2d vBlock = vPlayer; + + bool bAllowPush = false; + bool bTest = true; + + while (bTest) + { + if (vLevel[id(vBlock)] != nullptr) + { + if (vLevel[id(vBlock)]->Push((dirPush + 2) % 4)) + { + // Block allows push + switch (dirPush) + { + case NORTH: vBlock.y--; break; + case SOUTH: vBlock.y++; break; + case EAST: vBlock.x++; break; + case WEST: vBlock.x--; break; + } + } + else + bTest = false; + } + else + { + bAllowPush = true; + bTest = false; + } + } + + if (bAllowPush) + { + while (vBlock != vPlayer) + { + olc::vi2d vSource = vBlock; + switch (dirPush) + { + case NORTH: vSource.y++; break; + case SOUTH: vSource.y--; break; + case EAST: vSource.x--; break; + case WEST: vSource.x++; break; + } + + + if (vLevel[id(vSource)] != nullptr) vLevel[id(vSource)]->Move(); + std::swap(vLevel[id(vSource)], vLevel[id(vBlock)]); + + vBlock = vSource; + } + + switch (dirPush) + { + case NORTH: vPlayer.y--; break; + case SOUTH: vPlayer.y++; break; + case EAST: vPlayer.x++; break; + case WEST: vPlayer.x--; break; + } + + } + + } + + + int nGoals = 0; + for (auto& g : vGoals) + if (vLevel[id(g)]) nGoals++; + + + Clear(olc::BLACK); + + + for (auto& g : vGoals) + FillCircle(g * vBlockSize + vBlockSize / 2, vBlockSize.x / 2 - 2, olc::YELLOW); + + + olc::vi2d vTilePos = { 0,0 }; + for (vTilePos.y = 0; vTilePos.y < vLevelSize.y; vTilePos.y++) + { + for (vTilePos.x = 0; vTilePos.x < vLevelSize.x; vTilePos.x++) + { + + auto& b = vLevel[id(vTilePos)]; + + + if (b) + { + b->DrawSelf(this, vTilePos, vBlockSize, gfxTiles); + } + } + } + + DrawString(4, 4, "Goals: " + std::to_string(nGoals) + " / " + std::to_string(vGoals.size()), olc::WHITE); + + + return true; + } +}; + +int main() +{ + BlockPuzzle demo; + if (demo.Construct(256, 240, 4, 4)) + demo.Start(); + return 0; +} \ No newline at end of file