/* gcompris - erase.c * * Copyright (C) 2001 Bruno Coudoin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include "gcompris/gcompris.h" #define SOUNDLISTFILE PACKAGE #define MAX_LAYERS 3 typedef struct {gint count; gint max;} counter; static GcomprisBoard *gcomprisBoard = NULL; static gboolean board_paused = TRUE; static GdkPixbuf *CoverPixmap[MAX_LAYERS]; static gulong event_handle_id; static void start_board (GcomprisBoard *agcomprisBoard); static void pause_board (gboolean pause); static void end_board (void); static gboolean is_our_board (GcomprisBoard *gcomprisBoard); static void set_level (guint level); static int gamewon; static void game_won(void); static GnomeCanvasGroup *boardRootItem = NULL; static GnomeCanvasItem *erase_create_item(); static void erase_destroy_all_items(void); static void erase_next_level(void); static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data); static void shuffle_image_list(char *list[], int size); static gint canvas_event(GnomeCanvas *canvas, GdkEvent *event); static int number_of_items = 0; static int number_of_item_x = 0; static int number_of_item_y = 0; static int *items_per_cell = 0; static guint normal_delay_id = 0; static gint timer_id = 0; #define NORMAL 0 #define CLIC 1 #define DOUBLECLIC 2 static gint board_mode = NORMAL; // List of images to use in the game static gchar *imageList[] = { "opt/animals/bear001.jpg", "opt/animals/black-headed-gull.jpg", "opt/animals/butterfly.jpg", "opt/animals/cat1.jpg", "opt/animals/cat2.jpg", "opt/animals/donkey.jpg", "opt/animals/elephanteauxgc.jpg", "opt/animals/flamentrosegc.jpg", "opt/animals/girafegc.jpg", "opt/animals/hypogc.jpg", "opt/animals/joybear001.jpg", "opt/animals/joybear002.jpg", "opt/animals/jumentmulassieregc.jpg", "opt/animals/malaybear002.jpg", "opt/animals/pigeon.jpg", "opt/animals/polabear011.jpg", "opt/animals/polarbear001.jpg", "opt/animals/poolbears001.jpg", "opt/animals/rhinogc.jpg", "opt/animals/singegc.jpg", "opt/animals/spectbear001.jpg", "opt/animals/tetegorillegc.jpg", "opt/animals/tiger1_by_Ralf_Schmode.jpg", "opt/animals/tigercub003.jpg", "opt/animals/tigerdrink001.jpg", "opt/animals/tigerplay001.jpg", "opt/animals/horses.jpg", "opt/animals/horses2.jpg", "opt/animals/squirrel.jpg", "opt/animals/sheep_irish.jpg", "opt/animals/sheep_irish2.jpg", "opt/animals/cow.jpg", "opt/animals/maki1.jpg", "opt/animals/maki2.jpg", "opt/animals/maki3.jpg", "opt/animals/maki4.jpg", "opt/animals/maki5.jpg", "opt/animals/maki6.jpg", }; #define NUMBER_OF_IMAGES G_N_ELEMENTS(imageList) /* Store the image index to use */ static int current_image; /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, N_("Move the mouse"), N_("Move the mouse to erase the area and discover the background"), "Bruno Coudoin ", NULL, NULL, NULL, NULL, start_board, pause_board, end_board, is_our_board, NULL, NULL, set_level, NULL, NULL, NULL, NULL }; /* * Main entry point mandatory for each Gcompris's game * --------------------------------------------------- * */ GET_BPLUGIN_INFO(erase) /* * in : boolean TRUE = PAUSE : FALSE = CONTINUE * */ static void pause_board (gboolean pause) { if(gcomprisBoard==NULL) return; if (timer_id) { gtk_timeout_remove (timer_id); timer_id = 0; } if(gamewon == TRUE && pause == FALSE) /* the game is won */ { game_won(); } board_paused = pause; } /* */ static void start_board (GcomprisBoard *agcomprisBoard) { if(agcomprisBoard!=NULL) { gcomprisBoard=agcomprisBoard; gcomprisBoard->level=1; gcomprisBoard->maxlevel=6; gcomprisBoard->sublevel=1; gcomprisBoard->number_of_sublevel=10; /* Go to next level after this number of 'play' */ gc_bar_set(GC_BAR_LEVEL); /* CAUTION: CoverPixmap has MAX_LAYERS elements */ CoverPixmap[0] = gc_pixmap_load("images/transparent_square.png"); CoverPixmap[1] = gc_pixmap_load("images/transparent_square_green.png"); CoverPixmap[2] = gc_pixmap_load("images/transparent_square_yellow.png"); event_handle_id = gtk_signal_connect(GTK_OBJECT(gcomprisBoard->canvas), "event", (GtkSignalFunc) canvas_event, 0); if (strcmp(gcomprisBoard->mode,"clic")==0) board_mode = CLIC; else if (strcmp(gcomprisBoard->mode,"doubleclic")==0) board_mode = DOUBLECLIC; else { board_mode = NORMAL; gcomprisBoard->maxlevel=8; } current_image = 0; shuffle_image_list(imageList, NUMBER_OF_IMAGES); erase_next_level(); gamewon = FALSE; pause_board(FALSE); GdkPixbuf *cursor_pixbuf = gc_pixmap_load("images/sponge.png"); if(cursor_pixbuf) { GdkCursor *cursor = NULL; cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf, gdk_pixbuf_get_width(cursor_pixbuf)/2, gdk_pixbuf_get_height(cursor_pixbuf)/2); gdk_window_set_cursor(gc_get_window()->window, cursor); gdk_cursor_unref(cursor); gdk_pixbuf_unref(cursor_pixbuf); } } } /* ======================================= */ static void end_board () { int i; for(i=0; icanvas), event_handle_id); pause_board(TRUE); erase_destroy_all_items(); } gcomprisBoard = NULL; } /* ======================================= */ static void set_level (guint level) { if(gcomprisBoard!=NULL) { gcomprisBoard->level=level; gcomprisBoard->sublevel=1; erase_next_level(); } } /* ======================================= */ static gboolean is_our_board (GcomprisBoard *gcomprisBoard) { if (gcomprisBoard) { if(g_strcasecmp(gcomprisBoard->type, "erase")==0) { /* Set the plugin entry */ gcomprisBoard->plugin=&menu_bp; return TRUE; } } return FALSE; } static int get_num_layers() { int layers; /* Select the number of layer depending on the level */ if(gcomprisBoard->level>6) layers = 4; else if(gcomprisBoard->level>4) layers = 3; else if(gcomprisBoard->level>2) layers = 2; else layers = 1; return layers; } /*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/ /* set initial values for the next level */ static void erase_next_level() { gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), imageList[current_image++]); if(current_image>=NUMBER_OF_IMAGES) current_image=0; gc_bar_set_level(gcomprisBoard); erase_destroy_all_items(); gamewon = FALSE; /* Select level difficulty */ if (board_mode != NORMAL) { number_of_item_x = 5; number_of_item_y = 5; } else { number_of_item_x = ((gcomprisBoard->level+1)%2+1)*5; number_of_item_y = ((gcomprisBoard->level+1)%2+1)*5; assert(!items_per_cell); items_per_cell = g_new0(int, number_of_item_x * number_of_item_y); } /* Try the next level */ erase_create_item(); } /* ==================================== */ /* Destroy all the items */ static void erase_destroy_all_items() { if (normal_delay_id) { g_source_remove (normal_delay_id); normal_delay_id = 0; } if (timer_id) { gtk_timeout_remove (timer_id); timer_id = 0; } if(boardRootItem!=NULL) { gtk_object_destroy (GTK_OBJECT(boardRootItem)); boardRootItem = NULL; } number_of_items = 0; if (items_per_cell) { g_free(items_per_cell); items_per_cell = NULL; } } static void add_one_item(int i, int j, int protect) { int current_layer = get_num_layers(); double w = (BOARDWIDTH/number_of_item_x) ; double h = (BOARDHEIGHT/number_of_item_y) ; int item_x = i / w; int item_y = j / h; i = item_x * w; j = item_y * h; if ((board_mode != NORMAL) && ((item_x+item_y) %2 == 0)) return; if (current_layer == 4) current_layer = 1; while(current_layer--) { assert(CoverPixmap[current_layer]); GnomeCanvasItem *item = gnome_canvas_item_new (boardRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", CoverPixmap[current_layer], "x", (double) i, "y", (double) j, "width", w, "height", h, "width_set", TRUE, "height_set", TRUE, "anchor", GTK_ANCHOR_NW, NULL); counter *c = g_new (counter, 1); c->count = 0 ; c->max = protect; protect = 0; /* only protect the top item */ /* if item is not first, it must be keep first time mouse * pass over in normal mode or in layer 4 */ if (current_layer > 0 || get_num_layers() == 4) c->max += 1; g_signal_connect_data (item, "event", (GCallback) item_event,(gpointer)c, (GClosureNotify) g_free, 0); number_of_items++; if (items_per_cell) items_per_cell[item_x * number_of_item_x + item_y]++; } } /* ==================================== */ static GnomeCanvasItem *erase_create_item() { int i,j; boardRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas), gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); assert(number_of_items == 0); for(i=0; isublevel++; if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) { /* Try the next level */ gcomprisBoard->sublevel=1; gcomprisBoard->level++; if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out timer_id = gtk_timeout_add (2000, (GtkFunction) finished, NULL); return; } gc_sound_play_ogg ("sounds/bonus.wav", NULL); } erase_next_level(); } static gboolean erase_one_item (GnomeCanvasItem *item) { double screen_x, screen_y; int x,y; SoundPolicy sound_policy = gc_sound_policy_get(); g_object_get(item, "x", &screen_x, "y", &screen_y, NULL); x = screen_x / (BOARDWIDTH/number_of_item_x); y = screen_y / (BOARDHEIGHT/number_of_item_y); if (items_per_cell) items_per_cell[(int) (x * number_of_item_x + y)]--; gtk_object_destroy(GTK_OBJECT(item)); if(--number_of_items == 0) { gamewon = TRUE; erase_destroy_all_items(); timer_id = gtk_timeout_add (4000, (GtkFunction) bonus, NULL); } /* force a cleanup of the sound queue */ if(number_of_items == 0) gc_sound_policy_set(PLAY_AND_INTERRUPT); if(number_of_items%2) gc_sound_play_ogg ("sounds/eraser1.wav", NULL); else gc_sound_play_ogg ("sounds/eraser2.wav", NULL); if(number_of_items == 0) gc_sound_policy_set(sound_policy); normal_delay_id = 0; return FALSE; } /* ==================================== */ static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data) { static GnomeCanvasItem *previous_clicked_item = NULL; static guint32 previous_click_time = 0; counter *c = (counter *) data; if(board_paused) return FALSE; if (event->type == GDK_MOTION_NOTIFY) return FALSE; if (board_mode == NORMAL) { if (event->type == GDK_ENTER_NOTIFY) { if (c->count < c->max){ c->count++ ; return FALSE ; } /* Are enter & leave always sent in pairs? Don't assume. */ if (normal_delay_id) g_source_remove (normal_delay_id); normal_delay_id = g_timeout_add (50, (GSourceFunc) erase_one_item, item); } else if (event->type == GDK_LEAVE_NOTIFY) { if (normal_delay_id) g_source_remove (normal_delay_id); normal_delay_id = 0; } return FALSE; } if (board_mode == CLIC) if (event->type != GDK_BUTTON_PRESS) return FALSE; if (board_mode == DOUBLECLIC) { if (event->type != GDK_BUTTON_PRESS) return FALSE; else { guint32 d = event->button.time - previous_click_time; /* Click duration handicap depending on the level */ d += gcomprisBoard->level * 100; if (previous_clicked_item != item || d >= 850) { previous_clicked_item = item; previous_click_time = event->button.time; return FALSE; } } } erase_one_item (item); return FALSE; } static gint canvas_event(GnomeCanvas *canvas, GdkEvent *event) { if (!gcomprisBoard || board_paused || gamewon) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: if (board_mode == NORMAL) { int x = event->button.x; int y = event->button.y; int item_x = x / (BOARDWIDTH/number_of_item_x); int item_y = y / (BOARDHEIGHT/number_of_item_y); if (items_per_cell[item_x * number_of_item_x + item_y] == 0) add_one_item(x, y, 1); } break; default: break; } return FALSE; } /** \brief shuffle_image_list, takes a char* array and it's length. * it swaps a random number of items in it in order to provide * the same list but in a random order. * * \param list: the array to shuffle * \param size: the size of the array * */ void shuffle_image_list(char *list[], int size) { int i; char *olditem; for(i=0; i < size - 1; i++) { int random1 = g_random_int_range(i, size-1); if (i == random1) continue; olditem = list[i]; list[i] = list[random1]; list[random1] = olditem; } }