parent
3a750d7a89
commit
ac1072fd1f
@ -0,0 +1,452 @@ |
||||
/*
|
||||
OneLoneCoder.com - Ludum Dare 42 - Running Out Of Space |
||||
"What's all this about Fish?" - @Javidx9 |
||||
|
||||
License |
||||
~~~~~~~ |
||||
One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 |
||||
This program comes with ABSOLUTELY NO WARRANTY. |
||||
This is free software, and you are welcome to redistribute it |
||||
under certain conditions; See license for details. |
||||
Original works located at: |
||||
https://www.github.com/onelonecoder
|
||||
https://www.onelonecoder.com
|
||||
https://www.youtube.com/javidx9
|
||||
GNU GPLv3 |
||||
https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||||
|
||||
From Javidx9 :) |
||||
~~~~~~~~~~~~~~~ |
||||
Hello! Ultimately I don't care what you use this for. It's intended to be |
||||
educational, and perhaps to the oddly minded - a little bit of fun. |
||||
Please hack this, change it and use it in any way you see fit. You acknowledge |
||||
that I am not responsible for anything bad that happens as a result of |
||||
your actions. However this code is protected by GNU GPLv3, see the license in the |
||||
github repo. This means you must attribute me if you use it. You can view this |
||||
license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||||
Cheers! |
||||
|
||||
Background |
||||
~~~~~~~~~~ |
||||
Ludum Dare 42 - A quick and lazy speedcode following the theme of "Running Out Of Space" |
||||
|
||||
OBJECTIVE: Stay Alive! Use Arrow Keys to move player. Press space to deploy bombs |
||||
to blow up encroaching walls. Collect bombs when they appear. Stay away from the |
||||
blasts, and dont get trapped in the walls! |
||||
|
||||
|
||||
Video |
||||
~~~~~ |
||||
https://www.twitch.tv/videos/295730019
|
||||
|
||||
Author |
||||
~~~~~~ |
||||
Twitter: @javidx9 |
||||
Blog: http://www.onelonecoder.com
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
|
||||
|
||||
Last Updated: 11/08/2018 |
||||
*/ |
||||
|
||||
|
||||
// For Windows 7
|
||||
//#include "olcConsoleGameEngineGL.h"
|
||||
|
||||
// For Windows
|
||||
#include "olcConsoleGameEngine.h" |
||||
|
||||
// For Linux
|
||||
//#include "olcConsoleGameEngineSDL.h"
|
||||
|
||||
#include <algorithm> |
||||
#include <string> |
||||
using namespace std; |
||||
|
||||
|
||||
class LudumDare42 : public olcConsoleGameEngine |
||||
{ |
||||
public: |
||||
LudumDare42() |
||||
{ |
||||
m_sAppName = L"Ludum Dare 42 - Running Out Of Space"; |
||||
} |
||||
|
||||
|
||||
private: |
||||
int nWorldWidth = 60; |
||||
int nWorldHeight = 60; |
||||
|
||||
int *world = nullptr; |
||||
|
||||
int nBlocksPlaced = 0; |
||||
|
||||
float fBlockTimeElapsed = 0.0f; |
||||
float fBlockTime = 0.02f; |
||||
|
||||
int nPlayerX; |
||||
int nPlayerY; |
||||
|
||||
|
||||
|
||||
float fSurvivalTime = 0.0f; |
||||
|
||||
enum
|
||||
{ |
||||
EMPTY, |
||||
BORDER,
|
||||
BLOCK, |
||||
}; |
||||
|
||||
|
||||
struct sParticle |
||||
{ |
||||
float px, py; |
||||
float vx, vy; |
||||
float lifetime; |
||||
bool dead = false; |
||||
}; |
||||
|
||||
struct sBomb |
||||
{ |
||||
int px, py; |
||||
float fuse; |
||||
}; |
||||
|
||||
|
||||
list<sParticle> listParticles; |
||||
list<sBomb> listBombs; |
||||
|
||||
int nBombPickupX = 20; |
||||
int nBombPickupY = 20; |
||||
bool bBombsVisible = true; |
||||
int nBoomsticks = 1; |
||||
|
||||
int nPlayerHealth = 100; |
||||
float fHighScore = 0.0f; |
||||
|
||||
bool bReset = true; |
||||
|
||||
public: |
||||
bool OnUserCreate() override |
||||
{ |
||||
world = new int[nWorldWidth * nWorldHeight]{ 0 }; |
||||
bReset = true;
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
void Boom(int xc, int yc, int r) |
||||
{ |
||||
// Taken from wikipedia
|
||||
int x = 0; |
||||
int y = r; |
||||
int p = 3 - 2 * r; |
||||
if (!r) return; |
||||
|
||||
auto drawline = [&](int sx, int ex, int ny) |
||||
{ |
||||
if (ny <= 0 || ny >= nWorldHeight) return; |
||||
sx = max(sx, 0); |
||||
ex = min(ex, nWorldWidth); |
||||
for (int i = sx; i <= ex; i++) |
||||
{ |
||||
|
||||
if (world[ny * nWorldWidth + i] != BORDER) |
||||
{ |
||||
world[ny * nWorldWidth + i] = 0; |
||||
nBlocksPlaced--; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
while (y >= x) |
||||
{ |
||||
// Modified to draw scan-lines instead of edges
|
||||
drawline(xc - x, xc + x, yc - y); |
||||
drawline(xc - y, xc + y, yc - x); |
||||
drawline(xc - x, xc + x, yc + y); |
||||
drawline(xc - y, xc + y, yc + x); |
||||
if (p < 0) p += 4 * x++ + 6; |
||||
else p += 4 * (x++ - y--) + 10; |
||||
} |
||||
}; |
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override |
||||
{ |
||||
if (bReset) |
||||
{ |
||||
for (int x = 0; x<nWorldWidth; x++) |
||||
for (int y = 0; y < nWorldHeight; y++) |
||||
{ |
||||
if (x == 0 || y == 0 || x == nWorldWidth - 1 || y == nWorldHeight - 1) |
||||
world[y*nWorldWidth + x] = BORDER; |
||||
else |
||||
world[y*nWorldWidth + x] = EMPTY; |
||||
} |
||||
|
||||
nPlayerX = nWorldWidth / 2; |
||||
nPlayerY = nWorldHeight / 2; |
||||
nPlayerHealth = 100; |
||||
fSurvivalTime = 0; |
||||
nBombPickupX = 20; |
||||
nBombPickupY = 20; |
||||
bBombsVisible = true; |
||||
nBoomsticks = 1; |
||||
listBombs.clear(); |
||||
listParticles.clear(); |
||||
bReset = false; |
||||
} |
||||
|
||||
|
||||
if (nPlayerHealth <= 0) |
||||
{ |
||||
// Show Game Over
|
||||
|
||||
// Clear Screen
|
||||
Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_BLACK); |
||||
DrawString(10, 10, L"Oh Dear! You ran out of space..."); |
||||
DrawString(10, 12, L"Your Score: " + to_wstring((int)fSurvivalTime)); |
||||
DrawString(10, 14, L"High Score: " + to_wstring((int)fHighScore)); |
||||
DrawString(10, 20, L"OneLoneCoder - www.youtube.com/javidx9"); |
||||
DrawString(10, 22, L"Ludum Dare 42 - 'Running Out Of Space'"); |
||||
DrawString(10, 30, L"Press 'R' to restart"); |
||||
|
||||
if (GetKey(L'R').bReleased) |
||||
bReset = true; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
|
||||
// Place Block, only on borders, or attached to
|
||||
// neighbouring blocks
|
||||
|
||||
auto Get = [&](int x, int y) |
||||
{ |
||||
return world[y*nWorldWidth + x]; |
||||
}; |
||||
|
||||
auto Set = [&](int x, int y, int i) |
||||
{ |
||||
world[y*nWorldWidth + x] = i; |
||||
}; |
||||
|
||||
fSurvivalTime += fElapsedTime; |
||||
|
||||
fBlockTimeElapsed += fElapsedTime; |
||||
if (fBlockTimeElapsed >= fBlockTime) |
||||
{ |
||||
fBlockTimeElapsed -= fBlockTime; |
||||
// TODO: perfect !
|
||||
// May come back to this later to make it optimal
|
||||
if (nBlocksPlaced < (nWorldWidth * nWorldHeight) - 2 * (nWorldWidth - 2) - 2 * (nWorldHeight - 2) - 4) // hmmm maybe? :D
|
||||
{ |
||||
bool bBlockPlaced = false; |
||||
while (!bBlockPlaced) |
||||
{ |
||||
int x = (rand() % (nWorldWidth - 2) + 1); |
||||
int y = (rand() % (nWorldHeight - 2) + 1); |
||||
|
||||
// Check if location is suitable, it is
|
||||
// if neighbouring cell is not empty
|
||||
|
||||
if (!Get(x, y) && |
||||
(Get(x - 1, y) || Get(x + 1, y) || Get(x, y - 1) || Get(x, y + 1))) |
||||
{ |
||||
Set(x, y, BLOCK); |
||||
bBlockPlaced = true; |
||||
nBlocksPlaced++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!bBombsVisible && nBoomsticks == 0) |
||||
{ |
||||
bool bBlockPlaced = false; |
||||
while (!bBlockPlaced) |
||||
{ |
||||
int x = (rand() % (nWorldWidth - 2) + 1); |
||||
int y = (rand() % (nWorldHeight - 2) + 1); |
||||
if (!Get(x, y)) |
||||
{ |
||||
bBlockPlaced = true; |
||||
nBombPickupX = x; |
||||
nBombPickupY = y; |
||||
bBombsVisible = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
// Handle Player
|
||||
if (IsFocused()) |
||||
{ |
||||
if (GetKey(VK_UP).bHeld) |
||||
{ |
||||
if (!Get(nPlayerX, nPlayerY - 1)) |
||||
nPlayerY--; |
||||
} |
||||
|
||||
if (GetKey(VK_DOWN).bHeld) |
||||
{ |
||||
if (!Get(nPlayerX, nPlayerY + 1)) |
||||
nPlayerY++; |
||||
} |
||||
|
||||
if (GetKey(VK_LEFT).bHeld) |
||||
{ |
||||
if (!Get(nPlayerX - 1, nPlayerY)) |
||||
nPlayerX--; |
||||
} |
||||
|
||||
if (GetKey(VK_RIGHT).bHeld) |
||||
{ |
||||
if (!Get(nPlayerX + 1, nPlayerY)) |
||||
nPlayerX++; |
||||
} |
||||
|
||||
if (bBombsVisible && (pow(nPlayerX-nBombPickupX,2) + pow(nPlayerY-nBombPickupY, 2)) <= 6) |
||||
{ |
||||
bBombsVisible = false; |
||||
nBoomsticks += 5; |
||||
} |
||||
|
||||
// Check if player is trapped
|
||||
if (Get(nPlayerX - 1, nPlayerY) && Get(nPlayerX + 1, nPlayerY) && |
||||
Get(nPlayerX, nPlayerY - 1) && Get(nPlayerX, nPlayerY + 1)) |
||||
{ |
||||
if (nBoomsticks == 0) |
||||
nPlayerHealth = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Update Particles
|
||||
for (auto &p : listParticles) |
||||
{ |
||||
p.vy += 100.0f * fElapsedTime; |
||||
|
||||
p.px += p.vx * fElapsedTime; |
||||
p.py += p.vy * fElapsedTime; |
||||
|
||||
if (p.px < 0 || p.px >= nWorldWidth || p.py < 0 || p.py >= nWorldHeight) |
||||
{ |
||||
p.dead = true; |
||||
} |
||||
} |
||||
|
||||
listParticles.remove_if([&](const sParticle &p) { return p.dead; }); |
||||
|
||||
|
||||
// Update Bombs
|
||||
for (auto &b : listBombs) |
||||
{ |
||||
b.fuse -= fElapsedTime; |
||||
if (b.fuse < 0) |
||||
{ |
||||
// Boom! if boomsticks are available
|
||||
Boom(b.px, b.py, 5); |
||||
|
||||
// Some sort of visual effect for explosion
|
||||
for (int i = 0; i < 20; i++) |
||||
{ |
||||
sParticle p; |
||||
p.px = b.px + (2.5f * (((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f)); |
||||
p.py = b.py + (2.5f * (((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f)); |
||||
p.vx = 1.1f * 50.0f * (((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f); |
||||
p.vy = 1.1f * 50.0f * (((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f); |
||||
p.dead = false; |
||||
|
||||
listParticles.push_back(p); |
||||
} |
||||
|
||||
// Injure player
|
||||
nPlayerHealth -= max(0, 4 * (5 - sqrtf(pow(nPlayerX - b.px, 2) + pow(nPlayerY - b.py, 2)))); |
||||
|
||||
} |
||||
} |
||||
|
||||
listBombs.remove_if([&](const sBomb &b) { return b.fuse < 0; }); |
||||
|
||||
if (IsFocused() && GetKey(VK_SPACE).bReleased) |
||||
{ |
||||
if (nBoomsticks > 0) |
||||
{ |
||||
listBombs.push_back({ nPlayerX, nPlayerY, 5.0f }); |
||||
nBoomsticks--; |
||||
} |
||||
} |
||||
|
||||
// Clear Screen
|
||||
Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_BLACK); |
||||
|
||||
// Draw World
|
||||
for (int x = 0; x < nWorldWidth; x++) |
||||
{ |
||||
for (int y = 0; y < nWorldHeight; y++) |
||||
{ |
||||
switch (world[y*nWorldWidth + x]) |
||||
{ |
||||
case BORDER: |
||||
Draw(x, y, PIXEL_SOLID, FG_WHITE); |
||||
break; |
||||
|
||||
case BLOCK: |
||||
Draw(x, y, PIXEL_SOLID, FG_RED); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Draw Boom
|
||||
for (auto &p : listParticles) |
||||
{ |
||||
Draw(p.px, p.py, PIXEL_QUARTER, FG_YELLOW); |
||||
} |
||||
|
||||
// Draw Bombs
|
||||
for (auto &b : listBombs) |
||||
{ |
||||
Draw(b.px - 1, b.py, PIXEL_SOLID, FG_GREEN); |
||||
Draw(b.px + 1, b.py, PIXEL_SOLID, FG_GREEN); |
||||
Draw(b.px, b.py - 1, PIXEL_SOLID, FG_GREEN); |
||||
Draw(b.px, b.py + 1, PIXEL_SOLID, FG_GREEN); |
||||
DrawString(b.px, b.py, to_wstring((int)b.fuse + 1)); |
||||
} |
||||
|
||||
// Draw Bomb Pickup
|
||||
if (bBombsVisible) |
||||
{ |
||||
Fill(nBombPickupX - 1, nBombPickupY - 1, nBombPickupX + 2, nBombPickupY + 2, PIXEL_SOLID, FG_MAGENTA); |
||||
Draw(nBombPickupX, nBombPickupY, PIXEL_SOLID, FG_GREY); |
||||
} |
||||
|
||||
|
||||
fHighScore = max(fHighScore, fSurvivalTime); |
||||
|
||||
// Draw Player
|
||||
Draw(nPlayerX, nPlayerY, L'P', FG_CYAN); |
||||
|
||||
DrawString(2, ScreenHeight() - 2, L"Survived: " + to_wstring((int)fSurvivalTime)); |
||||
DrawString(18, ScreenHeight() - 2, L"Bombs: " + to_wstring(nBoomsticks)); |
||||
DrawString(30, ScreenHeight() - 2, L"Health: " + to_wstring(nPlayerHealth)); |
||||
DrawString(45, ScreenHeight() - 2, L"High Score: " + to_wstring((int)fHighScore)); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
}; |
||||
|
||||
|
||||
|
||||
|
||||
int main() |
||||
{ |
||||
LudumDare42 demo; |
||||
if (demo.ConstructConsole(60, 63, 14, 14)) |
||||
demo.Start(); |
||||
return 0; |
||||
} |
||||
|
Loading…
Reference in new issue