Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

inspiration.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "inspiration.h"
#include "widget/wmisc.h"
#include "util/warning.h"
#include <gtkmm/label.h>
#include <gtkmm/drawingarea.h>
#include <gtkmm/main.h>
#include <gtkmm/style.h>
#include <gtkmm/stock.h>
#include <gdkmm.h>
#include <util/stringutil.h>

// To build as stand-alone program:
// g++ -g -DSTANDALONE_INSPIRATION -Wall -o inspiration `pkg-config gtkmm-2.0 --cflags --libs` inspiration.cc 
#ifdef STANDALONE_INSPIRATION
unsigned int border_width = 12;
#endif

class Game: public Gtk::DrawingArea {
  SigC::Connection connection;
  Glib::RefPtr<Gdk::Window> win;
  Glib::RefPtr<Gdk::Visual> visual;
  Glib::RefPtr<Gdk::GC> gc;
  Glib::RefPtr<Gdk::Image> image;
  static const int candy_size = 10;
  int score, grow_count;
  bool has_moved;
  struct Vect {int x, y;};
  Vect candy_pos;
  struct Snake {
    static const int width = 2;
    Vect direction, head, tail;
  } snake;
  virtual bool on_configure_event(GdkEventConfigure*);
  virtual bool on_expose_event(GdkEventExpose*);
  bool grow();
  void shrink();
  bool update();
  bool check_clean(int x, int y, int w, int h);
  void candy_found();
  Gdk::Color snake_color, bg_color, candy_color;
  void put_pixel(Glib::RefPtr<Gdk::Image>, int x, int y, Gdk::Color &color);
  void draw_rect(Gdk::Color &color, bool filled, int x, int y, int w, int h);
public:
  SigC::Signal1<void, long> score_change_signal;
  Game();
  bool on_key_press_event(GdkEventKey*);
  void new_game();
  void stop_game();
};

Game::Game():
  Gtk::DrawingArea(), has_moved(false), snake_color("green"), 
  bg_color("black"), candy_color("light green")
{
  set_events(Gdk::EXPOSURE_MASK|
           Gdk::KEY_PRESS_MASK);
}

bool Game::on_configure_event(GdkEventConfigure*) {
  Glib::RefPtr<Gdk::Colormap> colormap = get_colormap();
  if(!(colormap->alloc_color(snake_color)
       && colormap->alloc_color(candy_color)
       && colormap->alloc_color(bg_color)))
    warning << "Inspiration:  Failed to allocate required colors" << std::endl;
  win = get_window();
  visual = win->get_visual();

  if(!image) {
      gc = get_style()->get_white_gc();
      image = Gdk::Image::create(Gdk::IMAGE_NORMAL, visual, 
                         get_width(), get_height());
      draw_rect(bg_color, true, 1, 1, get_width()-2, get_height()-2);
    }
  return true;
}

bool Game::on_key_press_event(GdkEventKey *key) {
  if(!has_moved) // to avoid moving back into itself
    return false;
  has_moved = false;
  switch(key->keyval) {
  case 65361:
    if(snake.direction.x != 1) {
      snake.direction.x = -1;
      snake.direction.y = 0;
    }
    return true;
    break;
  case 65362:
    if(snake.direction.y != 1) {
      snake.direction.x = 0;
      snake.direction.y = -1;
    }
    return true;
    break;
  case 65363:
    if(snake.direction.x != -1) {
      snake.direction.x = 1;
      snake.direction.y = 0;
    }
    return true;
    break;
  case 65364:
    if(snake.direction.y != -1) {
      snake.direction.x = 0;
      snake.direction.y = 1;
    }
    return true;
    break;
  default:
    return false;
    break;
  }
}

bool Game::on_expose_event(GdkEventExpose *event) {
  gc = get_style()->get_fg_gc(get_state());

  if(!image)
    new_game();
  else
    win->draw_image(gc ,
                image,
                event->area.x, event->area.y,
                event->area.x, event->area.y,
                event->area.width, event->area.height);
  return false;
}

void Game::put_pixel(Glib::RefPtr<Gdk::Image>, 
                 int x, int y, Gdk::Color &color)
{
  if(x >= 0 && y >= 0 && x < get_width() && y < get_height())
    image->put_pixel(x, y, color.get_pixel());
}

void Game::draw_rect(Gdk::Color &color, bool filled, 
                 int x, int y, int w, int h)
{
  if(filled) {
    for(int i = 0; i < w; i++)
      for(int j = 0; j < h; j++)
      put_pixel(image, x + i, y + j, color);
  } else {
    for(int i = 0; i < w; i++) {
      put_pixel(image, x + i, y, color);
      put_pixel(image, x + i, y + h - 1, color);
    }
    for(int j = 0; j < h; j++) {
      put_pixel(image, x, y + j, color);
      put_pixel(image, x + w - 1, y + j, color);
    }
  }
}

bool Game::check_clean(int x, int y, int w, int h) {
  for(int i = 0; i < w; i++)
    for(int j = 0; j < h; j++)
      if(image->get_pixel(x + i, y + j) != bg_color.get_pixel())  
      return false;
  return true;
}

void Game::candy_found() {
  static const int maxcount = 100;
  int i = 0;

  grow_count = 20;
  if(candy_pos.x >= 0 && candy_pos.y >= 0) {
    draw_rect(bg_color, true, 
            candy_pos.x, candy_pos.y, 
            candy_size, candy_size);
  }
  score_change_signal(score += 100);
  do {
    candy_pos.x = int((get_width() - candy_size) * 
                  (double(rand()) / RAND_MAX));
    candy_pos.y = int((get_height() - candy_size) 
                  * (double(rand()) / RAND_MAX));
  } while(i++ < maxcount && 
      !check_clean(candy_pos.x, candy_pos.y, candy_size, candy_size));
  if(i < maxcount) {
    draw_rect(candy_color, true, 
            candy_pos.x, candy_pos.y, 
            candy_size, candy_size);
    } else {
      // ?
    }
}

void Game::new_game() {
  stop_game();
  grow_count = 0;
  score = -100;
  image = Gdk::Image::create(Gdk::IMAGE_NORMAL, visual, 
                       get_width(), get_height());
  draw_rect(bg_color, true, 0, 0, get_width(), get_height());
  draw_rect(snake_color, false, 0, 0, get_width(), get_height());
  candy_pos.x = -1;
  snake.direction.x = 1; snake.direction.y = 0;
  snake.head.x = get_width() / 2; snake.head.y = get_height() / 2;
  snake.tail = snake.head;
  for(int i = 0; i < 10; i++)
    grow();
  candy_found();
  connection = Glib::signal_timeout().connect
    (slot(*this, &Game::update), 20);  
}

void Game::stop_game() {
  connection.disconnect();
}

bool Game::grow() {
  snake.head.x += snake.direction.x * snake.width;
  snake.head.y += snake.direction.y * snake.width;
  if(snake.head.x + snake.width - 1 >= candy_pos.x 
     && snake.head.y + snake.width - 1 >= candy_pos.y 
     && snake.head.x <= candy_pos.x + candy_size - 1 
     && snake.head.y <= candy_pos.y + candy_size - 1)
    candy_found();
  bool inside = 
    snake.head.x >= 0 && snake.head.x < get_width()
    && snake.head.y >= 0 && snake.head.y < get_height();
  guint32 pixel; 
  if(inside)
    pixel = image->get_pixel(snake.head.x, snake.head.y);
  draw_rect(snake_color, true, snake.head.x, snake.head.y, 
          snake.width,
          snake.width);
  return !(!inside || pixel != bg_color.get_pixel());
}

void Game::shrink() {
  if(grow_count > 0) {
      --grow_count;
      return;
    }
  draw_rect(bg_color, true, snake.tail.x, snake.tail.y, 
          snake.width, snake.width);

  Vect tmp = snake.tail;
  snake.tail = tmp; snake.tail.x += snake.width;
  if(image->get_pixel(snake.tail.x, snake.tail.y) == snake_color.get_pixel())
    return;
  snake.tail = tmp; snake.tail.x -= snake.width;
  if(image->get_pixel(snake.tail.x, snake.tail.y) == snake_color.get_pixel())
    return;
  snake.tail = tmp; snake.tail.y += snake.width;
  if(image->get_pixel(snake.tail.x, snake.tail.y) == snake_color.get_pixel())
    return;
  snake.tail = tmp; snake.tail.y -= snake.width;
  if(image->get_pixel(snake.tail.x, snake.tail.y) == snake_color.get_pixel())
    return;
}

bool Game::update() {
  shrink(); shrink();
  if(!grow() || !grow())
    connection.disconnect();
  queue_draw();
  has_moved = true;
  return true;
}

//**************************
//Inspiration
//**************************

Inspiration *Inspiration::_instance = 0;

Inspiration &Inspiration::instance() {
  if(!_instance)
    _instance = new Inspiration();
  return *_instance;
}

Inspiration::Inspiration()
  :DialogWrap("Inspiration"), score(manage(new Gtk::Label())), 
  game(new Game())
{
  set_events(Gdk::KEY_PRESS_MASK);
  game->set_size_request(320, 240);
  set_border_width(border_width);
  set_resizable(false);

  get_vbox()->pack_start(*score, Gtk::PACK_EXPAND_PADDING, 0);
  get_vbox()->pack_start(*game, Gtk::PACK_EXPAND_PADDING, 0);
  add_button(Gtk::Stock::CLOSE, 0);
  add_button(Gtk::Stock::EXECUTE, 1);

  game->score_change_signal.connect(slot(*this, &Inspiration::score_change));
}

Inspiration::~Inspiration() {}

void Inspiration::on_response(int response_id) {
  switch(response_id) {
  case 0: che_alert(); break;
  case 1: game->new_game(); break;
  default: break;
  }
}

bool Inspiration::on_key_press_event(GdkEventKey *key) {
  return game->on_key_press_event(key)
    ? false
    : Gtk::Window::on_key_press_event(key);
}

void Inspiration::che_alert() {
  game->stop_game();
  hide();
}

void Inspiration::score_change(long score_) {
  score->set_text("Score: " + tostr(int(score_)));
}

#ifdef STANDALONE_INSPIRATION
int main(int argc, char **argv) {
  Gtk::Main kit(argc, argv);
  Inspiration inspiration;
  Gtk::Main::run(inspiration);
  return 0;
}
#endif

Generated by  Doxygen 1.6.0   Back to index