one should be in the correct folder when initializing the git project...
This commit is contained in:
142
src/Game.cpp
Normal file
142
src/Game.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "Game.hpp"
|
||||
|
||||
Game::Game( ) :
|
||||
window(nullptr, SDL_DestroyWindow),
|
||||
gameOver(false),
|
||||
startSequence(true),
|
||||
quit(false) { }
|
||||
|
||||
bool Game::init(const char* title, int w, int h) {
|
||||
window.reset(SDL_CreateWindow(
|
||||
title,
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
w, h,
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
|
||||
));
|
||||
|
||||
if (!window) {
|
||||
SDL_Log("Failed to create window: %s", SDL_GetError( ));
|
||||
return false;
|
||||
}
|
||||
|
||||
renderer = std::shared_ptr<SDL_Renderer>(
|
||||
SDL_CreateRenderer(window.get( ), -1, SDL_RENDERER_ACCELERATED),
|
||||
[ ](SDL_Renderer* r) { SDL_DestroyRenderer(r); }
|
||||
);
|
||||
|
||||
if (!renderer) {
|
||||
SDL_Log("Failed to create renderer: %s", SDL_GetError( ));
|
||||
return false;
|
||||
}
|
||||
|
||||
gameRenderer = make_shared<Renderer>(renderer);
|
||||
gameBoard = make_shared<GameBoard>( );
|
||||
|
||||
bgm = std::shared_ptr<Mix_Music>(Mix_LoadMUS("/home/crylia/Dokumente/git/tetris_sdl/assets/bgm.mp3"), [ ](Mix_Music* m) {
|
||||
Mix_FreeMusic(m);
|
||||
});
|
||||
|
||||
if (!bgm) {
|
||||
SDL_Log("Failed to load background music: %s", Mix_GetError( ));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::run( ) {
|
||||
while (startSequence) {
|
||||
if (quit) return;
|
||||
inputHandler( );
|
||||
gameRenderer->renderStartScreen( );
|
||||
}
|
||||
|
||||
Mix_PlayMusic(bgm.get( ), -1);
|
||||
|
||||
lastUpdateTime = SDL_GetTicks( );
|
||||
while (!gameOver && !gameBoard->isCollision( )) {
|
||||
if (quit) return;
|
||||
inputHandler( );
|
||||
update( );
|
||||
render( );
|
||||
}
|
||||
gameOver = true;
|
||||
Mix_PauseMusic( );
|
||||
|
||||
while (gameOver) {
|
||||
if (quit) return;
|
||||
inputHandler( );
|
||||
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ));
|
||||
}
|
||||
}
|
||||
|
||||
void Game::inputHandler( ) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT) { SDL_Quit( ); } else if (event.type == SDL_KEYDOWN) {
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_LEFT:
|
||||
case SDLK_a:
|
||||
gameBoard->tryMoveCurrentTetromino(-1, 0);
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
case SDLK_d:
|
||||
gameBoard->tryMoveCurrentTetromino(1, 0);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_s:
|
||||
gameBoard->moveToBottom( );
|
||||
break;
|
||||
case SDLK_SPACE:
|
||||
gameBoard->tryRotateCurrentTetromino( );
|
||||
break;
|
||||
case SDLK_ESCAPE:
|
||||
break;
|
||||
case SDLK_g:
|
||||
if (startSequence)
|
||||
startSequence = false;
|
||||
break;
|
||||
case SDLK_r:
|
||||
if (isGameOver( ))
|
||||
restart( );
|
||||
break;
|
||||
case SDLK_q:
|
||||
SDL_Quit( );
|
||||
quit = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::update( ) {
|
||||
Uint32 currentTime = SDL_GetTicks( );
|
||||
Uint32 deltaTime = currentTime - lastUpdateTime;
|
||||
|
||||
if (deltaTime >= max(50, 1000 - (gameBoard->getLevel( ) * 100))) {
|
||||
gameBoard->update( );
|
||||
lastUpdateTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::render( ) {
|
||||
//Set default color to black
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 20, 20, 20, 255);
|
||||
SDL_RenderClear(renderer.get( ));
|
||||
|
||||
gameRenderer->renderBoard(*gameBoard);
|
||||
|
||||
SDL_RenderPresent(renderer.get( ));
|
||||
}
|
||||
|
||||
bool Game::isGameOver( ) { return gameOver; }
|
||||
void Game::setGameOver(bool value) { gameOver = value; }
|
||||
|
||||
void Game::restart( ) {
|
||||
gameOver = false;
|
||||
startSequence = true;
|
||||
gameBoard = make_shared<GameBoard>( );
|
||||
}
|
||||
|
||||
const bool Game::isGameQuit( )const { return quit; }
|
||||
46
src/Game.hpp
Normal file
46
src/Game.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
extern "C" {
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
}
|
||||
|
||||
#include "Renderer.hpp"
|
||||
#include "GameBoard.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Game {
|
||||
private:
|
||||
void update( );
|
||||
void render( );
|
||||
void inputHandler( );
|
||||
|
||||
unique_ptr<SDL_Window, void(*)(SDL_Window*)> window;
|
||||
shared_ptr<SDL_Renderer> renderer;
|
||||
|
||||
shared_ptr<GameBoard> gameBoard;
|
||||
shared_ptr<Renderer> gameRenderer;
|
||||
|
||||
shared_ptr<Mix_Music> bgm;
|
||||
|
||||
Uint32 lastUpdateTime = 0;
|
||||
int dropInterval = 1000;
|
||||
|
||||
bool gameOver;
|
||||
bool startSequence;
|
||||
bool quit;
|
||||
|
||||
public:
|
||||
Game( );
|
||||
|
||||
bool init(const char* title, int w, int h);
|
||||
void run( );
|
||||
void restart( );
|
||||
bool isGameOver( );
|
||||
void setGameOver(bool value);
|
||||
|
||||
const bool isGameQuit( ) const;
|
||||
};
|
||||
120
src/GameBoard.cpp
Normal file
120
src/GameBoard.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "GameBoard.hpp"
|
||||
#include <iostream>
|
||||
|
||||
GameBoard::GameBoard( )
|
||||
: lockedTetrominos(20, vector<int>(10, 0)), lockedColors(20, std::vector<SDL_Color>(10, { 0, 0, 0, 0 })), score(0), level(1), collision(false) {
|
||||
spawnNewTetromino( );
|
||||
}
|
||||
|
||||
bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) {
|
||||
currentTetromino->move(dx, dy);
|
||||
if (checkCollision(*currentTetromino)) {
|
||||
currentTetromino->move(-dx, -dy);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBoard::tryRotateCurrentTetromino( ) {
|
||||
currentTetromino->rotate(*this);
|
||||
if (checkCollision(*currentTetromino))
|
||||
for (int i = 0; i < 3; i++)
|
||||
currentTetromino->rotate(*this);
|
||||
}
|
||||
|
||||
bool GameBoard::checkCollision(const Tetromino& tetromino) const {
|
||||
const auto& shape = tetromino.getShape( );
|
||||
int x = tetromino.getX( ), y = tetromino.getY( );
|
||||
|
||||
for (int row = 0; row < shape.size( ); ++row)
|
||||
for (int col = 0; col < shape[row].size( ); ++col)
|
||||
if (shape[row][col] != 0) {
|
||||
int newX = x + col;
|
||||
int newY = y + row;
|
||||
|
||||
if (newX < 0 || newX >= width || newY >= height || (newY >= 0 && lockedTetrominos[newY][newX] != 0)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameBoard::lockTetromino( ) {
|
||||
const auto& shape = currentTetromino->getShape( );
|
||||
int x = currentTetromino->getX( ), y = currentTetromino->getY( );
|
||||
|
||||
for (int row = 0; row < shape.size( ); ++row)
|
||||
for (int col = 0; col < shape[row].size( ); ++col)
|
||||
if (shape[row][col] != 0) {
|
||||
int lockedTetrominosX = x + col;
|
||||
int lockedTetrominosY = y + row;
|
||||
if (lockedTetrominosY >= 0 && lockedTetrominosX < height) {
|
||||
lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = 1;
|
||||
lockedColors[lockedTetrominosY][lockedTetrominosX] = currentTetromino->getColor( );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameBoard::clearLines( ) {
|
||||
for (int row = 0; row < height; row++)
|
||||
if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) {return cell != 0;})) {
|
||||
lockedTetrominos.erase(lockedTetrominos.begin( ) + row);
|
||||
lockedTetrominos.insert(lockedTetrominos.begin( ), vector<int>(width, 0));
|
||||
score += 100;
|
||||
if ((score % 1000) == 0)
|
||||
level++;
|
||||
}
|
||||
}
|
||||
|
||||
void GameBoard::spawnNewTetromino( ) {
|
||||
random_device dev;
|
||||
mt19937 rng(dev( ));
|
||||
uniform_int_distribution<mt19937::result_type> dist6(0, static_cast<int>(TetrominoShape::COUNT) - 1);
|
||||
TetrominoShape shape = static_cast<TetrominoShape>(dist6(rng));
|
||||
currentTetromino = make_unique<Tetromino>(shape);
|
||||
currentTetromino->setTexture(shape);
|
||||
currentTetromino->move(width / 2 - 1, 0);
|
||||
|
||||
if (checkCollision(*currentTetromino))
|
||||
collision = true;
|
||||
}
|
||||
|
||||
void GameBoard::update( ) {
|
||||
if (!tryMoveCurrentTetromino(0, 1)) {
|
||||
lockTetromino( );
|
||||
clearLines( );
|
||||
spawnNewTetromino( );
|
||||
} else
|
||||
clearLines( );
|
||||
}
|
||||
|
||||
bool GameBoard::isValidPosition(const vector<vector<int>>& shape, int x, int y) const {
|
||||
for (int row = 0; row < shape.size( ); ++row)
|
||||
for (int col = 0; col < shape[row].size( ); ++col)
|
||||
if (shape[row][col]) {
|
||||
int newX = x + col;
|
||||
int newY = y + row;
|
||||
|
||||
if (newX < 0 || newX >= width || newY < 0 || newY >= height) return false;
|
||||
if (lockedTetrominos[newY][newX]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBoard::moveToBottom( ) {
|
||||
while (isValidPosition(currentTetromino->getShape( ), currentTetromino->getX( ), currentTetromino->getY( ) + 1)) currentTetromino->move(0, 1);
|
||||
}
|
||||
|
||||
const bool GameBoard::isCollision( ) const { return collision; }
|
||||
const int GameBoard::getScore( ) const { return score; }
|
||||
const int GameBoard::getLevel( ) const { return level; }
|
||||
|
||||
const vector<vector<int>>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; }
|
||||
const vector<vector<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
||||
const Tetromino& GameBoard::getCurrentTetromino( ) const { return *currentTetromino; }
|
||||
|
||||
void GameBoard::reset( ) {
|
||||
lockedTetrominos.clear( );
|
||||
lockedColors.clear( );
|
||||
score = 0;
|
||||
level = 1;
|
||||
}
|
||||
40
src/GameBoard.hpp
Normal file
40
src/GameBoard.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include "Tetromino.hpp"
|
||||
|
||||
class GameBoard {
|
||||
private:
|
||||
void spawnNewTetromino( );
|
||||
bool checkCollision(const Tetromino& tetromino) const;
|
||||
void lockTetromino( );
|
||||
void clearLines( );
|
||||
|
||||
vector<vector<int>> lockedTetrominos;
|
||||
vector<vector<SDL_Color>> lockedColors;
|
||||
unique_ptr<Tetromino> currentTetromino;
|
||||
const int width = 10;
|
||||
const int height = 20;
|
||||
bool collision;
|
||||
int score;
|
||||
int level;
|
||||
|
||||
public:
|
||||
GameBoard( );
|
||||
void update( );
|
||||
bool tryMoveCurrentTetromino(int dx, int dy);
|
||||
void tryRotateCurrentTetromino( );
|
||||
bool isValidPosition(const vector<vector<int>>& shape, int x, int y) const;
|
||||
void moveToBottom( );
|
||||
void reset( );
|
||||
|
||||
const bool isCollision( ) const;
|
||||
const int getScore( ) const;
|
||||
const int getLevel( ) const;
|
||||
|
||||
const vector<vector<int>>& getLockedTetrominos( ) const;
|
||||
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
||||
const Tetromino& getCurrentTetromino( ) const;
|
||||
};
|
||||
151
src/Renderer.cpp
Normal file
151
src/Renderer.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "Renderer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
Renderer::Renderer(shared_ptr<SDL_Renderer> renderer) : renderer(renderer), blockSize(30) {
|
||||
setRenderer(renderer);
|
||||
}
|
||||
|
||||
void Renderer::setRenderer(shared_ptr<SDL_Renderer> renderer) {
|
||||
this->renderer = renderer;
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::I);
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::J);
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::L);
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::S);
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::O);
|
||||
loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::Z);
|
||||
}
|
||||
|
||||
void Renderer::renderBoard(const GameBoard& gameBoard) {
|
||||
drawGrid(gameBoard);
|
||||
drawLockedBlocks(gameBoard);
|
||||
drawTetromino(gameBoard.getCurrentTetromino( ));
|
||||
drawScoreboard(gameBoard.getScore( ), gameBoard.getLevel( ));
|
||||
}
|
||||
|
||||
void Renderer::drawScoreboard(int score, int level) {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
||||
renderText(fmt::format("Score: {0}", score), 350, 100, 24, SDL_Color{ 200,128,200 });
|
||||
renderText(fmt::format("Level: {0}", level), 350, 200, 24, SDL_Color{ 128,200,200 });
|
||||
}
|
||||
|
||||
void Renderer::drawGrid(const GameBoard& board) {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 100, 100, 100, 255);
|
||||
|
||||
int w = board.getLockedTetrominos( )[0].size( ), h = board.getLockedTetrominos( ).size( );
|
||||
for (int x = 0; x <= w; ++x)
|
||||
SDL_RenderDrawLine(renderer.get( ), x * blockSize, 0, x * blockSize, h * blockSize);
|
||||
|
||||
for (int y = 0; y <= h; ++y)
|
||||
SDL_RenderDrawLine(renderer.get( ), 0, y * blockSize, w * blockSize, y * blockSize);
|
||||
}
|
||||
|
||||
void Renderer::drawLockedBlocks(const GameBoard& gameBoard) {
|
||||
const auto& lockedTetrominos = gameBoard.getLockedTetrominos( );
|
||||
const auto& lockedColors = gameBoard.getLockedColors( );
|
||||
|
||||
for (int row = 0; row < lockedTetrominos.size( ); ++row) {
|
||||
for (int col = 0; col < lockedTetrominos[row].size( ); ++col) {
|
||||
int blockType = lockedTetrominos[row][col];
|
||||
if (blockType != 0) {
|
||||
SDL_Rect block = { col * blockSize, row * blockSize, blockSize, blockSize };
|
||||
|
||||
SDL_SetTextureBlendMode(textures[TetrominoShape::I], SDL_BLENDMODE_BLEND);
|
||||
|
||||
SDL_Color color = lockedColors[row][col];
|
||||
if (textures[TetrominoShape::I]) {
|
||||
SDL_SetTextureColorMod(textures[TetrominoShape::I], color.r, color.g, color.b);
|
||||
renderTexture(TetrominoShape::I, block.x, block.y, block.w, block.h);
|
||||
} else {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), color.r, color.g, color.b, 255);
|
||||
SDL_RenderFillRect(renderer.get( ), &block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::drawTetromino(const Tetromino& tetromino) {
|
||||
const auto& shape = tetromino.getShape( );
|
||||
int x = tetromino.getX( ), y = tetromino.getY( );
|
||||
auto t = tetromino.getTexture( );
|
||||
|
||||
double angle = tetromino.getRotationAngle( );
|
||||
|
||||
SDL_SetTextureBlendMode(textures[t], SDL_BLENDMODE_BLEND);
|
||||
SDL_Color color = tetromino.getColor( );
|
||||
SDL_SetTextureColorMod(textures[t], color.r, color.g, color.b);
|
||||
|
||||
for (int row = 0; row < shape.size( ); ++row) {
|
||||
for (int col = 0; col < shape[row].size( ); ++col) {
|
||||
if (shape[row][col] != 0) {
|
||||
SDL_Rect destRect = { (x + col) * blockSize, (y + row) * blockSize, blockSize, blockSize };
|
||||
SDL_Point center = { blockSize / 2, blockSize / 2 };
|
||||
SDL_RenderCopyEx(renderer.get( ), textures[t], nullptr, &destRect, angle, ¢er, SDL_FLIP_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::loadTexture(const string& filePath, const TetrominoShape shape) {
|
||||
SDL_Texture* texture = IMG_LoadTexture(renderer.get( ), filePath.c_str( ));
|
||||
if (!texture) {
|
||||
SDL_Log("Failed to load Texture: %s\n%s", filePath, SDL_GetError( ));
|
||||
return false;
|
||||
}
|
||||
textures[shape] = texture;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::renderTexture(const TetrominoShape shape, int x, int y, int width, int height) {
|
||||
SDL_Rect destRect{ x,y,width,height };
|
||||
SDL_RenderCopy(renderer.get( ), textures[shape], nullptr, &destRect);
|
||||
}
|
||||
|
||||
void Renderer::renderStartScreen( ) {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer.get( ));
|
||||
|
||||
renderText("Tetris", 100, 100, 128, SDL_Color{ 200,200,200 });
|
||||
|
||||
SDL_RenderPresent(renderer.get( ));
|
||||
}
|
||||
|
||||
void Renderer::renderGameOver(int score, int level) {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 128);
|
||||
SDL_RenderClear(renderer.get( ));
|
||||
|
||||
renderText("Game Over", 100, 100, 128, SDL_Color{ 200, 200, 200 });
|
||||
renderText(fmt::format("Score: {0}", score), 100, 250, 64, SDL_Color{ 200, 128, 200 });
|
||||
renderText(fmt::format("Level: {0}", level), 100, 350, 64, SDL_Color{ 128, 200, 200 });
|
||||
|
||||
|
||||
SDL_RenderPresent(renderer.get( ));
|
||||
}
|
||||
|
||||
void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) {
|
||||
TTF_Font* font = TTF_OpenFont("/usr/share/fonts/TTF/OpenSans-Regular.ttf", size);
|
||||
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color);
|
||||
if (!surface) {
|
||||
SDL_Log("Failed to create surface: %s", TTF_GetError( ));
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer.get( ), surface);
|
||||
if (!texture) {
|
||||
SDL_Log("Failed to create texture from surface: %s", SDL_GetError( ));
|
||||
SDL_FreeSurface(surface);
|
||||
TTF_CloseFont(font);
|
||||
return;
|
||||
}
|
||||
|
||||
int width = surface->w;
|
||||
int height = surface->h;
|
||||
|
||||
SDL_Rect destRect = { x, y, width, height };
|
||||
|
||||
SDL_RenderCopy(renderer.get( ), texture, nullptr, &destRect);
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
SDL_DestroyTexture(texture);
|
||||
TTF_CloseFont(font);
|
||||
}
|
||||
37
src/Renderer.hpp
Normal file
37
src/Renderer.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
extern "C" {
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
}
|
||||
|
||||
#include "GameBoard.hpp"
|
||||
|
||||
class Renderer {
|
||||
private:
|
||||
void drawGrid(const GameBoard& gameBoard);
|
||||
void drawLockedBlocks(const GameBoard& gameBoard);
|
||||
void drawTetromino(const Tetromino& tetromino);
|
||||
void drawScoreboard(int score, int level);
|
||||
|
||||
shared_ptr<SDL_Renderer> renderer;
|
||||
int blockSize;
|
||||
|
||||
unordered_map<TetrominoShape, SDL_Texture*> textures;
|
||||
public:
|
||||
Renderer(shared_ptr<SDL_Renderer> renderer);
|
||||
|
||||
void setRenderer(shared_ptr<SDL_Renderer> renderer);
|
||||
void renderBoard(const GameBoard& gb);
|
||||
|
||||
bool loadTexture(const string& filePath, const TetrominoShape shape);
|
||||
void renderTexture(const TetrominoShape shape, int x, int y, int width, int height);
|
||||
void renderStartScreen( );
|
||||
void renderGameOver(int score, int level);
|
||||
void renderText(string text, int x, int y, int size, SDL_Color color);
|
||||
};
|
||||
112
src/Tetromino.cpp
Normal file
112
src/Tetromino.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "Tetromino.hpp"
|
||||
#include "GameBoard.hpp"
|
||||
|
||||
Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0) {
|
||||
initializeShape(shape);
|
||||
currentRotationState = 1;
|
||||
|
||||
color.a = 255;
|
||||
color.r = 0;
|
||||
color.g = 0;
|
||||
color.b = 0;
|
||||
|
||||
random_device dev;
|
||||
mt19937 rng(dev( ));
|
||||
uniform_int_distribution<mt19937::result_type> dist6(0, 5);
|
||||
|
||||
switch (dist6(rng)) {
|
||||
case 0:
|
||||
color.r = 255;
|
||||
break;
|
||||
case 1:
|
||||
color.g = 255;
|
||||
break;
|
||||
case 2:
|
||||
color.b = 255;
|
||||
break;
|
||||
case 3:
|
||||
color.r = 255;
|
||||
color.g = 255;
|
||||
break;
|
||||
case 4:
|
||||
color.g = 255;
|
||||
color.b = 255;
|
||||
break;
|
||||
case 5:
|
||||
color.b = 255;
|
||||
color.r = 255;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Tetromino::initializeShape(TetrominoShape s) {
|
||||
switch (s) {
|
||||
case TetrominoShape::I:
|
||||
shape = { {1, 1, 1, 1} };
|
||||
break;
|
||||
case TetrominoShape::O:
|
||||
shape = { {1, 1}, {1, 1} };
|
||||
break;
|
||||
case TetrominoShape::S:
|
||||
shape = { {0, 1, 1}, {1, 1, 0} };
|
||||
break;
|
||||
case TetrominoShape::Z:
|
||||
shape = { {1, 1, 0}, {0, 1, 1} };
|
||||
break;
|
||||
case TetrominoShape::J:
|
||||
shape = { {1, 0, 0}, {1, 1, 1} };
|
||||
break;
|
||||
case TetrominoShape::L:
|
||||
shape = { {0, 0, 1}, {1, 1, 1} };
|
||||
break;
|
||||
default:
|
||||
shape = { {0} };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Tetromino::rotate(GameBoard& gameBoard) {
|
||||
vector<vector<int>> rotated(shape[0].size( ), vector<int>(shape.size( )));
|
||||
for (int row = 0; row < shape.size( ); ++row)
|
||||
for (int col = 0; col < shape[0].size( ); ++col)
|
||||
rotated[col][shape.size( ) - 1 - row] = shape[row][col];
|
||||
|
||||
if (gameBoard.isValidPosition(rotated, x, y)) {
|
||||
shape = rotated;
|
||||
currentRotationState = (currentRotationState + 1) % 4;
|
||||
} else {
|
||||
int dx = x - 1;
|
||||
while (!gameBoard.isValidPosition(rotated, dx, y)) {
|
||||
//If the shape before rotation wont have a valid position then rotation is not possible and we abort
|
||||
if (!gameBoard.isValidPosition(shape, dx, y)) {
|
||||
shape = rotated;
|
||||
return;
|
||||
}
|
||||
dx--;
|
||||
}
|
||||
x = dx;
|
||||
shape = rotated;
|
||||
currentRotationState = (currentRotationState + 1) % 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Tetromino::move(int dx, int dy) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
double Tetromino::getRotationAngle( ) const {
|
||||
return currentRotationState * 90.0;
|
||||
}
|
||||
|
||||
const vector<vector<int>>& Tetromino::getShape( ) const { return shape; }
|
||||
|
||||
int Tetromino::getX( ) const { return x; }
|
||||
int Tetromino::getY( ) const { return y; }
|
||||
|
||||
void Tetromino::setTexture(const TetrominoShape shape) { this->textureShape = shape; }
|
||||
const TetrominoShape Tetromino::getTexture( ) const { return textureShape; }
|
||||
|
||||
SDL_Color Tetromino::getColor( ) const { return color; }
|
||||
51
src/Tetromino.hpp
Normal file
51
src/Tetromino.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <SDL2/SDL.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <random>
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum class TetrominoShape {
|
||||
L,
|
||||
I,
|
||||
O,
|
||||
S,
|
||||
Z,
|
||||
J,
|
||||
COUNT //Used to know the ammount of shapes
|
||||
};
|
||||
|
||||
class GameBoard;
|
||||
|
||||
class Tetromino {
|
||||
private:
|
||||
void initializeShape(TetrominoShape shape);
|
||||
vector<vector<int>> shape;
|
||||
|
||||
int x, y;
|
||||
|
||||
int currentRotationState;
|
||||
|
||||
TetrominoShape textureShape;
|
||||
|
||||
SDL_Color color;
|
||||
public:
|
||||
Tetromino(TetrominoShape shape);
|
||||
|
||||
void rotate(GameBoard& gameBoard);
|
||||
void move(int dx, int dy);
|
||||
double getRotationAngle( ) const;
|
||||
|
||||
const vector<vector<int>>& getShape( ) const;
|
||||
int getX( ) const;
|
||||
int getY( ) const;
|
||||
|
||||
void setTexture(const TetrominoShape textureName);
|
||||
const TetrominoShape getTexture( ) const;
|
||||
SDL_Color getColor( ) const;
|
||||
};
|
||||
46
src/main.cpp
Normal file
46
src/main.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
|
||||
extern "C" {
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
}
|
||||
|
||||
#include "Game.hpp"
|
||||
|
||||
int main( ) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
|
||||
SDL_Log("Couldn't init SDL: %s", SDL_GetError( ));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (IMG_Init(IMG_INIT_PNG) == 0) {
|
||||
std::cerr << "Failed to initialize SDL_image: " << IMG_GetError( ) << std::endl;
|
||||
SDL_Quit( );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (TTF_Init( ) == -1) {
|
||||
std::cerr << "Failed to initialize SDL_ttf: " << TTF_GetError( ) << std::endl;
|
||||
SDL_Quit( );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
|
||||
SDL_Log("SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError( ));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Game game;
|
||||
if (!game.init("Tetris", 800, 600)) {
|
||||
SDL_Log("Failed to init game");
|
||||
SDL_Quit( );
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!game.isGameQuit( ))
|
||||
game.run( );
|
||||
|
||||
SDL_Quit( );
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user