/* gcompris - hanoi_real.c * * Copyright (C) 2005, 2008 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 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 GooCanvasItem *boardRootItem = NULL; static GooCanvasItem *hanoi_create_item(GooCanvasItem *parent); static void hanoi_destroy_all_items(void); static void hanoi_next_level(void); /* * Contains the piece information */ typedef struct { GooCanvasItem *item; gint i; gint j; double x; double y; gboolean on_top; gint width; } PieceItem; static gboolean item_event (GooCanvasItem *item, GooCanvasItem *target, GdkEvent *event, PieceItem *data); /* This contains the layout of the pieces */ #define MAX_NUMBER_X 3 #define MAX_NUMBER_Y 10 static PieceItem *position[MAX_NUMBER_X][MAX_NUMBER_Y]; static int number_of_item = 0; static int number_of_item_x = MAX_NUMBER_X; static int number_of_item_y = 0; static int item_width; static int item_height; /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, N_("Tower of Hanoi"), "", "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(hanoi_real) /* * 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=2; gcomprisBoard->sublevel=1; gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */ gc_bar_set(GC_BAR_LEVEL); gc_set_default_background(goo_canvas_get_root_item(gcomprisBoard->canvas)); boardRootItem = NULL; gc_drag_start(goo_canvas_get_root_item(gcomprisBoard->canvas), (GcDragFunc)item_event, GC_DRAG_MODE_DEFAULT); hanoi_next_level(); gamewon = FALSE; pause_board(FALSE); } } /* ======================================= */ static void end_board () { if(gcomprisBoard!=NULL) { gc_drag_stop(goo_canvas_get_root_item(gcomprisBoard->canvas)); pause_board(TRUE); hanoi_destroy_all_items(); } gcomprisBoard = NULL; } /* ======================================= */ static void set_level (guint level) { if(gcomprisBoard!=NULL) { gcomprisBoard->level=level; gcomprisBoard->sublevel=1; hanoi_next_level(); } } /* ======================================= */ static gboolean is_our_board (GcomprisBoard *gcomprisBoard) { if (gcomprisBoard) { if(g_ascii_strcasecmp(gcomprisBoard->type, "hanoi_real")==0) { /* Set the plugin entry */ gcomprisBoard->plugin=&menu_bp; return TRUE; } } return FALSE; } /*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/ /* set initial values for the next level */ static void hanoi_next_level() { gc_bar_set_level(gcomprisBoard); hanoi_destroy_all_items(); gamewon = FALSE; /* Select level difficulty */ number_of_item_y = gcomprisBoard->level + 2; /* Try the next level */ hanoi_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas)); } /* ==================================== */ /* Destroy all the items */ static void hanoi_destroy_all_items() { guint i,j; if(boardRootItem!=NULL) { goo_canvas_item_remove(boardRootItem); /* Cleanup our memory structure */ for(i=0; ii, position[i][j]->j, position[i][j]->width, position[i][j]->on_top); } printf("\n"); } } #endif /* ==================================== */ static GooCanvasItem * hanoi_create_item(GooCanvasItem *parent) { int i,j; double gap_x, gap_y; double baseline; GooCanvasItem *item; GdkPixbuf *pixmap; boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas), NULL); item = goo_canvas_svg_new (boardRootItem, gc_skin_rsvg_get(), "svg-id", "#BAR_BG", NULL); SET_ITEM_LOCATION_CENTER(item, BOARDWIDTH/2, BOARDHEIGHT - 100); goo_canvas_text_new (boardRootItem, _("Move the entire stack to the right peg, one disc at a time"), BOARDWIDTH/2, BOARDHEIGHT - 100, -1, GTK_ANCHOR_CENTER, "font", gc_skin_font_board_small, "fill_color_rgba", gc_skin_color_text_button, "alignment", PANGO_ALIGN_CENTER, NULL); /*----------------------------------------*/ /* Empty the solution */ for(i=0; iwidth = -1; position[i][j]->i = i; position[i][j]->j = j; position[i][j]->on_top = FALSE; } } /* Initialize the left positions */ for(j=0; jwidth = number_of_item_y - j; } /* Mark the top piece */ position[0][number_of_item_y-1]->on_top = TRUE; /*dump_solution();*/ /*----------------------------------------*/ /* Display it now */ item_width = BOARDWIDTH / (number_of_item_x); item_height = 30; gap_x = item_width * 0.1; gap_y = item_height * 0.25; baseline = BOARDHEIGHT/2 + item_height * number_of_item_y/2; number_of_item = 0; for(i=0; ix = item_width * i + item_width/2; position[i][j]->y = baseline - item_height * j - item_height; if(position[i][j]->width != -1) { pixmap = gc_pixmap_load ("%s%d.png", "hanoi_real/disc", j+1); int w = gdk_pixbuf_get_width(pixmap); item = goo_canvas_image_new (boardRootItem, pixmap, 0, 0, NULL); #if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24 gdk_pixbuf_unref(pixmap); #else g_object_unref(pixmap); #endif goo_canvas_item_translate(item, position[i][j]->x - w/2, position[i][j]->y); position[i][j]->item = item; g_signal_connect(item, "button_press_event", (GCallback) gc_drag_event, position[i][j]); g_signal_connect(item, "button_release_event", (GCallback) gc_drag_event, position[i][j]); } } } 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) gcomprisBoard->level = gcomprisBoard->maxlevel; gc_sound_play_ogg ("sounds/bonus.wav", NULL); } hanoi_next_level(); } /* * Returns TRUE is the goal is reached */ static gboolean is_completed() { gint j; gboolean done = TRUE; for(j=0; jwidth != number_of_item_y - j) done = FALSE; } return done; } /* ==================================== */ static gboolean item_event (GooCanvasItem *item, GooCanvasItem *target, GdkEvent *event, PieceItem *data) { double item_x, item_y; if(!gcomprisBoard) return FALSE; if(board_paused) return FALSE; if(data && !data->on_top) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: switch(event->button.button) { case 1: gc_drag_offset_save(event); goo_canvas_item_raise(data->item, NULL); break; } break; case GDK_MOTION_NOTIFY: gc_drag_item_move(event, NULL); break; case GDK_BUTTON_RELEASE: { gint i; gint tmpi, tmpj; double tmpx, tmpy; PieceItem *piece_src; PieceItem *piece_dst; gint line; gint col=-1; double disc_w, disc_h; GooCanvasBounds bounds; item_x = event->button.x; item_y = event->button.y; goo_canvas_item_get_bounds(data->item, &bounds); disc_w = (bounds.x2 - bounds.x1)/2; disc_h = (bounds.y2 - bounds.y1)/2; gc_canvas_item_ungrab(data->item, event->button.time); goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(data->item), data->item, &item_x, &item_y); /* Search the column (x) where this item is ungrabbed */ if(item_x > (position[number_of_item_x-1][0]->x - (position[number_of_item_x-1][0]->x - position[number_of_item_x-2][0]->x) / 2)) col = number_of_item_x-1; else if(item_x < position[0][0]->x) col = 0; else { for(i=0; ix - position[i][0]->x) / 2; if(position[i][0]->x - distance < item_x && position[i+1][0]->x - distance > item_x) col = i; } } /* Bad drop / Outside of column area */ /* Bad drop / On the same column */ if(col<0 || col > number_of_item_x || col == data->i) { /* Return to the original position */ gc_item_absolute_move (data->item , data->x - disc_w, data->y); return FALSE; } /* Now search the free line (y) */ line = number_of_item_y; for(i=number_of_item_y-1; i>=0; i--) if(position[col][i]->width == -1) line = i; /* Bad drop / Too many pieces here or larger disc is above */ if(line > number_of_item_y || (line > 0 && position[col][line-1]->width != -1 && position[col][line-1]->width < data->width)) { /* Return to the original position */ gc_item_absolute_move (data->item , data->x - disc_w, data->y - disc_h); return FALSE; } /* Update ontop values for the piece under the grabbed one */ if(data->j>0) position[data->i][data->j-1]->on_top = TRUE; /* Update ontop values for the piece under the ungrabbed one */ if(line>0) position[col][line-1]->on_top = FALSE; /* Move the piece */ piece_dst = position[col][line]; piece_src = data; gc_item_absolute_move (data->item, piece_dst->x - disc_w, piece_dst->y); /* Swap values in the pieces */ tmpx = data->x; tmpy = data->y; piece_src->x = piece_dst->x; piece_src->y = piece_dst->y; piece_dst->x = tmpx; piece_dst->y = tmpy; tmpi = data->i; tmpj = data->j; position[tmpi][tmpj]->i = piece_dst->i; position[tmpi][tmpj]->j = piece_dst->j; piece_dst->i = tmpi; piece_dst->j = tmpj; position[piece_src->i][piece_src->j] = piece_src; position[piece_dst->i][piece_dst->j] = piece_dst; /*dump_solution();*/ if(is_completed()) { gamewon = TRUE; hanoi_destroy_all_items(); gc_bonus_display(gamewon, GC_BONUS_SMILEY); } } break; default: break; } return FALSE; }