3
* Copyright (C) 2006 Miguel de Izarra
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"
20
#include <string.h> // for strcmp
22
#define SOUNDLISTFILE PACKAGE
24
static GcomprisBoard *gcomprisBoard = NULL;
25
static gboolean board_paused = TRUE;
27
static void start_board (GcomprisBoard *agcomprisBoard);
28
static void pause_board (gboolean pause);
29
static void end_board (void);
30
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
31
static void config_start(GcomprisBoard *agcomprisBoard,
32
GcomprisProfile *aProfile);
33
static void config_stop();
34
static void set_level (guint level);
35
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
36
static void process_ok(void);
38
static void game_won(void);
40
static GnomeCanvasGroup *boardRootItem = NULL;
41
static GnomeCanvasGroup *group_g = NULL, *group_d= NULL;
42
static GnomeCanvasItem *bras = NULL;
43
static GnomeCanvasItem *answer_item = NULL;
44
GString *answer_string = NULL;
46
static void scale_destroy_all_items(void);
47
static void scale_next_level(void);
50
#define ITEM_X_MAX 500
51
#define ITEM_Y_MIN 250
52
#define ITEM_Y_MAX 400
56
#define PLATE_X1 -40.0
57
#define PLATE_X2 246.0
59
#define PLATE_Y_DELTA 15.0
65
#define PLATE_SIZE 4 // number of place in the plate
68
GnomeCanvasItem * item;
70
int plate; // 0 not place, 1 in plate_g, -1 in plate_d
71
int plate_index; // position in the plate
75
static GList *item_list = NULL;
76
static int objet_weight = 0;
78
static gint drag_mode;
79
static const char *imageList[] = {
80
"gcompris/food/chocolate_cake.png",
81
"gcompris/food/pear.png",
82
"gcompris/food/orange.png",
83
"gcompris/food/suggar_box.png",
84
"gcompris/misc/flowerpot.png",
85
"gcompris/misc/glass.png"
88
static const int imageListCount = G_N_ELEMENTS(imageList);
90
static int scale_drag_event(GnomeCanvasItem *w, GdkEvent *event, ScaleItem *item);
92
/* Description of this plugin */
93
static BoardPlugin menu_bp =
98
"Balance the scales properly",
99
"Miguel de Izarra <miguel2i@free.fr>",
118
* Main entry point mandatory for each Gcompris's game
119
* ---------------------------------------------------
123
GET_BPLUGIN_INFO(scale)
126
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
129
static void pause_board (gboolean pause)
131
if(gcomprisBoard==NULL)
134
if(gamewon == TRUE && pause == FALSE) /* the game is won */
139
board_paused = pause;
142
static void start_board (GcomprisBoard *agcomprisBoard)
144
if(agcomprisBoard!=NULL)
146
gcomprisBoard=agcomprisBoard;
147
gcomprisBoard->level=1;
148
gcomprisBoard->sublevel=1;
149
gcomprisBoard->number_of_sublevel=5; /* Go to next level after this number of 'play' */
150
gcomprisBoard->maxlevel = 4;
151
gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK|GC_BAR_CONFIG);
156
GHashTable *config = gc_db_get_board_conf();
157
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
159
if (drag_mode_str && (strcmp (drag_mode_str, "NULL") != 0))
160
drag_mode = g_ascii_strtod(drag_mode_str, NULL);
164
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
167
gc_drag_start(gnome_canvas_root(gcomprisBoard->canvas),
168
(gc_Drag_Func)scale_drag_event, drag_mode);
170
gc_score_start(SCORESTYLE_NOTE,
171
gcomprisBoard->width - 220,
173
gcomprisBoard->number_of_sublevel);
179
static void end_board ()
181
if(gcomprisBoard!=NULL)
183
gc_drag_stop(gnome_canvas_root(gcomprisBoard->canvas));
185
scale_destroy_all_items();
188
gcomprisBoard = NULL;
191
/* ======================================= */
192
static void set_level (guint level)
195
if(gcomprisBoard!=NULL)
197
gcomprisBoard->level=level;
198
gcomprisBoard->sublevel=1;
202
/* ======================================= */
203
gboolean is_our_board (GcomprisBoard *gcomprisBoard)
206
if(g_strcasecmp(gcomprisBoard->type, "scale")==0)
208
/* Set the plugin entry */
209
gcomprisBoard->plugin=&menu_bp;
216
/* ======================================= */
217
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
219
if(board_paused || !boardRootItem)
222
/* Add some filter for control and shift key */
225
/* Avoid all this keys to be interpreted by this game */
251
g_string_truncate(answer_string, answer_string->len -1);
258
gchar c = commit_str ? commit_str[0] : 0;
260
/* Limit the user entry to 5 digits */
261
if(c>='0' && c<='9' && answer_string->len < 5)
262
answer_string = g_string_append_c(answer_string, c);
264
tmpstr = g_strdup_printf(_("Weight = %s"), answer_string->str);
265
gnome_canvas_item_set(answer_item,
275
// plate = -1 plate d
276
// plate = 0 plate g - plate d
277
int get_weight_plate(int plate)
283
for(list = item_list; list; list=list->next)
286
if(item->plate == plate || plate==0)
288
result+= item->weight * item->plate;
296
static double last_delta=0;
297
void scale_anim_plate(void)
304
diff = get_weight_plate(0);
305
delta_y = CLAMP(PLATE_Y_DELTA / 10.0 * diff,
306
-PLATE_Y_DELTA, PLATE_Y_DELTA);
307
if(get_weight_plate(1)==0)
308
delta_y = -PLATE_Y_DELTA;
309
if(last_delta != delta_y)
311
last_delta = delta_y;
313
angle = tan(delta_y / 138) * 180 / M_PI;
315
gtk_object_get (GTK_OBJECT (group_g), "x", &x, NULL);
316
art_affine_translate(affine, x, delta_y);
317
gnome_canvas_item_affine_absolute(GNOME_CANVAS_ITEM(group_g), affine);
319
gtk_object_get (GTK_OBJECT (group_d), "x", &x, NULL);
320
art_affine_translate(affine, x, -delta_y);
321
gnome_canvas_item_affine_absolute(GNOME_CANVAS_ITEM(group_d), affine);
323
gc_item_rotate_with_center(bras, -angle, 138, 84);
325
if(diff ==0 && (gcomprisBoard->level == 2 || gcomprisBoard->level == 4))
327
GdkPixbuf *button_pixmap;
328
double x_offset = 40, y_offset = 150;
330
button_pixmap = gc_skin_pixmap_load("button_large2.png");
331
gnome_canvas_item_new (boardRootItem,
332
gnome_canvas_pixbuf_get_type (),
333
"pixbuf", button_pixmap,
337
answer_item = gnome_canvas_item_new(boardRootItem,
338
gnome_canvas_text_get_type(),
339
"font", gc_skin_font_board_title_bold,
340
"x", x_offset + gdk_pixbuf_get_width(button_pixmap)/2,
341
"y", y_offset + gdk_pixbuf_get_height(button_pixmap)/2,
342
"anchor", GTK_ANCHOR_CENTER,
343
"fill_color", "black",
345
gdk_pixbuf_unref(button_pixmap);
347
answer_string = g_string_new(NULL);
348
key_press(0,NULL,NULL);
352
// if plate = 1 , move to plate g
353
// if plate = -1, move to plate d
354
// if plate = 0 , move to the item list
355
void scale_item_move_to(ScaleItem *item, int plate)
365
item->plate_index = -1;
367
gc_sound_play_ogg ("sounds/eraser1.wav", NULL);
369
// find the first free place in the plate
370
for(index=0; index < PLATE_SIZE; index ++)
373
for(list = item_list; list; list = list->next)
376
if(scale->plate_index == index && scale->plate == plate)
380
{ // move to the plate
382
item->plate_index=index;
383
gnome_canvas_item_reparent(item->item, plate == 1 ? group_g : group_d);
384
gnome_canvas_item_set(item->item,
385
"x", (double)index * ITEM_W,
386
"y", (double)PLATE_Y-ITEM_H + 5,
391
if(found) // can't find place
395
{ // move the item to the list
397
gc_sound_play_ogg ("sounds/eraser1.wav", NULL);
399
gnome_canvas_item_reparent(item->item, boardRootItem);
400
gnome_canvas_item_set(item->item,
408
static int scale_item_event(GnomeCanvasItem *w, GdkEvent *event, ScaleItem *scale)
413
if(event->type == GDK_BUTTON_PRESS && event->button.button==3)
414
scale_item_move_to(scale, 0);
418
static int scale_drag_event(GnomeCanvasItem *w, GdkEvent *event, ScaleItem *scale)
423
if(answer_string) // disable, waiting a answer
428
case GDK_BUTTON_PRESS:
429
gc_drag_offset_save(event);
430
g_object_get(G_OBJECT(scale->item), "x", &x, "y", &y, NULL);
431
gnome_canvas_item_i2w(scale->item, &x,&y);
432
gnome_canvas_item_reparent(scale->item,boardRootItem);
433
gnome_canvas_item_w2i(scale->item, &x, &y);
434
gnome_canvas_item_set(scale->item, "x", x, "y", y, NULL);
436
case GDK_MOTION_NOTIFY:
437
gc_drag_item_move(event);
439
case GDK_BUTTON_RELEASE:
442
gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(group_g), &x, &y);
443
if(-ITEM_W < x && x < PLATE_W + ITEM_W && abs(y-PLATE_Y) < ITEM_H)
449
gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(group_d), &x, &y);
450
if(-ITEM_W < x && x < PLATE_W + ITEM_W && abs(y-PLATE_Y) < ITEM_H)
455
scale_item_move_to(scale, plate);
464
static ScaleItem * scale_list_add_weight(gint weight)
472
last=g_list_last(item_list);
475
new_item = last->data;
476
x = new_item->x + ITEM_W;
483
g_warning("No more place for new item");
492
new_item = g_new0(ScaleItem, 1);
495
new_item->weight = weight;
497
filename = g_strdup_printf("scales/masse%d.png", weight);
498
pixmap = gc_pixmap_load(filename);
499
new_item->item = gnome_canvas_item_new(boardRootItem,
500
gnome_canvas_pixbuf_get_type(),
503
"y", new_item->y, NULL);
505
gdk_pixbuf_unref(pixmap);
507
g_signal_connect(new_item->item, "event", (GtkSignalFunc)gc_item_focus_event, NULL);
508
g_signal_connect(new_item->item, "event", (GtkSignalFunc)gc_drag_event, new_item);
509
g_signal_connect(new_item->item, "event", (GtkSignalFunc) scale_item_event, new_item);
511
item_list = g_list_append(item_list, new_item);
515
static ScaleItem * scale_list_add_object(GdkPixbuf *pixmap, int weight, int plate, gboolean show_weight)
517
GnomeCanvasItem *item;
518
ScaleItem * new_item;
520
item = gnome_canvas_item_new(group_d,
521
gnome_canvas_pixbuf_get_type(),
523
"x", ((double)PLATE_SIZE * ITEM_W - gdk_pixbuf_get_width(pixmap))/2.0,
524
"y", PLATE_Y + 5 - gdk_pixbuf_get_height(pixmap), NULL);
525
gnome_canvas_item_lower_to_bottom(item);
528
{ // display the object weight
532
x = PLATE_SIZE * ITEM_W * .5;
534
text = g_strdup_printf("%d", objet_weight);
535
gnome_canvas_item_new(group_d,
536
gnome_canvas_text_get_type(),
538
"font", gc_skin_font_board_medium,
541
"anchor", GTK_ANCHOR_CENTER,
542
"fill_color_rgba", gc_skin_color_shadow,
544
gnome_canvas_item_new(group_d,
545
gnome_canvas_text_get_type(),
547
"font", gc_skin_font_board_medium,
550
"anchor", GTK_ANCHOR_CENTER,
551
"fill_color_rgba", gc_skin_color_content,
556
new_item = g_new0(ScaleItem, 1);
557
new_item->weight = weight;
558
new_item->plate = plate;
559
new_item->plate_index = -1;
560
new_item->item = item;
562
item_list = g_list_append(item_list, new_item);
566
// test if adding elements in table can produce total
567
static gboolean test_addition(int total, int *table, int len)
576
if(table[i] <= total && table[i] != 0)
582
result = test_addition(total-cur, table, len);
591
static void scale_make_level()
594
gboolean show_weight=FALSE;
595
const int default_list_weight[10] = { 1,2,2,5,5,10,10};
596
int list_weight[10]= {0};
599
switch(gcomprisBoard->level)
603
objet_weight = g_random_int_range(5,20);
605
list_weight[i] = default_list_weight[i];
606
show_weight = gcomprisBoard->level == 1;
614
tmp[i] = default_list_weight[g_random_int_range(0,10)];
619
objet_weight += g_random_int_range(-1,2) * tmp[i];
620
objet_weight = abs(objet_weight);
622
if(!test_addition(objet_weight, tmp, 5))
626
list_weight[i] = tmp[i];
627
show_weight = gcomprisBoard->level == 3;
631
for(i=0; list_weight[i] ; i++)
632
scale_list_add_weight(list_weight[i]);
634
pixmap = gc_pixmap_load(imageList[g_random_int_range(0,imageListCount)]);
635
scale_list_add_object(pixmap, objet_weight,-1, show_weight);
636
gdk_pixbuf_unref(pixmap);
640
static void scale_next_level()
642
GdkPixbuf *pixmap, *pixmap2;
643
GnomeCanvasItem *item, *balance;
644
double balance_x, balance_y;
646
gc_bar_set_level(gcomprisBoard);
648
scale_destroy_all_items();
651
gc_score_set(gcomprisBoard->sublevel);
652
// create the balance
653
pixmap = gc_pixmap_load("scales/balance.png");
654
balance_x = (BOARDWIDTH - gdk_pixbuf_get_width(pixmap))/2;
655
balance_y = (BOARDHEIGHT - gdk_pixbuf_get_height(pixmap))/2;
657
boardRootItem = GNOME_CANVAS_GROUP(gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
658
gnome_canvas_group_get_type (),
663
balance = item = gnome_canvas_item_new(boardRootItem,
664
gnome_canvas_pixbuf_get_type(),
669
gdk_pixbuf_unref(pixmap);
672
group_g = GNOME_CANVAS_GROUP(gnome_canvas_item_new(boardRootItem,
673
gnome_canvas_group_get_type(),
677
pixmap = gc_pixmap_load("scales/plateau.png");
678
item = gnome_canvas_item_new(group_g,
679
gnome_canvas_pixbuf_get_type(),
681
"x", 0.0, "y", PLATE_Y, NULL);
682
gdk_pixbuf_unref(pixmap);
684
// create plate right
685
group_d = GNOME_CANVAS_GROUP(gnome_canvas_item_new(boardRootItem,
686
gnome_canvas_group_get_type(),
690
pixmap = gc_pixmap_load("scales/plateau.png");
691
pixmap2 = gdk_pixbuf_flip(pixmap, TRUE);
692
item = gnome_canvas_item_new(group_d,
693
gnome_canvas_pixbuf_get_type(),
695
"x", 0.0, "y", PLATE_Y, NULL);
696
gdk_pixbuf_unref(pixmap);
697
gdk_pixbuf_unref(pixmap2);
699
pixmap = gc_pixmap_load("scales/bras.png");
700
bras = item = gnome_canvas_item_new(boardRootItem,
701
gnome_canvas_pixbuf_get_type(),
706
gdk_pixbuf_unref(pixmap);
707
gnome_canvas_item_raise_to_top(balance);
709
/* display some hint */
710
if(gcomprisBoard->level > 2)
711
gnome_canvas_item_new(boardRootItem,
712
gnome_canvas_text_get_type(),
713
"text", _("Take care, you can drop masses on both sides of the scale."),
714
"font", gc_skin_font_board_medium,
717
"anchor", GTK_ANCHOR_CENTER,
718
"fill_color", "darkblue",
726
static void scale_destroy_all_items()
731
gtk_object_destroy (GTK_OBJECT(boardRootItem));
732
boardRootItem = NULL;
736
for(list = item_list; list ; list = list->next)
738
g_list_free(item_list);
743
g_string_free(answer_string, TRUE);
744
answer_string = NULL;
748
static void game_won()
750
gcomprisBoard->sublevel++;
752
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
753
/* Try the next level */
754
gcomprisBoard->sublevel=1;
755
gcomprisBoard->level++;
756
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
757
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
760
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
765
/* ==================================== */
766
static void process_ok()
768
gboolean good_answer = TRUE;
776
answer_weight = g_strtod(answer_string->str, NULL);
777
good_answer = answer_weight == objet_weight;
779
if(get_weight_plate(0)==0 && good_answer)
782
scale_destroy_all_items();
783
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
786
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
789
/* ************************************* */
790
/* * Configuration * */
791
/* ************************************* */
794
/* ======================= */
795
/* = config_start = */
796
/* ======================= */
798
static GcomprisProfile *profile_conf;
799
static GcomprisBoard *board_conf;
801
static void save_table (gpointer key,
805
gc_db_set_board_conf ( profile_conf,
811
static void conf_ok(GHashTable *table)
819
g_hash_table_foreach(table, (GHFunc) save_table, NULL);
825
config = gc_db_get_board_conf();
829
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
831
if (drag_mode_str && (g_strcasecmp (drag_mode_str, "NULL") != 0))
832
drag_mode = (gint ) g_ascii_strtod(drag_mode_str, NULL);
837
g_hash_table_destroy(config);
839
gc_drag_change_mode( drag_mode);
852
config_start(GcomprisBoard *agcomprisBoard,
853
GcomprisProfile *aProfile)
855
board_conf = agcomprisBoard;
856
profile_conf = aProfile;
861
gchar * label = g_strdup_printf(_("<b>%s</b> configuration\n for profile <b>%s</b>"),
862
agcomprisBoard->name,
863
aProfile? aProfile->name : "");
864
GcomprisBoardConf *bconf;
865
bconf = gc_board_config_window_display( label,
866
(GcomprisConfCallback )conf_ok);
870
/* init the combo to previously saved value */
871
GHashTable *config = gc_db_get_conf( profile_conf, board_conf);
873
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
876
if (drag_mode_str && (strcmp (drag_mode_str, "NULL") != 0))
877
drag_previous = (gint ) g_ascii_strtod(drag_mode_str, NULL);
881
gc_board_config_combo_drag(bconf, drag_mode);
886
/* ======================= */
887
/* = config_stop = */
888
/* ======================= */
889
static void config_stop()