diff --git a/c/gtk3/jumper/ghost.png b/c/gtk3/jumper/ghost.png new file mode 100644 index 0000000..532ea2d Binary files /dev/null and b/c/gtk3/jumper/ghost.png differ diff --git a/c/gtk3/jumper/jumper.c b/c/gtk3/jumper/jumper.c new file mode 100644 index 0000000..d4257d2 --- /dev/null +++ b/c/gtk3/jumper/jumper.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include + + +#define COUNT 10 + +const double speed = 10.0; +const double jump = 40.0; +const double gravity = 2.0; + +typedef struct { + double left; + double top; + double right; + double bottom; +} Block; + +cairo_surface_t *surface; +GtkWidget *window; +Block blocks[COUNT]; +gboolean left_key = FALSE; +gboolean right_key = FALSE; +double px = 0.0; +double py = 0.0; +double vx = 0.0; +double vy = 0.0; +double score = 0.0; +double max_score = 0.0; + + +void generate_block(Block *block, double top) { + double window_width = gtk_widget_get_allocated_width(window); + double width = ((double)rand()/(double)RAND_MAX + 1.0) * window_width / 6.0; + block->left = (double)rand()/(double)RAND_MAX * (window_width - width); + block->right = block->left + width; + block->top = top; + block->bottom = block->top + 10; +} + +void generate_blocks() { + srand(time(NULL)); + double window_height = gtk_widget_get_allocated_height(window); + for(int i = 0; i < COUNT; ++i) + generate_block(&blocks[i], window_height); +} + +void scroll_blocks(double dy) { + double window_height = gtk_widget_get_allocated_height(window); + score += dy; + if (score > max_score) max_score = score; + py += dy; + for(int i = 0; i < COUNT; ++i) { + blocks[i].top += dy; + blocks[i].bottom += dy; + if (blocks[i].top < 0) { + generate_block(&blocks[i], window_height + (double)rand()/(double)RAND_MAX*fabs(dy)); + } else + if (blocks[i].top > window_height) { + generate_block(&blocks[i], -10.0 - (double)rand()/(double)RAND_MAX*fabs(dy)); + } + } +} + +gboolean timeout(gpointer data) { + double window_height = gtk_widget_get_allocated_height(window); + + vy += gravity; + + if (left_key) px -= speed; + if (right_key) px += speed; + + if (vy > 0.0) { + gboolean found = FALSE; + double top = py + vy; + for(int i = 0; i < COUNT; ++i) { + if ( px >= blocks[i].left + && px <= blocks[i].right + && blocks[i].top >= py + && blocks[i].top <= top ) + { + found = TRUE; + top = blocks[i].top; + } + } + if (found) { + py = top; + vy = -jump; + } else { + py += vy; + } + } else { + py += vy; + } + + scroll_blocks(window_height/2.0 - py); + + gtk_widget_queue_draw(window); + return TRUE; +} + +gboolean key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) { + if (event->keyval == GDK_KEY_Left) left_key = TRUE; + if (event->keyval == GDK_KEY_Right) right_key = TRUE; + if (event->keyval == GDK_KEY_space) generate_blocks(); + return TRUE; +} + +gboolean key_release(GtkWidget *widget, GdkEventKey *event, gpointer data) { + if (event->keyval == GDK_KEY_Left) left_key = FALSE; + if (event->keyval == GDK_KEY_Right) right_key = FALSE; + return TRUE; +} + +gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) { + cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 1); + cairo_paint(cr); + + cairo_set_source_rgba(cr, 0, 0, 0, 1); + for(int i = 0; i < COUNT; ++i) { + cairo_rectangle( + cr, + blocks[i].left, + blocks[i].top, + blocks[i].right - blocks[i].left, + blocks[i].bottom - blocks[i].top ); + cairo_fill(cr); + } + + cairo_set_source_surface( + cr, + surface, + px - cairo_image_surface_get_width(surface)/2.0, + py - cairo_image_surface_get_height(surface) ); + cairo_paint(cr); + + cairo_set_source_rgba(cr, 0, 0, 0, 1); + cairo_set_font_size(cr, 15); + char text[1024]; + sprintf(text, "Score: %.0f", score); + cairo_move_to(cr, 10, 30); + cairo_show_text(cr, text); + sprintf(text, "Max score: %.0f", max_score); + cairo_move_to(cr, 10, 50); + cairo_show_text(cr, text); + + return TRUE; +} + +void activate(GtkApplication* app, gpointer data) { + window = gtk_application_window_new(app); + g_signal_connect(window, "draw", G_CALLBACK(draw), NULL); + g_signal_connect(window, "key-press-event", G_CALLBACK(key_press), NULL); + g_signal_connect(window, "key-release-event", G_CALLBACK(key_release), NULL); + gtk_widget_add_events(window, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + gtk_window_set_default_size(GTK_WINDOW(window), 800, 500); + gtk_widget_show_all(window); + + generate_blocks(); + px = (blocks[0].left + blocks[0].right) / 2; + py = blocks[0].top; + + g_timeout_add(50, timeout, NULL); +} + +int main(int argc, char **argv) { + surface = cairo_image_surface_create_from_png("ghost.png"); + + GtkApplication *application = gtk_application_new(NULL, 0); + g_signal_connect(application, "activate", G_CALLBACK(activate), NULL); + int status = g_application_run(G_APPLICATION(application), argc, argv); + g_object_unref(application); + + cairo_surface_destroy(surface); + return status; +} +