/* gcompris - clockgame.c
*
* Copyright (C) 2000 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
#include
#include "gcompris/gcompris.h"
#define SOUNDLISTFILE PACKAGE
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static GnomeCanvasGroup *boardRootItem = NULL;
static GnomeCanvasItem *second_item;
static GnomeCanvasItem *hour_item;
static GnomeCanvasItem *minute_item;
static GnomeCanvasItem *digital_time_item;
static GnomeCanvasItem *digital_time_item_s;
static GnomeCanvasItem *time_to_find_item;
static GnomeCanvasItem *time_to_find_item_s;
/* Center of the clock and it's size */
double cx;
double cy;
double clock_size;
typedef struct {
guint hour;
guint minute;
guint second;
} GcomprisTime;
static GcomprisTime timeToFind, currentTime;
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 void process_ok(void);
static int gamewon;
static void game_won(void);
static void clockgame_create_item(GnomeCanvasGroup *parent);
static void destroy_all_items(void);
static void get_random_hour(GcomprisTime *time);
static void clockgame_next_level(void);
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
static void display_hour(guint hour);
static void display_minute(guint minute);
static void display_second(guint second);
static gboolean time_equal(GcomprisTime *time1, GcomprisTime *time2);
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
"Learning Clock",
"Learn how to tell the time",
"Bruno Coudoin ",
NULL,
NULL,
NULL,
NULL,
start_board,
pause_board,
end_board,
is_our_board,
NULL,
process_ok,
set_level,
NULL,
NULL,
NULL,
NULL
};
/*
* Main entry point mandatory for each Gcompris's game
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO(clockgame)
/*
* 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)
{
gchar *img;
gcomprisBoard=agcomprisBoard;
img = gc_skin_image_get("clockgame-bg.jpg");
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
img);
g_free(img);
/* set initial values for this level adjusted to fit the watch background */
cx = gcomprisBoard->width/2;
cy = gcomprisBoard->height*0.4 + 42;
clock_size = gcomprisBoard->height*0.3;
gcomprisBoard->level=1;
gcomprisBoard->maxlevel=6;
gcomprisBoard->sublevel=1;
gcomprisBoard->number_of_sublevel=3; /* Go to next level after this number of 'play' */
gc_score_start(SCORESTYLE_NOTE,
gcomprisBoard->width - 220,
gcomprisBoard->height - 50,
gcomprisBoard->number_of_sublevel);
gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK);
clockgame_next_level();
gamewon = FALSE;
pause_board(FALSE);
}
}
static void
end_board ()
{
if(gcomprisBoard!=NULL)
{
pause_board(TRUE);
gc_score_end();
destroy_all_items();
}
gcomprisBoard = NULL;
}
static void
set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
gcomprisBoard->sublevel=1;
clockgame_next_level();
}
}
static gboolean
is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_strcasecmp(gcomprisBoard->type, "clockgame")==0)
{
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void clockgame_next_level()
{
gc_bar_set_level(gcomprisBoard);
gc_score_set(gcomprisBoard->sublevel);
destroy_all_items();
/* Try the next level */
get_random_hour(&timeToFind);
/* Avoid to show up the solution directly */
do {
get_random_hour(¤tTime);
} while(time_equal(&timeToFind, ¤tTime));
clockgame_create_item(gnome_canvas_root(gcomprisBoard->canvas));
}
/* ==================================== */
/* Destroy all the items */
static void
destroy_all_items()
{
if(boardRootItem!=NULL)
gtk_object_destroy (GTK_OBJECT(boardRootItem));
boardRootItem = NULL;
}
static void display_digital_time(GnomeCanvasItem *item, GcomprisTime *time)
{
gchar *text = NULL;
int temps;
temps = (time->hour+12)*3600 + time->minute*60 + time->second;
time->hour = (temps / 3600) % 12;
time->minute = (temps / 60) % 60;
time->second = temps % 60;
if(item==NULL)
return;
if(gcomprisBoard->level<=2)
text = g_strdup_printf("%.2d:%.2d", time->hour, time->minute);
else
text = g_strdup_printf("%.2d:%.2d:%.2d", time->hour, time->minute, time->second);
gnome_canvas_item_set (item,
"text", text,
NULL);
g_free(text);
}
static void display_hour(guint hour)
{
double needle_size = clock_size*0.70;
double ang;
GnomeCanvasPoints *canvasPoints;
if(hour_item==NULL)
return;
/* Calc the needle angle */
ang = ((hour > 12) ? hour-12 : hour) * M_PI / 6;
ang += currentTime.minute * M_PI / 360;
ang += currentTime.second * M_PI / 21600;
canvasPoints = gnome_canvas_points_new (2);
canvasPoints->coords[0]=cx;
canvasPoints->coords[1]=cy;
canvasPoints->coords[2]=cx + needle_size * sin(ang);
canvasPoints->coords[3]=cy - needle_size * cos(ang);
gnome_canvas_item_set (hour_item,
"points", canvasPoints,
"fill_color", "darkblue",
"width_units", (double)1,
"width_pixels", (guint) 4,
"last_arrowhead", TRUE,
"arrow_shape_a", (double) needle_size,
"arrow_shape_b", (double) needle_size-20,
"arrow_shape_c", (double) 8.0,
NULL);
gnome_canvas_points_free(canvasPoints);
currentTime.hour=hour;
display_digital_time(digital_time_item, ¤tTime);
display_digital_time(digital_time_item_s, ¤tTime);
}
static void display_minute(guint minute)
{
double needle_size = clock_size;
double ang;
GnomeCanvasPoints *canvasPoints;
if(minute_item==NULL)
return;
ang = minute * M_PI / 30;
ang += currentTime.second * M_PI / 1800;
canvasPoints = gnome_canvas_points_new (2);
canvasPoints->coords[0]=cx;
canvasPoints->coords[1]=cy;
canvasPoints->coords[2]=cx + needle_size * sin(ang);
canvasPoints->coords[3]=cy - needle_size * cos(ang);
gnome_canvas_item_set (minute_item,
"points", canvasPoints,
"fill_color", "red",
"width_units", (double)1,
"width_pixels", (guint) 4,
"last_arrowhead", TRUE,
"arrow_shape_a", (double) needle_size,
"arrow_shape_b", (double) needle_size-10,
"arrow_shape_c", (double) 3.0,
NULL);
gnome_canvas_points_free(canvasPoints);
currentTime.minute=minute;
display_digital_time(digital_time_item, ¤tTime);
display_digital_time(digital_time_item_s, ¤tTime);
}
static void display_second(guint second)
{
double needle_size = clock_size;
double ang;
GnomeCanvasPoints *canvasPoints;
/* No seconds at first levels */
if(second_item==NULL || gcomprisBoard->level<=2)
return;
ang = second * M_PI / 30;
canvasPoints = gnome_canvas_points_new (2);
canvasPoints->coords[0]=cx;
canvasPoints->coords[1]=cy;
canvasPoints->coords[2]=cx + needle_size * sin(ang);
canvasPoints->coords[3]=cy - needle_size * cos(ang);
gnome_canvas_item_set (second_item,
"points", canvasPoints,
"fill_color_rgba", 0x68c46fFF,
"width_units", (double)1,
"width_pixels", (guint) 4,
"last_arrowhead", TRUE,
"arrow_shape_a", (double) 0,
"arrow_shape_b", (double) 0,
"arrow_shape_c", (double) 0,
NULL);
gnome_canvas_points_free(canvasPoints);
currentTime.second=second;
display_digital_time(digital_time_item, ¤tTime);
display_digital_time(digital_time_item_s, ¤tTime);
}
static void
clockgame_create_item(GnomeCanvasGroup *parent)
{
GnomeCanvasItem *item;
double needle_size = clock_size;
double min_point_size = clock_size*0.05;
double hour_point_size = clock_size*0.1;
double ang;
guint min;
GnomeCanvasPoints *canvasPoints;
char *color;
char *color_text;
gchar *mtext = NULL;
gchar *font = NULL;
boardRootItem = GNOME_CANVAS_GROUP(
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
gnome_canvas_group_get_type (),
"x", (double) 0,
"y", (double) 0,
NULL));
canvasPoints = gnome_canvas_points_new (2);
for(min = 1 ; min <= 60 ; min += 1)
{
ang = min * M_PI / 30;
if(min%5==0)
{
/* Hour point */
canvasPoints->coords[0]=cx + (needle_size-hour_point_size) * sin(ang);
canvasPoints->coords[1]=cy - (needle_size-hour_point_size) * cos(ang);
color="darkblue";
color_text="red";
font = gc_skin_font_board_small;
}
else
{
/* Minute point */
canvasPoints->coords[0]=cx + (needle_size-min_point_size) * sin(ang);
canvasPoints->coords[1]=cy - (needle_size-min_point_size) * cos(ang);
color="red";
color_text="red";
font = gc_skin_font_board_tiny;
}
canvasPoints->coords[2]=cx + needle_size * sin(ang);
canvasPoints->coords[3]=cy - needle_size * cos(ang);
item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_line_get_type (),
"points", canvasPoints,
"fill_color", color,
"width_units", (double)1,
"width_pixels", (guint) 2,
NULL);
/* Display minute number */
if(gcomprisBoard->level<5)
{
mtext = g_strdup_printf("%d", min);
item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", mtext,
"font", font,
"x", (double) cx + (needle_size+10) * sin(ang),
"y", (double) cy - (needle_size+10) * cos(ang),
"anchor", GTK_ANCHOR_CENTER,
"fill_color", color_text,
"justification", GTK_JUSTIFY_CENTER,
NULL);
g_free(mtext);
}
/* Display hour numbers */
if(gcomprisBoard->level<6)
if(min%5==0)
{
mtext = g_strdup_printf( "%d", min/5);
item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", mtext,
"font", font,
"x", (double) cx + (needle_size-30) * sin(ang),
"y", (double) cy - (needle_size-30) * cos(ang),
"anchor", GTK_ANCHOR_CENTER,
"fill_color", "blue",
"justification", GTK_JUSTIFY_CENTER,
NULL);
g_free(mtext);
}
}
/* Create the text area for the digital time display */
if(gcomprisBoard->level<4)
{
digital_time_item_s =
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", "",
"font", gc_skin_font_board_medium,
"x", (double) cx + 1.0,
"y", (double) cy + needle_size/2 + 1.0,
"anchor", GTK_ANCHOR_CENTER,
"fill_color_rgba", 0xc4c4c4ff,
NULL);
display_digital_time(digital_time_item_s, ¤tTime);
digital_time_item =
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", "",
"font", gc_skin_font_board_medium,
"x", (double) cx,
"y", (double) cy + needle_size/2,
"anchor", GTK_ANCHOR_CENTER,
"fill_color", "blue",
NULL);
display_digital_time(digital_time_item, ¤tTime);
}
else
{
digital_time_item_s = NULL;
digital_time_item = NULL;
}
/* Create the Hour needle */
canvasPoints->coords[0]=0;
canvasPoints->coords[1]=0;
canvasPoints->coords[2]=0;
canvasPoints->coords[3]=0;
hour_item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_line_get_type (),
"points", canvasPoints,
"fill_color", "darkblue",
"width_units", (double)1,
"width_pixels", (guint) 0,
NULL);
gtk_signal_connect(GTK_OBJECT(hour_item), "event",
(GtkSignalFunc) item_event,
NULL);
display_hour(currentTime.hour);
/* Create the minute needle */
minute_item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_line_get_type (),
"points", canvasPoints,
"fill_color", "darkblue",
"width_units", (double)1,
"width_pixels", (guint) 0,
NULL);
gtk_signal_connect(GTK_OBJECT(minute_item), "event",
(GtkSignalFunc) item_event,
NULL);
display_minute(currentTime.minute);
/* Create the second needle */
second_item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_line_get_type (),
"points", canvasPoints,
"fill_color", "darkblue",
"width_units", (double)1,
"width_pixels", (guint) 0,
NULL);
gtk_signal_connect(GTK_OBJECT(second_item), "event",
(GtkSignalFunc) item_event,
NULL);
display_second(currentTime.second);
/* Create the text area for the time to find display */
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", _("Set the watch to:"),
"font", gc_skin_font_board_small,
"x", (double) gcomprisBoard->width*0.17 + 1.0,
"y", (double) cy + needle_size + needle_size / 3 - 30 + 1.0,
"anchor", GTK_ANCHOR_CENTER,
"fill_color_rgba", gc_skin_color_shadow,
NULL);
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", _("Set the watch to:"),
"font", gc_skin_font_board_small,
"x", (double) gcomprisBoard->width*0.17,
"y", (double) cy + needle_size + needle_size / 3 - 30,
"anchor", GTK_ANCHOR_CENTER,
"fill_color_rgba", gc_skin_get_color("clockgame/text"),
NULL);
time_to_find_item_s =
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", "",
"font", gc_skin_font_board_big_bold,
"x", (double) gcomprisBoard->width*0.17 + 1.0,
"y", (double) cy + needle_size + needle_size / 3 + 1.0,
"anchor", GTK_ANCHOR_CENTER,
"fill_color_rgba", gc_skin_color_shadow,
NULL);
display_digital_time(time_to_find_item_s, &timeToFind);
time_to_find_item =
gnome_canvas_item_new (boardRootItem,
gnome_canvas_text_get_type (),
"text", "",
"font", gc_skin_font_board_big_bold,
"x", (double) gcomprisBoard->width*0.17,
"y", (double) cy + needle_size + needle_size / 3,
"anchor", GTK_ANCHOR_CENTER,
"fill_color_rgba", gc_skin_get_color("clockgame/text"),
NULL);
display_digital_time(time_to_find_item, &timeToFind);
gnome_canvas_points_free(canvasPoints);
}
/*
* Returns true is given times are equal
*/
static gboolean
time_equal(GcomprisTime *time1, GcomprisTime *time2)
{
/* No seconds at first levels */
if(second_item==NULL || gcomprisBoard->level<=2)
return(time1->hour==time2->hour
&&time1->minute==time2->minute);
else
return(time1->hour==time2->hour
&&time1->minute==time2->minute
&&time1->second==time2->second);
}
/* ==================================== */
static void game_won()
{
gamewon = FALSE;
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);
}
clockgame_next_level();
}
/* ==================================== */
static void process_ok()
{
if(time_equal(&timeToFind, ¤tTime))
{
gamewon = TRUE;
}
else
{
/* Oups, you're wrong */
gamewon = FALSE;
}
gc_bonus_display(gamewon, GC_BONUS_FLOWER);
}
/* Callback for the 'toBeFoundItem' */
static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
static double x, y;
double item_x, item_y;
static int dragging;
GdkCursor *fleur;
double new_x, new_y;
if(board_paused)
return FALSE;
item_x = event->button.x;
item_y = event->button.y;
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
switch (event->type)
{
case GDK_BUTTON_PRESS:
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
x = item_x;
y = item_y;
fleur = gdk_cursor_new(GDK_FLEUR);
gc_canvas_item_grab(item,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
fleur,
event->button.time);
gdk_cursor_destroy(fleur);
dragging = TRUE;
break;
case GDK_MOTION_NOTIFY:
if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
{
double angle;
/* only use coords relative to the center, with standard
direction for axis (y's need to be negated for this) */
new_x = item_x - cx;
new_y = - item_y + cy;
/* angle as mesured relatively to noon position */
/* Thanks to Martin Herweg for this code */
angle = atan2(new_x,new_y);
if (angle<0) {angle= angle + 2.0*M_PI;}
if(item==hour_item)
display_hour(angle * 6 / M_PI);
else if(item==minute_item)
{
if(currentTime.minute > 45 && angle * 30 / M_PI < 15)
{
currentTime.hour++;
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
}
if(currentTime.minute < 15 && angle * 30 / M_PI > 45)
{
currentTime.hour--;
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
}
display_minute(angle * 30 / M_PI);
display_hour(currentTime.hour);
}
else if(item==second_item)
{
if(currentTime.second > 45 && angle * 30 / M_PI < 15)
{
currentTime.minute++;
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
}
if(currentTime.second < 15 && angle * 30 / M_PI > 45)
{
currentTime.minute--;
gc_sound_play_ogg ("sounds/paint1.wav", NULL);
}
display_second(angle * 30 / M_PI);
display_minute(currentTime.minute);
display_hour(currentTime.hour);
}
x = new_x + cx;
y = new_y + cy;
}
break;
case GDK_BUTTON_RELEASE:
if(dragging)
{
gc_canvas_item_ungrab(item, event->button.time);
dragging = FALSE;
}
break;
default:
break;
}
return FALSE;
}
/* Returns a random time based on the current level */
static void get_random_hour(GcomprisTime *time)
{
time->hour=g_random_int()%12;
if(gcomprisBoard->level>3)
time->second=g_random_int()%60;
else time->second=0;
time->minute=g_random_int()%60;
switch(gcomprisBoard->level)
{
case 1:
time->minute=g_random_int()%4*15;
break;
case 2:
time->minute=g_random_int()%12*5;
break;
default:
break;
}
}