1
/* gcompris - reversecount.c
3
* Copyright (C) 2002, 2008 Bruno Coudoin
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, see <http://www.gnu.org/licenses/>.
19
#include "gcompris/gcompris.h"
21
#define SOUNDLISTFILE PACKAGE
23
static GcomprisBoard *gcomprisBoard = NULL;
24
static gboolean board_paused = TRUE;
25
static gint animate_id = 0;
27
static void start_board (GcomprisBoard *agcomprisBoard);
28
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
29
static void pause_board (gboolean pause);
30
static void end_board (void);
31
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
32
static void set_level (guint level);
34
static void game_won(void);
41
#define TUX_TO_BORDER_GAP 10
43
static GooCanvasItem *boardRootItem = NULL;
45
static void process_ok(void);
46
static void process_error(void);
47
static GooCanvasItem *reversecount_create_item(GooCanvasItem *parent);
48
static void reversecount_destroy_all_items(void);
49
static void reversecount_next_level(void);
50
static gboolean item_event (GooCanvasItem *item,
51
GooCanvasItem *target,
52
GdkEventButton *event,
54
static GooCanvasItem *display_item_at(gchar *imagename, int block);
55
static void display_random_fish();
56
static void create_clock(double x, double y, int value);
57
static void update_clock(int value);
58
static gint animate_tux();
59
static void rotate_tux(GooCanvasItem *tuxitem, gint direction,
61
static void move_item_at(GooCanvasItem *item,
62
int block, double ratio);
64
static int number_of_item = 0;
65
static int number_of_item_x = 0;
66
static int number_of_item_y = 0;
68
static int errors = 0;
69
static int number_of_dices = 0;
70
static int max_dice_number = 0;
71
static int number_of_fish = 0;
73
static int tux_index = 0;
74
static int tux_destination = 0;
75
static int fish_index = 0;
76
static int animate_speed = 0;
78
#define ANIMATE_SPEED 800
80
static gdouble tux_ratio = 0;
82
static int dicevalue_array[10];
83
static GooCanvasItem *fishItem;
84
static GooCanvasItem *tuxItem;
85
static GooCanvasItem *tuxRootItem;
86
static GooCanvasItem *clock_image_item;
88
// List of images to use in the game
89
static gchar *imageList[] =
91
"reversecount/baleine.svgz",
92
"reversecount/phoque.svgz",
93
"reversecount/ourspolaire.svgz",
94
"reversecount/morse.svgz",
95
"reversecount/elephant_mer.svgz",
96
"reversecount/epaulard.svgz",
97
"reversecount/narval.svgz",
99
#define NUMBER_OF_IMAGES 10
101
// List of fish to use in the game
102
static gchar *fishList[] =
104
"reversecount/blueking2_0.png",
105
"reversecount/butfish_0.png",
106
"reversecount/cichlid1_0.png",
107
"reversecount/cichlid4_0.png",
108
"reversecount/collaris_0.png",
109
"reversecount/discus2_0.png",
110
"reversecount/discus3_0.png",
111
"reversecount/eel_0.png",
112
"reversecount/f00_0.png",
113
"reversecount/f01_0.png",
114
"reversecount/f02_0.png",
115
"reversecount/f03_0.png",
116
"reversecount/f04_0.png",
117
"reversecount/f05_0.png",
118
"reversecount/f06_0.png",
119
"reversecount/f07_0.png",
120
"reversecount/f08_0.png",
121
"reversecount/f09_0.png",
122
"reversecount/f10_0.png",
123
"reversecount/f11_0.png",
124
"reversecount/f12_0.png",
125
"reversecount/f13_0.png",
126
"reversecount/manta_0.png",
127
"reversecount/newf1_0.png",
128
"reversecount/QueenAngel_0.png",
129
"reversecount/shark1_0.png",
130
"reversecount/six_barred_0.png",
131
"reversecount/teeth_0.png"
133
#define NUMBER_OF_FISHES 27
135
/* Description of this plugin */
136
static BoardPlugin menu_bp =
141
"Practice substraction with a funny game",
142
"Bruno Coudoin <bruno.coudoin@free.fr>",
161
* Main entry point mandatory for each Gcompris's game
162
* ---------------------------------------------------
166
GET_BPLUGIN_INFO(reversecount)
169
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
172
static void pause_board (gboolean pause)
174
if(gcomprisBoard==NULL)
177
if(gamewon == TRUE && pause == FALSE) /* the game is won */
183
board_paused = pause;
188
static void start_board (GcomprisBoard *agcomprisBoard)
191
if(agcomprisBoard!=NULL)
193
gcomprisBoard=agcomprisBoard;
195
/* disable im_context */
196
gcomprisBoard->disable_im_context = TRUE;
198
gcomprisBoard->level=1;
199
gcomprisBoard->maxlevel=7;
200
gcomprisBoard->sublevel=1;
201
gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
202
gc_bar_set(GC_BAR_LEVEL);
203
gc_bar_location(10, -1, 0.7);
205
reversecount_next_level();
212
/* ======================================= */
213
static void end_board ()
215
if(gcomprisBoard!=NULL)
218
reversecount_destroy_all_items();
220
gcomprisBoard = NULL;
223
/* ======================================= */
224
static void set_level (guint level)
227
if(gcomprisBoard!=NULL)
229
gcomprisBoard->level=level;
230
gcomprisBoard->sublevel=1;
231
reversecount_next_level();
235
/* ======================================= */
236
gboolean is_our_board (GcomprisBoard *gcomprisBoard)
240
if(g_strcasecmp(gcomprisBoard->type, "reversecount")==0)
242
/* Set the plugin entry */
243
gcomprisBoard->plugin=&menu_bp;
251
/* ======================================= */
252
gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
258
/* Add some filter for control and shift key */
261
/* Avoid all this keys to be interpreted by this game */
288
static void process_error()
290
gc_sound_play_ogg ("sounds/crash.wav", NULL);
295
reversecount_destroy_all_items();
296
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
300
update_clock(errors);
304
/* ======================================= */
305
static void process_ok()
309
tux_destination = tux_index;
311
for(i=0; i<number_of_dices; i++)
312
tux_destination += dicevalue_array[i];
315
if(tux_destination >= number_of_item)
316
tux_destination = tux_destination - (number_of_item);
318
// Do not allow going at a position after the fish
319
if((tux_destination > fish_index)
320
|| (tux_destination == tux_index))
327
animate_id = gtk_timeout_add (animate_speed,
328
(GtkFunction) animate_tux, NULL);
333
/*-------------------------------------------------------------------------------*/
334
/*-------------------------------------------------------------------------------*/
336
/* set initial values for the next level */
337
static void reversecount_next_level()
340
gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
341
imageList[gcomprisBoard->level-1]);
343
reversecount_destroy_all_items();
346
/* Select level difficulty */
347
switch(gcomprisBoard->level)
350
number_of_item_x = 5;
351
number_of_item_y = 5;
357
number_of_item_x = 5;
358
number_of_item_y = 5;
364
number_of_item_x = 6;
365
number_of_item_y = 6;
371
number_of_item_x = 8;
372
number_of_item_y = 6;
378
number_of_item_x = 8;
379
number_of_item_y = 6;
385
number_of_item_x = 8;
386
number_of_item_y = 8;
392
number_of_item_x = 10;
393
number_of_item_y = 10;
400
animate_speed = ANIMATE_SPEED - gcomprisBoard->level * 60;
402
number_of_item = number_of_item_x * 2 + (number_of_item_y - 2) * 2;
404
/* Try the next level */
405
reversecount_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
407
gc_bar_set_level(gcomprisBoard);
410
/* ==================================== */
411
/* Destroy all the items */
412
static void reversecount_destroy_all_items()
417
if(boardRootItem!=NULL)
418
goo_canvas_item_remove(boardRootItem);
420
boardRootItem = NULL;
422
/* ==================================== */
423
static GooCanvasItem *reversecount_create_item(GooCanvasItem *parent)
426
GooCanvasItem *item = NULL;
427
gdouble block_width, block_height;
429
gdouble xratio, yratio;
430
GcomprisProperties *properties = gc_prop_get();
431
RsvgHandle *svg_handle;
433
boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
437
block_width = BOARDWIDTH/number_of_item_x;
438
block_height = (BOARDHEIGHT-BARHEIGHT)/number_of_item_y;
440
/* Timer is not requested */
441
if(properties->timer > 0)
443
errors = number_of_dices + 4 - (MIN(properties->timer, 4));
444
create_clock(BOARDWIDTH - block_width - 100,
445
BOARDHEIGHT - block_height - 100 - BARHEIGHT,
454
svg_handle = gc_rsvg_load("reversecount/iceblock.svgz");
455
RsvgDimensionData rsvg_dimension;
456
rsvg_handle_get_dimensions (svg_handle, &rsvg_dimension);
458
xratio = block_width / rsvg_dimension.width;
459
yratio = block_height / rsvg_dimension.height;
461
for(i=0; i<BOARDWIDTH; i+=block_width)
464
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
465
goo_canvas_item_translate(item, i, j);
466
goo_canvas_item_scale(item, xratio, yratio);
468
j=BOARDHEIGHT-BARHEIGHT-block_height;
469
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
470
goo_canvas_item_translate(item, i, j);
471
goo_canvas_item_scale(item, xratio, yratio);
474
for(j = block_height; j<=BOARDHEIGHT - (block_height*2) - BARHEIGHT;
478
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
479
goo_canvas_item_translate(item, i, j);
480
goo_canvas_item_scale(item, xratio, yratio);
483
i = BOARDWIDTH - block_width;
484
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
485
goo_canvas_item_translate(item, i, j);
486
goo_canvas_item_scale(item, xratio, yratio);
489
g_object_unref(svg_handle);
492
//----------------------------------------
493
// Create the dice area
494
svg_handle = gc_rsvg_load("reversecount/dice_area.svgz");
495
rsvg_handle_get_dimensions (svg_handle, &rsvg_dimension);
497
dice_area_x = BOARDWIDTH - block_width - rsvg_dimension.width - 20;
499
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
500
goo_canvas_item_translate(item,
501
dice_area_x, block_height + 20);
503
g_object_unref(svg_handle);
505
//----------------------------------------
507
svg_handle = gc_rsvg_load("reversecount/dice1.svgz");
508
rsvg_handle_get_dimensions (svg_handle, &rsvg_dimension);
509
guint dice_width = 78;
511
for(d=0; d<number_of_dices; d++)
515
i = dice_area_x + dice_width * d + 20;
516
j = block_height + 24 + d*7;
518
item = goo_canvas_svg_new (boardRootItem, svg_handle, NULL);
519
goo_canvas_item_translate(item,
522
xratio = (gdouble)dice_width / rsvg_dimension.width;
523
goo_canvas_item_scale(item, xratio, xratio);
525
dicevalue_array[d] = 1;
526
val = g_new(gint, 1);
529
g_signal_connect(item, "button_press_event",
530
(GtkSignalFunc) item_event,
532
gc_item_focus_init(item, NULL);
534
g_object_unref(svg_handle);
536
// OK Button (Validation)
537
item = goo_canvas_svg_new (boardRootItem,
541
SET_ITEM_LOCATION(item,
544
g_signal_connect(item, "button_press_event",
545
(GtkSignalFunc) process_ok, NULL);
546
gc_item_focus_init(item, NULL);
549
tuxRootItem = goo_canvas_group_new (boardRootItem, NULL);
551
svg_handle = gc_rsvg_load("reversecount/tux_top_south.svgz");
552
tuxItem = goo_canvas_svg_new (tuxRootItem, svg_handle, NULL);
554
RsvgDimensionData dimension;
555
rsvg_handle_get_dimensions(svg_handle, &dimension);
557
/* Calc the tux best ratio to display it */
558
xratio = block_width / (dimension.width + TUX_TO_BORDER_GAP);
559
yratio = block_height / (dimension.height + TUX_TO_BORDER_GAP);
560
tux_ratio = yratio = MIN(xratio, yratio);
562
goo_canvas_item_scale(tuxItem, tux_ratio, tux_ratio);
564
goo_canvas_item_translate(tuxItem, (BOARDWIDTH - dimension.width)/2,
565
(BOARDHEIGHT - dimension.height)/2);
566
rotate_tux(tuxItem, EAST, tux_ratio);
567
g_object_unref (svg_handle);
571
// Display the first fish
572
display_random_fish();
577
static void display_random_fish()
580
fish_index = tux_index +
581
g_random_int()%(max_dice_number*number_of_dices) + 1;
584
if(fish_index >= number_of_item)
585
fish_index = fish_index - (number_of_item);
587
fishItem = display_item_at(fishList[g_random_int()%NUMBER_OF_FISHES],
591
/* ==================================== */
593
* Display given imagename on the given ice block.
595
static GooCanvasItem*
596
display_item_at(gchar *imagename, int block)
598
double block_width, block_height;
599
double xratio, yratio;
605
block_width = BOARDWIDTH/number_of_item_x;
606
block_height = (BOARDHEIGHT-BARHEIGHT)/number_of_item_y;
608
if(block < number_of_item_x)
611
g_warning(" // Upper line\n");
612
i = block_width * block;
615
else if(block < number_of_item_x + number_of_item_y - 2)
618
g_warning(" // Right line\n");
619
i = block_width * (number_of_item_x - 1);
620
j = block_height * (block - (number_of_item_x-1));
622
else if(block < number_of_item_x*2 + number_of_item_y - 2)
625
g_warning(" // Bottom line\n");
626
i = block_width * (number_of_item_x - (block-
627
(number_of_item_x+number_of_item_y-1))-2);
628
j = block_height * (number_of_item_y-1);
633
g_warning(" // Left line\n");
635
j = block_height * (number_of_item_y - (block - (number_of_item_x*2 +
636
number_of_item_y-4)));
639
g_warning("display_tux %d i=%d j=%d\n", block, i, j);
641
/* Calculation to thrink the item while keeping the ratio */
642
pixmap = gc_pixmap_load(imagename);
643
xratio = block_width / (gdk_pixbuf_get_width (pixmap) + TUX_TO_BORDER_GAP);
644
yratio = block_height / (gdk_pixbuf_get_height(pixmap) + TUX_TO_BORDER_GAP);
645
xratio = yratio = MIN(xratio, yratio);
647
pixmap2 = gdk_pixbuf_scale_simple(pixmap,
648
gdk_pixbuf_get_width (pixmap) * xratio,
649
gdk_pixbuf_get_height(pixmap) * xratio,
650
GDK_INTERP_BILINEAR);
651
gdk_pixbuf_unref(pixmap);
653
item = goo_canvas_image_new (boardRootItem,
656
(gdk_pixbuf_get_width (pixmap2))) / 2,
658
(gdk_pixbuf_get_height (pixmap2))) / 2,
661
gdk_pixbuf_unref(pixmap2);
667
* Move given GooCanvasItem on the given ice block.
670
move_item_at(GooCanvasItem *item, int block, double ratio)
672
double block_width, block_height;
674
GooCanvasBounds bounds;
676
block_width = BOARDWIDTH/number_of_item_x;
677
block_height = (BOARDHEIGHT-BARHEIGHT)/number_of_item_y;
679
if(block < number_of_item_x)
682
g_warning(" // Upper line\n");
683
i = block_width * block;
686
else if(block < number_of_item_x + number_of_item_y - 2)
689
g_warning(" // Right line\n");
690
i = block_width * (number_of_item_x - 1);
691
j = block_height * (block - (number_of_item_x-1));
693
else if(block < number_of_item_x*2 + number_of_item_y - 2)
696
g_warning(" // Bottom line\n");
697
i = block_width * (number_of_item_x - (block-
698
(number_of_item_x+number_of_item_y-1))-2);
699
j = block_height * (number_of_item_y-1);
704
g_warning(" // Left line\n");
706
j = block_height * (number_of_item_y - (block - (number_of_item_x*2 +
707
number_of_item_y-4)));
710
g_warning("move_item_at %d i=%d j=%d\n", block, i, j);
712
goo_canvas_item_get_bounds(item, &bounds);
714
goo_canvas_item_animate(item,
722
GOO_CANVAS_ANIMATE_FREEZE);
726
/* ==================================== */
727
static void game_won()
729
gcomprisBoard->sublevel++;
731
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
732
/* Try the next level */
733
gcomprisBoard->sublevel=1;
734
gcomprisBoard->level++;
735
if(gcomprisBoard->level> gcomprisBoard->maxlevel) { // the current board is finished : bail out
736
gcomprisBoard->level = gcomprisBoard->maxlevel;
739
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
741
reversecount_next_level();
744
/* ==================================== */
746
* Increment the dices when they are clicked
750
item_event (GooCanvasItem *item,
751
GooCanvasItem *target,
752
GdkEventButton *event,
756
RsvgHandle *rsvg_handle;
757
gint i = *dice_index;
762
switch(event->button)
765
if(dicevalue_array[i]++ >= max_dice_number)
766
dicevalue_array[i] = (number_of_dices==1 ? 1 : 0);
770
if(dicevalue_array[i]-- == (number_of_dices==1 ? 1 : 0))
771
dicevalue_array[i] = max_dice_number;
777
str = g_strdup_printf("reversecount/dice%d.svgz", dicevalue_array[i]);
778
rsvg_handle = gc_rsvg_load(str);
781
"svg-handle", rsvg_handle,
783
gc_item_focus_init(item, NULL);
784
g_object_unref(rsvg_handle);
794
static void create_clock(double x, double y, int value)
796
GdkPixbuf *pixmap = NULL;
802
str = g_strdup_printf("%s%d.png", "timers/clock",value);
804
pixmap = gc_skin_pixmap_load(str);
806
clock_image_item = goo_canvas_image_new (boardRootItem,
812
gdk_pixbuf_unref(pixmap);
816
static void update_clock(int value)
818
GdkPixbuf *pixmap = NULL;
824
str = g_strdup_printf("%s%d.png", "timers/clock",value);
826
pixmap = gc_skin_pixmap_load(str);
828
g_object_set (clock_image_item,
832
gdk_pixbuf_unref(pixmap);
836
static gint animate_tux()
841
move_item_at(tuxRootItem, tux_index, tux_ratio);
843
g_warning("=========== tux_index=%d tux_destination=%d fish_index=%d\n",
844
tux_index, tux_destination, fish_index);
847
if(tux_index >= number_of_item)
848
tux_index = tux_index - (number_of_item);
850
/* Calculate which tux should be displayed */
851
if(tux_index<number_of_item_x-1)
852
rotate_tux(tuxItem, EAST, tux_ratio);
853
else if(tux_index<number_of_item_x+number_of_item_y-2)
854
rotate_tux(tuxItem, SOUTH, tux_ratio);
855
else if(tux_index<2*number_of_item_x+number_of_item_y-3)
856
rotate_tux(tuxItem, WEST, tux_ratio);
858
rotate_tux(tuxItem, NORTH, tux_ratio);
860
/* Rearm the timer to go to the next spot */
861
if(tux_index != tux_destination)
863
animate_id = gtk_timeout_add (animate_speed,
864
(GtkFunction) animate_tux, NULL);
870
if(tux_destination != fish_index)
878
goo_canvas_item_remove(fishItem);
880
gc_sound_play_ogg ("sounds/gobble.wav", NULL);
882
if(--number_of_fish == 0)
885
reversecount_destroy_all_items();
886
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
890
display_random_fish();
899
rotate_tux(GooCanvasItem *tuxitem, gint direction,
903
GooCanvasBounds bounds;
905
/* Our svg image of tux is faced south */
922
goo_canvas_item_set_transform(tuxitem, NULL);
924
goo_canvas_item_get_bounds(tuxitem, &bounds);
926
goo_canvas_item_scale(tuxitem, scale, scale);
928
goo_canvas_item_rotate(tuxitem, rotation,
929
(bounds.x2-bounds.x1)/2,
930
(bounds.y2-bounds.y1)/2);