Всё фигня, давай по новой
This commit is contained in:
parent
485ac0ca40
commit
f8e1e664a7
13 changed files with 199 additions and 479 deletions
|
|
@ -1,10 +1,18 @@
|
||||||
#ifndef GAME_LOGIC_H
|
#ifndef AUTOMATO_H
|
||||||
#define GAME_LOGIC_H
|
#define AUTOMATO_H
|
||||||
|
|
||||||
|
#include "00_tetris.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "tetris.h" // для использования GameInfo_t
|
|
||||||
|
|
||||||
// Типы фигур
|
typedef enum {
|
||||||
|
Init,
|
||||||
|
Spawn,
|
||||||
|
Moving,
|
||||||
|
Move,
|
||||||
|
Attaching,
|
||||||
|
GameOver
|
||||||
|
} Automato_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
I = 0,
|
I = 0,
|
||||||
J,
|
J,
|
||||||
|
|
@ -16,47 +24,45 @@ typedef enum {
|
||||||
FIGURE_COUNT
|
FIGURE_COUNT
|
||||||
} FigureType;
|
} FigureType;
|
||||||
|
|
||||||
// FSM состояния
|
|
||||||
typedef enum {
|
|
||||||
FSM_Start,
|
|
||||||
FSM_Spawn,
|
|
||||||
FSM_Moving,
|
|
||||||
FSM_Move,
|
|
||||||
FSM_Attaching,
|
|
||||||
FSM_GameOver
|
|
||||||
} FSMState_t;
|
|
||||||
|
|
||||||
// Структура фигуры
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int x, y; // Позиция фигуры на поле
|
int x, y; // Позиция фигуры на поле
|
||||||
|
int mtrx[4][4]; // сама матрица
|
||||||
FigureType type; // Тип фигуры
|
FigureType type; // Тип фигуры
|
||||||
int rotation; // Поворот (0–3)
|
int rotation; // Поворот (0–3)
|
||||||
} Figure;
|
} Figure;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int game_field[FIELD_HEIGHT][FIELD_WIDTH];
|
Figure curr;
|
||||||
Figure current_figure;
|
Figure next;
|
||||||
Figure next_figure;
|
Automato_t state;
|
||||||
FSMState_t state;
|
int field[FIELD_HEIGHT][FIELD_WIDTH];
|
||||||
int score;
|
int score;
|
||||||
int high_score;
|
int high_score;
|
||||||
int level;
|
int level;
|
||||||
long long drop_time;
|
int speed;
|
||||||
bool game_over;
|
long long last_time;
|
||||||
bool paused;
|
} GameState_t;
|
||||||
} GameStateData;
|
|
||||||
|
|
||||||
// Внутренние функции
|
GameState_t* get_game_state(void);
|
||||||
bool check_collision(void);
|
|
||||||
void init_game(void);
|
// Функции состояний
|
||||||
void fsm_transition(void);
|
void do_start(void);
|
||||||
int get_random_figure(void);
|
void do_spawn(void);
|
||||||
|
void do_move(void);
|
||||||
|
void do_moving(void);
|
||||||
|
void do_attaching(void);
|
||||||
|
void do_gameover(void);
|
||||||
|
|
||||||
|
// Вспомогательные
|
||||||
|
void place_figure_on_field();
|
||||||
|
int check_collision();
|
||||||
|
void clear_lines();
|
||||||
|
int is_game_over();
|
||||||
|
|
||||||
|
// Функции фигур
|
||||||
const int (*get_figure_shape(FigureType type, int rotation))[4];
|
const int (*get_figure_shape(FigureType type, int rotation))[4];
|
||||||
GameStateData* get_game_state(void);
|
|
||||||
void place_figure(void);
|
|
||||||
void clear_completed_lines(void);
|
|
||||||
|
|
||||||
// Фигуры
|
// Остальные фигуры...
|
||||||
const int (*i_fig_up())[4];
|
const int (*i_fig_up())[4];
|
||||||
const int (*i_fig_right())[4];
|
const int (*i_fig_right())[4];
|
||||||
const int (*i_fig_down())[4];
|
const int (*i_fig_down())[4];
|
||||||
|
|
@ -84,4 +90,5 @@ const int (*z_fig_down())[4];
|
||||||
const int (*z_fig_left())[4];
|
const int (*z_fig_left())[4];
|
||||||
const int (*empty_fig())[4];
|
const int (*empty_fig())[4];
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
12
src/brick_game/tetris/02_automato.c
Normal file
12
src/brick_game/tetris/02_automato.c
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
static GameState_t g_state = {0};
|
||||||
|
|
||||||
|
GameState_t* get_game_state(void) {
|
||||||
|
static int initialized = 0;
|
||||||
|
if (!initialized) {
|
||||||
|
g_state.state = GameOver;
|
||||||
|
initialized = 1;
|
||||||
|
}
|
||||||
|
return &g_state;
|
||||||
|
}
|
||||||
63
src/brick_game/tetris/03_tetris.c
Normal file
63
src/brick_game/tetris/03_tetris.c
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void userInput(UserAction_t action, bool hold) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case Start:
|
||||||
|
state->state = Start;
|
||||||
|
break;
|
||||||
|
case Terminate:
|
||||||
|
if (state->score > state->high_score) {
|
||||||
|
state->high_score = state->score;
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case Left:
|
||||||
|
case Right:
|
||||||
|
case Action:
|
||||||
|
state->state = Moving;
|
||||||
|
break;
|
||||||
|
case Down:
|
||||||
|
// Ускорение падения — будет обрабатываться в do_move
|
||||||
|
break;
|
||||||
|
case Pause:
|
||||||
|
// Обработка на UI
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInfo_t updateCurrentState() {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
switch (state->state) {
|
||||||
|
case Start:
|
||||||
|
do_start();
|
||||||
|
break;
|
||||||
|
case Spawn:
|
||||||
|
do_spawn();
|
||||||
|
break;
|
||||||
|
case Move:
|
||||||
|
do_move();
|
||||||
|
break;
|
||||||
|
case Moving:
|
||||||
|
do_moving();
|
||||||
|
break;
|
||||||
|
case Attaching:
|
||||||
|
do_attaching();
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
do_gameover();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInfo_t info = {0};
|
||||||
|
info.field = (int**)state->field;
|
||||||
|
info.next = (int**)state->next.mtrx; // теперь next.mtrx
|
||||||
|
info.score = state->score;
|
||||||
|
info.high_score = state->high_score;
|
||||||
|
info.level = state->level;
|
||||||
|
info.speed = state->speed;
|
||||||
|
info.pause = 0;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
14
src/brick_game/tetris/04_start.c
Normal file
14
src/brick_game/tetris/04_start.c
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
void do_start(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Очистка поля
|
||||||
|
for (int i = 0; i < FIELD_HEIGHT; i++)
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++)
|
||||||
|
state->field[i][j] = 0;
|
||||||
|
|
||||||
|
state->score = 0;
|
||||||
|
state->level = 1;
|
||||||
|
state->speed = 1;
|
||||||
|
state->state = Spawn;
|
||||||
|
}
|
||||||
26
src/brick_game/tetris/05_spawn.c
Normal file
26
src/brick_game/tetris/05_spawn.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void do_spawn(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Присваиваем curr = next
|
||||||
|
state->curr = state->next;
|
||||||
|
state->curr.x = FIELD_WIDTH / 2 - 2;
|
||||||
|
state->curr.y = 0;
|
||||||
|
|
||||||
|
// Генерим следующую фигуру
|
||||||
|
state->next.type = rand() % FIGURE_COUNT;
|
||||||
|
state->next.rotation = 0;
|
||||||
|
const int (*shape)[4] = get_figure_shape(state->next.type, 0);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
state->next.mtrx[i][j] = shape[i][j];
|
||||||
|
|
||||||
|
// Проверка на GameOver
|
||||||
|
if (check_collision()) {
|
||||||
|
state->state = GameOver;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->state = Move;
|
||||||
|
}
|
||||||
8
src/brick_game/tetris/06_move.c
Normal file
8
src/brick_game/tetris/06_move.c
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
void do_move(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Проверка таймера и перемещение вниз
|
||||||
|
// Если hold Down — ускорение
|
||||||
|
// Если коллизия — переход в Attaching
|
||||||
|
}
|
||||||
8
src/brick_game/tetris/07_moving.c
Normal file
8
src/brick_game/tetris/07_moving.c
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
void do_moving(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Обработка Left/Right/Action
|
||||||
|
// Проверка коллизии
|
||||||
|
// Возврат в Move или остаться в Moving
|
||||||
|
}
|
||||||
19
src/brick_game/tetris/08_attaching.c
Normal file
19
src/brick_game/tetris/08_attaching.c
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
void do_attaching(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Закрепляем фигуру на поле
|
||||||
|
place_figure_on_field();
|
||||||
|
|
||||||
|
// Удаляем линии
|
||||||
|
clear_lines();
|
||||||
|
|
||||||
|
// Проверяем GameOver
|
||||||
|
if (is_game_over()) {
|
||||||
|
state->state = GameOver;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переход в Spawn
|
||||||
|
state->state = Spawn;
|
||||||
|
}
|
||||||
9
src/brick_game/tetris/09_gameover.c
Normal file
9
src/brick_game/tetris/09_gameover.c
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "01_automato.h"
|
||||||
|
|
||||||
|
void do_gameover(void) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
// Сброс next в пустую фигуру
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
state->next.mtrx[i][j] = 0;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "game_logic.h"
|
#include "01_automato.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
const int (*get_figure_shape(FigureType type, int rotation))[4] {
|
const int (*get_figure_shape(FigureType type, int rotation))[4] {
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
#include "tetris.h"
|
|
||||||
#include "game_logic.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
static GameStateData* get_instance() {
|
|
||||||
static GameStateData instance = {0};
|
|
||||||
static bool initialized_local = false;
|
|
||||||
|
|
||||||
if (!initialized_local) {
|
|
||||||
// Инициализация экземпляра
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
instance.game_field[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instance.score = 0;
|
|
||||||
instance.high_score = 0;
|
|
||||||
instance.level = 1;
|
|
||||||
instance.state = FSM_Start;
|
|
||||||
instance.game_over = false;
|
|
||||||
instance.paused = false;
|
|
||||||
initialized_local = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_game() {
|
|
||||||
GameStateData* state = get_instance();
|
|
||||||
|
|
||||||
// Инициализация игрового поля
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
state->game_field[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srand((unsigned int)time(NULL));
|
|
||||||
|
|
||||||
state->next_figure.type = get_random_figure();
|
|
||||||
state->next_figure.x = 0;
|
|
||||||
state->next_figure.y = 0;
|
|
||||||
state->next_figure.rotation = 0;
|
|
||||||
|
|
||||||
state->score = 0;
|
|
||||||
state->high_score = 0;
|
|
||||||
state->level = 1;
|
|
||||||
state->drop_time = time(NULL) * 1000;
|
|
||||||
state->game_over = false;
|
|
||||||
state->paused = false;
|
|
||||||
state->state = FSM_Start; // Начинаем в состоянии FSM_Start
|
|
||||||
}
|
|
||||||
|
|
||||||
void place_figure() {
|
|
||||||
GameStateData* state = get_instance();
|
|
||||||
Figure* f = &state->current_figure;
|
|
||||||
const int (*shape)[4] = get_figure_shape(f->type, f->rotation);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
if (shape[i][j]) {
|
|
||||||
int x = f->x + j;
|
|
||||||
int y = f->y + i;
|
|
||||||
if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) {
|
|
||||||
state->game_field[y][x] = 1; // Фиксируем блок
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем и удаляем заполненные строки
|
|
||||||
clear_completed_lines();
|
|
||||||
|
|
||||||
// Спавним новую фигуру
|
|
||||||
state->current_figure = state->next_figure;
|
|
||||||
state->current_figure.x = FIELD_WIDTH / 2 - 2;
|
|
||||||
state->current_figure.y = 0;
|
|
||||||
state->current_figure.rotation = 0;
|
|
||||||
|
|
||||||
// Генерируем следующую фигуру
|
|
||||||
state->next_figure.type = get_random_figure();
|
|
||||||
state->next_figure.rotation = 0;
|
|
||||||
|
|
||||||
// Проверяем, возможно ли размещение новой фигуры
|
|
||||||
if (check_collision()) {
|
|
||||||
state->state = FSM_GameOver;
|
|
||||||
} else {
|
|
||||||
state->state = FSM_Moving;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_completed_lines() {
|
|
||||||
GameStateData* state = get_instance();
|
|
||||||
int lines_cleared = 0;
|
|
||||||
int write_row = FIELD_HEIGHT - 1;
|
|
||||||
|
|
||||||
// Проходим снизу вверх
|
|
||||||
for (int read_row = FIELD_HEIGHT - 1; read_row >= 0; read_row--) {
|
|
||||||
bool line_complete = true;
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
if (state->game_field[read_row][j] == 0) {
|
|
||||||
line_complete = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_complete) {
|
|
||||||
lines_cleared++;
|
|
||||||
} else {
|
|
||||||
// Копируем неполные строки вниз
|
|
||||||
if (write_row != read_row) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
state->game_field[write_row][j] = state->game_field[read_row][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_row--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Заполняем пустые строки наверху
|
|
||||||
for (int i = 0; i <= write_row; i++) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
state->game_field[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines_cleared > 0) {
|
|
||||||
// Обновляем счет в соответствии с количеством очищенных линий
|
|
||||||
int points[] = {0, 100, 300, 700, 1500}; // 0, 1, 2, 3, 4 линии
|
|
||||||
int score_points = 0;
|
|
||||||
if (lines_cleared <= 4) {
|
|
||||||
score_points = points[lines_cleared];
|
|
||||||
} else {
|
|
||||||
score_points = points[4]; // для > 4 линий
|
|
||||||
}
|
|
||||||
state->score += score_points; // УБРАЛ УМНОЖЕНИЕ НА УРОВЕНЬ
|
|
||||||
|
|
||||||
if (state->score > state->high_score) {
|
|
||||||
state->high_score = state->score;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Увеличиваем уровень каждые 600 очков, максимум 10
|
|
||||||
int new_level = state->score / 600 + 1;
|
|
||||||
if (new_level > 10) new_level = 10;
|
|
||||||
if (new_level != state->level) {
|
|
||||||
state->level = new_level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_random_figure() {
|
|
||||||
return rand() % FIGURE_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_collision() {
|
|
||||||
GameStateData* state = get_instance();
|
|
||||||
Figure* f = &state->current_figure;
|
|
||||||
const int (*shape)[4] = get_figure_shape(f->type, f->rotation);
|
|
||||||
bool collision = false;
|
|
||||||
|
|
||||||
for (int i = 0; !collision && i < 4; i++) {
|
|
||||||
for (int j = 0; !collision && j < 4; j++) {
|
|
||||||
if (shape[i][j]) {
|
|
||||||
int x = f->x + j;
|
|
||||||
int y = f->y + i;
|
|
||||||
|
|
||||||
if (x < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT
|
|
||||||
|| (y >= 0 && state->game_field[y][x] != 0)) {
|
|
||||||
collision = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return collision;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsm_transition() {
|
|
||||||
GameStateData* state = get_instance();
|
|
||||||
|
|
||||||
switch (state->state) {
|
|
||||||
case FSM_Start:
|
|
||||||
// Ожидание начала игры - не делаем ничего, пока не будет нажата Start
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSM_Spawn:
|
|
||||||
state->current_figure = state->next_figure;
|
|
||||||
state->current_figure.x = FIELD_WIDTH / 2 - 2;
|
|
||||||
state->current_figure.y = 0;
|
|
||||||
state->current_figure.rotation = 0;
|
|
||||||
|
|
||||||
state->next_figure.type = get_random_figure();
|
|
||||||
state->next_figure.rotation = 0;
|
|
||||||
|
|
||||||
if (check_collision()) {
|
|
||||||
state->state = FSM_GameOver;
|
|
||||||
} else {
|
|
||||||
state->state = FSM_Moving;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSM_Moving:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSM_Move:
|
|
||||||
state->current_figure.y++;
|
|
||||||
if (check_collision()) {
|
|
||||||
state->current_figure.y--;
|
|
||||||
state->state = FSM_Attaching;
|
|
||||||
} else {
|
|
||||||
state->state = FSM_Moving;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSM_Attaching:
|
|
||||||
place_figure();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSM_GameOver:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameStateData* get_game_state() {
|
|
||||||
return get_instance();
|
|
||||||
}
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
#include "tetris.h"
|
|
||||||
#include "game_logic.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
static GameInfo_t* get_game_info_instance() {
|
|
||||||
static GameInfo_t instance = {0};
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
instance.field = malloc(FIELD_HEIGHT * sizeof(int*));
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
instance.field[i] = malloc(FIELD_WIDTH * sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.next = malloc(4 * sizeof(int*));
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
instance.next[i] = malloc(4 * sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация полей
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
instance.field[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
instance.next[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void userInput(UserAction_t action, bool hold) {
|
|
||||||
GameStateData* state = get_game_state();
|
|
||||||
GameInfo_t* game_info = get_game_info_instance();
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case Terminate:
|
|
||||||
// Освобождаем память при завершении
|
|
||||||
if (game_info->field != NULL) {
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
if (game_info->field[i] != NULL) {
|
|
||||||
free(game_info->field[i]);
|
|
||||||
game_info->field[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(game_info->field);
|
|
||||||
game_info->field = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game_info->next != NULL) {
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
if (game_info->next[i] != NULL) {
|
|
||||||
free(game_info->next[i]);
|
|
||||||
game_info->next[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(game_info->next);
|
|
||||||
game_info->next = NULL;
|
|
||||||
}
|
|
||||||
return; // ВАЖНО: выходим из функции, не делаем ничего после Terminate
|
|
||||||
|
|
||||||
case Start:
|
|
||||||
if (state->state == FSM_GameOver) {
|
|
||||||
// Перезапуск игры после Game Over
|
|
||||||
int saved_high_score = state->high_score;
|
|
||||||
init_game();
|
|
||||||
state->high_score = saved_high_score;
|
|
||||||
// Не меняем состояние, пусть остается в FSM_Start до следующего нажатия
|
|
||||||
} else if (state->state == FSM_Start) {
|
|
||||||
// Начинаем игру из состояния Start
|
|
||||||
state->state = FSM_Spawn;
|
|
||||||
} else {
|
|
||||||
// Для всех других состояний (Moving, Move) - ставим на паузу
|
|
||||||
state->paused = !state->paused;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Pause:
|
|
||||||
state->paused = !state->paused;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (state->state == FSM_GameOver || state->paused || state->state == FSM_Start) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((state->state == FSM_Moving || state->state == FSM_Move) && !state->game_over) {
|
|
||||||
int old_x = state->current_figure.x;
|
|
||||||
int old_y = state->current_figure.y;
|
|
||||||
int old_rot = state->current_figure.rotation;
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case Left:
|
|
||||||
state->current_figure.x--;
|
|
||||||
break;
|
|
||||||
case Right:
|
|
||||||
state->current_figure.x++;
|
|
||||||
break;
|
|
||||||
case Down:
|
|
||||||
state->current_figure.y++;
|
|
||||||
break;
|
|
||||||
case Up:
|
|
||||||
state->current_figure.rotation = (state->current_figure.rotation + 1) % 4;
|
|
||||||
break;
|
|
||||||
case Action:
|
|
||||||
if (hold) {
|
|
||||||
// Быстрый сброс вниз
|
|
||||||
while (!check_collision()) {
|
|
||||||
state->current_figure.y++;
|
|
||||||
}
|
|
||||||
state->current_figure.y--; // Вернуть на место перед столкновением
|
|
||||||
state->state = FSM_Attaching;
|
|
||||||
} else {
|
|
||||||
state->current_figure.rotation = (state->current_figure.rotation + 1) % 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_collision()) {
|
|
||||||
// Возвращаем старые значения
|
|
||||||
state->current_figure.x = old_x;
|
|
||||||
state->current_figure.y = old_y;
|
|
||||||
state->current_figure.rotation = old_rot;
|
|
||||||
} else if (action == Down) {
|
|
||||||
// Если перемещение вниз успешно, обновляем время падения
|
|
||||||
state->drop_time = time(NULL) * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameInfo_t updateCurrentState() {
|
|
||||||
GameStateData* state = get_game_state();
|
|
||||||
GameInfo_t* game_info = get_game_info_instance();
|
|
||||||
|
|
||||||
if (state->state != FSM_Start && !state->game_over && !state->paused) {
|
|
||||||
long long current_time = time(NULL) * 1000;
|
|
||||||
|
|
||||||
// Определяем интервал падения в зависимости от уровня
|
|
||||||
int drop_interval = 1000 - (state->level - 1) * 50;
|
|
||||||
if (drop_interval < 100) drop_interval = 100; // Минимальный интервал
|
|
||||||
|
|
||||||
if (current_time - state->drop_time >= drop_interval) {
|
|
||||||
state->state = FSM_Move; // Переходим к автоматическому движению
|
|
||||||
state->drop_time = current_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Выполняем переходы FSM (но не в состоянии Start)
|
|
||||||
if (state->state != FSM_Start) {
|
|
||||||
fsm_transition();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обновляем game_info.field из state->game_field
|
|
||||||
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
|
||||||
for (int j = 0; j < FIELD_WIDTH; j++) {
|
|
||||||
game_info->field[i][j] = state->game_field[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Добавляем активную фигуру на поле для отображения (если не в Start или GameOver)
|
|
||||||
if ((state->state == FSM_Moving || state->state == FSM_Move) && !state->game_over) {
|
|
||||||
Figure* f = &state->current_figure;
|
|
||||||
const int (*shape)[4] = get_figure_shape(f->type, f->rotation);
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
if (shape[i][j]) {
|
|
||||||
int x = f->x + j;
|
|
||||||
int y = f->y + i;
|
|
||||||
// Проверяем границы перед записью
|
|
||||||
if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) {
|
|
||||||
// Не перезаписываем уже зафиксированные блоки
|
|
||||||
if (state->game_field[y][x] == 0) {
|
|
||||||
game_info->field[y][x] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обновляем next
|
|
||||||
const int (*next_shape)[4];
|
|
||||||
if (state->state == FSM_GameOver) {
|
|
||||||
// При game_over показываем пустую фигуру
|
|
||||||
next_shape = empty_fig();
|
|
||||||
} else {
|
|
||||||
next_shape = get_figure_shape(state->next_figure.type, state->next_figure.rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
game_info->next[i][j] = next_shape[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обновляем остальные поля
|
|
||||||
game_info->score = state->score;
|
|
||||||
game_info->high_score = state->high_score;
|
|
||||||
game_info->level = state->level;
|
|
||||||
game_info->speed = 0; // Заглушка
|
|
||||||
game_info->pause = state->paused ? 1 : 0;
|
|
||||||
|
|
||||||
return *game_info;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue