parent
7b0ebf18ee
commit
6662e68da0
@ -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<olc::vi2d> 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<std::unique_ptr<block>> 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<block_solid>()); |
||||
break; |
||||
|
||||
case 'P': |
||||
vLevel.emplace_back(std::make_unique<block_player>()); |
||||
vPlayer = { x, y }; |
||||
break; |
||||
|
||||
case '+': |
||||
vLevel.emplace_back(std::make_unique<block_simple>()); |
||||
break; |
||||
|
||||
case '-': |
||||
vLevel.emplace_back(std::make_unique<block_horizontal>()); |
||||
break; |
||||
|
||||
case '|': |
||||
vLevel.emplace_back(std::make_unique<block_vertical>()); |
||||
break; |
||||
|
||||
case '5': |
||||
vLevel.emplace_back(std::make_unique<block_countdown>(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; |
||||
} |
Loading…
Reference in new issue