1541 lines
33 KiB
C
1541 lines
33 KiB
C
/*
|
|
* Copyright (c) 2019-2021, yzrh <yzrh@noema.org>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef __linux__
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <poll.h>
|
|
|
|
#endif /* __linux__ */
|
|
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_ttf.h>
|
|
|
|
#ifdef SOUND
|
|
|
|
#include <SDL2/SDL_mixer.h>
|
|
|
|
#endif /* SOUND */
|
|
|
|
#include "version.h"
|
|
#include "extern.h"
|
|
#include "screen.h"
|
|
|
|
#ifdef GPIO
|
|
|
|
#include "gpio.h"
|
|
|
|
#endif /* GPIO */
|
|
|
|
static pthread_mutex_t mutex;
|
|
static pthread_cond_t cond;
|
|
|
|
static SDL_Renderer *renderer;
|
|
static TTF_Font *font_20;
|
|
static TTF_Font *font_24;
|
|
|
|
/*
|
|
* Default to keyboard,
|
|
* GPIO inputs are mapped to controller keys
|
|
*/
|
|
static Snake_Device device = SNAKE_KEYBOARD;
|
|
static int selection = 0;
|
|
|
|
static Snake_Text text_title;
|
|
static Snake_Text text_help_top;
|
|
static Snake_Text text_help_0;
|
|
static Snake_Text text_help_1;
|
|
static Snake_Text text_help_2;
|
|
static Snake_Text text_help_3;
|
|
static Snake_Text text_help_4;
|
|
static Snake_Text text_help_5;
|
|
static Snake_Text text_help_6;
|
|
static Snake_Text text_help_7;
|
|
|
|
static Snake_Text text_score;
|
|
|
|
static Snake_Text text_prompt_start;
|
|
static Snake_Text text_prompt_resume;
|
|
|
|
static Snake_Text text_prompt_0;
|
|
static Snake_Text text_prompt_1;
|
|
static Snake_Text text_prompt_2;
|
|
|
|
static bool start = 1;
|
|
static bool end = 0;
|
|
static bool begin = 0;
|
|
|
|
static bool god = 0;
|
|
static bool reset = 0;
|
|
static bool wait = 0;
|
|
static bool multiplayer = 0;
|
|
static bool flappy = 0;
|
|
|
|
static struct timespec delay = {
|
|
.tv_sec = 0,
|
|
.tv_nsec = TICK_TIME_INIT
|
|
};
|
|
|
|
static int score;
|
|
static int result;
|
|
static int distance;
|
|
static int speed;
|
|
|
|
static Snake_Input input = SNAKE_EMPTY;
|
|
static Snake_Input head[2][2];
|
|
|
|
static Snake_Pos *snake0 = NULL;
|
|
static Snake_Pos *snake1 = NULL;
|
|
|
|
static int fruit[2];
|
|
|
|
static Snake_Pos *gap = NULL;
|
|
|
|
static void*
|
|
handler_rendering(void *args)
|
|
{
|
|
const SDL_Color fg = {
|
|
COLOUR_FOREGROUND_R,
|
|
COLOUR_FOREGROUND_G,
|
|
COLOUR_FOREGROUND_B
|
|
};
|
|
const SDL_Color bg = {
|
|
COLOUR_BACKGROUND_R,
|
|
COLOUR_BACKGROUND_G,
|
|
COLOUR_BACKGROUND_B
|
|
};
|
|
const SDL_Color bg_s = {
|
|
COLOUR_BACKGROUND_SHADE_R,
|
|
COLOUR_BACKGROUND_SHADE_G,
|
|
COLOUR_BACKGROUND_SHADE_B
|
|
};
|
|
|
|
char result_str[23];
|
|
char score_str[11];
|
|
|
|
Snake_Text text_result;
|
|
|
|
Snake_Text text_welcome;
|
|
Snake_Text text_clear;
|
|
Snake_Text text_gameover;
|
|
Snake_Text text_pause;
|
|
|
|
while (!end) {
|
|
draw_clear(renderer);
|
|
draw_colour(renderer, SNAKE_FOREGROUND);
|
|
|
|
draw_wall(renderer);
|
|
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + 4 * SCREEN_UNIT_X,
|
|
SCREEN_UNIT_Y,
|
|
"Snake",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_title);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + 4 * SCREEN_UNIT_X,
|
|
2 * SCREEN_UNIT_Y,
|
|
"Help",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_top);
|
|
if (god)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
2.5 * SCREEN_UNIT_Y,
|
|
"0. Don't forget to cheat",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_0);
|
|
if (device == SNAKE_KEYBOARD) {
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3 * SCREEN_UNIT_Y,
|
|
"1. Classic: use arrow keys to move",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_1);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3.5 * SCREEN_UNIT_Y,
|
|
"2. Multiplayer: use s, e, d, f keys",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_2);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4 * SCREEN_UNIT_Y,
|
|
"3. Flappy: SPACE key to jump",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_3);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4.5 * SCREEN_UNIT_Y,
|
|
"4. ESCAPE key to pause",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_4);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5 * SCREEN_UNIT_Y,
|
|
"5. r key to reset",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_5);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5.5 * SCREEN_UNIT_Y,
|
|
"6. m key to toggle music",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_6);
|
|
} else if (device == SNAKE_TOUCHSCREEN) {
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3 * SCREEN_UNIT_Y,
|
|
"1. Classic: swipe to move",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_1);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3.5 * SCREEN_UNIT_Y,
|
|
"2. Multiplayer: use different side",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_2);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4 * SCREEN_UNIT_Y,
|
|
"3. Flappy: tap to jump",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_3);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4.5 * SCREEN_UNIT_Y,
|
|
"4. Tap here to pause",
|
|
0,
|
|
1,
|
|
fg,
|
|
bg,
|
|
&text_help_4);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5 * SCREEN_UNIT_Y,
|
|
"5. Tap here to reset",
|
|
0,
|
|
1,
|
|
fg,
|
|
bg,
|
|
&text_help_5);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5.5 * SCREEN_UNIT_Y,
|
|
"6. Tap here to toggle music",
|
|
0,
|
|
1,
|
|
fg,
|
|
bg,
|
|
&text_help_6);
|
|
} else if (device == SNAKE_CONTROLLER) {
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3 * SCREEN_UNIT_Y,
|
|
"1. Classic: use D-pad to move",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_1);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
3.5 * SCREEN_UNIT_Y,
|
|
"2. Multiplayer: second controller",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_2);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4 * SCREEN_UNIT_Y,
|
|
"3. Flappy: Press A to jump",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_3);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
4.5 * SCREEN_UNIT_Y,
|
|
"4. Press BACK to pause",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_4);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5 * SCREEN_UNIT_Y,
|
|
"5. Press X to reset",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_5);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
5.5 * SCREEN_UNIT_Y,
|
|
"6. Press Y to toggle music",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_6);
|
|
}
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + SCREEN_UNIT_X / 4,
|
|
6 * SCREEN_UNIT_Y,
|
|
"7. Have fun",
|
|
0,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_help_7);
|
|
|
|
snprintf(score_str, 11, "Score: %d", score);
|
|
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT + 4 * SCREEN_UNIT_X,
|
|
7 * SCREEN_UNIT_Y,
|
|
score_str,
|
|
1,
|
|
0,
|
|
fg,
|
|
bg,
|
|
&text_score);
|
|
|
|
draw_snake(renderer, &snake0);
|
|
|
|
if (flappy) {
|
|
draw_pipe(renderer, &gap);
|
|
} else if (multiplayer)
|
|
draw_snake(renderer, &snake1);
|
|
else if (!start)
|
|
draw_fruit(renderer, fruit);
|
|
|
|
if (start || reset) {
|
|
if (multiplayer) {
|
|
if (result == 3)
|
|
snprintf(result_str, 23, "Draw");
|
|
else if (result > 0)
|
|
snprintf(result_str, 23,
|
|
"%s is the winner",
|
|
result == 1 ?
|
|
"Player 2" : "Player 1");
|
|
else
|
|
snprintf(result_str, 23, "Reset");
|
|
text_result.cache = 0;
|
|
|
|
draw_text(renderer,
|
|
font_24,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 -
|
|
0.75 * SCREEN_UNIT_Y,
|
|
result_str,
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_result);
|
|
}
|
|
if (start)
|
|
draw_text(renderer,
|
|
font_24,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2,
|
|
"Welcome",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_welcome);
|
|
else if (!flappy && !multiplayer &&
|
|
score == SCREEN_BLOCK / 2)
|
|
draw_text(renderer,
|
|
font_24,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2,
|
|
"Why don't you have legs?",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_clear);
|
|
else
|
|
draw_text(renderer,
|
|
font_24,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2,
|
|
"Game over!",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_gameover);
|
|
if (device == SNAKE_KEYBOARD)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Press RETURN key for selection",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_start);
|
|
else if (device == SNAKE_TOUCHSCREEN)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Tap the option to select",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_start);
|
|
else if (device == SNAKE_CONTROLLER)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Press START to select",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_start);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 + 1.5 * SCREEN_UNIT_Y,
|
|
"Classic",
|
|
1,
|
|
device == SNAKE_TOUCHSCREEN ||
|
|
selection == 0 ? 1 : 0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_0);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 + 2.25 * SCREEN_UNIT_Y,
|
|
"Multiplayer",
|
|
1,
|
|
device == SNAKE_TOUCHSCREEN ||
|
|
selection == 1 ? 1 : 0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_1);
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 + 3 * SCREEN_UNIT_Y,
|
|
"Flappy",
|
|
1,
|
|
device == SNAKE_TOUCHSCREEN ||
|
|
selection == 2 ? 1 : 0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_2);
|
|
} else if (wait) {
|
|
draw_text(renderer,
|
|
font_24,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2,
|
|
"Paused",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_pause);
|
|
if (device == SNAKE_KEYBOARD)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Press ESCAPE key to resume",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_resume);
|
|
else if (device == SNAKE_TOUCHSCREEN)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Tap here to resume",
|
|
1,
|
|
1,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_resume);
|
|
else if (device == SNAKE_CONTROLLER)
|
|
draw_text(renderer,
|
|
font_20,
|
|
SCREEN_HEIGHT / 2,
|
|
SCREEN_HEIGHT / 2 +
|
|
0.75 * SCREEN_UNIT_Y,
|
|
"Press START to resume",
|
|
1,
|
|
0,
|
|
fg,
|
|
bg_s,
|
|
&text_prompt_resume);
|
|
}
|
|
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void*
|
|
handler_logic(void *args)
|
|
{
|
|
struct timespec clock_start;
|
|
struct timespec clock_end;
|
|
double clock_pass = 0;
|
|
int pipe_dim[6];
|
|
int pipe_pos = 0;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &clock_start);
|
|
|
|
for (;;) {
|
|
pthread_mutex_lock(&mutex);
|
|
while (start || reset || wait) {
|
|
if (!wait) {
|
|
clock_pass = 0;
|
|
pipe_pos = 0;
|
|
}
|
|
pthread_cond_wait(&cond, &mutex);
|
|
clock_gettime(CLOCK_MONOTONIC, &clock_start);
|
|
}
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
if (end)
|
|
return 0;
|
|
|
|
if (flappy) {
|
|
pthread_mutex_lock(&mutex);
|
|
while (!begin)
|
|
pthread_cond_wait(&cond, &mutex);
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
game_pipe_bound(&gap, pipe_dim);
|
|
|
|
if (pipe_pos - SCREEN_UNIT > pipe_dim[0]) {
|
|
score++;
|
|
text_score.cache = 0;
|
|
}
|
|
pipe_pos = pipe_dim[0];
|
|
|
|
if (!god && (collision(
|
|
(int[]) {snake0->next->x, snake0->next->y,
|
|
2 * SCREEN_UNIT, SCREEN_UNIT},
|
|
(int[]) {pipe_dim[0], pipe_dim[2],
|
|
pipe_dim[1], pipe_dim[3]}) ||
|
|
collision(
|
|
(int[]) {snake0->next->x, snake0->next->y,
|
|
2 * SCREEN_UNIT, SCREEN_UNIT},
|
|
(int[]) {pipe_dim[0], pipe_dim[4],
|
|
pipe_dim[1], pipe_dim[5]}) ||
|
|
snake0->y < 0 ||
|
|
snake0->y + SCREEN_UNIT > SCREEN_HEIGHT)) {
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
} else {
|
|
game_pipe_move(&gap);
|
|
|
|
game_pipe_pos(&gap, &result);
|
|
if (result == 3) {
|
|
game_pipe_del(&gap);
|
|
game_pipe_add(&gap);
|
|
} else if (result == 2) {
|
|
game_pipe_add(&gap);
|
|
} else if (result == 1) {
|
|
game_pipe_del(&gap);
|
|
}
|
|
}
|
|
} else if (!multiplayer) {
|
|
game_snake_move(&snake0, NULL,
|
|
head[0], !god, &result);
|
|
|
|
if (result == 0 || god) {
|
|
head[1][0] = head[0][0];
|
|
|
|
if (collision_block(fruit,
|
|
(int[]) {snake0->x, snake0->y})) {
|
|
score++;
|
|
text_score.cache = 0;
|
|
game_snake_grow(&snake0);
|
|
|
|
if (score == SCREEN_BLOCK / 2) {
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
|
|
game_fruit(fruit, &snake0);
|
|
|
|
if (delay.tv_nsec - TICK_TIME_DEC >=
|
|
TICK_TIME_MIN)
|
|
delay.tv_nsec -= TICK_TIME_DEC;
|
|
}
|
|
} else {
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
} else {
|
|
game_snake_move(&snake0, &snake1,
|
|
head[0], !god, &result);
|
|
|
|
if (result == 0 || god) {
|
|
head[1][0] = head[0][0];
|
|
head[1][1] = head[0][1];
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &clock_end);
|
|
clock_pass += (clock_end.tv_sec -
|
|
clock_start.tv_sec) +
|
|
(clock_end.tv_nsec -
|
|
clock_start.tv_nsec) * 0.000000001;
|
|
clock_gettime(CLOCK_MONOTONIC, &clock_start);
|
|
|
|
score = clock_pass;
|
|
text_score.cache = 0;
|
|
game_snake_grow(&snake0);
|
|
game_snake_grow(&snake1);
|
|
} else {
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
}
|
|
|
|
nanosleep(&delay, NULL);
|
|
}
|
|
}
|
|
|
|
static void*
|
|
handler_gravity(void *args)
|
|
{
|
|
const struct timespec delay = {
|
|
.tv_sec = 0,
|
|
.tv_nsec = 1000000000 / SCREEN_UNIT
|
|
};
|
|
|
|
for (;;) {
|
|
pthread_mutex_lock(&mutex);
|
|
while (!flappy || !begin || start || reset || wait)
|
|
pthread_cond_wait(&cond, &mutex);
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
if (end)
|
|
return 0;
|
|
|
|
if (distance > 0) {
|
|
game_snake_fall(&snake0, 0,
|
|
distance > speed ? speed : distance);
|
|
speed++;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
distance -= distance > speed ? speed : distance;
|
|
if (distance == 0)
|
|
speed = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
} else {
|
|
game_snake_fall(&snake0, 1, speed);
|
|
speed++;
|
|
}
|
|
|
|
nanosleep(&delay, NULL);
|
|
}
|
|
}
|
|
|
|
#ifdef GPIO
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
static void*
|
|
handler_gpio(void *args)
|
|
{
|
|
const struct timespec delay = {
|
|
.tv_sec = 0,
|
|
.tv_nsec = 50000000
|
|
};
|
|
|
|
SDL_Event event;
|
|
event.type = SDL_CONTROLLERBUTTONUP;
|
|
|
|
while (!end) {
|
|
if (gpio_read(GPIO_PIN_IN0) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_LEFT;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN1) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_UP;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN2) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_DOWN;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN3) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN4) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_BACK;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN5) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_GUIDE;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN6) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_START;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN7) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_A;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN8) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_B;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN9) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_X;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN10) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_Y;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN11) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN12) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN13) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_LEFTSTICK;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN14) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_RIGHTSTICK;
|
|
event.cbutton.which = 0;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN15) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_LEFT;
|
|
event.cbutton.which = 1;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN16) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_UP;
|
|
event.cbutton.which = 1;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN17) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_DOWN;
|
|
event.cbutton.which = 1;
|
|
SDL_PushEvent(&event);
|
|
} else if (gpio_read(GPIO_PIN_IN18) == 0) {
|
|
event.cbutton.button =
|
|
SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
|
event.cbutton.which = 1;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
nanosleep(&delay, NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#elif __linux__
|
|
|
|
static void*
|
|
handler_gpio(void *args)
|
|
{
|
|
struct pollfd pfd[19];
|
|
char path[29];
|
|
|
|
for (int i = 0; i < 19; i++)
|
|
pfd[i].events = POLLPRI | POLLERR;
|
|
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN0);
|
|
pfd[0].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN1);
|
|
pfd[1].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN2);
|
|
pfd[2].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN3);
|
|
pfd[3].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN4);
|
|
pfd[4].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN5);
|
|
pfd[5].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN6);
|
|
pfd[6].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN7);
|
|
pfd[7].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN8);
|
|
pfd[8].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN9);
|
|
pfd[9].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN10);
|
|
pfd[10].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN11);
|
|
pfd[11].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN12);
|
|
pfd[12].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN13);
|
|
pfd[13].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN14);
|
|
pfd[14].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN15);
|
|
pfd[15].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN16);
|
|
pfd[16].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN17);
|
|
pfd[17].fd = open(path, O_RDONLY);
|
|
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN18);
|
|
pfd[18].fd = open(path, O_RDONLY);
|
|
|
|
SDL_Event event;
|
|
event.type = SDL_CONTROLLERBUTTONUP;
|
|
|
|
char val[2];
|
|
|
|
while (!end) {
|
|
poll(pfd, 19, -1);
|
|
|
|
for (int i = 0; i < 19; i++) {
|
|
if (pfd[i].revents & (POLLPRI | POLLERR)) {
|
|
pfd[i].revents &= ~(POLLPRI | POLLERR);
|
|
read(pfd[i].fd, val, 2);
|
|
lseek(pfd[i].fd, 0, SEEK_SET);
|
|
|
|
if (atoi(val) != 0)
|
|
continue;
|
|
|
|
input_gpio(i % 15, &event.cbutton.button);
|
|
|
|
if (i < 15)
|
|
event.cbutton.which = 0;
|
|
else
|
|
event.cbutton.which = 1;
|
|
|
|
SDL_PushEvent(&event);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* __FreeBSD__ */
|
|
|
|
#endif /* GPIO */
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
printf("Snake " VERSION "." RELEASE "." PATCH EXTRA "\n"
|
|
"Copyright (c) 2019-2021, yzrh <yzrh@noema.org>\n");
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO |
|
|
SDL_INIT_AUDIO |
|
|
SDL_INIT_GAMECONTROLLER) != 0) {
|
|
fprintf(stderr, "SDL initialisation failure: %s\n",
|
|
SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
SDL_Window *window = SDL_CreateWindow(
|
|
"Snake " VERSION "." RELEASE "." PATCH EXTRA,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SCREEN_WIDTH,
|
|
SCREEN_HEIGHT,
|
|
SDL_WINDOW_OPENGL);
|
|
|
|
if (window == NULL) {
|
|
fprintf(stderr, "Window creation failure: %s\n",
|
|
SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
renderer = SDL_CreateRenderer(
|
|
window,
|
|
-1,
|
|
SDL_RENDERER_ACCELERATED |
|
|
SDL_RENDERER_PRESENTVSYNC);
|
|
|
|
if (renderer == NULL) {
|
|
fprintf(stderr, "Renderer creation failure: %s\n",
|
|
SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
if (TTF_Init() != 0) {
|
|
fprintf(stderr, "TTF initialisation failure: %s\n",
|
|
TTF_GetError());
|
|
return 1;
|
|
}
|
|
|
|
font_20 = TTF_OpenFont("../contrib/font.ttf", 20);
|
|
font_24 = TTF_OpenFont("../contrib/font.ttf", 24);
|
|
|
|
if (font_20 == NULL) {
|
|
font_20 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 20);
|
|
font_24 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 24);
|
|
}
|
|
|
|
if (font_20 == NULL) {
|
|
font_20 = TTF_OpenFont("/usr/share/snake/font.ttf", 20);
|
|
font_24 = TTF_OpenFont("/usr/share/snake/font.ttf", 24);
|
|
}
|
|
|
|
if (font_20 == NULL) {
|
|
fprintf(stderr, "%s\n", TTF_GetError());
|
|
return 1;
|
|
}
|
|
|
|
#ifdef SOUND
|
|
|
|
if ((Mix_Init(MIX_INIT_FLAC) & MIX_INIT_FLAC) != MIX_INIT_FLAC)
|
|
fprintf(stderr, "Mixer initialisation failure: %s\n",
|
|
Mix_GetError());
|
|
|
|
if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 4096) == -1)
|
|
fprintf(stderr, "%s\n", Mix_GetError());
|
|
|
|
Mix_Music *music = Mix_LoadMUS("../contrib/music.flac");
|
|
|
|
if (music == NULL)
|
|
music = Mix_LoadMUS("/usr/local/share/snake/music.flac");
|
|
|
|
if (music == NULL)
|
|
music = Mix_LoadMUS("/usr/share/snake/music.flac");
|
|
|
|
if (music == NULL)
|
|
fprintf(stderr, "%s\n", Mix_GetError());
|
|
|
|
if (Mix_PlayMusic(music, -1) == -1)
|
|
fprintf(stderr, "Audio playback failure: %s\n",
|
|
Mix_GetError());
|
|
|
|
#endif /* SOUND */
|
|
|
|
#ifdef GPIO
|
|
|
|
#ifdef __linux__
|
|
|
|
gpio_export(GPIO_PIN_IN0);
|
|
gpio_export(GPIO_PIN_IN1);
|
|
gpio_export(GPIO_PIN_IN2);
|
|
gpio_export(GPIO_PIN_IN3);
|
|
gpio_export(GPIO_PIN_IN4);
|
|
gpio_export(GPIO_PIN_IN5);
|
|
gpio_export(GPIO_PIN_IN6);
|
|
gpio_export(GPIO_PIN_IN7);
|
|
gpio_export(GPIO_PIN_IN8);
|
|
gpio_export(GPIO_PIN_IN9);
|
|
gpio_export(GPIO_PIN_IN10);
|
|
gpio_export(GPIO_PIN_IN11);
|
|
gpio_export(GPIO_PIN_IN12);
|
|
gpio_export(GPIO_PIN_IN13);
|
|
gpio_export(GPIO_PIN_IN14);
|
|
gpio_export(GPIO_PIN_IN15);
|
|
gpio_export(GPIO_PIN_IN16);
|
|
gpio_export(GPIO_PIN_IN17);
|
|
gpio_export(GPIO_PIN_IN18);
|
|
|
|
/* Wait for sysfs directory to show up */
|
|
sleep(1);
|
|
|
|
#endif /* __linux__ */
|
|
|
|
gpio_config(GPIO_PIN_IN0, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN1, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN2, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN3, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN4, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN5, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN6, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN7, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN8, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN9, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN10, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN11, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN12, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN13, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN14, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN15, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN16, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN17, GPIO_IN, GPIO_INT);
|
|
gpio_config(GPIO_PIN_IN18, GPIO_IN, GPIO_INT);
|
|
|
|
#endif /* GPIO */
|
|
|
|
int seed;
|
|
int random = open("/dev/random", O_RDONLY);
|
|
if (read(random, &seed, sizeof(int)) > 0)
|
|
srand(seed);
|
|
close(random);
|
|
|
|
SDL_JoystickID player = 0;
|
|
SDL_Event event;
|
|
|
|
pthread_mutex_init(&mutex, NULL);
|
|
pthread_cond_init(&cond, NULL);
|
|
pthread_t th_renderer;
|
|
pthread_t th_logic;
|
|
pthread_t th_gravity;
|
|
|
|
#ifdef GPIO
|
|
|
|
pthread_t th_gpio;
|
|
|
|
#endif /* GPIO */
|
|
|
|
pthread_create(&th_renderer, NULL, handler_rendering, NULL);
|
|
pthread_create(&th_logic, NULL, handler_logic, NULL);
|
|
pthread_create(&th_gravity, NULL, handler_gravity, NULL);
|
|
|
|
#ifdef GPIO
|
|
|
|
pthread_create(&th_gpio, NULL, handler_gpio, NULL);
|
|
|
|
#endif /* GPIO */
|
|
|
|
while (SDL_WaitEvent(&event)) {
|
|
if (event.type == SDL_QUIT)
|
|
break;
|
|
|
|
switch (event.type) {
|
|
case SDL_KEYUP:
|
|
if (device != SNAKE_KEYBOARD) {
|
|
device = SNAKE_KEYBOARD;
|
|
text_help_1.cache = 0;
|
|
text_help_2.cache = 0;
|
|
text_help_3.cache = 0;
|
|
text_help_4.cache = 0;
|
|
text_help_5.cache = 0;
|
|
text_help_6.cache = 0;
|
|
text_prompt_start.cache = 0;
|
|
text_prompt_resume.cache = 0;
|
|
}
|
|
input_keyboard(event.key.keysym.sym, &input);
|
|
if (multiplayer &&
|
|
(event.key.keysym.sym ==
|
|
SDLK_s ||
|
|
event.key.keysym.sym ==
|
|
SDLK_e ||
|
|
event.key.keysym.sym ==
|
|
SDLK_d ||
|
|
event.key.keysym.sym ==
|
|
SDLK_f))
|
|
player = 1;
|
|
else
|
|
player = 0;
|
|
break;
|
|
case SDL_FINGERUP:
|
|
if (device != SNAKE_TOUCHSCREEN) {
|
|
device = SNAKE_TOUCHSCREEN;
|
|
text_help_1.cache = 0;
|
|
text_help_2.cache = 0;
|
|
text_help_3.cache = 0;
|
|
text_help_4.cache = 0;
|
|
text_help_5.cache = 0;
|
|
text_help_6.cache = 0;
|
|
text_prompt_start.cache = 0;
|
|
text_prompt_resume.cache = 0;
|
|
}
|
|
if (start || reset || wait) {
|
|
if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_5)) {
|
|
input = SNAKE_RESET;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_6)) {
|
|
input = SNAKE_MUSIC;
|
|
} else if (wait) {
|
|
if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_prompt_resume))
|
|
input = SNAKE_PAUSE;
|
|
} else {
|
|
if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_prompt_0)) {
|
|
selection = 0;
|
|
input = SNAKE_SELECT;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_prompt_1)) {
|
|
selection = 1;
|
|
input = SNAKE_SELECT;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_prompt_2)) {
|
|
selection = 2;
|
|
input = SNAKE_SELECT;
|
|
}
|
|
}
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_4)) {
|
|
input = SNAKE_PAUSE;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_5)) {
|
|
input = SNAKE_RESET;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_6)) {
|
|
input = SNAKE_MUSIC;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_top)) {
|
|
input = SNAKE_GOD;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_score)) {
|
|
input = SNAKE_GROW;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_title)) {
|
|
input = SNAKE_TELEPORT;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_1)) {
|
|
input = SNAKE_TICK_DECREASE;
|
|
} else if (collision_bound(
|
|
event.tfinger.x,
|
|
event.tfinger.y,
|
|
&text_help_7)) {
|
|
input = SNAKE_TICK_INCREASE;
|
|
} else if (flappy) {
|
|
input = SNAKE_JUMP;
|
|
} else {
|
|
input = SNAKE_EMPTY;
|
|
}
|
|
break;
|
|
case SDL_FINGERMOTION:
|
|
if (device != SNAKE_TOUCHSCREEN) {
|
|
device = SNAKE_TOUCHSCREEN;
|
|
text_help_1.cache = 0;
|
|
text_help_2.cache = 0;
|
|
text_help_3.cache = 0;
|
|
text_help_4.cache = 0;
|
|
text_help_5.cache = 0;
|
|
text_help_6.cache = 0;
|
|
text_prompt_start.cache = 0;
|
|
text_prompt_resume.cache = 0;
|
|
}
|
|
if ((event.tfinger.dx *
|
|
event.tfinger.dx) >
|
|
(event.tfinger.dy *
|
|
event.tfinger.dy)) {
|
|
if (event.tfinger.dx > 1)
|
|
input = SNAKE_RIGHT;
|
|
else if (event.tfinger.dx < -1)
|
|
input = SNAKE_LEFT;
|
|
} else {
|
|
if (event.tfinger.dy > 1)
|
|
input = SNAKE_DOWN;
|
|
else if (event.tfinger.dy < -1)
|
|
input = SNAKE_UP;
|
|
}
|
|
if (multiplayer &&
|
|
event.tfinger.x > SCREEN_HEIGHT / 2)
|
|
player = 1;
|
|
else
|
|
player = 0;
|
|
break;
|
|
case SDL_CONTROLLERBUTTONUP:
|
|
if (device != SNAKE_CONTROLLER) {
|
|
device = SNAKE_CONTROLLER;
|
|
text_help_1.cache = 0;
|
|
text_help_2.cache = 0;
|
|
text_help_3.cache = 0;
|
|
text_help_4.cache = 0;
|
|
text_help_5.cache = 0;
|
|
text_help_6.cache = 0;
|
|
text_prompt_start.cache = 0;
|
|
text_prompt_resume.cache = 0;
|
|
}
|
|
input_controller(event.cbutton.button, &input);
|
|
player = event.cbutton.which;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!start && !reset && !wait) {
|
|
switch (input) {
|
|
case SNAKE_LEFT:
|
|
if (head[1][player] != SNAKE_RIGHT)
|
|
head[0][player] = SNAKE_LEFT;
|
|
break;
|
|
case SNAKE_UP:
|
|
if (head[1][player] != SNAKE_DOWN)
|
|
head[0][player] = SNAKE_UP;
|
|
break;
|
|
case SNAKE_DOWN:
|
|
if (head[1][player] != SNAKE_UP)
|
|
head[0][player] = SNAKE_DOWN;
|
|
break;
|
|
case SNAKE_RIGHT:
|
|
if (head[1][player] != SNAKE_LEFT)
|
|
head[0][player] = SNAKE_RIGHT;
|
|
break;
|
|
case SNAKE_JUMP:
|
|
if (!begin) {
|
|
pthread_mutex_lock(&mutex);
|
|
begin = 1;
|
|
pthread_cond_broadcast(&cond);
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
if (flappy) {
|
|
pthread_mutex_lock(&mutex);
|
|
distance += PIPE_GAP / 2 *
|
|
SCREEN_UNIT;
|
|
if (distance == 0)
|
|
speed = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
break;
|
|
case SNAKE_RESET:
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
break;
|
|
case SNAKE_GOD:
|
|
god = 1;
|
|
break;
|
|
case SNAKE_GROW:
|
|
if (god && !flappy && !multiplayer)
|
|
game_snake_grow(&snake0);
|
|
break;
|
|
case SNAKE_TELEPORT:
|
|
if (god) {
|
|
snake0->x = snake0->y =
|
|
SCREEN_HEIGHT / 2;
|
|
if (flappy) {
|
|
snake0->next->x =
|
|
SCREEN_HEIGHT /
|
|
2 - SCREEN_UNIT;
|
|
snake0->next->y =
|
|
SCREEN_HEIGHT /
|
|
2;
|
|
}
|
|
}
|
|
break;
|
|
case SNAKE_MUSIC:
|
|
#ifdef SOUND
|
|
if (Mix_PausedMusic())
|
|
Mix_ResumeMusic();
|
|
else
|
|
Mix_PauseMusic();
|
|
#endif /* SOUND */
|
|
break;
|
|
case SNAKE_TICK_DECREASE:
|
|
if (god && !flappy && delay.tv_nsec -
|
|
TICK_TIME_DEC >= TICK_TIME_MIN)
|
|
delay.tv_nsec -= TICK_TIME_DEC;
|
|
break;
|
|
case SNAKE_TICK_INCREASE:
|
|
if (god && !flappy)
|
|
delay.tv_nsec += TICK_TIME_DEC;
|
|
break;
|
|
case SNAKE_PAUSE:
|
|
wait = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (input) {
|
|
case SNAKE_RESET:
|
|
wait = 0;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
break;
|
|
case SNAKE_MUSIC:
|
|
#ifdef SOUND
|
|
if (Mix_PausedMusic())
|
|
Mix_ResumeMusic();
|
|
else
|
|
Mix_PauseMusic();
|
|
#endif /* SOUND */
|
|
break;
|
|
case SNAKE_SELECT:
|
|
if (wait)
|
|
break;
|
|
|
|
if (selection == 0) {
|
|
flappy = 0;
|
|
multiplayer = 0;
|
|
} else if (selection == 1) {
|
|
flappy = 0;
|
|
multiplayer = 1;
|
|
} else if (selection == 2) {
|
|
multiplayer = 0;
|
|
flappy = 1;
|
|
}
|
|
|
|
delay.tv_nsec = TICK_TIME_INIT;
|
|
|
|
god = 0;
|
|
|
|
head[0][0] = head[0][1] =
|
|
SNAKE_UP;
|
|
head[1][0] = head[1][1] =
|
|
SNAKE_EMPTY;
|
|
|
|
score = 0;
|
|
text_score.cache = 0;
|
|
|
|
if (flappy) {
|
|
delay.tv_nsec /= SCREEN_UNIT;
|
|
|
|
game_snake_init(&snake0, 0);
|
|
|
|
snake0->next->x =
|
|
SCREEN_HEIGHT / 2 -
|
|
SCREEN_UNIT;
|
|
snake0->next->y =
|
|
SCREEN_HEIGHT / 2;
|
|
|
|
game_pipe_init(&gap);
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
begin = 0;
|
|
distance = 0;
|
|
speed = 1;
|
|
pthread_mutex_unlock(&mutex);
|
|
} else if (!multiplayer) {
|
|
game_snake_init(
|
|
&snake0, 0);
|
|
game_fruit(
|
|
fruit,
|
|
&snake0);
|
|
} else {
|
|
game_snake_init(
|
|
&snake0,
|
|
-4 *
|
|
SCREEN_UNIT);
|
|
game_snake_init(
|
|
&snake1,
|
|
4 *
|
|
SCREEN_UNIT);
|
|
}
|
|
start = 0;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 0;
|
|
pthread_cond_broadcast(&cond);
|
|
pthread_mutex_unlock(&mutex);
|
|
break;
|
|
case SNAKE_PAUSE:
|
|
wait = 0;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
pthread_cond_broadcast(&cond);
|
|
pthread_mutex_unlock(&mutex);
|
|
break;
|
|
case SNAKE_UP:
|
|
if ((start || reset) &&
|
|
selection - 1 >= 0)
|
|
selection--;
|
|
break;
|
|
case SNAKE_DOWN:
|
|
if ((start || reset) &&
|
|
selection + 1 <= 2)
|
|
selection++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
input = SNAKE_EMPTY;
|
|
}
|
|
|
|
start = 0;
|
|
end = 1;
|
|
|
|
wait = 0;
|
|
flappy = 1;
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
reset = 0;
|
|
pthread_cond_broadcast(&cond);
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
#ifdef GPIO
|
|
|
|
pthread_join(th_gpio, NULL);
|
|
|
|
#endif /* GPIO */
|
|
|
|
pthread_join(th_gravity, NULL);
|
|
pthread_join(th_logic, NULL);
|
|
pthread_join(th_renderer, NULL);
|
|
pthread_cond_destroy(&cond);
|
|
pthread_mutex_destroy(&mutex);
|
|
|
|
#ifdef GPIO
|
|
|
|
#ifdef __linux__
|
|
|
|
gpio_unexport(GPIO_PIN_IN0);
|
|
gpio_unexport(GPIO_PIN_IN1);
|
|
gpio_unexport(GPIO_PIN_IN2);
|
|
gpio_unexport(GPIO_PIN_IN3);
|
|
gpio_unexport(GPIO_PIN_IN4);
|
|
gpio_unexport(GPIO_PIN_IN5);
|
|
gpio_unexport(GPIO_PIN_IN6);
|
|
gpio_unexport(GPIO_PIN_IN7);
|
|
gpio_unexport(GPIO_PIN_IN8);
|
|
gpio_unexport(GPIO_PIN_IN9);
|
|
gpio_unexport(GPIO_PIN_IN10);
|
|
gpio_unexport(GPIO_PIN_IN11);
|
|
gpio_unexport(GPIO_PIN_IN12);
|
|
gpio_unexport(GPIO_PIN_IN13);
|
|
gpio_unexport(GPIO_PIN_IN14);
|
|
gpio_unexport(GPIO_PIN_IN15);
|
|
gpio_unexport(GPIO_PIN_IN16);
|
|
gpio_unexport(GPIO_PIN_IN17);
|
|
gpio_unexport(GPIO_PIN_IN18);
|
|
|
|
#endif /* __linux__ */
|
|
|
|
#endif /* GPIO */
|
|
|
|
#ifdef SOUND
|
|
Mix_Quit();
|
|
#endif /* SOUND */
|
|
|
|
TTF_Quit();
|
|
SDL_DestroyRenderer(renderer);
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
}
|