diff --git a/Makefile b/Makefile index 4b19473..d71d436 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,6 @@ clean: rm *.o rm game -game: main.o game.o world.o snake.o - gcc -rdynamic -g main.o game.o world.o snake.o -lcurses -lm -o game +game: main.o world.o snake.o + gcc -rdynamic -g main.o world.o snake.o -lcurses -lm -o game diff --git a/game.c b/game.c index 8464a73..1ec7102 100644 --- a/game.c +++ b/game.c @@ -4,16 +4,17 @@ #include "world.h" #include "game.h" #include "snake.h" - +// This file contains functions for drawing the the game and changing the state // Start is called one in the beginning -void* init_game(struct world* world){ - +void* init_game(){ // Allocate memory for the state struct state* st = calloc(1,(sizeof(struct state))); - st->snake = NULL; - st->sx = 1; - st->sy = 0; + return st; + +} + +void init_snake(struct event* world, struct state* st){ int cy = world->height/2; int cx = world->width/2 - 5; for (int i = 0; i < 5; i++){ @@ -26,55 +27,60 @@ void* init_game(struct world* world){ st->foodx[i] = rand() % w; st->foody[i] = rand() % h; } - return st; - } // Step is called in a loop once in interval. // It should modify the state and draw it. -int world_event(struct world* w,void* game){ +int world_event(struct event* w,void* game){ // Get state pointer struct state* st = game; - int key = w->key; + if (w->type == EVENT_START){ + init_snake(w,st); + } + else if (w->type == EVENT_KEY){ + int key = w->key; - if (key == KEY_RIGHT){ - st->sx = 1; - st->sy = 0; - } - else if (key == KEY_LEFT){ - st->sx = -1; - st->sy = 0; - } - else if (key == KEY_DOWN){ - st->sx = 0; - st->sy = 1; - } - else if (key == KEY_UP){ - st->sx = 0; - st->sy = -1; - } - else if (key == KEY_ENTER){ - // Non zero means finish the loop and stop the game. - return 1; - } - st->width = w->width; - st->height = w->height; - int r = step_state(st); - // Draw snake - struct snake* sn = st->snake; - while (sn != NULL){ - set_cell(w,'x',sn->x,sn->y); - sn = sn->next; - } - for (int i = 0 ; i < FOOD_COUNT; i++){ - if (st->foodx[i] >= 0 && st->foody[i] >= 0){ - set_cell(w,'*',st->foodx[i],st->foody[i]); + if (key == KEY_RIGHT){ + st->sx = 1; + st->sy = 0; + } + else if (key == KEY_LEFT){ + st->sx = -1; + st->sy = 0; + } + else if (key == KEY_DOWN){ + st->sx = 0; + st->sy = 1; + } + else if (key == KEY_UP){ + st->sx = 0; + st->sy = -1; } } - if (r){ - char message[] = "Koniec"; - for (int i = 0; i < 6; i++){ - set_cell(w,message[i],10+i,10); + else if (w->type == EVENT_ESC){ + // Non zero means finish the loop and stop the game. + return 1; + } + else if (w->type == EVENT_TIMEOUT){ + st->width = w->width; + st->height = w->height; + int r = step_state(st); + // Draw snake + struct snake* sn = st->snake; + while (sn != NULL){ + set_cell('x',sn->x,sn->y); + sn = sn->next; + } + for (int i = 0 ; i < FOOD_COUNT; i++){ + if (st->foodx[i] >= 0 && st->foody[i] >= 0){ + set_cell('*',st->foodx[i],st->foody[i]); + } + } + if (r){ + char message[] = "Koniec"; + for (int i = 0; i < 6; i++){ + set_cell(message[i],10+i,10); + } } } return 0; diff --git a/game.h b/game.h index 67af71e..c3eea84 100644 --- a/game.h +++ b/game.h @@ -2,25 +2,10 @@ #define _GAME_H_INCLUDE_ #include "world.h" -// Set of variables that expresses state of the game. -// -struct game { - // X position of the cat - int catx; - // Y opsition of the cat - int caty; - // X position of the mouse - int mousex; - // Y position of the mouse - int mousey; - // Funky message - char message[100]; -}; - -// Returns pointer to newly allocated world -void* init_game(struct world* g); +// Returns pointer to newly allocated state +void* init_game(); // Changes world according to the game state (pressed key, screen size or other event) -int world_event(struct world* world,void* game); +int world_event(struct event* event,void* state); #endif diff --git a/main.c b/main.c index cacab63..9e95e66 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,93 @@ -#include "game.h" #include "world.h" #include +#include +#include +#include +#include "snake.h" +// This file contains functions for drawing the the game and changing the state + +// Start is called one in the beginning +void* init_game(){ + // Allocate memory for the state + struct state* st = calloc(1,(sizeof(struct state))); + return st; + +} + +void init_snake(struct event* world, struct state* st){ + int cy = world->height/2; + int cx = world->width/2 - 5; + for (int i = 0; i < 5; i++){ + st->snake = add_snake(st->snake,cx + i ,cy); + } + + int h = world->height; + int w = world->width; + for (int i = 0; i < 5; i++){ + st->foodx[i] = rand() % w; + st->foody[i] = rand() % h; + } +} + +// Step is called in a loop once in interval. +// It should modify the state and draw it. +int world_event(struct event* w,void* game){ + // Get state pointer + struct state* st = game; + if (w->type == EVENT_START){ + init_snake(w,st); + } + else if (w->type == EVENT_KEY){ + int key = w->key; + + if (key == KEY_RIGHT){ + st->sx = 1; + st->sy = 0; + } + else if (key == KEY_LEFT){ + st->sx = -1; + st->sy = 0; + } + else if (key == KEY_DOWN){ + st->sx = 0; + st->sy = 1; + } + else if (key == KEY_UP){ + st->sx = 0; + st->sy = -1; + } + } + else if (w->type == EVENT_ESC){ + // Non zero means finish the loop and stop the game. + return 1; + } + else if (w->type == EVENT_TIMEOUT){ + st->width = w->width; + st->height = w->height; + int r = step_state(st); + // Draw snake + struct snake* sn = st->snake; + while (sn != NULL){ + set_cell('x',sn->x,sn->y); + sn = sn->next; + } + for (int i = 0 ; i < FOOD_COUNT; i++){ + if (st->foodx[i] >= 0 && st->foody[i] >= 0){ + set_cell('*',st->foodx[i],st->foody[i]); + } + } + if (r){ + char message[] = "Koniec"; + for (int i = 0; i < 6; i++){ + set_cell(message[i],10+i,10); + } + } + } + return 0; +} int main(int argc, char** argv){ - start_world(world_event,init_game,free); + start_world(init_game,world_event,free); return 0; } diff --git a/world.c b/world.c index bc519e1..a45c109 100644 --- a/world.c +++ b/world.c @@ -5,67 +5,80 @@ #include #include -#include -#include - -void print_backtrace() { - //https://linux.die.net/man/3/backtrace_symbols - int j, nptrs; - void *buffer[100]; - char **strings; - nptrs = backtrace(buffer, 100); - printf("backtrace() returned %d addresses\n", nptrs); - - /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) - would produce similar output to the following: */ - - strings = backtrace_symbols(buffer, nptrs); - if (strings == NULL) { - perror("backtrace_symbols"); - exit(EXIT_FAILURE); - } - for (j = 0; j < nptrs; j++) - printf("%s\n", strings[j]); - free(strings); -} +int TIMEOUT; void abort_game(const char* message){ endwin(); - print_backtrace(); puts(message); exit(1); } +void check_bounds(const char* source,int x, int y){ + char msg[200]; + if (x < 0 || x >= COLS){ + sprintf(msg,"%s:: width %d is out of bounds (0,%d)",source,x,COLS); + abort_game(msg); + } + if (y < 0 || y >= LINES){ + sprintf(msg,"%s:: height %d is out of bounds (0,%d)",source,y,LINES); + abort_game(msg); + } +} + +void clear_screen(){ + // Clear screen + mvaddch(0,0,' '); + int screenchars = LINES*COLS; + for (int j = 1; j < screenchars;j++ ){ + addch(' '); + } +} + +void game_speed(int value){ + if (value < 0){ + abort_game("world_seed:: cannot be negative\n"); + } + TIMEOUT =value; +} + +void set_message(const char* message,int x,int y) { + int l = strlen(message); + for (int i = 0; i < l; i++){ + check_bounds("set_message",x+i,y); + set_cell(message[i],x+i,y); + } +} + void assert_message(int event,const char* message){ if (event == 0){ abort_game(message); } } -void stamp_cell(struct world* w,int character,enum color_stamp pen, int x,int y) { - assert_message(w != NULL,"stamp_cell:: world is NULL"); - if (x < 0 || x >= w->width){ - char msg[100]; - sprintf(msg,"stamp_cell:: width %d is out of bounds (0,%d)",x,w->width); - abort_game(msg); - } - if (y < 0 || y >= w->height){ - char msg[100]; - sprintf(msg,"stamp_cell:: height %d is out of bounds (0,%d)",y,w->height); - abort_game(msg); - } - attron(pen); - mvaddch(y,x,character); - attroff(pen); + +void set_cell(int character,int x,int y) { + check_bounds("set_cell",x,y); + set_color_cell(character,x,y,COLOR_WHITE,COLOR_BLACK); } -void set_cell(struct world* w,int character,int x,int y) { - stamp_cell(w,character,0,x,y); +void set_color_cell(int character,int x,int y,short front_color,short back_color){ + check_bounds("set_color_cell",x,y); + if (has_colors()){ + int pair = COLOR_COUNT * front_color + back_color; + attron(COLOR_PAIR(pair)); + mvaddch(y,x,character); + attroff(COLOR_PAIR(pair)); + } + else{ + mvaddch(y,x,character); + } } -int start_world(int (*world_event)(struct world* world,void* game),void* (*init_game)(struct world*),void (*destroy_game)(void*)){ +int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void*)){ srand(time(NULL)); int r = 1; + // Speed global variable + TIMEOUT = 100; if (initscr() == NULL){ // TODO Which Error? puts("Curses Error."); @@ -76,60 +89,107 @@ int start_world(int (*world_event)(struct world* world,void* game),void* (*init_ nodelay(stdscr,TRUE); // Nečakaj na stlačenie keypad(stdscr,TRUE); // Aktivuje šípky curs_set(FALSE); // Neviditeľný kurzor + /* Get all the mouse events */ + mousemask(ALL_MOUSE_EVENTS, NULL); + MEVENT mouse_event; if (has_colors()){ // Zistenie či terminál podporuje farby start_color(); - // Pera na ciernom podklade - init_pair(BLACK_FRONT, COLOR_BLACK, COLOR_BLACK); - init_pair(WHITE_FRONT, COLOR_WHITE, COLOR_BLACK); - init_pair(RED_FRONT, COLOR_RED, COLOR_BLACK); - init_pair(GREEN_FRONT, COLOR_GREEN, COLOR_BLACK); - init_pair(BLUE_FRONT, COLOR_BLUE, COLOR_BLACK); - init_pair(CYAN_FRONT, COLOR_CYAN, COLOR_BLACK); - init_pair(MAGENTA_FRONT,COLOR_MAGENTA, COLOR_BLACK); - init_pair(YELLOW_FRONT, COLOR_YELLOW, COLOR_BLACK); - init_pair(BLACK_BACK, COLOR_BLACK, COLOR_BLACK); - init_pair(WHITE_BACK, COLOR_BLACK, COLOR_WHITE); - init_pair(RED_BACK, COLOR_BLACK, COLOR_RED); - init_pair(GREEN_BACK, COLOR_BLACK, COLOR_GREEN); - init_pair(BLUE_BACK, COLOR_BLACK, COLOR_BLUE); - init_pair(CYAN_BACK, COLOR_BLACK, COLOR_CYAN); - init_pair(MAGENTA_BACK, COLOR_BLACK, COLOR_MAGENTA); - init_pair(YELLOW_BACK, COLOR_BLACK, COLOR_YELLOW); + for (int i = 0; i < COLOR_COUNT;i++){ + for (int j = 0; j < COLOR_COUNT;j++){ + init_pair(i * COLOR_COUNT + j, i,j); + } + } } else { puts("No colors!\n"); } - struct world world; - world.height = LINES; - world.width = COLS; - world.interval = 100; - world.key = ERR; - void* game = NULL; if (init_game != NULL){ - game = init_game(&world); - assert_message(game != NULL,"init_game should return non null pointer"); + game = init_game(); + assert_message(game != NULL,"init_game:: should return non null pointer"); } - timeout(world.interval); + timeout(TIMEOUT); // Initial step - r = world_event(&world,game); + struct event event; + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.type = EVENT_START; + clock_t start_time = clock(); + clock_t last_timeout = start_time; + clock_t next_timeout = last_timeout + TIMEOUT; + event.time_ms = start_time; + // Start event + r = world_event(&event,game); refresh(); while (!r) { - world.height = LINES; - world.width = COLS; - world.key = getch(); - // Clear screen - mvaddch(0,0,' '); - int screenchars = LINES*COLS; - for (int j = 1; j < screenchars;j++ ){ - addch(' '); + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.key = getch(); + // No key was pressed + if (event.key == ERR){ + event.type = EVENT_TIMEOUT; + last_timeout = clock(); + next_timeout = last_timeout + TIMEOUT; + } + // Mouse event + else if (event.key == KEY_MOUSE ){ + event.type = EVENT_MOUSE; + if(getmouse(&mouse_event) == OK){ + event.mouse_x = mouse_event.x; + event.mouse_y = mouse_event.y; + if(mouse_event.bstate & BUTTON1_PRESSED){ + event.mouse_left = 1; + } + if(mouse_event.bstate & BUTTON2_PRESSED){ + event.mouse_middle = 1; + } + if(mouse_event.bstate & BUTTON3_PRESSED){ + event.mouse_right = 1; + } + } + } + else if (event.key == KEY_RESIZE) { + event.type = EVENT_RESIZE; + } + else{ + event.type = EVENT_KEY; + if (event.key == 27){ + int k = getch(); + if (k == -1){ + // Esc Was pressed + event.type = EVENT_ESC; + } + else { + // Alt was pressed + event.key = k; + event.alt_key = 1; + } + } } // Draw new world - r = world_event(&world,game); + event.time_ms = clock(); + r = world_event(&event,game); refresh(); + event.time_ms = clock(); // set new timeout - timeout(world.interval); + int nt = next_timeout - event.time_ms; + //printf("%d\n",nt); + if (nt > 0){ + timeout(nt); + } + else { + timeout(TIMEOUT); + next_timeout = event.time_ms + TIMEOUT; + } } + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.type = EVENT_END; + event.time_ms = clock(); + world_event(&event,game); if (destroy_game != NULL){ destroy_game(game); } diff --git a/world.h b/world.h index e83e126..73be057 100644 --- a/world.h +++ b/world.h @@ -2,6 +2,7 @@ #define _WORLD_H_ #include + /** * World represented as a rectangular matrix of colorful characters. * @@ -9,7 +10,17 @@ * */ -struct world { +enum event_type { + EVENT_START, + EVENT_TIMEOUT, + EVENT_KEY, + EVENT_MOUSE, + EVENT_RESIZE, + EVENT_ESC, + EVENT_END, +}; + +struct event { /** * Last width of the screen. */ @@ -18,10 +29,6 @@ struct world { * Last height of the screen. */ int height; - /** - * Interval in miliseconds to wait for next step. - */ - int interval; /** * Last pressed key or Curses event. * @@ -48,53 +55,59 @@ struct world { * KEY_BACKSPACE */ int key; -}; - - -enum color_stamp { - BLACK_FRONT, - WHITE_FRONT, - RED_FRONT, - GREEN_FRONT, - BLUE_FRONT, - CYAN_FRONT, - MAGENTA_FRONT, - YELLOW_FRONT, - BLACK_BACK, - WHITE_BACK, - RED_BACK, - GREEN_BACK, - BLUE_BACK, - CYAN_BACK, - MAGENTA_BACK, - YELLOW_BACK, + int alt_key; + enum event_type type; + int mouse_x; + int mouse_y; + int mouse_left; + int mouse_right; + int mouse_middle; + long int time_ms; }; /** * Sets cell to a state. - * @param world + * @param event * @param x coordinate of cell * @param y coordinate of cell * @param new state of the cell */ -void set_cell(struct world* w,int character,int x,int y); +void set_cell(int character,int x,int y); + +/** + * COLOR_BLACK 0 + * COLOR_RED 1 + * COLOR_GREEN 2 + * COLOR_YELLOW 3 + * COLOR_BLUE 4 + * COLOR_MAGENTA 5 + * COLOR_CYAN 6 + * COLOR_WHITE 7 + */ + +#define COLOR_COUNT 8 + +void set_color_cell(int character,int x,int y,short front_color,short back_color); -void stamp_cell(struct world* w,int character,enum color_stamp stamp,int x,int y); /** * - * @param world + * @param event * @param number of commandline arguments * @param init_world * @param destroy_world * - * void init_world(struct world* w); + * void init_world(struct event* w); * Initializes user state. * Free user state. - * @param world + * @param event */ -int start_world(int (*world_event)(struct world* world,void* game),void* (*init_game)(struct world* world),void (*destroy_game)(void* game)); +int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void* game)); +void game_speed(int value); + +void set_message(const char* message,int x,int y); +void clear_screen(); #endif