Minesweeper/source/game/tilemap.cpp
2024-05-08 13:50:29 -04:00

453 lines
No EOL
9.7 KiB
C++

// Header
#include <game/tilemap.h>
// 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();
}