/* gcompris - target.c * * Copyright (C) 2001 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); #define VERTICAL_SEPARATION 30 #define HORIZONTAL_SEPARATION 30 #define TEXT_COLOR "white" static GnomeCanvasGroup *boardRootItem = NULL; static GnomeCanvasGroup *speedRootItem = NULL; static double wind_speed; static double ang; static GnomeCanvasItem *answer_item = NULL; static gchar answer_string[10]; static guint answer_string_index = 0; static GnomeCanvasItem *animate_item = NULL; static gint animate_id = 0; static gint animate_item_distance = 0; static gint animate_item_size = 0; static double animate_item_x = 0; static double animate_item_y = 0; #define MAX_DART_SIZE 20 #define MIN_DART_SIZE 3 static guint user_points = 0; /* * Functions Definition */ static void process_ok(void); static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str); static GnomeCanvasItem *target_create_item(GnomeCanvasGroup *parent); static void target_destroy_all_items(void); static void target_next_level(void); static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data); static void animate_items(void); static void launch_dart(double item_x, double item_y); /* * distance to target, min wind speed, max wind speed, target1 width(center), * value1 (for target1), ... to target10, value10 * * a target width of 0 means no such target */ #define MAX_NUMBER_OF_TARGET 10 typedef struct { guint number_of_arrow; guint target_distance; guint target_min_wind_speed; guint target_max_wind_speed; gint target_width_value[MAX_NUMBER_OF_TARGET*2]; } TargetDefinition; /* * Definition of targets one line by level based on TargetDefinition */ static TargetDefinition targetDefinition[] = { { 3, 100, 2, 5, { 40, 5 , 80, 3, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, { 5, 150, 2, 7, { 30, 10, 50, 5, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, { 7, 200, 4, 9, { 20, 10, 40, 5, 60, 3, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, { 7, 200, 5, 10, { 15, 100, 35, 50, 55, 10, 75, 5, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} } }; #define NUMBER_OF_TARGET 4 #define TARGET_CENTER_X 235 #define TARGET_CENTER_Y 260 #define SPEED_CENTER_X 660 #define SPEED_CENTER_Y 125 static guint target_colors[] = { 0xAA0000FF, 0x00AA00FF, 0x0000AAFF, 0xAAAA00FF, 0x00AAAAFF, 0xAA00AAFF, 0xAA0000FF, 0x00AA00FF, 0x0000AAFF, 0xAA0000FF }; static guint number_of_arrow = 0; /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, "Practice addition with a target game", "Hit the target and count your points", "Bruno Coudoin ", NULL, NULL, NULL, NULL, start_board, pause_board, end_board, is_our_board, key_press, process_ok, set_level, NULL, NULL, NULL, NULL }; /* * Main entry point mandatory for each Gcompris's game * --------------------------------------------------- * */ GET_BPLUGIN_INFO(target) /* * 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(); } if(pause) { if (animate_id) { gtk_timeout_remove (animate_id); animate_id = 0; } } else { if(animate_item) { animate_id = gtk_timeout_add (200, (GtkFunction) animate_items, NULL); } } board_paused = pause; } /* */ static void start_board (GcomprisBoard *agcomprisBoard) { if(agcomprisBoard!=NULL) { gcomprisBoard=agcomprisBoard; /* disable im_context */ gcomprisBoard->disable_im_context = TRUE; gcomprisBoard->level=1; gcomprisBoard->maxlevel=NUMBER_OF_TARGET; gcomprisBoard->sublevel=1; gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */ gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "opt/target_background.jpg"); target_next_level(); gamewon = FALSE; pause_board(FALSE); } } /* ======================================= */ static void end_board () { if(gcomprisBoard!=NULL) { pause_board(TRUE); target_destroy_all_items(); } gcomprisBoard = NULL; } /* ======================================= */ static void set_level (guint level) { if(gcomprisBoard!=NULL) { gcomprisBoard->level=level; gcomprisBoard->sublevel=1; target_next_level(); } } /* ======================================= */ static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str) { guint c; if(board_paused || !gcomprisBoard) return FALSE; /* Add some filter for control and shift key */ switch (keyval) { /* Avoid all this keys to be interpreted by this game */ case GDK_Shift_L: case GDK_Shift_R: case GDK_Control_L: case GDK_Control_R: case GDK_Caps_Lock: case GDK_Shift_Lock: case GDK_Meta_L: case GDK_Meta_R: case GDK_Alt_L: case GDK_Alt_R: case GDK_Super_L: case GDK_Super_R: case GDK_Hyper_L: case GDK_Hyper_R: case GDK_Num_Lock: return FALSE; case GDK_KP_Enter: case GDK_Return: process_ok(); return TRUE; case GDK_Right: case GDK_Left: break; case GDK_BackSpace: if(answer_string_index>0) { answer_string_index--; answer_string[answer_string_index] = 0; } break; } c = tolower(keyval); /* Limit the user entry to 5 digits */ if(c>='0' && c<='9' && answer_string_index<5) { answer_string[answer_string_index++] = c; answer_string[answer_string_index] = 0; } if(answer_item) { gchar *tmpstr = g_strdup_printf(_("Points = %s"), answer_string); gnome_canvas_item_set(answer_item, "text", tmpstr, NULL); g_free(tmpstr); } return TRUE; } /* ======================================= */ static gboolean is_our_board (GcomprisBoard *gcomprisBoard) { if (gcomprisBoard) { if(g_strcasecmp(gcomprisBoard->type, "target")==0) { /* Set the plugin entry */ gcomprisBoard->plugin=&menu_bp; return TRUE; } } return FALSE; } /*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/ /* set initial values for the next level */ static void target_next_level() { gc_bar_set(GC_BAR_LEVEL); gc_bar_set_level(gcomprisBoard); target_destroy_all_items(); gamewon = FALSE; /* Try the next level */ target_create_item(gnome_canvas_root(gcomprisBoard->canvas)); } /* ==================================== */ /* Destroy all the items */ static void target_destroy_all_items() { if(boardRootItem!=NULL) gtk_object_destroy (GTK_OBJECT(boardRootItem)); boardRootItem = NULL; if(speedRootItem!=NULL) gtk_object_destroy (GTK_OBJECT(speedRootItem)); animate_item = NULL; answer_item = NULL; answer_string_index = 0; user_points = 0; speedRootItem = NULL; } /* * Display a random wind speed */ static void display_windspeed() { guint second = 0; guint needle_zoom = 15; gchar *tmpstr; GnomeCanvasPoints *canvasPoints; canvasPoints = gnome_canvas_points_new (2); if(speedRootItem!=NULL) gtk_object_destroy (GTK_OBJECT(speedRootItem)); speedRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas), gnome_canvas_group_get_type (), "x", (double) 0, "y", (double) 0, NULL)); /* Speed orientation */ second = g_random_int()%60; ang = second * M_PI / 30; /* Speed force */ wind_speed = targetDefinition[gcomprisBoard->level-1].target_min_wind_speed \ + g_random_int()%(targetDefinition[gcomprisBoard->level-1].target_max_wind_speed \ - targetDefinition[gcomprisBoard->level-1].target_min_wind_speed); canvasPoints->coords[0]=SPEED_CENTER_X; canvasPoints->coords[1]=SPEED_CENTER_Y; canvasPoints->coords[2]=SPEED_CENTER_X + wind_speed * sin(ang) * needle_zoom; canvasPoints->coords[3]=SPEED_CENTER_Y - wind_speed * cos(ang) * needle_zoom; gnome_canvas_item_new (speedRootItem, gnome_canvas_line_get_type (), "points", canvasPoints, "fill_color", "red", "width_units", (double)1, "width_pixels", (guint) 4, "last_arrowhead", TRUE, "arrow_shape_a", (double) wind_speed, "arrow_shape_b", (double) wind_speed-15, "arrow_shape_c", (double) 5.0, NULL); gnome_canvas_points_free(canvasPoints); /* Draw the center of the speedometer */ gnome_canvas_item_new (speedRootItem, gnome_canvas_ellipse_get_type(), "x1", (double)SPEED_CENTER_X-5, "y1", (double)SPEED_CENTER_Y-5, "x2", (double)SPEED_CENTER_X+5, "y2", (double)SPEED_CENTER_Y+5, "fill_color", "red", "outline_color", "red", "width_units", (double)1, NULL); tmpstr = g_strdup_printf(_("Wind speed = %d\nkilometers/hour"), (guint)wind_speed); gnome_canvas_item_new (speedRootItem, gnome_canvas_text_get_type (), "text", tmpstr, "font", gc_skin_font_board_medium, "x", (double) SPEED_CENTER_X, "y", (double) SPEED_CENTER_Y + 110, "anchor", GTK_ANCHOR_CENTER, "fill_color", "white", NULL); g_free(tmpstr); } /* ==================================== */ static GnomeCanvasItem *target_create_item(GnomeCanvasGroup *parent) { int i; gchar *tmpstr; GnomeCanvasItem *item = NULL; boardRootItem = GNOME_CANVAS_GROUP( gnome_canvas_item_new (parent, gnome_canvas_group_get_type (), "x", (double) TARGET_CENTER_X, "y", (double) TARGET_CENTER_Y, NULL)); for(i=0; ilevel-1].target_width_value[i*2]>0) { item = gnome_canvas_item_new (boardRootItem, gnome_canvas_ellipse_get_type(), "x1", (double)-targetDefinition[gcomprisBoard->level-1].target_width_value[i*2], "y1", (double)-targetDefinition[gcomprisBoard->level-1].target_width_value[i*2], "x2", (double)targetDefinition[gcomprisBoard->level-1].target_width_value[i*2], "y2", (double)targetDefinition[gcomprisBoard->level-1].target_width_value[i*2], "fill_color_rgba", target_colors[i], "outline_color", "black", "width_units", (double)1, NULL); gnome_canvas_item_lower_to_bottom(item); gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event, NULL); /* Display the value for this target */ tmpstr = g_strdup_printf("%d", targetDefinition[gcomprisBoard->level-1].target_width_value[i*2+1]); item = gnome_canvas_item_new (boardRootItem, gnome_canvas_text_get_type (), "text", tmpstr, "font", gc_skin_font_board_medium, "x", (double) 0, "y", (double) targetDefinition[gcomprisBoard->level-1].target_width_value[i*2] - 10, "anchor", GTK_ANCHOR_CENTER, "fill_color", "white", NULL); g_free(tmpstr); gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event, NULL); } } number_of_arrow = targetDefinition[gcomprisBoard->level-1].number_of_arrow; tmpstr = g_strdup_printf(_("Distance to target = %d meters"), targetDefinition[gcomprisBoard->level-1].target_distance); gnome_canvas_item_new (boardRootItem, gnome_canvas_text_get_type (), "text", tmpstr, "font", gc_skin_font_board_medium, "x", (double) 0, "y", (double) BOARDHEIGHT-TARGET_CENTER_Y -45, "anchor", GTK_ANCHOR_CENTER, "fill_color", "black", NULL); g_free(tmpstr); display_windspeed(); 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) { // the current board is finished : bail out gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM); return; } gc_sound_play_ogg ("sounds/bonus.wav", NULL); } target_next_level(); } static void process_ok() { guint answer_points = atoi(answer_string); if(answer_item) { if(answer_points == user_points) { gamewon = TRUE; target_destroy_all_items(); gc_bonus_display(gamewon, GC_BONUS_SMILEY); } else { gamewon = FALSE; gc_bonus_display(gamewon, GC_BONUS_SMILEY); } } } /* * Request score * */ static void request_score() { GdkPixbuf *button_pixmap = NULL; double y_offset = 160; double x_offset = 245; gchar *tmpstr; gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK); button_pixmap = gc_skin_pixmap_load("button_large2.png"); gnome_canvas_item_new (boardRootItem, gnome_canvas_pixbuf_get_type (), "pixbuf", button_pixmap, "x", x_offset, "y", y_offset, NULL); tmpstr = g_strdup_printf(_("Points = %s"), ""); answer_item = gnome_canvas_item_new (boardRootItem, gnome_canvas_text_get_type (), "text", tmpstr, "font", gc_skin_font_board_title_bold, "x", (double) x_offset + gdk_pixbuf_get_width(button_pixmap)/2, "y", (double) y_offset + gdk_pixbuf_get_height(button_pixmap)/2, "anchor", GTK_ANCHOR_CENTER, "fill_color", "black", NULL); g_free(tmpstr); gdk_pixbuf_unref(button_pixmap); } static void add_points(double x, double y) { guint i; double diametre; // Calculate the distance diametre = sqrt(x*x+y*y); for(i=0; ilevel-1].target_width_value[i*2]) { user_points += targetDefinition[gcomprisBoard->level-1].target_width_value[i*2+1]; break; } } } /* * Dart animation * */ static void animate_items() { if(board_paused) return; if(!animate_item) return; // Apply the wind move animate_item_x = animate_item_x + wind_speed * sin(ang); animate_item_y = animate_item_y - wind_speed * cos(ang); gnome_canvas_item_set (animate_item, "x1", (double)animate_item_x - animate_item_size, "y1", (double)animate_item_y - animate_item_size, "x2", (double)animate_item_x + animate_item_size, "y2", (double)animate_item_y + animate_item_size, NULL); if(animate_item_size>MIN_DART_SIZE) animate_item_size--; if(--animate_item_distance == 0) { gc_sound_play_ogg ("sounds/brick.wav", NULL); gtk_timeout_remove (animate_id); animate_id = 0; animate_item = NULL; // Calc the point for this dart add_points(animate_item_x, animate_item_y); // Change the wind for the next target display_windspeed(); } } /* * */ static void launch_dart(double item_x, double item_y) { animate_item_x = item_x; animate_item_y = item_y; animate_item_size = MAX_DART_SIZE; animate_item_distance = targetDefinition[gcomprisBoard->level-1].target_distance/10; gc_sound_play_ogg ("sounds/line_end.wav", NULL); animate_item = gnome_canvas_item_new (boardRootItem, gnome_canvas_ellipse_get_type(), "x1", (double)item_x-MAX_DART_SIZE, "y1", (double)item_y-MAX_DART_SIZE, "x2", (double)item_x+MAX_DART_SIZE, "y2", (double)item_y+MAX_DART_SIZE, "fill_color_rgba", 0xFF80FFFF, "outline_color", "white", "width_units", (double)1, NULL); animate_id = gtk_timeout_add (200, (GtkFunction) animate_items, NULL); if(--number_of_arrow == 0) { request_score(); } } /* ==================================== */ static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data) { double item_x, item_y; if(board_paused) return FALSE; /* Is there already a dart on air */ if(number_of_arrow == 0 || animate_item) return FALSE; switch (event->type) { case GDK_BUTTON_PRESS: switch(event->button.button) { case 1: case 2: case 3: item_x = event->button.x; item_y = event->button.y; gnome_canvas_item_w2i(item->parent, &item_x, &item_y); launch_dart(item_x, item_y); break; default: break; } default: break; } return FALSE; }