Back to index

gcompris  8.2.2
fifteen.c
Go to the documentation of this file.
00001 /* gcompris - fifteen.c
00002  *
00003  * Copyright (C) 2003 Bruno Coudoin
00004  *
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU General Public License as published by
00008  *   the Free Software Foundation; either version 2 of the License, or
00009  *   (at your option) any later version.
00010  *
00011  *   This program is distributed in the hope that it will be useful,
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *   GNU General Public License for more details.
00015  *
00016  *   You should have received a copy of the GNU General Public License
00017  *   along with this program; if not, write to the Free Software
00018  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 #include "gcompris/gcompris.h"
00022 #include <time.h>
00023 
00024 #define PIECE_SIZE 50
00025 
00026 #define SOUNDLISTFILE PACKAGE
00027 
00028 static GcomprisBoard *gcomprisBoard = NULL;
00029 static gboolean board_paused = TRUE;
00030 
00031 static void    start_board (GcomprisBoard *agcomprisBoard);
00032 static void    pause_board (gboolean pause);
00033 static void    end_board (void);
00034 static gboolean       is_our_board (GcomprisBoard *gcomprisBoard);
00035 static void    set_level (guint level);
00036 static int     gamewon;
00037 static void    game_won(void);
00038 
00039 static GnomeCanvasGroup *boardRootItem = NULL;
00040 
00041 static GnomeCanvasItem      *fifteen_create_item(GnomeCanvasGroup *parent);
00042 static void           fifteen_destroy_all_items(void);
00043 static void           fifteen_next_level(void);
00044 
00045 static void           free_stuff (GtkObject *obj, gpointer data);
00046 static gint           piece_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00047 static void           scramble (GnomeCanvasItem **board, guint number_of_scrambles);
00048 static char          *get_piece_color (int piece);
00049 
00050 /* Description of this plugin */
00051 static BoardPlugin menu_bp =
00052   {
00053     NULL,
00054     NULL,
00055     "The fifteen game",
00056     "Move the items one by one, to rearrange them in increasing order",
00057     "Bruno Coudoin <bruno.coudoin@free.fr>",
00058     NULL,
00059     NULL,
00060     NULL,
00061     NULL,
00062     start_board,
00063     pause_board,
00064     end_board,
00065     is_our_board,
00066     NULL,
00067     NULL,
00068     set_level,
00069     NULL,
00070     NULL,
00071     NULL,
00072     NULL
00073   };
00074 
00075 /*
00076  * Main entry point mandatory for each Gcompris's game
00077  * ---------------------------------------------------
00078  *
00079  */
00080 
00081 GET_BPLUGIN_INFO(fifteen)
00082 
00083 /*
00084  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00085  *
00086  */
00087 static void pause_board (gboolean pause)
00088 {
00089   if(gcomprisBoard==NULL)
00090     return;
00091 
00092   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00093     {
00094       game_won();
00095     }
00096 
00097   board_paused = pause;
00098 }
00099 
00100 /*
00101  */
00102 static void start_board (GcomprisBoard *agcomprisBoard)
00103 {
00104 
00105   if(agcomprisBoard!=NULL)
00106     {
00107       gcomprisBoard=agcomprisBoard;
00108       gcomprisBoard->level=1;
00109       gcomprisBoard->maxlevel=6;
00110       gcomprisBoard->sublevel=1;
00111       gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
00112       gc_bar_set(GC_BAR_LEVEL);
00113 
00114       fifteen_next_level();
00115 
00116       gamewon = FALSE;
00117       pause_board(FALSE);
00118     }
00119 }
00120 /* ======================================= */
00121 static void end_board ()
00122 {
00123   if(gcomprisBoard!=NULL)
00124     {
00125       pause_board(TRUE);
00126       fifteen_destroy_all_items();
00127     }
00128   gcomprisBoard = NULL;
00129 }
00130 
00131 /* ======================================= */
00132 static void set_level (guint level)
00133 {
00134 
00135   if(gcomprisBoard!=NULL)
00136     {
00137       gcomprisBoard->level=level;
00138       gcomprisBoard->sublevel=1;
00139       fifteen_next_level();
00140     }
00141 }
00142 /* ======================================= */
00143 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00144 {
00145   if (gcomprisBoard)
00146     {
00147       if(g_strcasecmp(gcomprisBoard->type, "fifteen")==0)
00148        {
00149          /* Set the plugin entry */
00150          gcomprisBoard->plugin=&menu_bp;
00151 
00152          return TRUE;
00153        }
00154     }
00155   return FALSE;
00156 }
00157 
00158 /*-------------------------------------------------------------------------------*/
00159 /*-------------------------------------------------------------------------------*/
00160 /* set initial values for the next level */
00161 static void fifteen_next_level()
00162 {
00163   gchar *img;
00164 
00165   img = gc_skin_image_get("gcompris-bg.jpg");
00166   gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
00167                        img);
00168   g_free(img);
00169 
00170   gc_bar_set_level(gcomprisBoard);
00171 
00172   fifteen_destroy_all_items();
00173   gamewon = FALSE;
00174 
00175   /* Create the level */
00176   fifteen_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00177 
00178 
00179 }
00180 /* ==================================== */
00181 /* Destroy all the items */
00182 static void fifteen_destroy_all_items()
00183 {
00184   if(boardRootItem!=NULL)
00185     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00186 
00187   boardRootItem = NULL;
00188 }
00189 /* ==================================== */
00190 static GnomeCanvasItem *fifteen_create_item(GnomeCanvasGroup *parent)
00191 {
00192   int i;
00193   int x, y;
00194   GnomeCanvasItem **board;
00195   GnomeCanvasItem *text;
00196   char buf[20];
00197   GdkPixbuf *pixmap = NULL;
00198 
00199   boardRootItem = GNOME_CANVAS_GROUP(
00200                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00201                                                      gnome_canvas_group_get_type (),
00202                                                      "x", (double) (BOARDWIDTH-(4*PIECE_SIZE))/2,
00203                                                      "y", (double) (BOARDHEIGHT-(4*PIECE_SIZE))/2,
00204                                                      NULL));
00205 
00206   /* Load the cute frame */
00207   pixmap = gc_pixmap_load("images/fifteen_frame.png");
00208 
00209   gnome_canvas_item_new (boardRootItem,
00210                       gnome_canvas_pixbuf_get_type (),
00211                       "pixbuf", pixmap,
00212                       "x", (double)-1*((gdk_pixbuf_get_width(pixmap)-(4*PIECE_SIZE))/2),
00213                       "y", (double)-1*((gdk_pixbuf_get_height(pixmap)-(4*PIECE_SIZE))/2)-2,
00214                       NULL);
00215   gdk_pixbuf_unref(pixmap);
00216 
00217 
00218   board = g_new (GnomeCanvasItem *, 16);
00219   g_object_set_data (G_OBJECT (boardRootItem), "board", board);
00220   g_signal_connect (boardRootItem, "destroy",
00221                   G_CALLBACK (free_stuff),
00222                   board);
00223 
00224   for (i = 0; i < 15; i++) {
00225     y = i / 4;
00226     x = i % 4;
00227 
00228     board[i] = gnome_canvas_item_new (boardRootItem,
00229                                   gnome_canvas_group_get_type (),
00230                                   "x", (double) (x * PIECE_SIZE),
00231                                   "y", (double) (y * PIECE_SIZE),
00232                                   NULL);
00233 
00234     gnome_canvas_item_new (GNOME_CANVAS_GROUP (board[i]),
00235                         gnome_canvas_rect_get_type (),
00236                         "x1", 0.0,
00237                         "y1", 0.0,
00238                         "x2", (double) PIECE_SIZE,
00239                         "y2", (double) PIECE_SIZE,
00240                         "fill_color", get_piece_color (i),
00241                         "outline_color", "black",
00242                         "width_pixels", 0,
00243                         NULL);
00244 
00245     sprintf (buf, "%d", i + 1);
00246 
00247     text = gnome_canvas_item_new (GNOME_CANVAS_GROUP (board[i]),
00248                               gnome_canvas_text_get_type (),
00249                               "text", buf,
00250                               "x", (double) PIECE_SIZE / 2.0,
00251                               "y", (double) PIECE_SIZE / 2.0,
00252                               "font", gc_skin_font_board_medium,
00253                               "anchor", GTK_ANCHOR_CENTER,
00254                               "fill_color", "black",
00255                               NULL);
00256 
00257     g_object_set_data (G_OBJECT (board[i]), "piece_num", GINT_TO_POINTER (i));
00258     g_object_set_data (G_OBJECT (board[i]), "piece_pos", GINT_TO_POINTER (i));
00259     g_object_set_data (G_OBJECT (board[i]), "text", text);
00260     g_signal_connect (board[i], "event",
00261                     G_CALLBACK (piece_event),
00262                     NULL);
00263   }
00264 
00265   board[15] = NULL;
00266 
00267   /* Select level difficulty */
00268   switch(gcomprisBoard->level)
00269     {
00270     case 1:
00271       scramble(board, 10);
00272       break;
00273     case 2:
00274       scramble(board, 50);
00275       break;
00276     case 3:
00277     case 4:
00278       scramble(board, 100);
00279       break;
00280     case 5:
00281       scramble(board, 150);
00282       break;
00283     default:
00284       scramble(board, 256);
00285     }
00286 
00287   return NULL;
00288 }
00289 /* ==================================== */
00290 static void game_won()
00291 {
00292   gcomprisBoard->sublevel++;
00293 
00294   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00295     /* Try the next level */
00296     gcomprisBoard->sublevel=1;
00297     gcomprisBoard->level++;
00298     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00299       gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00300       return;
00301     }
00302     gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00303   }
00304   fifteen_next_level();
00305 }
00306 
00307 /*==================================================*/
00308 /*   Code taken from libgnomecanvas demo fifteen    */
00309 
00310 
00311 static void
00312 free_stuff (GtkObject *obj, gpointer data)
00313 {
00314   g_free (data);
00315 }
00316 
00317 static void
00318 test_win (GnomeCanvasItem **board)
00319 {
00320   int i;
00321 
00322   for (i = 0; i < 15; i++)
00323     if (!board[i] || (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (board[i]), "piece_num")) != i))
00324       return;
00325 
00326 
00327   gamewon = TRUE;
00328   fifteen_destroy_all_items();
00329   gc_bonus_display(gamewon, BONUS_SMILEY);
00330 
00331 }
00332 
00333 static char *
00334 get_piece_color (int piece)
00335 {
00336   static char buf[50];
00337   int x, y;
00338   int r, g, b;
00339 
00340   y = piece / 4;
00341   x = piece % 4;
00342 
00343   r = ((4 - x) * 255) / 4;
00344   g = ((4 - y) * 255) / 4;
00345   b = 128;
00346 
00347   sprintf (buf, "#%02x%02x%02x", r, g, b);
00348 
00349   return buf;
00350 }
00351 
00352 static gint
00353 piece_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00354 {
00355   GnomeCanvasItem **board;
00356   GnomeCanvasItem *text;
00357   int num, pos, newpos;
00358   int x, y;
00359   double dx = 0.0, dy = 0.0;
00360   int move;
00361 
00362   board = g_object_get_data (G_OBJECT (item->parent), "board");
00363   num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "piece_num"));
00364   pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "piece_pos"));
00365   text = g_object_get_data (G_OBJECT (item), "text");
00366 
00367   switch (event->type) {
00368   case GDK_ENTER_NOTIFY:
00369     gnome_canvas_item_set (text,
00370                         "fill_color", "white",
00371                         NULL);
00372     break;
00373 
00374   case GDK_LEAVE_NOTIFY:
00375     gnome_canvas_item_set (text,
00376                         "fill_color", "black",
00377                         NULL);
00378     break;
00379 
00380   case GDK_BUTTON_PRESS:
00381     y = pos / 4;
00382     x = pos % 4;
00383 
00384     move = TRUE;
00385 
00386     if ((y > 0) && (board[(y - 1) * 4 + x] == NULL)) {
00387       dx = 0.0;
00388       dy = -1.0;
00389       y--;
00390     } else if ((y < 3) && (board[(y + 1) * 4 + x] == NULL)) {
00391       dx = 0.0;
00392       dy = 1.0;
00393       y++;
00394     } else if ((x > 0) && (board[y * 4 + x - 1] == NULL)) {
00395       dx = -1.0;
00396       dy = 0.0;
00397       x--;
00398     } else if ((x < 3) && (board[y * 4 + x + 1] == NULL)) {
00399       dx = 1.0;
00400       dy = 0.0;
00401       x++;
00402     } else
00403       move = FALSE;
00404 
00405     if (move) {
00406       newpos = y * 4 + x;
00407       board[pos] = NULL;
00408       board[newpos] = item;
00409       g_object_set_data (G_OBJECT (item), "piece_pos", GINT_TO_POINTER (newpos));
00410       gnome_canvas_item_move (item, dx * PIECE_SIZE, dy * PIECE_SIZE);
00411       /* FIXME : Workaround for bugged canvas */
00412       gnome_canvas_update_now(gcomprisBoard->canvas);
00413 
00414       test_win (board);
00415     }
00416 
00417     break;
00418 
00419   default:
00420     break;
00421   }
00422 
00423   return FALSE;
00424 }
00425 
00426 static void
00427 scramble (GnomeCanvasItem **board, guint number_of_scrambles)
00428 {
00429   int i;
00430   int pos, oldpos;
00431   int dir;
00432   int x, y;
00433 
00434   srand (time (NULL));
00435 
00436   /* First, find the blank spot */
00437 
00438   for (pos = 0; pos < 16; pos++)
00439     if (board[pos] == NULL)
00440       break;
00441 
00442   /* "Move the blank spot" around in order to scramble the pieces */
00443 
00444   for (i = 0; i < number_of_scrambles; i++) {
00445   retry_scramble:
00446     dir = rand () % 4;
00447 
00448     x = y = 0;
00449 
00450     if ((dir == 0) && (pos > 3)) /* up */
00451       y = -1;
00452     else if ((dir == 1) && (pos < 12)) /* down */
00453       y = 1;
00454     else if ((dir == 2) && ((pos % 4) != 0)) /* left */
00455       x = -1;
00456     else if ((dir == 3) && ((pos % 4) != 3)) /* right */
00457       x = 1;
00458     else
00459       goto retry_scramble;
00460 
00461     oldpos = pos + y * 4 + x;
00462     board[pos] = board[oldpos];
00463     board[oldpos] = NULL;
00464     g_object_set_data (G_OBJECT (board[pos]), "piece_pos", GINT_TO_POINTER (pos));
00465     gnome_canvas_item_move (board[pos], -x * PIECE_SIZE, -y * PIECE_SIZE);
00466     pos = oldpos;
00467   }
00468   /* FIXME : Workaround for bugged canvas */
00469   gnome_canvas_update_now(gcomprisBoard->canvas);
00470 }
00471