168 lines
3.5 KiB
C
168 lines
3.5 KiB
C
#include "game.h"
|
|
#include "debug.h"
|
|
#include "grid.h"
|
|
#include "render.h"
|
|
#include "shape.h"
|
|
#include <SDL3/SDL_events.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// coordinates for the current shape (the one that is moving)
|
|
static uint8_t current_x;
|
|
static uint8_t current_y;
|
|
|
|
// current shape and next shape.
|
|
static shape_t shape;
|
|
static shape_t next_shape;
|
|
|
|
static uint32_t score;
|
|
static uint8_t game_over;
|
|
static grid_t grid;
|
|
|
|
void game_init() {
|
|
game_over = 0;
|
|
score = 0;
|
|
grid_init(grid);
|
|
|
|
// Create next shape first. So game_spawn_shape can use it.
|
|
shape_create(&next_shape, rand() % NUM_SHAPES);
|
|
|
|
game_spawn_shape();
|
|
}
|
|
|
|
void game_place_shape() {
|
|
uint8_t x = current_x;
|
|
uint8_t y = current_y;
|
|
|
|
tile_t color = shape_color(&shape);
|
|
|
|
grid_cell(grid, x, y) = color;
|
|
for (int i=0; i < 6; i += 2) {
|
|
int8_t offset_x = shape.coordinates[i];
|
|
int8_t offset_y = shape.coordinates[i+1];
|
|
grid_cell(grid, x + offset_x, y + offset_y) = color;
|
|
}
|
|
|
|
debug_printf("shape placed\n");
|
|
}
|
|
|
|
void game_spawn_shape() {
|
|
current_x = GRID_WIDTH / 2;
|
|
current_y = 0;
|
|
|
|
// Copy the next shape and generate a new next_shape.
|
|
shape_copy(&shape, &next_shape);
|
|
shape_create(&next_shape, rand() % NUM_SHAPES);
|
|
}
|
|
|
|
void game_move_shape(int direction) {
|
|
int8_t new_x = current_x + direction;
|
|
|
|
// Reject immediately if the shape origin would leave board bounds.
|
|
if (grid_check_collision(grid, new_x, current_y)) {
|
|
return;
|
|
}
|
|
|
|
// Then validate every block offset around that origin.
|
|
for (int i=0; i < 6; i += 2) {
|
|
uint8_t x = shape.coordinates[i] + new_x;
|
|
uint8_t y = shape.coordinates[i+1] + current_y;
|
|
if (grid_check_collision(grid, x, y)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
current_x = new_x;
|
|
}
|
|
|
|
void game_rotate_shape() {
|
|
// Temporary array to store new coordinates in.
|
|
int8_t tmp[6];
|
|
|
|
// O-Shapes are the same when rotated.
|
|
if (shape.type == SHAPE_O) {
|
|
return;
|
|
}
|
|
|
|
// Rotate the shape clockwise and check collision.
|
|
// Coordinates are stored relative to the shape origin.
|
|
// 90-degree clockwise rotation: (x, y) -> (-y, x).
|
|
for(int i = 0; i < 6; i += 2) {
|
|
int x = -1 * shape.coordinates[i+1];
|
|
int y = shape.coordinates[i];
|
|
|
|
if (grid_check_collision(grid, current_x + x, current_y + y)) {
|
|
debug_printf("collide(rotate)\n");
|
|
return;
|
|
}
|
|
|
|
// Set the new coordiate.
|
|
tmp[i] = x;
|
|
tmp[i+1] = y;
|
|
}
|
|
|
|
// shape is rotated and collision passed.
|
|
// Update the shape coordinates with the rotated ones.
|
|
memcpy(shape.coordinates, tmp, sizeof(tmp[0]) * 6);
|
|
}
|
|
|
|
|
|
int game_move_shape_down() {
|
|
uint8_t new_y = current_y + 1;
|
|
|
|
// Check origin first
|
|
if (grid_check_collision(grid, current_x, new_y)) {
|
|
debug_printf("collide(1): %d, %d\n", current_x, new_y);
|
|
game_reset_shape();
|
|
return 1;
|
|
}
|
|
|
|
// Then each offset cell.
|
|
for (int i = 0; i < 6; i += 2) {
|
|
uint8_t x = shape.coordinates[i] + current_x;
|
|
uint8_t y = shape.coordinates[i+1] + new_y;
|
|
|
|
if (grid_check_collision(grid, x, y)) {
|
|
debug_printf("collide(2): %d, %d\n", x, y);
|
|
game_reset_shape();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
current_y = new_y;
|
|
return 0;
|
|
}
|
|
|
|
void game_render() {
|
|
render_score(score);
|
|
render_board_border();
|
|
render_board(grid);
|
|
render_shape(&shape, current_x, current_y);
|
|
render_next_shape(&next_shape);
|
|
}
|
|
|
|
// places the current shape on the board.
|
|
// clears any full rows.
|
|
// spawn a new shape.
|
|
void game_reset_shape() {
|
|
game_place_shape();
|
|
score += grid_clear_full_rows(grid) * 100;
|
|
if (!game_is_done()) {
|
|
game_spawn_shape();
|
|
}
|
|
}
|
|
|
|
int game_is_done() {
|
|
if (game_over) {
|
|
return 1;
|
|
}
|
|
|
|
for(int x = 0; x < GRID_WIDTH; x++) {
|
|
if (grid_cell(grid, x, 0)) {
|
|
game_over = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|