Blob Blame Raw
#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;
}