1
/* gcompris - clockgame.c
3
* Copyright (C) 2000 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/>.
22
#include "gcompris/gcompris.h"
24
#define SOUNDLISTFILE PACKAGE
26
static GcomprisBoard *gcomprisBoard = NULL;
27
static gboolean board_paused = TRUE;
29
static GnomeCanvasGroup *boardRootItem = NULL;
31
static GnomeCanvasItem *second_item;
32
static GnomeCanvasItem *hour_item;
33
static GnomeCanvasItem *minute_item;
34
static GnomeCanvasItem *digital_time_item;
35
static GnomeCanvasItem *digital_time_item_s;
36
static GnomeCanvasItem *time_to_find_item;
37
static GnomeCanvasItem *time_to_find_item_s;
39
/* Center of the clock and it's size */
49
static GcomprisTime timeToFind, currentTime;
51
static void start_board (GcomprisBoard *agcomprisBoard);
52
static void pause_board (gboolean pause);
53
static void end_board (void);
54
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
55
static void set_level (guint level);
56
static void process_ok(void);
59
static void game_won(void);
61
static void clockgame_create_item(GnomeCanvasGroup *parent);
62
static void destroy_all_items(void);
63
static void get_random_hour(GcomprisTime *time);
64
static void clockgame_next_level(void);
65
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
66
static void display_hour(guint hour);
67
static void display_minute(guint minute);
68
static void display_second(guint second);
69
static gboolean time_equal(GcomprisTime *time1, GcomprisTime *time2);
71
/* Description of this plugin */
72
static BoardPlugin menu_bp =
77
"Learn how to tell the time",
78
"Bruno Coudoin <bruno.coudoin@free.fr>",
98
* Main entry point mandatory for each Gcompris's game
99
* ---------------------------------------------------
103
GET_BPLUGIN_INFO(clockgame)
106
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
109
static void pause_board (gboolean pause)
112
if(gcomprisBoard==NULL)
115
if(gamewon == TRUE && pause == FALSE) /* the game is won */
120
board_paused = pause;
125
static void start_board (GcomprisBoard *agcomprisBoard)
128
if(agcomprisBoard!=NULL)
131
gcomprisBoard=agcomprisBoard;
133
img = gc_skin_image_get("clockgame-bg.jpg");
134
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
138
/* set initial values for this level adjusted to fit the watch background */
139
cx = gcomprisBoard->width/2;
140
cy = gcomprisBoard->height*0.4 + 42;
141
clock_size = gcomprisBoard->height*0.3;
143
gcomprisBoard->level=1;
144
gcomprisBoard->maxlevel=6;
145
gcomprisBoard->sublevel=1;
146
gcomprisBoard->number_of_sublevel=3; /* Go to next level after this number of 'play' */
147
gc_score_start(SCORESTYLE_NOTE,
148
gcomprisBoard->width - 220,
149
gcomprisBoard->height - 50,
150
gcomprisBoard->number_of_sublevel);
151
gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK);
153
clockgame_next_level();
166
if(gcomprisBoard!=NULL)
172
gcomprisBoard = NULL;
176
set_level (guint level)
179
if(gcomprisBoard!=NULL)
181
gcomprisBoard->level=level;
182
gcomprisBoard->sublevel=1;
183
clockgame_next_level();
188
is_our_board (GcomprisBoard *gcomprisBoard)
192
if(g_strcasecmp(gcomprisBoard->type, "clockgame")==0)
194
/* Set the plugin entry */
195
gcomprisBoard->plugin=&menu_bp;
204
/*-------------------------------------------------------------------------------*/
205
/*-------------------------------------------------------------------------------*/
206
/*-------------------------------------------------------------------------------*/
207
/*-------------------------------------------------------------------------------*/
209
/* set initial values for the next level */
210
static void clockgame_next_level()
213
gc_bar_set_level(gcomprisBoard);
214
gc_score_set(gcomprisBoard->sublevel);
218
/* Try the next level */
219
get_random_hour(&timeToFind);
221
/* Avoid to show up the solution directly */
223
get_random_hour(¤tTime);
224
} while(time_equal(&timeToFind, ¤tTime));
226
clockgame_create_item(gnome_canvas_root(gcomprisBoard->canvas));
230
/* ==================================== */
231
/* Destroy all the items */
236
if(boardRootItem!=NULL)
237
gtk_object_destroy (GTK_OBJECT(boardRootItem));
239
boardRootItem = NULL;
243
static void display_digital_time(GnomeCanvasItem *item, GcomprisTime *time)
248
temps = (time->hour+12)*3600 + time->minute*60 + time->second;
249
time->hour = (temps / 3600) % 12;
250
time->minute = (temps / 60) % 60;
251
time->second = temps % 60;
256
if(gcomprisBoard->level<=2)
257
text = g_strdup_printf("%.2d:%.2d", time->hour, time->minute);
259
text = g_strdup_printf("%.2d:%.2d:%.2d", time->hour, time->minute, time->second);
261
gnome_canvas_item_set (item,
268
static void display_hour(guint hour)
270
double needle_size = clock_size*0.70;
272
GnomeCanvasPoints *canvasPoints;
277
/* Calc the needle angle */
278
ang = ((hour > 12) ? hour-12 : hour) * M_PI / 6;
279
ang += currentTime.minute * M_PI / 360;
280
ang += currentTime.second * M_PI / 21600;
282
canvasPoints = gnome_canvas_points_new (2);
283
canvasPoints->coords[0]=cx;
284
canvasPoints->coords[1]=cy;
285
canvasPoints->coords[2]=cx + needle_size * sin(ang);
286
canvasPoints->coords[3]=cy - needle_size * cos(ang);
287
gnome_canvas_item_set (hour_item,
288
"points", canvasPoints,
289
"fill_color", "darkblue",
290
"width_units", (double)1,
291
"width_pixels", (guint) 4,
292
"last_arrowhead", TRUE,
293
"arrow_shape_a", (double) needle_size,
294
"arrow_shape_b", (double) needle_size-20,
295
"arrow_shape_c", (double) 8.0,
297
gnome_canvas_points_free(canvasPoints);
299
currentTime.hour=hour;
300
display_digital_time(digital_time_item, ¤tTime);
301
display_digital_time(digital_time_item_s, ¤tTime);
304
static void display_minute(guint minute)
306
double needle_size = clock_size;
308
GnomeCanvasPoints *canvasPoints;
310
if(minute_item==NULL)
313
ang = minute * M_PI / 30;
314
ang += currentTime.second * M_PI / 1800;
316
canvasPoints = gnome_canvas_points_new (2);
317
canvasPoints->coords[0]=cx;
318
canvasPoints->coords[1]=cy;
319
canvasPoints->coords[2]=cx + needle_size * sin(ang);
320
canvasPoints->coords[3]=cy - needle_size * cos(ang);
321
gnome_canvas_item_set (minute_item,
322
"points", canvasPoints,
324
"width_units", (double)1,
325
"width_pixels", (guint) 4,
326
"last_arrowhead", TRUE,
327
"arrow_shape_a", (double) needle_size,
328
"arrow_shape_b", (double) needle_size-10,
329
"arrow_shape_c", (double) 3.0,
331
gnome_canvas_points_free(canvasPoints);
333
currentTime.minute=minute;
334
display_digital_time(digital_time_item, ¤tTime);
335
display_digital_time(digital_time_item_s, ¤tTime);
338
static void display_second(guint second)
340
double needle_size = clock_size;
342
GnomeCanvasPoints *canvasPoints;
344
/* No seconds at first levels */
345
if(second_item==NULL || gcomprisBoard->level<=2)
348
ang = second * M_PI / 30;
350
canvasPoints = gnome_canvas_points_new (2);
351
canvasPoints->coords[0]=cx;
352
canvasPoints->coords[1]=cy;
353
canvasPoints->coords[2]=cx + needle_size * sin(ang);
354
canvasPoints->coords[3]=cy - needle_size * cos(ang);
355
gnome_canvas_item_set (second_item,
356
"points", canvasPoints,
357
"fill_color_rgba", 0x68c46fFF,
358
"width_units", (double)1,
359
"width_pixels", (guint) 4,
360
"last_arrowhead", TRUE,
361
"arrow_shape_a", (double) 0,
362
"arrow_shape_b", (double) 0,
363
"arrow_shape_c", (double) 0,
365
gnome_canvas_points_free(canvasPoints);
367
currentTime.second=second;
368
display_digital_time(digital_time_item, ¤tTime);
369
display_digital_time(digital_time_item_s, ¤tTime);
374
clockgame_create_item(GnomeCanvasGroup *parent)
376
GnomeCanvasItem *item;
377
double needle_size = clock_size;
378
double min_point_size = clock_size*0.05;
379
double hour_point_size = clock_size*0.1;
382
GnomeCanvasPoints *canvasPoints;
388
boardRootItem = GNOME_CANVAS_GROUP(
389
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
390
gnome_canvas_group_get_type (),
395
canvasPoints = gnome_canvas_points_new (2);
397
for(min = 1 ; min <= 60 ; min += 1)
399
ang = min * M_PI / 30;
403
canvasPoints->coords[0]=cx + (needle_size-hour_point_size) * sin(ang);
404
canvasPoints->coords[1]=cy - (needle_size-hour_point_size) * cos(ang);
407
font = gc_skin_font_board_small;
412
canvasPoints->coords[0]=cx + (needle_size-min_point_size) * sin(ang);
413
canvasPoints->coords[1]=cy - (needle_size-min_point_size) * cos(ang);
416
font = gc_skin_font_board_tiny;
419
canvasPoints->coords[2]=cx + needle_size * sin(ang);
420
canvasPoints->coords[3]=cy - needle_size * cos(ang);
421
item = gnome_canvas_item_new (boardRootItem,
422
gnome_canvas_line_get_type (),
423
"points", canvasPoints,
425
"width_units", (double)1,
426
"width_pixels", (guint) 2,
429
/* Display minute number */
430
if(gcomprisBoard->level<5)
432
mtext = g_strdup_printf("%d", min);
433
item = gnome_canvas_item_new (boardRootItem,
434
gnome_canvas_text_get_type (),
437
"x", (double) cx + (needle_size+10) * sin(ang),
438
"y", (double) cy - (needle_size+10) * cos(ang),
439
"anchor", GTK_ANCHOR_CENTER,
440
"fill_color", color_text,
441
"justification", GTK_JUSTIFY_CENTER,
446
/* Display hour numbers */
447
if(gcomprisBoard->level<6)
450
mtext = g_strdup_printf( "%d", min/5);
451
item = gnome_canvas_item_new (boardRootItem,
452
gnome_canvas_text_get_type (),
455
"x", (double) cx + (needle_size-30) * sin(ang),
456
"y", (double) cy - (needle_size-30) * cos(ang),
457
"anchor", GTK_ANCHOR_CENTER,
458
"fill_color", "blue",
459
"justification", GTK_JUSTIFY_CENTER,
465
/* Create the text area for the digital time display */
466
if(gcomprisBoard->level<4)
468
digital_time_item_s =
469
gnome_canvas_item_new (boardRootItem,
470
gnome_canvas_text_get_type (),
472
"font", gc_skin_font_board_medium,
473
"x", (double) cx + 1.0,
474
"y", (double) cy + needle_size/2 + 1.0,
475
"anchor", GTK_ANCHOR_CENTER,
476
"fill_color_rgba", 0xc4c4c4ff,
478
display_digital_time(digital_time_item_s, ¤tTime);
481
gnome_canvas_item_new (boardRootItem,
482
gnome_canvas_text_get_type (),
484
"font", gc_skin_font_board_medium,
486
"y", (double) cy + needle_size/2,
487
"anchor", GTK_ANCHOR_CENTER,
488
"fill_color", "blue",
490
display_digital_time(digital_time_item, ¤tTime);
494
digital_time_item_s = NULL;
495
digital_time_item = NULL;
498
/* Create the Hour needle */
500
canvasPoints->coords[0]=0;
501
canvasPoints->coords[1]=0;
502
canvasPoints->coords[2]=0;
503
canvasPoints->coords[3]=0;
504
hour_item = gnome_canvas_item_new (boardRootItem,
505
gnome_canvas_line_get_type (),
506
"points", canvasPoints,
507
"fill_color", "darkblue",
508
"width_units", (double)1,
509
"width_pixels", (guint) 0,
511
gtk_signal_connect(GTK_OBJECT(hour_item), "event",
512
(GtkSignalFunc) item_event,
514
display_hour(currentTime.hour);
516
/* Create the minute needle */
518
minute_item = gnome_canvas_item_new (boardRootItem,
519
gnome_canvas_line_get_type (),
520
"points", canvasPoints,
521
"fill_color", "darkblue",
522
"width_units", (double)1,
523
"width_pixels", (guint) 0,
525
gtk_signal_connect(GTK_OBJECT(minute_item), "event",
526
(GtkSignalFunc) item_event,
528
display_minute(currentTime.minute);
530
/* Create the second needle */
532
second_item = gnome_canvas_item_new (boardRootItem,
533
gnome_canvas_line_get_type (),
534
"points", canvasPoints,
535
"fill_color", "darkblue",
536
"width_units", (double)1,
537
"width_pixels", (guint) 0,
539
gtk_signal_connect(GTK_OBJECT(second_item), "event",
540
(GtkSignalFunc) item_event,
542
display_second(currentTime.second);
544
/* Create the text area for the time to find display */
545
gnome_canvas_item_new (boardRootItem,
546
gnome_canvas_text_get_type (),
547
"text", _("Set the watch to:"),
548
"font", gc_skin_font_board_small,
549
"x", (double) gcomprisBoard->width*0.17 + 1.0,
550
"y", (double) cy + needle_size + needle_size / 3 - 30 + 1.0,
551
"anchor", GTK_ANCHOR_CENTER,
552
"fill_color_rgba", gc_skin_color_shadow,
555
gnome_canvas_item_new (boardRootItem,
556
gnome_canvas_text_get_type (),
557
"text", _("Set the watch to:"),
558
"font", gc_skin_font_board_small,
559
"x", (double) gcomprisBoard->width*0.17,
560
"y", (double) cy + needle_size + needle_size / 3 - 30,
561
"anchor", GTK_ANCHOR_CENTER,
562
"fill_color_rgba", gc_skin_get_color("clockgame/text"),
565
time_to_find_item_s =
566
gnome_canvas_item_new (boardRootItem,
567
gnome_canvas_text_get_type (),
569
"font", gc_skin_font_board_big_bold,
570
"x", (double) gcomprisBoard->width*0.17 + 1.0,
571
"y", (double) cy + needle_size + needle_size / 3 + 1.0,
572
"anchor", GTK_ANCHOR_CENTER,
573
"fill_color_rgba", gc_skin_color_shadow,
575
display_digital_time(time_to_find_item_s, &timeToFind);
578
gnome_canvas_item_new (boardRootItem,
579
gnome_canvas_text_get_type (),
581
"font", gc_skin_font_board_big_bold,
582
"x", (double) gcomprisBoard->width*0.17,
583
"y", (double) cy + needle_size + needle_size / 3,
584
"anchor", GTK_ANCHOR_CENTER,
585
"fill_color_rgba", gc_skin_get_color("clockgame/text"),
587
display_digital_time(time_to_find_item, &timeToFind);
589
gnome_canvas_points_free(canvasPoints);
594
* Returns true is given times are equal
597
time_equal(GcomprisTime *time1, GcomprisTime *time2)
599
/* No seconds at first levels */
600
if(second_item==NULL || gcomprisBoard->level<=2)
601
return(time1->hour==time2->hour
602
&&time1->minute==time2->minute);
604
return(time1->hour==time2->hour
605
&&time1->minute==time2->minute
606
&&time1->second==time2->second);
610
/* ==================================== */
611
static void game_won()
614
gcomprisBoard->sublevel++;
616
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
617
/* Try the next level */
618
gcomprisBoard->sublevel=1;
619
gcomprisBoard->level++;
620
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
621
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
624
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
626
clockgame_next_level();
629
/* ==================================== */
630
static void process_ok()
632
if(time_equal(&timeToFind, ¤tTime))
638
/* Oups, you're wrong */
641
gc_bonus_display(gamewon, GC_BONUS_FLOWER);
645
/* Callback for the 'toBeFoundItem' */
647
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
650
double item_x, item_y;
658
item_x = event->button.x;
659
item_y = event->button.y;
660
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
664
case GDK_BUTTON_PRESS:
665
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
669
fleur = gdk_cursor_new(GDK_FLEUR);
670
gc_canvas_item_grab(item,
671
GDK_POINTER_MOTION_MASK |
672
GDK_BUTTON_RELEASE_MASK,
675
gdk_cursor_destroy(fleur);
679
case GDK_MOTION_NOTIFY:
680
if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
685
/* only use coords relative to the center, with standard
686
direction for axis (y's need to be negated for this) */
688
new_y = - item_y + cy;
690
/* angle as mesured relatively to noon position */
691
/* Thanks to Martin Herweg for this code */
692
angle = atan2(new_x,new_y);
693
if (angle<0) {angle= angle + 2.0*M_PI;}
696
display_hour(angle * 6 / M_PI);
697
else if(item==minute_item)
699
if(currentTime.minute > 45 && angle * 30 / M_PI < 15)
702
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
705
if(currentTime.minute < 15 && angle * 30 / M_PI > 45)
708
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
711
display_minute(angle * 30 / M_PI);
712
display_hour(currentTime.hour);
714
else if(item==second_item)
716
if(currentTime.second > 45 && angle * 30 / M_PI < 15)
718
currentTime.minute++;
719
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
722
if(currentTime.second < 15 && angle * 30 / M_PI > 45)
724
currentTime.minute--;
725
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
728
display_second(angle * 30 / M_PI);
729
display_minute(currentTime.minute);
730
display_hour(currentTime.hour);
738
case GDK_BUTTON_RELEASE:
741
gc_canvas_item_ungrab(item, event->button.time);
753
/* Returns a random time based on the current level */
754
static void get_random_hour(GcomprisTime *time)
757
time->hour=g_random_int()%12;
759
if(gcomprisBoard->level>3)
760
time->second=g_random_int()%60;
763
time->minute=g_random_int()%60;
765
switch(gcomprisBoard->level)
768
time->minute=g_random_int()%4*15;
771
time->minute=g_random_int()%12*5;