/* gcompris - fifteen.c
*
* Copyright (C) 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"
#include
#define PIECE_SIZE 50
#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 game_won(void);
static GnomeCanvasGroup *boardRootItem = NULL;
static GnomeCanvasItem *fifteen_create_item(GnomeCanvasGroup *parent);
static void fifteen_destroy_all_items(void);
static void fifteen_next_level(void);
static void free_stuff (GtkObject *obj, gpointer data);
static gint piece_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data);
static void scramble (GnomeCanvasItem **board, guint number_of_scrambles);
static char *get_piece_color (int piece);
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
"The fifteen game",
"Move the items one by one, to rearrange them in increasing order",
"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(fifteen)
/*
* 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)
{
if(agcomprisBoard!=NULL)
{
gcomprisBoard=agcomprisBoard;
gcomprisBoard->level=1;
gcomprisBoard->maxlevel=6;
gcomprisBoard->sublevel=1;
gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
gc_bar_set(GC_BAR_LEVEL);
fifteen_next_level();
gamewon = FALSE;
pause_board(FALSE);
}
}
/* ======================================= */
static void end_board ()
{
if(gcomprisBoard!=NULL)
{
pause_board(TRUE);
fifteen_destroy_all_items();
}
gcomprisBoard = NULL;
}
/* ======================================= */
static void set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
gcomprisBoard->sublevel=1;
fifteen_next_level();
}
}
/* ======================================= */
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_strcasecmp(gcomprisBoard->type, "fifteen")==0)
{
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void fifteen_next_level()
{
gchar *img;
img = gc_skin_image_get("gcompris-bg.jpg");
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
img);
g_free(img);
gc_bar_set_level(gcomprisBoard);
fifteen_destroy_all_items();
gamewon = FALSE;
/* Create the level */
fifteen_create_item(gnome_canvas_root(gcomprisBoard->canvas));
}
/* ==================================== */
/* Destroy all the items */
static void fifteen_destroy_all_items()
{
if(boardRootItem!=NULL)
gtk_object_destroy (GTK_OBJECT(boardRootItem));
boardRootItem = NULL;
}
/* ==================================== */
static GnomeCanvasItem *fifteen_create_item(GnomeCanvasGroup *parent)
{
int i;
int x, y;
GnomeCanvasItem **board;
GnomeCanvasItem *text;
char buf[20];
GdkPixbuf *pixmap = NULL;
boardRootItem = GNOME_CANVAS_GROUP(
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
gnome_canvas_group_get_type (),
"x", (double) (BOARDWIDTH-(4*PIECE_SIZE))/2,
"y", (double) (BOARDHEIGHT-(4*PIECE_SIZE))/2,
NULL));
/* Load the cute frame */
pixmap = gc_pixmap_load("images/fifteen_frame.png");
gnome_canvas_item_new (boardRootItem,
gnome_canvas_pixbuf_get_type (),
"pixbuf", pixmap,
"x", (double)-1*((gdk_pixbuf_get_width(pixmap)-(4*PIECE_SIZE))/2),
"y", (double)-1*((gdk_pixbuf_get_height(pixmap)-(4*PIECE_SIZE))/2)-2,
NULL);
gdk_pixbuf_unref(pixmap);
board = g_new (GnomeCanvasItem *, 16);
g_object_set_data (G_OBJECT (boardRootItem), "board", board);
g_signal_connect (boardRootItem, "destroy",
G_CALLBACK (free_stuff),
board);
for (i = 0; i < 15; i++) {
y = i / 4;
x = i % 4;
board[i] = gnome_canvas_item_new (boardRootItem,
gnome_canvas_group_get_type (),
"x", (double) (x * PIECE_SIZE),
"y", (double) (y * PIECE_SIZE),
NULL);
gnome_canvas_item_new (GNOME_CANVAS_GROUP (board[i]),
gnome_canvas_rect_get_type (),
"x1", 0.0,
"y1", 0.0,
"x2", (double) PIECE_SIZE,
"y2", (double) PIECE_SIZE,
"fill_color", get_piece_color (i),
"outline_color", "black",
"width_pixels", 0,
NULL);
sprintf (buf, "%d", i + 1);
text = gnome_canvas_item_new (GNOME_CANVAS_GROUP (board[i]),
gnome_canvas_text_get_type (),
"text", buf,
"x", (double) PIECE_SIZE / 2.0,
"y", (double) PIECE_SIZE / 2.0,
"font", gc_skin_font_board_medium,
"anchor", GTK_ANCHOR_CENTER,
"fill_color", "black",
NULL);
g_object_set_data (G_OBJECT (board[i]), "piece_num", GINT_TO_POINTER (i));
g_object_set_data (G_OBJECT (board[i]), "piece_pos", GINT_TO_POINTER (i));
g_object_set_data (G_OBJECT (board[i]), "text", text);
g_signal_connect (board[i], "event",
G_CALLBACK (piece_event),
NULL);
}
board[15] = NULL;
/* Select level difficulty */
switch(gcomprisBoard->level)
{
case 1:
scramble(board, 10);
break;
case 2:
scramble(board, 50);
break;
case 3:
case 4:
scramble(board, 100);
break;
case 5:
scramble(board, 150);
break;
default:
scramble(board, 256);
}
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);
}
fifteen_next_level();
}
/*==================================================*/
/* Code taken from libgnomecanvas demo fifteen */
static void
free_stuff (GtkObject *obj, gpointer data)
{
g_free (data);
}
static void
test_win (GnomeCanvasItem **board)
{
int i;
for (i = 0; i < 15; i++)
if (!board[i] || (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (board[i]), "piece_num")) != i))
return;
gamewon = TRUE;
fifteen_destroy_all_items();
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
}
static char *
get_piece_color (int piece)
{
static char buf[50];
int x, y;
int r, g, b;
y = piece / 4;
x = piece % 4;
r = ((4 - x) * 255) / 4;
g = ((4 - y) * 255) / 4;
b = 128;
sprintf (buf, "#%02x%02x%02x", r, g, b);
return buf;
}
static gint
piece_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
GnomeCanvasItem **board;
GnomeCanvasItem *text;
int num, pos, newpos;
int x, y;
double dx = 0.0, dy = 0.0;
int move;
board = g_object_get_data (G_OBJECT (item->parent), "board");
num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "piece_num"));
pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "piece_pos"));
text = g_object_get_data (G_OBJECT (item), "text");
switch (event->type) {
case GDK_ENTER_NOTIFY:
gnome_canvas_item_set (text,
"fill_color", "white",
NULL);
break;
case GDK_LEAVE_NOTIFY:
gnome_canvas_item_set (text,
"fill_color", "black",
NULL);
break;
case GDK_BUTTON_PRESS:
y = pos / 4;
x = pos % 4;
move = TRUE;
if ((y > 0) && (board[(y - 1) * 4 + x] == NULL)) {
dx = 0.0;
dy = -1.0;
y--;
} else if ((y < 3) && (board[(y + 1) * 4 + x] == NULL)) {
dx = 0.0;
dy = 1.0;
y++;
} else if ((x > 0) && (board[y * 4 + x - 1] == NULL)) {
dx = -1.0;
dy = 0.0;
x--;
} else if ((x < 3) && (board[y * 4 + x + 1] == NULL)) {
dx = 1.0;
dy = 0.0;
x++;
} else
move = FALSE;
if (move) {
newpos = y * 4 + x;
board[pos] = NULL;
board[newpos] = item;
g_object_set_data (G_OBJECT (item), "piece_pos", GINT_TO_POINTER (newpos));
gnome_canvas_item_move (item, dx * PIECE_SIZE, dy * PIECE_SIZE);
/* FIXME : Workaround for bugged canvas */
gnome_canvas_update_now(gcomprisBoard->canvas);
test_win (board);
}
break;
default:
break;
}
return FALSE;
}
static void
scramble (GnomeCanvasItem **board, guint number_of_scrambles)
{
int i;
int pos, oldpos;
int dir;
int x, y;
/* g_random are initialised in gcompris launch */
/* srand (time (NULL)); */
/* First, find the blank spot */
for (pos = 0; pos < 16; pos++)
if (board[pos] == NULL)
break;
/* "Move the blank spot" around in order to scramble the pieces */
for (i = 0; i < number_of_scrambles; i++) {
retry_scramble:
dir = g_random_int () % 4;
x = y = 0;
if ((dir == 0) && (pos > 3)) /* up */
y = -1;
else if ((dir == 1) && (pos < 12)) /* down */
y = 1;
else if ((dir == 2) && ((pos % 4) != 0)) /* left */
x = -1;
else if ((dir == 3) && ((pos % 4) != 3)) /* right */
x = 1;
else
goto retry_scramble;
oldpos = pos + y * 4 + x;
board[pos] = board[oldpos];
board[oldpos] = NULL;
g_object_set_data (G_OBJECT (board[pos]), "piece_pos", GINT_TO_POINTER (pos));
gnome_canvas_item_move (board[pos], -x * PIECE_SIZE, -y * PIECE_SIZE);
pos = oldpos;
}
/* FIXME : Workaround for bugged canvas */
gnome_canvas_update_now(gcomprisBoard->canvas);
}