/* gcompris - railroad.c * * Copyright (C) 2001 Pascal Georges * * 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 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 process_ok(void); static void game_won(void); static void repeat(void); static void animate_model(void); static gboolean animate_step(void); #define ENGINES 9 #define WAGONS 13 #define NUMBER_OF_SUBLEVELS 3 #define NUMBER_OF_LEVELS 3 #define MODEL_MAX_SIZE NUMBER_OF_LEVELS+1 static const int line[] = { 100,180,260,340, 420, 500}; static gboolean animation_pending; static gint animation_count = 0; static GnomeCanvasGroup *boardRootItem = NULL; static GnomeCanvasGroup *allwagonsRootItem = NULL; static GnomeCanvasGroup *modelRootItem = NULL; static GnomeCanvasGroup *answerRootItem = NULL; static GList * listPixmapEngines = NULL; static GList * listPixmapWagons = NULL; // ========================================== // In all the lists below, 0 is the LEFTmost vehicle| // ========================================== // contains the list of vehicles to be found. static GnomeCanvasItem *item_model[MODEL_MAX_SIZE]; // contains the list of vehicles proposed by child. static GList *item_answer_list = NULL; // contains the list of vehicles proposed by child. static GList *int_answer_list = NULL; // contains the list of vehicles to be found static GList *int_model_list = NULL; static int model_size = 0; static gint timer_id; static GnomeCanvasItem *railroad_create_item(GnomeCanvasGroup *parent); static void railroad_destroy_all_items(void); static void railroad_next_level(void); static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data); static gint answer_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data); static void reposition_model(void); static void reposition_answer(void); // helper function because g_list_free does not actually reset a list static GList * reset_list(GList * list); static void reset_all_lists(void); /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, N_("Memory game"), N_("Build a train according to the model"), "Pascal Georges pascal.georges1@free.fr>", NULL, NULL, NULL, NULL, start_board, pause_board, end_board, is_our_board, NULL, process_ok, set_level, NULL, repeat, NULL, NULL }; /* * Main entry point mandatory for each Gcompris's game * --------------------------------------------------- * */ GET_BPLUGIN_INFO(railroad) /* * 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(); } board_paused = pause; } /* ======================================= */ static void start_board (GcomprisBoard *agcomprisBoard) { int i; char *str; GdkPixbuf *pixmap = NULL; if(agcomprisBoard!=NULL) { gcomprisBoard=agcomprisBoard; gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "opt/railroad-bg.png"); for (i=0; ilevel=1; gcomprisBoard->maxlevel=NUMBER_OF_LEVELS; gcomprisBoard->sublevel=1; gcomprisBoard->number_of_sublevel=NUMBER_OF_SUBLEVELS; /* Go to next level after this number of 'play' */ gc_score_start(SCORESTYLE_NOTE, gcomprisBoard->width - 220, gcomprisBoard->height - 50, gcomprisBoard->number_of_sublevel); str = gc_skin_image_get("button_reload.png"); pixmap = gc_pixmap_load(str); g_free(str); if(pixmap) { gc_bar_set_repeat_icon(pixmap); gdk_pixbuf_unref(pixmap); gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK|GC_BAR_REPEAT_ICON); } else { gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK|GC_BAR_REPEAT); } railroad_next_level(); gamewon = FALSE; pause_board(FALSE); } } /* ======================================= */ static void end_board () { GdkPixbuf * pixmap = NULL; // If we don't end animation, there may be a segfault if leaving while the animation is pending if (timer_id) { gtk_timeout_remove (timer_id); timer_id = 0; } if(gcomprisBoard!=NULL) { pause_board(TRUE); gc_score_end(); railroad_destroy_all_items(); while(g_list_length(listPixmapEngines)>0) { pixmap = g_list_nth_data(listPixmapEngines, 0); listPixmapEngines = g_list_remove (listPixmapEngines, pixmap); gdk_pixbuf_unref(pixmap); } while(g_list_length(listPixmapWagons)>0) { pixmap = g_list_nth_data(listPixmapWagons, 0); listPixmapWagons = g_list_remove (listPixmapWagons, pixmap); gdk_pixbuf_unref(pixmap); } } gcomprisBoard = NULL; } /* ======================================= */ static void set_level (guint level) { if(gcomprisBoard!=NULL) { gcomprisBoard->level=level; gcomprisBoard->sublevel=1; railroad_next_level(); } } /* ======================================= */ gboolean is_our_board (GcomprisBoard *gcomprisBoard) { if (gcomprisBoard) { if(g_strcasecmp(gcomprisBoard->type, "railroad")==0) { /* Set the plugin entry */ gcomprisBoard->plugin=&menu_bp; return TRUE; } } return FALSE; } /* ======================================= */ static void repeat () { if(gcomprisBoard!=NULL && !animation_pending) { gnome_canvas_item_hide(GNOME_CANVAS_ITEM(answerRootItem)); gnome_canvas_item_show(GNOME_CANVAS_ITEM(modelRootItem)); gnome_canvas_item_hide(GNOME_CANVAS_ITEM(allwagonsRootItem)); reposition_model(); animate_model(); } } /* ==================================== */ /* set initial values for the next level */ static void railroad_next_level() { gc_bar_set_level(gcomprisBoard); reset_all_lists(); // I have big troubles with the GList API : the worst I have ever seen ! g_assert(g_list_length(item_answer_list) == 0 && g_list_length(int_answer_list) == 0 && g_list_length(int_model_list) == 0); model_size = 0; railroad_destroy_all_items(); gamewon = FALSE; gc_score_set(gcomprisBoard->sublevel); /* Try the next level */ railroad_create_item(gnome_canvas_root(gcomprisBoard->canvas)); } /* ==================================== */ /* Destroy all the items */ static void railroad_destroy_all_items() { if(boardRootItem!=NULL) gtk_object_destroy (GTK_OBJECT(boardRootItem)); boardRootItem = NULL; } /* ==================================== */ static GnomeCanvasItem *railroad_create_item(GnomeCanvasGroup *parent) { int xOffset = 0, yOffset = 0; int i, r, l = 1; GdkPixbuf * pixmap = NULL; GnomeCanvasItem *item; boardRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (parent, gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); /* Create a root group for the answer */ answerRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (boardRootItem, gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); // Create the vehicules allwagonsRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (boardRootItem, gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); for (i=0; i= gcomprisBoard->width) { xOffset = 0; l++; } yOffset = line[l] - gdk_pixbuf_get_height(pixmap); item = gnome_canvas_item_new (allwagonsRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", pixmap, "x", (double) xOffset, "y", (double) yOffset, NULL); xOffset += gdk_pixbuf_get_width(pixmap); gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event, GINT_TO_POINTER(i)); } // hide them gnome_canvas_item_hide(GNOME_CANVAS_ITEM(allwagonsRootItem)); // construct the model to be recognized modelRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (boardRootItem, gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); yOffset = line[0]; xOffset = 0; model_size = gcomprisBoard->level +1; // engine + cars // First the cars, depending of the level for (i=0; i=0 && r < WAGONS); // keep track of the answer int_model_list = g_list_append(int_model_list, GINT_TO_POINTER(r+ENGINES)); pixmap = g_list_nth_data(listPixmapWagons, r); g_assert(i >= 0 && i=0 && r < ENGINES); // keep track of the answer int_model_list = g_list_append(int_model_list, GINT_TO_POINTER(r)); pixmap = g_list_nth_data(listPixmapEngines, r); item_model[model_size-1] =gnome_canvas_item_new (modelRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", pixmap, "x", (double) xOffset, "y", (double) yOffset - gdk_pixbuf_get_height(pixmap), NULL); animate_model(); return NULL; } /* ==================================== */ 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); } railroad_next_level(); } /* ==================================== */ static void process_ok() { int i; gamewon = TRUE; // DEBUG g_warning("l answer = %d\tl model = %d\n", g_list_length(int_answer_list), g_list_length(int_model_list)); if (g_list_length(int_answer_list) != g_list_length(int_model_list)) gamewon = FALSE; else for (i=0; i différent\n", i); gamewon = FALSE; break; } } // DUMP lists g_warning("answer:\n"); for (i=0; ibutton.x; item_y = event->button.y; gnome_canvas_item_w2i(item->parent, &item_x, &item_y); if(board_paused) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: g_warning("GDK_BUTTON_PRESS item %d\tlength answer = %d\n",item_number,g_list_length(item_answer_list)); gc_sound_play_ogg ("sounds/bleep.wav", NULL); xOffset = 0; for (i=0; ibutton.x; item_y = event->button.y; gnome_canvas_item_w2i(item->parent, &item_x, &item_y); if(board_paused) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: gc_sound_play_ogg ("sounds/smudge.wav", NULL); g_warning("Deleting %d\n",item_number); local_item = g_list_nth_data(item_answer_list,item_number); item_answer_list = g_list_remove( item_answer_list, local_item ); // gtk_signal_disconnect(GTK_OBJECT(local_item), (GtkSignalFunc) answer_event, NULL); gtk_object_destroy (GTK_OBJECT(local_item)); int_answer_list = g_list_remove(int_answer_list, g_list_nth_data(int_answer_list, item_number) ); reposition_answer(); // setup the signals for the cars at the right side of the deleted object for (i=item_number; i= 160+MODEL_PAUSE) { if (timer_id) { gtk_timeout_remove (timer_id); timer_id = 0; } animation_pending = FALSE; gnome_canvas_item_hide(GNOME_CANVAS_ITEM(modelRootItem)); /* Move back the model to its 0 position */ gnome_canvas_item_set(GNOME_CANVAS_ITEM(modelRootItem), "x", 0.0, NULL); gnome_canvas_item_show(GNOME_CANVAS_ITEM(allwagonsRootItem)); gnome_canvas_item_show(GNOME_CANVAS_ITEM(answerRootItem)); return FALSE; } step = (double) (animation_count-MODEL_PAUSE) / 50.0; step *= step; gnome_canvas_item_move(GNOME_CANVAS_ITEM(modelRootItem), step, 0.0); return TRUE; } /* ==================================== */ static void animate_model() { animation_pending = TRUE; animation_count = 0; gc_sound_play_ogg( "sounds/train.wav", NULL ); // warning : if timeout is too low, the model will not be displayed timer_id = gtk_timeout_add (100, (GtkFunction) animate_step, NULL); } /* ==================================== */ static void reset_all_lists(void) { GnomeCanvasItem *item; int_model_list = reset_list(int_model_list); int_answer_list = reset_list(int_answer_list); while(g_list_length(item_answer_list)>0) { item = g_list_nth_data(item_answer_list, 0); item_answer_list = g_list_remove (item_answer_list, item); // causes segfaults // gtk_object_destroy (GTK_OBJECT(item)); } } /* ==================================== */ static GList * reset_list(GList * list) { while (g_list_length(list) > 0) list = g_list_remove(list, g_list_nth_data(list,0)); return list; }