/* gcompris - reversecount.c * * Copyright (C) 2002,2003 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 "gcompris/gcompris.h" #define SOUNDLISTFILE PACKAGE static GcomprisBoard *gcomprisBoard = NULL; static gboolean board_paused = TRUE; static gint animate_id = 0; static int leavenow; static void start_board (GcomprisBoard *agcomprisBoard); static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str); 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); #define TUX_IMG_NORTH "gcompris/misc/tux_top_north.png" #define TUX_IMG_SOUTH "gcompris/misc/tux_top_south.png" #define TUX_IMG_WEST "gcompris/misc/tux_top_west.png" #define TUX_IMG_EAST "gcompris/misc/tux_top_east.png" #define TUX_TO_BORDER_GAP 10 static GnomeCanvasGroup *boardRootItem = NULL; static void process_ok(void); static void process_error(void); static GnomeCanvasItem *reversecount_create_item(GnomeCanvasGroup *parent); static void reversecount_destroy_all_items(void); static void reversecount_next_level(void); static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gint *dice_index); static GnomeCanvasItem *display_item_at(gchar *imagename, int block, double ratio); static void display_random_fish(); static void create_clock(double x, double y, int value); static void update_clock(int value); static gint animate_tux(); static int number_of_item = 0; static int number_of_item_x = 0; static int number_of_item_y = 0; static int errors = 0; static int number_of_dices = 0; static int max_dice_number = 0; static int number_of_fish = 0; static int tux_index = 0; static int tux_destination = 0; static int fish_index = 0; static int animate_speed = 0; #define ANIMATE_SPEED 800 static double tux_ratio = 0; static int dicevalue_array[10]; static GnomeCanvasItem *fishItem; static GnomeCanvasItem *tuxItem; static GnomeCanvasItem *clock_image_item; // List of images to use in the game static gchar *imageList[] = { "opt/baleine.png", "opt/phoque.png", "opt/ourspolaire.png", "opt/morse.png", "opt/elephant_mer.png", "opt/epaulard.png", "opt/narval.png", }; #define NUM_IMAGELIST G_N_ELEMENTS(imageList) // List of fish to use in the game static gchar *fishList[] = { "images/fish00.png", "images/fish01.png", "images/fish02.png", "images/fish03.png", }; #define NUMBER_OF_FISHES G_N_ELEMENTS(fishList) /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, "Reverse count", "Practice substraction with a funny game", "Bruno Coudoin ", NULL, NULL, NULL, NULL, start_board, pause_board, end_board, is_our_board, key_press, process_ok, set_level, NULL, NULL, NULL, NULL }; /* * Main entry point mandatory for each Gcompris's game * --------------------------------------------------- * */ GET_BPLUGIN_INFO(reversecount) /* * in : boolean TRUE = PAUSE : FALSE = CONTINUE * */ static void pause_board (gboolean pause) { if(gcomprisBoard==NULL) return; if(gamewon == TRUE && pause == FALSE) /* the game is won */ { game_won(); } if(leavenow == TRUE && pause == FALSE) gc_bonus_end_display(GC_BOARD_FINISHED_TOOMANYERRORS); board_paused = pause; } /* */ static void start_board (GcomprisBoard *agcomprisBoard) { if(agcomprisBoard!=NULL) { gcomprisBoard=agcomprisBoard; /* disable im_context */ gcomprisBoard->disable_im_context = TRUE; gcomprisBoard->level=1; gcomprisBoard->maxlevel=7; gcomprisBoard->sublevel=1; gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */ gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK); reversecount_next_level(); gamewon = FALSE; leavenow = FALSE; pause_board(FALSE); } } /* ======================================= */ static void end_board () { if(gcomprisBoard!=NULL) { pause_board(TRUE); reversecount_destroy_all_items(); } gcomprisBoard = NULL; } /* ======================================= */ static void set_level (guint level) { if(gcomprisBoard!=NULL) { gcomprisBoard->level=level; gcomprisBoard->sublevel=1; reversecount_next_level(); } } /* ======================================= */ gboolean is_our_board (GcomprisBoard *gcomprisBoard) { if (gcomprisBoard) { if(g_strcasecmp(gcomprisBoard->type, "reversecount")==0) { /* Set the plugin entry */ gcomprisBoard->plugin=&menu_bp; return TRUE; } } return FALSE; } /* ======================================= */ gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str) { if(board_paused || !boardRootItem || !gcomprisBoard) return FALSE; /* Add some filter for control and shift key */ switch (keyval) { /* Avoid all this keys to be interpreted by this game */ case GDK_Shift_L: case GDK_Shift_R: case GDK_Control_L: case GDK_Control_R: case GDK_Caps_Lock: case GDK_Shift_Lock: case GDK_Meta_L: case GDK_Meta_R: case GDK_Alt_L: case GDK_Alt_R: case GDK_Super_L: case GDK_Super_R: case GDK_Hyper_L: case GDK_Hyper_R: case GDK_Num_Lock: return FALSE; case GDK_KP_Enter: case GDK_Return: process_ok(); return TRUE; } return TRUE; } static void process_error() { gc_sound_play_ogg ("sounds/crash.wav", NULL); errors--; if(errors==0) { gamewon = FALSE; leavenow = TRUE; reversecount_destroy_all_items(); gc_bonus_display(gamewon, GC_BONUS_SMILEY); } else { update_clock(errors); } } /* ======================================= */ static void process_ok() { guint i; tux_destination = tux_index; for(i=0; i= number_of_item) tux_destination = tux_destination - (number_of_item); // Do not allow going at a position after the fish if((tux_destination > fish_index) || (tux_destination == tux_index)) { process_error(); return; } if(!animate_id) { animate_id = gtk_timeout_add (animate_speed, (GtkFunction) animate_tux, NULL); } } /*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/ /* set initial values for the next level */ static void reversecount_next_level() { g_assert(NUM_IMAGELIST > gcomprisBoard->level-1); gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), imageList[gcomprisBoard->level-1]); gc_bar_set_level(gcomprisBoard); reversecount_destroy_all_items(); gamewon = FALSE; /* Select level difficulty */ switch(gcomprisBoard->level) { case 1: number_of_item_x = 5; number_of_item_y = 5; number_of_dices = 1; max_dice_number = 3; number_of_fish = 3; break; case 2: number_of_item_x = 5; number_of_item_y = 5; number_of_dices = 1; max_dice_number = 6; number_of_fish = 6; break; case 3: number_of_item_x = 6; number_of_item_y = 6; number_of_dices = 1; max_dice_number = 9; number_of_fish = 6; break; case 4: number_of_item_x = 8; number_of_item_y = 6; number_of_dices = 1; max_dice_number = 3; number_of_fish = 6; break; case 5: number_of_item_x = 8; number_of_item_y = 6; number_of_dices = 2; max_dice_number = 6; number_of_fish = 10; break; case 6: number_of_item_x = 8; number_of_item_y = 8; number_of_dices = 2; max_dice_number = 9; number_of_fish = 10; break; default: number_of_item_x = 10; number_of_item_y = 10; number_of_dices = 3; max_dice_number = 9; number_of_fish = 10; break; } animate_speed = ANIMATE_SPEED - gcomprisBoard->level * 60; number_of_item = number_of_item_x * 2 + (number_of_item_y - 2) * 2; /* Try the next level */ reversecount_create_item(gnome_canvas_root(gcomprisBoard->canvas)); } /* ==================================== */ /* Destroy all the items */ static void reversecount_destroy_all_items() { gc_timer_end(); if(boardRootItem!=NULL) gtk_object_destroy (GTK_OBJECT(boardRootItem)); boardRootItem = NULL; } /* ==================================== */ static GnomeCanvasItem *reversecount_create_item(GnomeCanvasGroup *parent) { int i,j,d; GnomeCanvasItem *item = NULL; GdkPixbuf *pixmap = NULL; double block_width, block_height; double dice_area_x; double xratio, yratio; GcomprisProperties *properties = gc_prop_get(); 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)); block_width = BOARDWIDTH/number_of_item_x; block_height = BOARDHEIGHT/number_of_item_y; /* Timer is not requested */ if(properties->timer>0) { errors = number_of_dices + 4 - (MIN(properties->timer, 4)); create_clock(BOARDWIDTH - block_width - 100, BOARDHEIGHT - block_height - 100, errors) ; } else { errors = -1; } /* Calc the tux best ratio to display it */ pixmap = gc_pixmap_load(TUX_IMG_EAST); xratio = block_width / (gdk_pixbuf_get_width (pixmap) + TUX_TO_BORDER_GAP); yratio = block_height / (gdk_pixbuf_get_height(pixmap) + TUX_TO_BORDER_GAP); tux_ratio = yratio = MIN(xratio, yratio); gdk_pixbuf_unref(pixmap); pixmap = gc_pixmap_load("reversecount/iceblock.png"); for(i=0; i= number_of_item) fish_index = fish_index - (number_of_item); fishItem = display_item_at(fishList[g_random_int()%NUMBER_OF_FISHES], fish_index, -1); } /* ==================================== */ /** * Display given imagename on the given ice block. */ static GnomeCanvasItem *display_item_at(gchar *imagename, int block, double ratio) { double block_width, block_height; double xratio, yratio; GnomeCanvasItem *item = NULL; GdkPixbuf *pixmap = NULL; int i,j; block_width = BOARDWIDTH/number_of_item_x; block_height = BOARDHEIGHT/number_of_item_y; pixmap = gc_pixmap_load(imagename); if(block < number_of_item_x) { // Upper line g_warning(" // Upper line\n"); i = block_width * block; j = 0; } else if(block < number_of_item_x + number_of_item_y - 2) { // Right line g_warning(" // Right line\n"); i = block_width * (number_of_item_x - 1); j = block_height * (block - (number_of_item_x-1)); } else if(block < number_of_item_x*2 + number_of_item_y - 2) { // Bottom line g_warning(" // Bottom line\n"); i = block_width * (number_of_item_x - (block- (number_of_item_x+number_of_item_y-1))-2); j = block_height * (number_of_item_y-1); } else { // Left line g_warning(" // Left line\n"); i = 0; j = block_height * (number_of_item_y - (block - (number_of_item_x*2 + number_of_item_y-4))); } g_warning("display_tux %d i=%d j=%d\n", block, i, j); /* Calculation to thrink the item while keeping the ratio */ if(ratio==-1) { xratio = block_width / (gdk_pixbuf_get_width (pixmap) + TUX_TO_BORDER_GAP); yratio = block_height / (gdk_pixbuf_get_height(pixmap) + TUX_TO_BORDER_GAP); xratio = yratio = MIN(xratio, yratio); } else { xratio = yratio = ratio; } item = gnome_canvas_item_new (boardRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", pixmap, "x", (double) i + (block_width - (gdk_pixbuf_get_width (pixmap) * xratio)) / 2, "y", (double) j + (block_height - (gdk_pixbuf_get_height (pixmap) * yratio)) / 2, "width", (double) gdk_pixbuf_get_width (pixmap) * xratio, "height", (double) gdk_pixbuf_get_height (pixmap) * yratio, "width_set", TRUE, "height_set", TRUE, NULL); gdk_pixbuf_unref(pixmap); return(item); } /* ==================================== */ static void game_won() { gcomprisBoard->sublevel++; 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 gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM); return; } gc_sound_play_ogg ("sounds/bonus.wav", NULL); } reversecount_next_level(); } /* ==================================== */ /** * Increment the dices when they are clicked */ static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gint *dice_index) { gchar *str; GdkPixbuf *pixmap = NULL; gint i = *dice_index; if(board_paused) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: switch(event->button.button) { case 1: if(dicevalue_array[i]++ >= max_dice_number) dicevalue_array[i] = (number_of_dices==1 ? 1 : 0); break; case 2: case 3: if(dicevalue_array[i]-- == (number_of_dices==1 ? 1 : 0)) dicevalue_array[i] = max_dice_number; break; default: break; } str = g_strdup_printf("gcompris/dice/small_dice_%d.png", dicevalue_array[i]); pixmap = gc_pixmap_load(str); /* Warning changing the image needs to update pixbuf_ref for the focus usage */ gc_item_focus_free(item, NULL); gnome_canvas_item_set (item, "pixbuf", pixmap, NULL); gdk_pixbuf_unref(pixmap); g_free(str); break; default: break; } return FALSE; } /* * Clock management */ static void create_clock(double x, double y, int value) { GdkPixbuf *pixmap = NULL; char *str = NULL; if(value<0) return; str = g_strdup_printf("%s%d.png", "gcompris/timers/tux",value); pixmap = gc_pixmap_load(str); clock_image_item = gnome_canvas_item_new (boardRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", pixmap, "x", (double) x, "y", (double) y, NULL); gdk_pixbuf_unref(pixmap); g_free(str); } static void update_clock(int value) { GdkPixbuf *pixmap = NULL; char *str = NULL; if(value<0) return; str = g_strdup_printf("%s%d.png", "gcompris/timers/tux",value); pixmap = gc_pixmap_load(str); gnome_canvas_item_set (clock_image_item, "pixbuf", pixmap, NULL); gdk_pixbuf_unref(pixmap); g_free(str); } static gint animate_tux() { // Move tux if(tuxItem!=NULL) gtk_object_destroy(GTK_OBJECT(tuxItem)); tux_index++; g_warning("=========== tux_index=%d tux_destination=%d fish_index=%d\n", tux_index, tux_destination, fish_index); // Wrapping if(tux_index >= number_of_item) tux_index = tux_index - (number_of_item); /* Caclulate which tux should be displayed */ if(tux_index