remove old code and move rewrite to src proper, added new textures
@@ -179,6 +179,8 @@ void M_B_Set_Next_Block_Type(void) {
|
|||||||
_next_block_type = M_B_Get_Random_Block_Type();
|
_next_block_type = M_B_Get_Random_Block_Type();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btype_t M_B_Get_Next_Block_Type(void) { return _next_block_type; }
|
||||||
|
|
||||||
btype_t M_B_Get_Random_Block_Type(void) {
|
btype_t M_B_Get_Random_Block_Type(void) {
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
return (btype_t)(rand() % 7) + 1;
|
return (btype_t)(rand() % 7) + 1;
|
||||||
@@ -10,6 +10,7 @@ void M_B_Destroy_Blocks(void);
|
|||||||
void M_B_Reset_Blocks_Updated(void);
|
void M_B_Reset_Blocks_Updated(void);
|
||||||
void M_B_Spawn_Blocks(void);
|
void M_B_Spawn_Blocks(void);
|
||||||
void M_B_Set_Next_Block_Type(void);
|
void M_B_Set_Next_Block_Type(void);
|
||||||
|
btype_t M_B_Get_Next_Block_Type(void);
|
||||||
btype_t M_B_Get_Random_Block_Type(void);
|
btype_t M_B_Get_Random_Block_Type(void);
|
||||||
int M_B_Try_Spawn_Blocks_With_Offset(point_offset_t *offsets);
|
int M_B_Try_Spawn_Blocks_With_Offset(point_offset_t *offsets);
|
||||||
block_t *M_B_Get_Block_At_Point(point_t point);
|
block_t *M_B_Get_Block_At_Point(point_t point);
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
CC=clang
|
|
||||||
CFLAGS=-Wall -Wextra -std=c89 -pedantic -include raylib.h
|
|
||||||
LIBS=-lraylib -lGL -lm -lpthread -ldl -lrt -lX11
|
|
||||||
|
|
||||||
tetris-clone: main.c manager.c renderer.c app.c
|
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f ./tetris-clone
|
|
||||||
rm -f ./*.o
|
|
||||||
rm -f ./*.gch
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "raylib.h"
|
|
||||||
#include "renderer.h"
|
|
||||||
#include "tetromino.h"
|
|
||||||
|
|
||||||
#define TARGET_FPS 60
|
|
||||||
|
|
||||||
int _a_tick_rate = 0;
|
|
||||||
|
|
||||||
void a_run(int tick_rate) {
|
|
||||||
_a_initialize(tick_rate);
|
|
||||||
_a_loop();
|
|
||||||
_a_cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_initialize(int tick_rate) {
|
|
||||||
_a_initialize_raylib();
|
|
||||||
m_initialize();
|
|
||||||
r_initialize();
|
|
||||||
_a_gameboard_clear();
|
|
||||||
_a_tick_rate = tick_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_initialize_raylib(void) {
|
|
||||||
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Tetris Clone");
|
|
||||||
SetTargetFPS(TARGET_FPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_gameboard_clear(void) {
|
|
||||||
m_blocks_set_empty();
|
|
||||||
int number_blocks_updated = 0;
|
|
||||||
struct TetrominoBlock **blocks_updated =
|
|
||||||
m_blocks_get_updated(&number_blocks_updated);
|
|
||||||
r_render_blocks(blocks_updated, number_blocks_updated);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_loop(void) {
|
|
||||||
int tick_rate_counter = 0;
|
|
||||||
while (!WindowShouldClose()) {
|
|
||||||
if (m_tetromino_can_spawn()) {
|
|
||||||
_a_input_process();
|
|
||||||
if (tick_rate_counter == 0) {
|
|
||||||
m_update();
|
|
||||||
tick_rate_counter = _a_tick_rate;
|
|
||||||
}
|
|
||||||
int number_blocks_updated = 0;
|
|
||||||
struct TetrominoBlock **blocks_updated =
|
|
||||||
m_blocks_get_updated(&number_blocks_updated);
|
|
||||||
r_render_blocks(blocks_updated, number_blocks_updated);
|
|
||||||
tick_rate_counter--;
|
|
||||||
} else {
|
|
||||||
r_render_game_over(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_input_process(void) {
|
|
||||||
if (IsKeyPressed(KEY_SPACE)) {
|
|
||||||
m_request_falling_tetromino_rotate();
|
|
||||||
}
|
|
||||||
if (IsKeyDown(KEY_A)) {
|
|
||||||
m_request_falling_tetromino_move_left();
|
|
||||||
} else if (IsKeyDown(KEY_D)) {
|
|
||||||
m_request_falling_tetromino_move_right();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _a_cleanup() {
|
|
||||||
m_deactivate();
|
|
||||||
CloseWindow();
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef TETRIS_CLONE_APP_H_
|
|
||||||
#define TETRIS_CLONE_APP_H_
|
|
||||||
|
|
||||||
void a_run(int tick_rate);
|
|
||||||
|
|
||||||
void _a_initialize(int tick_rate);
|
|
||||||
void _a_initialize_raylib(void);
|
|
||||||
void _a_gameboard_clear(void);
|
|
||||||
void _a_loop(void);
|
|
||||||
void _a_input_process(void);
|
|
||||||
void _a_cleanup(void);
|
|
||||||
|
|
||||||
#endif // TETRIS_CLONE_APP_H_
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
// Not great code if I were to need multiple options, but I just need to be
|
|
||||||
// able to debug
|
|
||||||
if (argc > 1)
|
|
||||||
{
|
|
||||||
if (strcmp(argv[1], "--speed") == 0)
|
|
||||||
{
|
|
||||||
a_run(atoi(argv[2]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
a_run(50);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
#include <stdlib.h> //srand,rand,
|
|
||||||
#include <time.h> //time
|
|
||||||
#define __USE_MISC
|
|
||||||
#include <math.h> //cos,sin
|
|
||||||
|
|
||||||
#include "manager.h"
|
|
||||||
|
|
||||||
#define FIELD_WIDTH 10
|
|
||||||
#define FIELD_HEIGHT 20
|
|
||||||
#define BLOCKS_WITHIN_A_TETROMINO 4
|
|
||||||
|
|
||||||
// should probably call these offsets
|
|
||||||
struct Offset {
|
|
||||||
int xOffset;
|
|
||||||
int yOffset;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Point {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Tetromino {
|
|
||||||
enum TetrominoType type;
|
|
||||||
struct TetrominoBlock *blocks[BLOCKS_WITHIN_A_TETROMINO];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetrominoBlock *_m_block_at_point(struct Point point);
|
|
||||||
void _m_blocks_updated_register_block(struct TetrominoBlock *pTetrominoBlock);
|
|
||||||
void _m_blocks_alloc(void);
|
|
||||||
void _m_blocks_updated_erase(void);
|
|
||||||
void _m_blocks_spawn_at_offset_from_spawn_point(enum TetrominoType type,
|
|
||||||
struct Offset *offsets);
|
|
||||||
void _m_tetromino_spawn(void);
|
|
||||||
enum TetrominoType _m_tetromino_type_random(void);
|
|
||||||
void _m_block_set_type(struct TetrominoBlock *pTetrominoBlock,
|
|
||||||
enum TetrominoType type);
|
|
||||||
void _m_falling_tetromino_rotate(void);
|
|
||||||
bool _m_falling_tetromino_can_move_right(void);
|
|
||||||
void _m_falling_tetromino_translate(struct Offset offset);
|
|
||||||
bool _m_point_intersects_static_block(struct Point point);
|
|
||||||
bool _m_block_can_fall(struct TetrominoBlock block);
|
|
||||||
bool _m_falling_tetromino_can_fall(void);
|
|
||||||
void _m_falling_tetromino_fall(void);
|
|
||||||
bool _m_block_can_move_left(struct TetrominoBlock block);
|
|
||||||
bool _m_falling_tetromino_can_move_left(void);
|
|
||||||
bool _m_block_can_move_right(struct TetrominoBlock block);
|
|
||||||
|
|
||||||
// To find block (x,y) index = (x % FIELD_WIDTH) + (y * FIELD_WIDTH)
|
|
||||||
// Height doesn't matter but height is just the factor of how tall the board
|
|
||||||
// is. And in the end each row is just the next representation of width *
|
|
||||||
// units, height just determines row count. Doesn't factor into determining
|
|
||||||
// X,Y
|
|
||||||
struct TetrominoBlock *_blocks[FIELD_HEIGHT * FIELD_WIDTH];
|
|
||||||
struct TetrominoBlock *_blocks_updated[FIELD_HEIGHT * FIELD_WIDTH] = {NULL};
|
|
||||||
int _blocks_updated_length = 0;
|
|
||||||
bool _should_spawn_tetromino;
|
|
||||||
struct Tetromino fallingTetromino;
|
|
||||||
const char kSpawnX = FIELD_WIDTH / 2 - 1;
|
|
||||||
const struct Offset kShiftDownOffset = {.xOffset = 0, .yOffset = 1};
|
|
||||||
const struct Offset kShiftLeftOffset = {.xOffset = -1, .yOffset = 0};
|
|
||||||
const struct Offset kShiftRightOffset = {.xOffset = 1, .yOffset = 0};
|
|
||||||
|
|
||||||
bool _should_tetromino_move_left = false;
|
|
||||||
bool _should_tetromino_move_right = false;
|
|
||||||
bool _should_tetromino_rotate = false;
|
|
||||||
bool _tetromino_can_spawn = true;
|
|
||||||
|
|
||||||
void m_initialize(void) {
|
|
||||||
_m_blocks_alloc();
|
|
||||||
_should_spawn_tetromino = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_update(void) {
|
|
||||||
_m_blocks_updated_erase();
|
|
||||||
if (_should_spawn_tetromino) {
|
|
||||||
_should_tetromino_move_left = false;
|
|
||||||
_should_tetromino_move_right = false;
|
|
||||||
_m_tetromino_spawn();
|
|
||||||
_should_spawn_tetromino = false;
|
|
||||||
} else {
|
|
||||||
// TODO move left and right refactor naming
|
|
||||||
|
|
||||||
if (_should_tetromino_rotate) {
|
|
||||||
_m_falling_tetromino_rotate();
|
|
||||||
_should_tetromino_rotate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_should_tetromino_move_right && _m_falling_tetromino_can_move_right()) {
|
|
||||||
_m_falling_tetromino_translate(kShiftRightOffset);
|
|
||||||
_should_tetromino_move_right = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_should_tetromino_move_left && _m_falling_tetromino_can_move_left()) {
|
|
||||||
_m_falling_tetromino_translate(kShiftLeftOffset);
|
|
||||||
_should_tetromino_move_left = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_m_falling_tetromino_fall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_blocks_set_empty(void) {
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT * FIELD_WIDTH; i++) {
|
|
||||||
_m_block_set_type(_blocks[i], TT_EMPTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool m_tetromino_can_spawn(void) { return _tetromino_can_spawn; }
|
|
||||||
|
|
||||||
void m_request_falling_tetromino_rotate(void) {
|
|
||||||
_should_tetromino_rotate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_request_falling_tetromino_move_left(void) {
|
|
||||||
_should_tetromino_move_left = true;
|
|
||||||
_should_tetromino_move_right = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_request_falling_tetromino_move_right(void) {
|
|
||||||
_should_tetromino_move_right = true;
|
|
||||||
_should_tetromino_move_left = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TetrominoBlock **m_blocks_get_updated(int *numberOfUpdatedBlocks) {
|
|
||||||
*numberOfUpdatedBlocks = _blocks_updated_length;
|
|
||||||
|
|
||||||
return &_blocks_updated[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_deactivate(void) {
|
|
||||||
for (int i = 0; i < sizeof(_blocks) / sizeof(struct TetrominoBlock *); i++) {
|
|
||||||
free(_blocks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_blocks_alloc(void) {
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT * FIELD_WIDTH; i++) {
|
|
||||||
struct TetrominoBlock *block = calloc(1, sizeof(struct TetrominoBlock));
|
|
||||||
block->x = i % FIELD_WIDTH;
|
|
||||||
block->y = i / FIELD_WIDTH;
|
|
||||||
|
|
||||||
_blocks[i] = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_blocks_updated_erase() { _blocks_updated_length = 0; }
|
|
||||||
|
|
||||||
void _m_tetromino_spawn() {
|
|
||||||
enum TetrominoType type = _m_tetromino_type_random();
|
|
||||||
|
|
||||||
struct Offset offsets[BLOCKS_WITHIN_A_TETROMINO];
|
|
||||||
|
|
||||||
if (type == TT_O) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 1;
|
|
||||||
offsets[1].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 0;
|
|
||||||
offsets[2].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 1;
|
|
||||||
offsets[3].yOffset = 1;
|
|
||||||
} else if (type == TT_J) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 0;
|
|
||||||
offsets[1].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 1;
|
|
||||||
offsets[2].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 2;
|
|
||||||
offsets[3].yOffset = 1;
|
|
||||||
} else if (type == TT_L) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 1;
|
|
||||||
offsets[1].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 2;
|
|
||||||
offsets[2].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 2;
|
|
||||||
offsets[3].yOffset = 0;
|
|
||||||
} else if (type == TT_I) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 0;
|
|
||||||
offsets[1].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 0;
|
|
||||||
offsets[2].yOffset = 2;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 0;
|
|
||||||
offsets[3].yOffset = 3;
|
|
||||||
} else if (type == TT_S) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 1;
|
|
||||||
offsets[1].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 1;
|
|
||||||
offsets[2].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 2;
|
|
||||||
offsets[3].yOffset = 0;
|
|
||||||
} else if (type == TT_Z) {
|
|
||||||
offsets[0].xOffset = 0;
|
|
||||||
offsets[0].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 1;
|
|
||||||
offsets[1].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 1;
|
|
||||||
offsets[2].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 2;
|
|
||||||
offsets[3].yOffset = 1;
|
|
||||||
} else if (type == TT_T) {
|
|
||||||
offsets[0].xOffset = 1;
|
|
||||||
offsets[0].yOffset = 0;
|
|
||||||
|
|
||||||
offsets[1].xOffset = 0;
|
|
||||||
offsets[1].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[2].xOffset = 1;
|
|
||||||
offsets[2].yOffset = 1;
|
|
||||||
|
|
||||||
offsets[3].xOffset = 2;
|
|
||||||
offsets[3].yOffset = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_m_blocks_spawn_at_offset_from_spawn_point(type, offsets);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TetrominoType _m_tetromino_type_random(void) {
|
|
||||||
srand(time(NULL));
|
|
||||||
return (enum TetrominoType)rand() % 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_blocks_spawn_at_offset_from_spawn_point(enum TetrominoType type,
|
|
||||||
struct Offset *offsets) {
|
|
||||||
fallingTetromino.type = type;
|
|
||||||
int index = 0;
|
|
||||||
while (index < BLOCKS_WITHIN_A_TETROMINO) {
|
|
||||||
struct Point spawnPoint;
|
|
||||||
spawnPoint.x = kSpawnX + offsets->xOffset;
|
|
||||||
spawnPoint.y = offsets->yOffset;
|
|
||||||
|
|
||||||
struct TetrominoBlock *spawnBlock = _m_block_at_point(spawnPoint);
|
|
||||||
if (spawnBlock->type != TT_EMPTY) {
|
|
||||||
_tetromino_can_spawn = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_m_block_set_type(spawnBlock, type);
|
|
||||||
|
|
||||||
// I'm not sure if this makes more sense to be here or return a list of
|
|
||||||
// spawnedBlocks to be handled in Parent function
|
|
||||||
fallingTetromino.blocks[index] = spawnBlock;
|
|
||||||
|
|
||||||
index++;
|
|
||||||
offsets++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TetrominoBlock *_m_block_at_point(struct Point point) {
|
|
||||||
return _blocks[(point.x % FIELD_WIDTH) + (point.y * FIELD_WIDTH)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_block_set_type(struct TetrominoBlock *pTetrominoBlock,
|
|
||||||
enum TetrominoType type) {
|
|
||||||
pTetrominoBlock->type = type;
|
|
||||||
_m_blocks_updated_register_block(pTetrominoBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_blocks_updated_register_block(struct TetrominoBlock *pTetrominoBlock) {
|
|
||||||
if (_blocks_updated_length < FIELD_HEIGHT * FIELD_WIDTH) {
|
|
||||||
_blocks_updated[_blocks_updated_length] = pTetrominoBlock;
|
|
||||||
_blocks_updated_length++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_falling_tetromino_rotate(void) {
|
|
||||||
if (fallingTetromino.type != TT_O) {
|
|
||||||
// i'm not a fan of this implementation
|
|
||||||
// check this out instead and re-write
|
|
||||||
// https://stackoverflow.com/questions/42519/how-do-you-rotate-a-two-dimensional-array?page=1&tab=scoredesc#tab-top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_falling_tetromino_can_move_right(void) {
|
|
||||||
bool result = true;
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
if (!_m_block_can_move_right(*(fallingTetromino.blocks[i]))) {
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_falling_tetromino_translate(struct Offset offset) {
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
_m_block_set_type(fallingTetromino.blocks[i], TT_EMPTY);
|
|
||||||
_m_blocks_updated_register_block(fallingTetromino.blocks[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
struct Point updatedPoint = {
|
|
||||||
.x = fallingTetromino.blocks[i]->x + offset.xOffset,
|
|
||||||
.y = fallingTetromino.blocks[i]->y + offset.yOffset};
|
|
||||||
fallingTetromino.blocks[i] = _m_block_at_point(updatedPoint);
|
|
||||||
|
|
||||||
_m_block_set_type(fallingTetromino.blocks[i], fallingTetromino.type);
|
|
||||||
_m_blocks_updated_register_block(fallingTetromino.blocks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_point_intersects_static_block(struct Point point) {
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
struct TetrominoBlock *blockAtPoint = _m_block_at_point(point);
|
|
||||||
|
|
||||||
if (blockAtPoint->type != TT_EMPTY) {
|
|
||||||
result = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
if (fallingTetromino.blocks[i] == blockAtPoint) {
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_block_can_fall(struct TetrominoBlock block) {
|
|
||||||
struct Point newBlockPoint = {.x = block.x, .y = block.y + 1};
|
|
||||||
if (newBlockPoint.y >= FIELD_HEIGHT) {
|
|
||||||
return false;
|
|
||||||
} else if (_m_point_intersects_static_block(newBlockPoint)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_falling_tetromino_can_fall(void) {
|
|
||||||
bool result = true;
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
if (!_m_block_can_fall(*(fallingTetromino.blocks[i]))) {
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _m_falling_tetromino_fall(void) {
|
|
||||||
if (_m_falling_tetromino_can_fall()) {
|
|
||||||
_m_falling_tetromino_translate(kShiftDownOffset);
|
|
||||||
} else {
|
|
||||||
_should_spawn_tetromino = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_block_can_move_left(struct TetrominoBlock block) {
|
|
||||||
struct Point newBlockPoint = {.x = block.x - 1, .y = block.y};
|
|
||||||
if (newBlockPoint.x < 0) {
|
|
||||||
return false;
|
|
||||||
} else if (_m_point_intersects_static_block(newBlockPoint)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_falling_tetromino_can_move_left(void) {
|
|
||||||
bool result = true;
|
|
||||||
for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) {
|
|
||||||
if (!_m_block_can_move_left(*(fallingTetromino.blocks[i]))) {
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _m_block_can_move_right(struct TetrominoBlock block) {
|
|
||||||
struct Point newBlockPoint = {.x = block.x + 1, .y = block.y};
|
|
||||||
if (newBlockPoint.x >= FIELD_WIDTH) {
|
|
||||||
return false;
|
|
||||||
} else if (_m_point_intersects_static_block(newBlockPoint)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef TETRIS_CLONE_MANAGER_H_
|
|
||||||
#define TETRIS_CLONE_MANAGER_H_
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "tetromino.h"
|
|
||||||
void m_initialize(void);
|
|
||||||
void m_update(void);
|
|
||||||
void m_blocks_set_empty(void);
|
|
||||||
bool m_tetromino_can_spawn(void); // Bad name + Doesn't follow naming
|
|
||||||
void m_request_falling_tetromino_rotate(void);
|
|
||||||
void m_request_falling_tetromino_move_left(void);
|
|
||||||
void m_request_falling_tetromino_move_right(void);
|
|
||||||
struct TetrominoBlock **m_blocks_get_updated(int *lengthOfBlocksUpdated);
|
|
||||||
void m_deactivate(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "renderer.h"
|
|
||||||
|
|
||||||
#define RES_PNG_FILE(name) "resources/" #name ".png"
|
|
||||||
#define TEXTURE_SIZE 32
|
|
||||||
|
|
||||||
Texture2D _yellow_texture;
|
|
||||||
Texture2D _blue_texture;
|
|
||||||
Texture2D _light_blue_texture;
|
|
||||||
Texture2D _red_texture;
|
|
||||||
Texture2D _purple_texture;
|
|
||||||
Texture2D _green_texture;
|
|
||||||
Texture2D _orange_texture;
|
|
||||||
Texture2D _empty_texture;
|
|
||||||
|
|
||||||
void r_initialize(void) {
|
|
||||||
_yellow_texture = LoadTexture(RES_PNG_FILE(yellow));
|
|
||||||
_blue_texture = LoadTexture(RES_PNG_FILE(blue));
|
|
||||||
_light_blue_texture = LoadTexture(RES_PNG_FILE(light - blue));
|
|
||||||
_red_texture = LoadTexture(RES_PNG_FILE(red));
|
|
||||||
_purple_texture = LoadTexture(RES_PNG_FILE(purple));
|
|
||||||
_green_texture = LoadTexture(RES_PNG_FILE(green));
|
|
||||||
_orange_texture = LoadTexture(RES_PNG_FILE(orange));
|
|
||||||
_empty_texture = LoadTexture(RES_PNG_FILE(black));
|
|
||||||
}
|
|
||||||
|
|
||||||
void r_render_blocks(struct TetrominoBlock **blocks, int length) {
|
|
||||||
BeginDrawing();
|
|
||||||
while (length--) {
|
|
||||||
_r_render_block(*(*blocks));
|
|
||||||
blocks++;
|
|
||||||
}
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void r_render_game_over(int score) {
|
|
||||||
BeginDrawing();
|
|
||||||
ClearBackground(BLACK);
|
|
||||||
DrawText("Game Over", SCREEN_WIDTH / 2 - MeasureText("Game Over", 24) / 2,
|
|
||||||
200, 24, RED);
|
|
||||||
char *score_message = TextFormat("Score: %d", score);
|
|
||||||
DrawText(score_message, SCREEN_WIDTH / 2 - MeasureText(score_message, 18) / 2,
|
|
||||||
232, 18, RED);
|
|
||||||
// Draw a play again button
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _r_render_block(struct TetrominoBlock blockToRender) {
|
|
||||||
Texture2D *textureToRender =
|
|
||||||
_r_get_texture_by_tetromino_type(blockToRender.type);
|
|
||||||
DrawTexture(*textureToRender, blockToRender.x * TEXTURE_SIZE,
|
|
||||||
blockToRender.y * TEXTURE_SIZE, WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture2D *_r_get_texture_by_tetromino_type(enum TetrominoType type) {
|
|
||||||
switch (type) {
|
|
||||||
case TT_O:
|
|
||||||
return &_yellow_texture;
|
|
||||||
case TT_I:
|
|
||||||
return &_light_blue_texture;
|
|
||||||
case TT_T:
|
|
||||||
return &_purple_texture;
|
|
||||||
case TT_J:
|
|
||||||
return &_blue_texture;
|
|
||||||
case TT_L:
|
|
||||||
return &_orange_texture;
|
|
||||||
case TT_S:
|
|
||||||
return &_green_texture;
|
|
||||||
case TT_Z:
|
|
||||||
return &_red_texture;
|
|
||||||
default:
|
|
||||||
return &_empty_texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef TETRIS_CLONE_RENDERER_H_
|
|
||||||
#define TETRIS_CLONE_RENDERER_H_
|
|
||||||
#include "raylib.h"
|
|
||||||
|
|
||||||
#include "tetromino.h"
|
|
||||||
|
|
||||||
#define SCREEN_HEIGHT 640
|
|
||||||
#define SCREEN_WIDTH 320
|
|
||||||
|
|
||||||
void r_initialize(void);
|
|
||||||
void r_render_blocks(struct TetrominoBlock **blocks, int length);
|
|
||||||
void r_render_game_over(int score);
|
|
||||||
|
|
||||||
void _r_render_block(struct TetrominoBlock blockToRender);
|
|
||||||
Texture2D *_r_get_texture_by_tetromino_type(enum TetrominoType type);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
Before Width: | Height: | Size: 881 B |
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 897 B |
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 899 B |
|
Before Width: | Height: | Size: 901 B |
@@ -1,12 +0,0 @@
|
|||||||
#ifndef TETRIS_CLONE_TETROMINO_H_
|
|
||||||
#define TETRIS_CLONE_TETROMINO_H_
|
|
||||||
|
|
||||||
enum TetrominoType { TT_I, TT_J, TT_L, TT_O, TT_S, TT_T, TT_Z, TT_EMPTY };
|
|
||||||
|
|
||||||
struct TetrominoBlock {
|
|
||||||
unsigned int x;
|
|
||||||
unsigned int y;
|
|
||||||
enum TetrominoType type;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -40,6 +40,7 @@ void R_Draw_Block(block_t block) {
|
|||||||
block.point.y * TEXTURE_SIZE, SC_White);
|
block.point.y * TEXTURE_SIZE, SC_White);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Rename to match style */
|
||||||
Texture2D *__R_Get_Texture_By_Block_Type(btype_t block_type) {
|
Texture2D *__R_Get_Texture_By_Block_Type(btype_t block_type) {
|
||||||
switch (block_type) {
|
switch (block_type) {
|
||||||
case bt_O:
|
case bt_O:
|
||||||
80
src/r_ui.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include "r_ui.h"
|
||||||
|
#include "m_block.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "sc_def.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define UI_PADDING 16
|
||||||
|
|
||||||
|
Texture2D i_texture;
|
||||||
|
Texture2D j_texture;
|
||||||
|
Texture2D l_texture;
|
||||||
|
Texture2D o_texture;
|
||||||
|
Texture2D s_texture;
|
||||||
|
Texture2D t_texture;
|
||||||
|
Texture2D z_texture;
|
||||||
|
|
||||||
|
void R_UI_Load_Textures(void) {
|
||||||
|
i_texture = LoadTexture("./resources/tetromino_I.png");
|
||||||
|
j_texture = LoadTexture("./resources/tetromino_J.png");
|
||||||
|
l_texture = LoadTexture("./resources/tetromino_L.png");
|
||||||
|
o_texture = LoadTexture("./resources/tetromino_O.png");
|
||||||
|
s_texture = LoadTexture("./resources/tetromino_S.png");
|
||||||
|
t_texture = LoadTexture("./resources/tetromino_T.png");
|
||||||
|
z_texture = LoadTexture("./resources/tetromino_Z.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_UI_Draw_Ui(void) {
|
||||||
|
int up_next_box_size;
|
||||||
|
btype_t next_block_type = M_B_Get_Next_Block_Type();
|
||||||
|
Texture2D *next_tetromino_texture =
|
||||||
|
R_UI_Get_Texture_By_Block_Type(next_block_type);
|
||||||
|
BeginDrawing();
|
||||||
|
DrawRectangle(SC_GAME_WIDTH, 0, SC_UI_WIDTH, SC_HEIGHT, SC_Gray);
|
||||||
|
up_next_box_size = SC_UI_WIDTH - UI_PADDING * 3;
|
||||||
|
DrawRectangle(SC_GAME_WIDTH + UI_PADDING, UI_PADDING, up_next_box_size,
|
||||||
|
up_next_box_size, SC_Black);
|
||||||
|
if (next_tetromino_texture) {
|
||||||
|
DrawTexture(*next_tetromino_texture,
|
||||||
|
(SC_GAME_WIDTH + UI_PADDING + up_next_box_size / 2) -
|
||||||
|
next_tetromino_texture->width / 2,
|
||||||
|
UI_PADDING +
|
||||||
|
(up_next_box_size - next_tetromino_texture->height) / 2,
|
||||||
|
SC_White);
|
||||||
|
}
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_UI_Draw_Game_Over(int score) {
|
||||||
|
const char *score_message = TextFormat("Score: %d", score);
|
||||||
|
BeginDrawing();
|
||||||
|
DrawRectangle(0, 0, SC_GAME_WIDTH, SC_HEIGHT, SC_Black);
|
||||||
|
DrawText("Game Over", SC_GAME_WIDTH / 2 - MeasureText("Game Over", 24) / 2,
|
||||||
|
200, 24, SC_Red);
|
||||||
|
DrawText(score_message,
|
||||||
|
SC_GAME_WIDTH / 2 - MeasureText(score_message, 18) / 2, 232, 18,
|
||||||
|
SC_Red);
|
||||||
|
/* Draw a play again button */
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D *R_UI_Get_Texture_By_Block_Type(btype_t block_type) {
|
||||||
|
switch (block_type) {
|
||||||
|
case bt_O:
|
||||||
|
return &o_texture;
|
||||||
|
case bt_I:
|
||||||
|
return &i_texture;
|
||||||
|
case bt_T:
|
||||||
|
return &t_texture;
|
||||||
|
case bt_J:
|
||||||
|
return &j_texture;
|
||||||
|
case bt_L:
|
||||||
|
return &l_texture;
|
||||||
|
case bt_S:
|
||||||
|
return &s_texture;
|
||||||
|
case bt_Z:
|
||||||
|
return &z_texture;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/r_ui.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef R_UI_H_
|
||||||
|
#define R_UI_H_
|
||||||
|
#include "block.h"
|
||||||
|
|
||||||
|
void R_UI_Load_Textures(void);
|
||||||
|
void R_UI_Draw_Ui(void);
|
||||||
|
void R_UI_Draw_Game_Over(int score);
|
||||||
|
Texture2D *R_UI_Get_Texture_By_Block_Type(btype_t block_type);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
src/resources/blue.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/resources/green.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/resources/light-blue.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/resources/orange.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
src/resources/purple.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/resources/red.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/resources/tetromino_I.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/resources/tetromino_J.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
src/resources/tetromino_L.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
src/resources/tetromino_O.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
src/resources/tetromino_S.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/resources/tetromino_T.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/resources/tetromino_Z.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
src/resources/yellow.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
@@ -1,28 +0,0 @@
|
|||||||
#include "r_ui.h"
|
|
||||||
#include "raylib.h"
|
|
||||||
#include "sc_def.h"
|
|
||||||
|
|
||||||
#define UI_PADDING 16
|
|
||||||
|
|
||||||
void R_Draw_Ui(void) {
|
|
||||||
int up_next_box_size;
|
|
||||||
BeginDrawing();
|
|
||||||
DrawRectangle(SC_GAME_WIDTH, 0, SC_UI_WIDTH, SC_HEIGHT, SC_Gray);
|
|
||||||
up_next_box_size = SC_UI_WIDTH - UI_PADDING * 3;
|
|
||||||
DrawRectangle(SC_GAME_WIDTH + UI_PADDING, 0 + UI_PADDING, up_next_box_size,
|
|
||||||
up_next_box_size, SC_Black);
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void R_Draw_Game_Over(int score) {
|
|
||||||
const char *score_message = TextFormat("Score: %d", score);
|
|
||||||
BeginDrawing();
|
|
||||||
DrawRectangle(0, 0, SC_GAME_WIDTH, SC_HEIGHT, SC_Black);
|
|
||||||
DrawText("Game Over", SC_GAME_WIDTH / 2 - MeasureText("Game Over", 24) / 2,
|
|
||||||
200, 24, SC_Red);
|
|
||||||
DrawText(score_message,
|
|
||||||
SC_GAME_WIDTH / 2 - MeasureText(score_message, 18) / 2, 232, 18,
|
|
||||||
SC_Red);
|
|
||||||
/* Draw a play again button */
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#ifndef R_UI_H_
|
|
||||||
#define R_UI_H_
|
|
||||||
|
|
||||||
void R_Draw_Ui(void);
|
|
||||||
void R_Draw_Game_Over(int score);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 897 B |
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 900 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 899 B |
|
Before Width: | Height: | Size: 901 B |
@@ -40,6 +40,7 @@ void TC_Initialize(void) {
|
|||||||
M_B_Create_Blocks();
|
M_B_Create_Blocks();
|
||||||
M_B_On_Block_Spawn(&M_T_Register_Falling_Blocks);
|
M_B_On_Block_Spawn(&M_T_Register_Falling_Blocks);
|
||||||
R_Load_Textures();
|
R_Load_Textures();
|
||||||
|
R_UI_Load_Textures();
|
||||||
blocks_created = M_B_Get_Blocks(&n_blocks_created);
|
blocks_created = M_B_Get_Blocks(&n_blocks_created);
|
||||||
R_Draw_Blocks(blocks_created, n_blocks_created);
|
R_Draw_Blocks(blocks_created, n_blocks_created);
|
||||||
}
|
}
|
||||||
@@ -48,8 +49,8 @@ void TC_Game_Loop(void) {
|
|||||||
int tick_rate = 0, n_updated_blocks = 0;
|
int tick_rate = 0, n_updated_blocks = 0;
|
||||||
block_t **updated_blocks = NULL;
|
block_t **updated_blocks = NULL;
|
||||||
while (!TC_Close_Window()) {
|
while (!TC_Close_Window()) {
|
||||||
|
R_UI_Draw_Ui();
|
||||||
if (M_B_Can_Spawn_Blocks()) {
|
if (M_B_Can_Spawn_Blocks()) {
|
||||||
R_Draw_Ui();
|
|
||||||
TC_Process_Input_Per_Frame();
|
TC_Process_Input_Per_Frame();
|
||||||
if (tick_rate == 0) {
|
if (tick_rate == 0) {
|
||||||
TC_Process_Input_Per_Tick();
|
TC_Process_Input_Per_Tick();
|
||||||
@@ -62,7 +63,7 @@ void TC_Game_Loop(void) {
|
|||||||
tick_rate--;
|
tick_rate--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
R_Draw_Game_Over(0);
|
R_UI_Draw_Game_Over(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||