diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..133ab4f --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# Variables +LIBS:= -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf +TARGET:= minesweeper + +# Instructions +build: compile link run + +compile: + g++ -c -I include source/engine/vectors.cpp -o bin/vectors.o + g++ -c -I include source/engine/engine.cpp -o bin/engine.o + g++ -c -I include source/game/tilemap.cpp -o bin/tilemap.o + g++ -c -I include source/game/navbar.cpp -o bin/navbar.o + g++ -c -I include source/game/message.cpp -o bin/message.o + g++ -c -I include source/main.cpp -o bin/main.o + +link: + g++ bin/*.o -L lib $(LIBS) -o build/$(TARGET) + +cpydat: + cp data -r build + +run: + ./build/$(TARGET) + +# Complimentary +clean: + rm build/* + rm bin/* \ No newline at end of file diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..740c8ce Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/piskel/Tileset.piskel b/assets/piskel/Tileset.piskel new file mode 100644 index 0000000..d1c3827 --- /dev/null +++ b/assets/piskel/Tileset.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"Tileset","description":"","fps":12,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":12,\"chunks\":[{\"layout\":[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAAAQCAYAAABA4nAoAAADLElEQVRoQ+2avW7CMBDHg1QxsXZhZ2Nphk48ACtT36ElSyekzpWYupT2HfoeTB3ShQdgQCxdmVAl2gu61rne+SskGOEsSAkXn8//n8/nuJVl2S7xuFarVbJYLJLhcOhhnSRo72UcjWIEDhSBlg8Aqnh9AKDi73Q6bHcGg0Eyn8/Frm42m+KZZG+KUbSP8fsFQCc0TkgoHgTA114n4AiAHmEO4JuLvDB6+0pLxtx9aj9er01zRvF81u0Wv1z7l4+vxnd8Ptyy9nm+9113pelfv6QJLB9/sK9IZ1el+2BfOwDv223R6HW7/a9xvIEzOAoefukFgFEg6p7B77P9gDw9l8WEvnHtv+SZOH536TMbA+w/xkp6gRRDNQP6AmArfh0ANuIH+yoAgD1CwMVfEj/GVIWgVgDoYNoAAE5y4kfnaZapEwAUvwsAOvFjH1QIqP8mAOhEcsgMgADg7G6YiIvHtH0EAAVueofr+GGGsAGAzvYIRiMAcANpAwCKH3+5ZZV6zzWAdEAke1X8PgDQmR7egXDoANAJhsukEYD/NQwndIhrowBAgz4ZIAQAqPhdANAJuGkAJF/U2kAFyGYJRLNDkxmAzv5cBpKEfhQAcABsagCfnSTIBHVlAPTdpwaQhGcLgGkZpGZSqf9YB3C+SADAf10h0AEg1QPq8shl/FwBkMahkSUQbXwymSTT6bR0GzsPN9Ui2LRubGIJdEgAaF1gWgLpALApgqX42ewC6WLP1Qg+AEAbUhEstc+JX8oA6mxP33eUXSBw4hwB0AkfB8ZlBrStAUIBgPODFsm2/XcBIJgaQA2ALQBgE8ouEPjiswSyEX4EgP8OwEEjiV/KAMEAAKLnLlwKSUsg3O+ntqfwHUAVP7cTRPtkOwOC3TlmAJ34gwZAEj8KACDgAKACOdaXYG4nCHyjH8SogEP4DlBlCXSoIlhXS7jUAFUAkHxgi2A4m7NcLk1+l56rRyGq2MNLQzsLFCoAdRfBJgBM26AwlqavwS67QD4AgA9ORyFGo9EOTnX6Xv1+vzgVWuWKh9n4w4CmmLosobh3Rfufs0C9Xs/rOLRpcOLzGIFTiMA3iwMNLeSNeBkAAAAASUVORK5CYII=\"}]}"],"hiddenFrames":[null]}} \ No newline at end of file diff --git a/assets/piskel/glyph.piskel b/assets/piskel/glyph.piskel new file mode 100644 index 0000000..c3e2713 --- /dev/null +++ b/assets/piskel/glyph.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"glyph","description":"","fps":12,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":13,\"chunks\":[{\"layout\":[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12]],\"base64PNG\":\"data:image/png;base64,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\"}]}"],"hiddenFrames":[null]}} \ No newline at end of file diff --git a/data/fonts/font.ttf b/data/fonts/font.ttf new file mode 100644 index 0000000..271a1c4 Binary files /dev/null and b/data/fonts/font.ttf differ diff --git a/data/sprites/glyph.png b/data/sprites/glyph.png new file mode 100644 index 0000000..d3fa7c6 Binary files /dev/null and b/data/sprites/glyph.png differ diff --git a/data/sprites/tileset.png b/data/sprites/tileset.png new file mode 100644 index 0000000..f3585b1 Binary files /dev/null and b/data/sprites/tileset.png differ diff --git a/include/engine/engine.h b/include/engine/engine.h new file mode 100644 index 0000000..8c76134 --- /dev/null +++ b/include/engine/engine.h @@ -0,0 +1,45 @@ +// Define once +#ifndef H_ENGINE +#define H_ENGINE + +// Includes +/// External +#include "externals.h" +/// Engine +#include "vectors.h" + +// Constants +#define SCR_WIDTH 512 +#define SCR_HEIGHT 562 + +#define SCR_H_BUFFER 50 + +// Classes +class Engine{ +private: + // Variables + SDL_Window* window; + Uint32 last; + +public: + // Variables + static double deltaTime; + static SDL_Renderer* renderer; + static Vector2 mousePos; + static int clicking; + bool running; + + // Constructor + Engine(); + + // Functions + void Update(); + + void Clear(); + void Display(); + + void Delete(); +}; + +// End definition +#endif \ No newline at end of file diff --git a/include/engine/externals.h b/include/engine/externals.h new file mode 100644 index 0000000..7941b09 --- /dev/null +++ b/include/engine/externals.h @@ -0,0 +1,20 @@ +// Define once +#ifndef H_EXTERNALS +#define H_EXTERNALS + +// Includes +/// System +#include +#include +#include +#include +#include +#include +#include +/// SDL2 +#include +#include +#include + +// End definition +#endif \ No newline at end of file diff --git a/include/engine/vectors.h b/include/engine/vectors.h new file mode 100644 index 0000000..af15204 --- /dev/null +++ b/include/engine/vectors.h @@ -0,0 +1,25 @@ +// Define once +#ifndef H_VECTORS +#define H_VECTORS + +// Includes +#include + +// Structure +typedef struct Vector2{ + // Variables + float x,y; + + // Constructor + Vector2(); + Vector2(float x, float y); + + // Operations + Vector2 operator+(Vector2 b); + Vector2 operator*(float b); + void operator+=(Vector2 b); + void operator*=(float b); +} Vector2; + +// End definition +#endif \ No newline at end of file diff --git a/include/game/message.h b/include/game/message.h new file mode 100644 index 0000000..9be4676 --- /dev/null +++ b/include/game/message.h @@ -0,0 +1,34 @@ +// Define once +#ifndef H_MESSAGE +#define H_MESSAGE + +// Includes +/// Externals +#include +/// Engine +#include +/// Game +#include "tilemap.h" + +// Classes +class Message{ +private: + // Variables + SDL_Texture* texture; + + int x,y; + int w,h; + +public: + // Variables + + // Constructors + Message(const char* msg); + + // Functions + void Update(); + void Draw(); +}; + +// End definition +#endif \ No newline at end of file diff --git a/include/game/navbar.h b/include/game/navbar.h new file mode 100644 index 0000000..36e669c --- /dev/null +++ b/include/game/navbar.h @@ -0,0 +1,42 @@ +// Define once +#ifndef H_NAVBAR +#define H_NAVBAR + +// Includes +/// Externals +#include +/// Engine +#include +/// Game +#include + +// Classes +class Navbar{ +private: + // Variables + Tilemap* tilemap; + SDL_Texture* texture; + + bool didClick; + + // Functions + void _drawBackground(); + void _drawFlagCounter(); + void _drawFace(); + void _drawTime(); + + Vector2 _getNumber(int number); + +public: + // Variables + + // Constructor + Navbar(Tilemap* tilemap); + + // Functions + void Update(); + void Draw(); +}; + +// End definition +#endif \ No newline at end of file diff --git a/include/game/tilemap.h b/include/game/tilemap.h new file mode 100644 index 0000000..31b8eb5 --- /dev/null +++ b/include/game/tilemap.h @@ -0,0 +1,65 @@ +// Define once +#ifndef H_TILEMAP +#define H_TILEMAP + +// Includes +/// Externals +#include +/// Engine +#include + +// Constants +#define GRID_W 10 +#define GRID_H 10 +#define GRID_B 10 + +// Classes +class Tilemap{ +private: + // Variables + std::vector grid_bomb; + std::vector grid_flag; + std::vector grid_show; + + SDL_Texture* texture; + + int savedClick = 0; + bool didClick; + bool gameBegan; + + bool cascading; + float timepassed; + + // Functions + void _init(); + void createGrid(Vector2 mousePos); + + int getSurrounding(Vector2 tilePos); + void revealTiles(Vector2 mousePos); + void spawnCascade(Vector2 position); + + void _leftClick(); + void _rightClick(); + + void winDetection(); + void clickDetection(); + +public: + // Variables + bool playing; + bool win; + int flags; + int gameTime; + + // Constructors + Tilemap(); + + // Functions + void Update(); + void Draw(); + + void Reset(); +}; + +// End definiton +#endif \ No newline at end of file diff --git a/source/engine/engine.cpp b/source/engine/engine.cpp new file mode 100644 index 0000000..672db52 --- /dev/null +++ b/source/engine/engine.cpp @@ -0,0 +1,85 @@ +// Header +#include + +// Variables +double Engine::deltaTime; +SDL_Renderer* Engine::renderer; +Vector2 Engine::mousePos; +int Engine::clicking; + +// Constructor +Engine::Engine(){ + // Init SDL2 + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); + + // Create a window + window = SDL_CreateWindow("Minesweeper", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + SCR_WIDTH, SCR_HEIGHT, + SDL_WINDOW_SHOWN); + if(window == NULL){ + throw("Failed to create an SDL window.\n"); + } + + // Create a renderer + Engine::renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED); + if(Engine::renderer == NULL){ + throw("Failed to create an SDL renderer.\n"); + } + + // Staring SDL2 subsystems + IMG_Init(IMG_INIT_PNG); + TTF_Init(); + + // We're running! + running = true; + clicking = false; +} + +// Functions +void Engine::Update(){ + // Getting events + SDL_Event event; + + // Polling + while(SDL_PollEvent(&event)){ + switch(event.type){ + case SDL_QUIT: + running = false; + break; + case SDL_MOUSEMOTION: + Engine::mousePos = Vector2(event.motion.x, event.motion.y); + break; + case SDL_MOUSEBUTTONDOWN: + if(event.button.button == SDL_BUTTON_LEFT){ + clicking = 1; + } + else if(event.button.button == SDL_BUTTON_RIGHT){ + clicking = 2; + } + break; + case SDL_MOUSEBUTTONUP: + clicking = 0; + break; + } + } + + // Calculate deltaTime + Uint32 now = SDL_GetTicks(); + Engine::deltaTime = (now - last) / 1000.0f; + last = now; +} + +void Engine::Clear(){ + SDL_SetRenderDrawColor(Engine::renderer, 0,0,0, 1); + SDL_RenderClear(Engine::renderer); +} +void Engine::Display(){ + SDL_RenderPresent(Engine::renderer); +} + +void Engine::Delete(){ + SDL_DestroyRenderer(Engine::renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} \ No newline at end of file diff --git a/source/engine/vectors.cpp b/source/engine/vectors.cpp new file mode 100644 index 0000000..f7eae03 --- /dev/null +++ b/source/engine/vectors.cpp @@ -0,0 +1,26 @@ +// Header +#include + +// Constructor +Vector2::Vector2(){ + x = 0; + y = 0; +} +Vector2::Vector2(float x, float y){ + this->x = x; + this->y = y; +} + +// Operations +Vector2 Vector2::operator+(Vector2 b){ + return Vector2(x + b.x, y + b.y); +} +Vector2 Vector2::operator*(float b){ + return Vector2(x * b, y * b); +} +void Vector2::operator+=(Vector2 b){ + x += b.x; y += b.y; +} +void Vector2::operator*=(float b){ + x *= b; y *= b; +} \ No newline at end of file diff --git a/source/game/message.cpp b/source/game/message.cpp new file mode 100644 index 0000000..791ecf1 --- /dev/null +++ b/source/game/message.cpp @@ -0,0 +1,38 @@ +// Header +#include + +// Constructors +Message::Message(const char* msg){ + // Loading fonts + TTF_Font* font = TTF_OpenFont("data/fonts/font.ttf", 64); + if(font == NULL){ + throw("Failed to load font.\n"); + } + + // Surface to texture + SDL_Surface* surf = TTF_RenderText_Blended(font, msg, {255,255,255}); + texture = SDL_CreateTextureFromSurface(Engine::renderer, surf); + + // Variables + this->x = (SCR_WIDTH/2) - (surf->w / 2); + this->y = (SCR_HEIGHT/2) - (surf->h / 2); + this->w = surf->w; + this->h = surf->h; + + // Cleanup + SDL_FreeSurface(surf); +} + +// Functions +void Message::Update(){ + +} +void Message::Draw(){ + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + + SDL_Rect rect; + rect.x = x; rect.y = y; + rect.w = w; rect.h = h; + + SDL_RenderCopy(Engine::renderer, texture, NULL, &rect); +} \ No newline at end of file diff --git a/source/game/navbar.cpp b/source/game/navbar.cpp new file mode 100644 index 0000000..1aabb50 --- /dev/null +++ b/source/game/navbar.cpp @@ -0,0 +1,359 @@ +// Header +#include + +// Constructor +Navbar::Navbar(Tilemap* tilemap){ + // Setting variables + this->tilemap = tilemap; + didClick = false; + + // Getting texture + SDL_Surface* surf = IMG_Load("data/sprites/glyph.png"); + if(surf == NULL){ + throw("Failed to load Glyph surface.\n"); + } + + texture = SDL_CreateTextureFromSurface(Engine::renderer, surf); + SDL_FreeSurface(surf); +} + +// Functions +/// Private +void Navbar::_drawBackground(){ + // Making a rect for the top + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = SCR_WIDTH; + rect.h = SCR_H_BUFFER; + + // Color + SDL_SetRenderDrawColor(Engine::renderer, 190,190,190, 1); + SDL_RenderFillRect(Engine::renderer, &rect); +} +void Navbar::_drawFlagCounter(){ + if(tilemap->flags < 10){ + // Positional rect + SDL_Rect rect; + rect.x = SCR_WIDTH / 10; + rect.y = 0; + rect.w = 50; rect.h = 50; + + // Frame + SDL_Rect frame; + frame.x = 0; frame.y = 0; + frame.w = 16; frame.h = 16; + + // What number? + switch(tilemap->flags){ + case 0: + frame.x = 0; + frame.y = 0; + break; + case 1: + frame.x = 1; + frame.y = 0; + break; + case 2: + frame.x = 2; + frame.y = 0; + break; + case 3: + frame.x = 3; + frame.y = 0; + break; + case 4: + frame.x = 4; + frame.y = 0; + break; + case 5: + frame.x = 0; + frame.y = 1; + break; + case 6: + frame.x = 1; + frame.y = 1; + break; + case 7: + frame.x = 2; + frame.y = 1; + break; + case 8: + frame.x = 3; + frame.y = 1; + break; + case 9: + frame.x = 4; + frame.y = 1; + break; + } + + // Scaling to size + frame.x *= 16; + frame.y *= 16; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + } + else{ + // Positional rect + SDL_Rect rect; + rect.x = SCR_WIDTH / 10; + rect.y = 0; + rect.w = 50; rect.h = 50; + + // Frame + SDL_Rect frame; + frame.x = 0; frame.y = 0; + frame.w = 16; frame.h = 16; + + Vector2 fPos = _getNumber(tilemap->flags%10); + frame.x = fPos.x; + frame.y = fPos.y; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + + // The second one + rect.x -= 50; + + fPos = _getNumber((tilemap->flags / 10)%10); + frame.x = fPos.x; + frame.y = fPos.y; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + } +} +void Navbar::_drawFace(){ + // Shapes + SDL_Rect rect; + SDL_Rect frame; + + rect.x = (SCR_WIDTH / 2) - (25); + rect.y = 0; + rect.w = 50; rect.h = 50; + + frame.w = 16; + frame.h = 16; + + // This face is gonna be weird to draw.. + if(Engine::clicking){ + // Anxious face + frame.x = 1; + frame.y = 2; + } + else if(!tilemap->playing && !tilemap->win){ + // Sad face + frame.x = 2; + frame.y = 2; + } + else{ + // Happy face + frame.x = 0; + frame.y = 2; + } + + // Scaling to size + frame.x *= 16; + frame.y *= 16; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); +} +void Navbar::_drawTime(){ + if(tilemap->gameTime < 10){ + // Positional rect + SDL_Rect rect; + rect.x = SCR_WIDTH / 1.2f; + rect.y = 0; + rect.w = 50; rect.h = 50; + + // Frame + SDL_Rect frame; + frame.x = 0; frame.y = 0; + frame.w = 16; frame.h = 16; + + // What number? + switch(tilemap->gameTime){ + case 0: + frame.x = 0; + frame.y = 0; + break; + case 1: + frame.x = 1; + frame.y = 0; + break; + case 2: + frame.x = 2; + frame.y = 0; + break; + case 3: + frame.x = 3; + frame.y = 0; + break; + case 4: + frame.x = 4; + frame.y = 0; + break; + case 5: + frame.x = 0; + frame.y = 1; + break; + case 6: + frame.x = 1; + frame.y = 1; + break; + case 7: + frame.x = 2; + frame.y = 1; + break; + case 8: + frame.x = 3; + frame.y = 1; + break; + case 9: + frame.x = 4; + frame.y = 1; + break; + } + + // Scaling to size + frame.x *= 16; + frame.y *= 16; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + } + else{ + // Positional rect + SDL_Rect rect; + rect.x = SCR_WIDTH / 1.2f; + rect.y = 0; + rect.w = 50; rect.h = 50; + + // Frame + SDL_Rect frame; + frame.x = 0; frame.y = 0; + frame.w = 16; frame.h = 16; + + Vector2 fPos = _getNumber(tilemap->gameTime%10); + frame.x = fPos.x; + frame.y = fPos.y; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + + // The second one + rect.x -= 50; + + fPos = _getNumber((tilemap->gameTime / 10)%10); + frame.x = fPos.x; + frame.y = fPos.y; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255,255,255, 1); + SDL_RenderCopy(Engine::renderer, texture, &frame, &rect); + } +} +Vector2 Navbar::_getNumber(int number){ + // Result + Vector2 frame(0,0); + + // What number? + switch(number){ + case 0: + frame.x = 0; + frame.y = 0; + break; + case 1: + frame.x = 1; + frame.y = 0; + break; + case 2: + frame.x = 2; + frame.y = 0; + break; + case 3: + frame.x = 3; + frame.y = 0; + break; + case 4: + frame.x = 4; + frame.y = 0; + break; + case 5: + frame.x = 0; + frame.y = 1; + break; + case 6: + frame.x = 1; + frame.y = 1; + break; + case 7: + frame.x = 2; + frame.y = 1; + break; + case 8: + frame.x = 3; + frame.y = 1; + break; + case 9: + frame.x = 4; + frame.y = 1; + break; + } + + // Scaling to size + frame.x *= 16; + frame.y *= 16; + + // Return result + return frame; +} +/// Public +void Navbar::Update(){ + // Did we click on the face? + if(Engine::clicking + && !didClick){ + // Clicking! + didClick = true; + + // Getting the mouse position + Vector2 mPos = Engine::mousePos; + + // Constant + const int mid_w = SCR_WIDTH / 2; + + // Face clicking? + if(mPos.x >= mid_w - 50 + && mPos.x <= mid_w + 50 + && mPos.y <= SCR_H_BUFFER){ + // Restart game + tilemap->Reset(); + } + } + if(!Engine::clicking){ + didClick = false; + } +} +void Navbar::Draw(){ + // Drawing stuff in order + /// Background + _drawBackground(); + + /// Flags counter + _drawFlagCounter(); + + /// Smiley face + _drawFace(); + + /// Time + _drawTime(); +} \ No newline at end of file diff --git a/source/game/tilemap.cpp b/source/game/tilemap.cpp new file mode 100644 index 0000000..ff7ce31 --- /dev/null +++ b/source/game/tilemap.cpp @@ -0,0 +1,453 @@ +// Header +#include + +// Constructors +Tilemap::Tilemap(){ + _init(); +} + +// Functions +/// Private +void Tilemap::_init(){ + // Reset all of our grids + grid_bomb.clear(); + grid_flag.clear(); + grid_show.clear(); + + // Adding all tiles to the map + for(int y = 0; y < GRID_H; y++){ + for(int x = 0; x < GRID_W; x++){ + grid_bomb.push_back(0); + grid_flag.push_back(0); + grid_show.push_back(0); + } + } + + // Loading texture + SDL_Surface* surf = IMG_Load("data/sprites/tileset.png"); + if(surf == NULL){ + printf("ERROR: %s\n", IMG_GetError()); + throw("Failed to load tiles.\n"); + } + + texture = SDL_CreateTextureFromSurface(Engine::renderer, surf); + SDL_FreeSurface(surf); + + // Variables + playing = true; + flags = GRID_B; + savedClick = 0; + didClick = true; + cascading = false; + win = true; + gameTime = 0; + timepassed = 0; + gameBegan = false; +} +void Tilemap::createGrid(Vector2 mousePos){ + for(int i = 0; i < GRID_B; i++){ + // Completly random number + srand(time(NULL)); + + // Placeholder position + Vector2 _bombPos = Vector2(0,0); + + // Loop! + while(true){ + _bombPos = Vector2( + floor(rand() % GRID_W), + floor(rand() % GRID_H) + ); + + if(!grid_bomb[_bombPos.y*GRID_H+_bombPos.x] + && _bombPos.x != mousePos.x + && _bombPos.y != mousePos.y){ + break; + } + } + + // Apply position + grid_bomb[_bombPos.y*GRID_H+_bombPos.x] = 1; + } + + gameBegan = true; +} +int Tilemap::getSurrounding(Vector2 tilePos){ + // Result + int result = 0; + + // Finding surrounding tiles + for(int y = -1; y < 2; y++){ + for(int x = -1; x < 2; x++){ + // Out of bounds? + if(tilePos.x + x < 0 + || tilePos.x + x >= GRID_W + || tilePos.y + y < 0 + || tilePos.y + y >= GRID_H){ + continue; + } + + // Otherwise good to go! + result += grid_bomb[(y+tilePos.y)*GRID_H+(x+tilePos.x)]; + } + } + + // Return result + return result; +} +void Tilemap::revealTiles(Vector2 mousePos){ + // What's the mouse position + int _gridPos = mousePos.y*GRID_H+mousePos.x; + printf("Grid Position: %i\n", _gridPos); // Debug + + // Was it a flag? + if(grid_flag[_gridPos]) {return;} + + // Setting the grid position to VISIBLE + grid_show[_gridPos] = 1; + + // Rippling + cascading = true; + while(cascading){ + spawnCascade(mousePos); + } + + // Clicked on a bomb? + // TODO: Add game over functionality + if(grid_bomb[_gridPos]){ + win = false; + playing = false; + } +} +void Tilemap::spawnCascade(Vector2 position){ + // For clicking on a tile with a bomb next to it, + // it gets annoying when it clears everything + // when you're trying to click on a tile right next + // to the bomb. + if(getSurrounding(position) != 0){ + cascading = false; + return; + } + + // Checking if there is a bomb near the current tile + // And duplication + for(int y = -1; y < 2; y++){ + for(int x = -1; x < 2; x++){ + // Checking for failure cases + if(x == 0 && y == 0){ + // Center + continue; + } + if(x + position.x < 0 || x + position.x > GRID_W + || y + position.y < 0 || y + position.y > GRID_H){ + // Out of bounds + continue; + } + + // Diagonal + if(x == -1 && y == -1 + && x == -1 && y == +1 + && x == +1 && y == +1 + && x == +1 && y == -1){ + continue; + } + + // Flag near? + if(grid_flag[(position.y+y)*GRID_W+(position.x+x)]){ + continue; + } + + // Bombs near? + if(getSurrounding(position + Vector2(x,y)) != 0){ + grid_show[(position.y+y)*GRID_W+(position.x+x)] = 1; + continue; + } + + // Duplication + if(!grid_show[(position.y+y)*GRID_W+(position.x+x)] + && !grid_bomb[(position.y+y)*GRID_W+(position.x+x)]){ + grid_show[(position.y+y)*GRID_W+(position.x+x)] = 1; + spawnCascade(Vector2(position.x + x, position.y + y)); + } + } + } + + // Checking for a failure + for(int y = -1; y < 2; y++){ + for(int x = -1; x < 2; x++){ + // Checking for failure cases + if(x == 0 && y == 0){ + // Center + continue; + } + if(x + position.x < 0 || x + position.x > GRID_W + || y + position.y < 0 || y + position.y > GRID_H){ + // Out of bounds + continue; + } + + // Checking if there is a space that needs to be filled + if(!grid_show[(position.y+y)*GRID_W+(position.x+x)] + && !grid_bomb[(position.y+y)*GRID_W+(position.x+x)]){ + return; + } + } + } + + // Failing + cascading = false; +} +void Tilemap::_leftClick(){ + // Convert mouse position into local grid position + Vector2 _mGridPos = Vector2( + floor(Engine::mousePos.x / (SCR_WIDTH / GRID_W)), + floor( + (Engine::mousePos.y + SCR_H_BUFFER) / ((SCR_HEIGHT - SCR_H_BUFFER) / GRID_H)) + - ((SCR_H_BUFFER / ((SCR_HEIGHT - SCR_H_BUFFER) / GRID_H)) + 2) + ); + + // Do we need a new grid? + int _totalBombs = 0; + for(int i = 0; i < grid_bomb.size(); i++){ + _totalBombs += grid_bomb[i]; + } + + if(_totalBombs == 0){ + createGrid(_mGridPos); + } + + // Reveal the tiles! + revealTiles(_mGridPos); +} +void Tilemap::_rightClick(){ + // Convert mouse position into local grid position + Vector2 _mGridPos = Vector2( + floor(Engine::mousePos.x / (SCR_WIDTH / GRID_W)), + floor( + (Engine::mousePos.y + SCR_H_BUFFER) / ((SCR_HEIGHT - SCR_H_BUFFER) / GRID_H)) + - ((SCR_H_BUFFER / ((SCR_HEIGHT - SCR_H_BUFFER) / GRID_H)) + 2) + ); + int _gridPos = _mGridPos.y*GRID_H+_mGridPos.x; + + // Are we clicking on an existing tile? + if(grid_show[_gridPos]) {return;} + + // Do we want to add or remove? + if(grid_flag[_gridPos]){ + // Remove + flags++; + grid_flag[_gridPos] = 0; + } + else{ + // Add + if(flags <= 0) {return;} + flags--; + grid_flag[_gridPos] = 1; + } +} +void Tilemap::winDetection(){ + // How many bombs are there, and do they match + // Up with our flag positions? + int _matchedBombs = 0; + + for(int y = 0; y < GRID_H; y++){ + for(int x = 0; x < GRID_W; x++){ + if(grid_bomb[y*GRID_H+x] + && grid_flag[y*GRID_H+x]){ + _matchedBombs++; + } + } + } + + // Matched bombs == Bombs? + if(_matchedBombs == GRID_B){ + playing = false; + win = true; + } +} +void Tilemap::clickDetection(){ + // Are we even playing? + if(!playing) {return;} + + // Left clicking + if(!didClick + && Engine::clicking == 1){ + // One click at a time! + savedClick = 1; + didClick = true; + } + // Right clicking + else if(!didClick + && Engine::clicking == 2){ + // One click at a time! + savedClick = 2; + didClick = true; + + // Right click + _rightClick(); + } + // Allow new click (LEFT) + else if(didClick + && savedClick == 1 + && Engine::clicking == 0){ + // Allow new click + didClick = false; + + // Left click + _leftClick(); + + savedClick = 0; + } + else if(didClick + && Engine::clicking == 0){ + didClick = false; + savedClick = 0; + } +} + +/// Public +void Tilemap::Update(){ + // Win detection + winDetection(); + + // Click detection + clickDetection(); + + // Time passing + if(gameBegan + && playing){ + if(timepassed >= 1.0f){ + timepassed = 0; + gameTime++; + + if(gameTime > 99) {gameTime = 99;} + } + else{ + timepassed += (float)Engine::deltaTime; + } + } +} +void Tilemap::Draw(){ + // Getting sizes + int tileWidth = (int)(SCR_WIDTH / GRID_W); + int tileHeight = (int)((SCR_HEIGHT - SCR_H_BUFFER) / GRID_H); + + // Going through all tiles + for(int y = 0; y < GRID_H; y++){ + for(int x = 0; x < GRID_W; x++){ + // Global Tile + SDL_Rect rect; + + rect.x = x * tileWidth; + rect.y = (y * tileHeight) + SCR_H_BUFFER; + + rect.w = tileWidth; + rect.h = tileHeight; + + // Frame + SDL_Rect frame_rect; + frame_rect.w = 16; + frame_rect.h = 16; + + int frame = 0; + + // Shown tile, not a bomb + if(!playing + && grid_bomb[y*GRID_H+x]){ + frame = 2; + } + else if(grid_show[y*GRID_W+x] + && !grid_bomb[y*GRID_H+x]){ + frame = 0; + } + else if(grid_flag[y*GRID_H+x]){ + frame = 1; + } + else{ + frame = 3; + } + + // Setting frame rect + switch(frame){ + case 0: + switch(getSurrounding(Vector2(x,y))){ + case 0: + frame_rect.x = 3; + frame_rect.y = 0; + break; + case 1: + frame_rect.x = 0; + frame_rect.y = 1; + break; + case 2: + frame_rect.x = 1; + frame_rect.y = 1; + break; + case 3: + frame_rect.x = 2; + frame_rect.y = 1; + break; + case 4: + frame_rect.x = 3; + frame_rect.y = 1; + break; + case 5: + frame_rect.x = 0; + frame_rect.y = 2; + break; + case 6: + frame_rect.x = 1; + frame_rect.y = 2; + break; + case 7: + frame_rect.x = 2; + frame_rect.y = 2; + break; + case 8: + frame_rect.x = 3; + frame_rect.y = 2; + break; + default: + printf("Something went very wrong while checking for SURROUNDING.\n"); + break; + } + break; + case 1: + frame_rect.x = 1; + frame_rect.y = 0; + break; + case 2: + frame_rect.x = 2; + frame_rect.y = 0; + break; + case 3: + frame_rect.x = 0; + frame_rect.y = 0; + break; + default: + printf("TILESET RENDERING ERROR.\n"); + break; + } + + // Multiplying + frame_rect.x *= 16; + frame_rect.y *= 16; + + // Drawing + SDL_SetRenderDrawColor(Engine::renderer, 255, 255,255, 1); + SDL_RenderFillRect(Engine::renderer, &rect); + + SDL_RenderCopy(Engine::renderer, texture, &frame_rect, &rect); + } + } + + // DEBUG +} + +void Tilemap::Reset(){ + // Resetting the game! (VERY DIFFICULT) + printf("Reset.\n"); + + // Reset! + _init(); +} \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..fa18757 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,60 @@ +// For windows +#define SDL_MAIN_HANDLED + +// Includes +/// External +#include +/// Engine +#include +/// Game +#include +#include +#include + +// Entry Point +int main(int argc, char* argv[]){ + // Creating an engine + Engine engine = Engine(); + Tilemap tilemap = Tilemap(); + Navbar navbar = Navbar(&tilemap); + + // Messages + Message gameOver("Game Over!"); + Message youWin("You Win!"); + + // Game loop + while(engine.running){ + // Update + engine.Update(); + + // Clear + engine.Clear(); + + // Game code.. + tilemap.Update(); + navbar.Update(); + + // Render code.. + tilemap.Draw(); + navbar.Draw(); + + /// Messages? + if(!tilemap.playing){ + if(tilemap.win){ + youWin.Draw(); + } + else{ + gameOver.Draw(); + } + } + + // Displaying + engine.Display(); + } + + // Cleanup + engine.Delete(); + + // Quit app + return 0; +} \ No newline at end of file