#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <math.h>
#include <gtk/gtk.h>
#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;
}