Back to index

gcompris  8.2.2
target.c
Go to the documentation of this file.
00001 /* gcompris - target.c
00002  *
00003  * Copyright (C) 2001 Bruno Coudoin
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include "gcompris/gcompris.h"
00021 
00022 #define SOUNDLISTFILE PACKAGE
00023 
00024 static GcomprisBoard *gcomprisBoard = NULL;
00025 static gboolean board_paused = TRUE;
00026 
00027 static void           start_board (GcomprisBoard *agcomprisBoard);
00028 static void           pause_board (gboolean pause);
00029 static void           end_board (void);
00030 static gboolean              is_our_board (GcomprisBoard *gcomprisBoard);
00031 static void           set_level (guint level);
00032 static int gamewon;
00033 static void           game_won(void);
00034 
00035 #define VERTICAL_SEPARATION 30
00036 #define HORIZONTAL_SEPARATION      30
00037 #define TEXT_COLOR          "white"
00038 
00039 static GnomeCanvasGroup *boardRootItem = NULL;
00040 static GnomeCanvasGroup *speedRootItem = NULL;
00041 
00042 static double wind_speed;
00043 static double ang;
00044 
00045 static GnomeCanvasItem  *answer_item = NULL;
00046 static gchar          answer_string[10];
00047 static guint          answer_string_index = 0;
00048 
00049 static GnomeCanvasItem *animate_item = NULL;
00050 static gint          animate_id = 0;
00051 static gint          animate_item_distance = 0;
00052 static gint          animate_item_size = 0;
00053 static double        animate_item_x = 0;
00054 static double        animate_item_y = 0;
00055 #define MAX_DART_SIZE 20
00056 #define MIN_DART_SIZE 3
00057 
00058 static guint         user_points = 0;
00059 
00060 /*
00061  * Functions Definition
00062  */
00063 static void          process_ok(void);
00064 static gint          key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
00065 static GnomeCanvasItem *target_create_item(GnomeCanvasGroup *parent);
00066 static void          target_destroy_all_items(void);
00067 static void          target_next_level(void);
00068 static gint          item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00069 static void          animate_items(void);
00070 static void          launch_dart(double item_x, double item_y);
00071 
00072 /*
00073  * distance to target, min wind speed, max wind speed, target1 width(center),
00074  * value1 (for target1), ... to target10, value10
00075  *
00076  * a target width of 0 means no such target
00077  */
00078 #define MAX_NUMBER_OF_TARGET 10
00079 
00080 typedef struct {
00081   guint number_of_arrow;
00082   guint target_distance;
00083   guint target_min_wind_speed;
00084   guint target_max_wind_speed;
00085   gint target_width_value[MAX_NUMBER_OF_TARGET*2];
00086 } TargetDefinition;
00087 
00088 
00089 /*
00090  * Definition of targets one line by level based on TargetDefinition
00091  */
00092 static TargetDefinition targetDefinition[] =
00093 {
00094   { 3, 100, 2, 5,  { 40, 5 , 80, 3, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
00095   { 5, 150, 2, 7,  { 30, 10, 50, 5, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
00096   { 7, 200, 4, 9,  { 20, 10, 40, 5, 60, 3, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
00097   { 7, 200, 5, 10, { 15, 100, 35, 50, 55, 10, 75, 5, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }
00098 };
00099 #define NUMBER_OF_TARGET 4
00100 
00101 #define TARGET_CENTER_X     235
00102 #define TARGET_CENTER_Y     260
00103 
00104 #define SPEED_CENTER_X      660
00105 #define SPEED_CENTER_Y      125
00106 
00107 static guint target_colors[] = {
00108   0xAA0000FF, 0x00AA00FF, 0x0000AAFF, 0xAAAA00FF, 0x00AAAAFF, 0xAA00AAFF, 0xAA0000FF, 0x00AA00FF, 0x0000AAFF, 0xAA0000FF
00109 };
00110 
00111 static guint number_of_arrow = 0;
00112 
00113 /* Description of this plugin */
00114 static BoardPlugin menu_bp =
00115   {
00116     NULL,
00117     NULL,
00118     "Practice addition with a target game",
00119     "Hit the target and count your points",
00120     "Bruno Coudoin <bruno.coudoin@free.fr>",
00121     NULL,
00122     NULL,
00123     NULL,
00124     NULL,
00125     start_board,
00126     pause_board,
00127     end_board,
00128     is_our_board,
00129     key_press,
00130     process_ok,
00131     set_level,
00132     NULL,
00133     NULL,
00134     NULL,
00135     NULL
00136   };
00137 
00138 /*
00139  * Main entry point mandatory for each Gcompris's game
00140  * ---------------------------------------------------
00141  *
00142  */
00143 
00144 GET_BPLUGIN_INFO(target)
00145 
00146 /*
00147  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00148  *
00149  */
00150 static void pause_board (gboolean pause)
00151 {
00152   if(gcomprisBoard==NULL)
00153     return;
00154 
00155   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00156     {
00157       game_won();
00158     }
00159 
00160   if(pause)
00161     {
00162       if (animate_id) {
00163        gtk_timeout_remove (animate_id);
00164        animate_id = 0;
00165       }
00166     }
00167   else
00168     {
00169       if(animate_item) {
00170        animate_id = gtk_timeout_add (200, (GtkFunction) animate_items, NULL);
00171       }
00172     }
00173 
00174   board_paused = pause;
00175 }
00176 
00177 /*
00178  */
00179 static void start_board (GcomprisBoard *agcomprisBoard)
00180 {
00181 
00182   if(agcomprisBoard!=NULL)
00183     {
00184       gcomprisBoard=agcomprisBoard;
00185 
00186       /* disable im_context */
00187       gcomprisBoard->disable_im_context = TRUE;
00188 
00189       gcomprisBoard->level=1;
00190       gcomprisBoard->maxlevel=NUMBER_OF_TARGET;
00191       gcomprisBoard->sublevel=1;
00192       gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
00193 
00194       gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "images/target_background.jpg");
00195 
00196       target_next_level();
00197 
00198       gamewon = FALSE;
00199       pause_board(FALSE);
00200     }
00201 }
00202 /* ======================================= */
00203 static void end_board ()
00204 {
00205   if(gcomprisBoard!=NULL)
00206     {
00207       pause_board(TRUE);
00208       target_destroy_all_items();
00209     }
00210   gcomprisBoard = NULL;
00211 }
00212 
00213 /* ======================================= */
00214 static void set_level (guint level)
00215 {
00216 
00217   if(gcomprisBoard!=NULL)
00218     {
00219       gcomprisBoard->level=level;
00220       gcomprisBoard->sublevel=1;
00221       target_next_level();
00222     }
00223 }
00224 
00225 
00226 /* ======================================= */
00227 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
00228 {
00229   guint c;
00230 
00231   if(!gcomprisBoard)
00232     return FALSE;
00233 
00234   /* Add some filter for control and shift key */
00235   switch (keyval)
00236     {
00237       /* Avoid all this keys to be interpreted by this game */
00238     case GDK_Shift_L:
00239     case GDK_Shift_R:
00240     case GDK_Control_L:
00241     case GDK_Control_R:
00242     case GDK_Caps_Lock:
00243     case GDK_Shift_Lock:
00244     case GDK_Meta_L:
00245     case GDK_Meta_R:
00246     case GDK_Alt_L:
00247     case GDK_Alt_R:
00248     case GDK_Super_L:
00249     case GDK_Super_R:
00250     case GDK_Hyper_L:
00251     case GDK_Hyper_R:
00252     case GDK_Num_Lock:
00253       return FALSE;
00254     case GDK_KP_Enter:
00255     case GDK_Return:
00256       process_ok();
00257       return TRUE;
00258     case GDK_Right:
00259     case GDK_Left:
00260       break;
00261     case GDK_BackSpace:
00262       if(answer_string_index>0)
00263        {
00264          answer_string_index--;
00265          answer_string[answer_string_index] = 0;
00266        }
00267       break;
00268     }
00269 
00270   c = tolower(keyval);
00271 
00272   /* Limit the user entry to 5 digits */
00273   if(c>='0' && c<='9' && answer_string_index<5)
00274     {
00275       answer_string[answer_string_index++] = c;
00276       answer_string[answer_string_index] = 0;
00277     }
00278 
00279   if(answer_item)
00280     {
00281       gchar *tmpstr = g_strdup_printf(_("Points = %s"), answer_string);
00282       gnome_canvas_item_set(answer_item,
00283                          "text", tmpstr,
00284                          NULL);
00285       g_free(tmpstr);
00286     }
00287 
00288   return TRUE;
00289 }
00290 
00291 
00292 /* ======================================= */
00293 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00294 {
00295   if (gcomprisBoard)
00296     {
00297       if(g_strcasecmp(gcomprisBoard->type, "target")==0)
00298        {
00299          /* Set the plugin entry */
00300          gcomprisBoard->plugin=&menu_bp;
00301 
00302          return TRUE;
00303        }
00304     }
00305   return FALSE;
00306 }
00307 
00308 /*-------------------------------------------------------------------------------*/
00309 /*-------------------------------------------------------------------------------*/
00310 /* set initial values for the next level */
00311 static void target_next_level()
00312 {
00313 
00314   gc_bar_set(GC_BAR_LEVEL);
00315   gc_bar_set_level(gcomprisBoard);
00316 
00317   target_destroy_all_items();
00318   gamewon = FALSE;
00319 
00320   /* Try the next level */
00321   target_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00322 }
00323 /* ==================================== */
00324 /* Destroy all the items */
00325 static void target_destroy_all_items()
00326 {
00327   if(boardRootItem!=NULL)
00328     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00329 
00330   boardRootItem = NULL;
00331 
00332   if(speedRootItem!=NULL)
00333     gtk_object_destroy (GTK_OBJECT(speedRootItem));
00334 
00335   animate_item = NULL;
00336   answer_item = NULL;
00337   answer_string_index = 0;
00338   user_points = 0;
00339 
00340   speedRootItem = NULL;
00341 }
00342 
00343 /*
00344  * Display a random wind speed
00345  */
00346 static void display_windspeed()
00347 {
00348   guint second = 0;
00349   guint needle_zoom = 15;
00350   gchar *tmpstr;
00351   GnomeCanvasPoints *canvasPoints;
00352   canvasPoints = gnome_canvas_points_new (2);
00353 
00354   if(speedRootItem!=NULL)
00355     gtk_object_destroy (GTK_OBJECT(speedRootItem));
00356 
00357   speedRootItem = GNOME_CANVAS_GROUP(
00358                                 gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00359                                                     gnome_canvas_group_get_type (),
00360                                                     "x", (double) 0,
00361                                                     "y", (double) 0,
00362                                                     NULL));
00363 
00364   /* Speed orientation */
00365   second = rand()%60;
00366   ang = second * M_PI / 30;
00367 
00368   /* Speed force */
00369   wind_speed = targetDefinition[gcomprisBoard->level-1].target_min_wind_speed \
00370     + rand()%(targetDefinition[gcomprisBoard->level-1].target_max_wind_speed \
00371              - targetDefinition[gcomprisBoard->level-1].target_min_wind_speed);
00372 
00373   canvasPoints->coords[0]=SPEED_CENTER_X;
00374   canvasPoints->coords[1]=SPEED_CENTER_Y;
00375   canvasPoints->coords[2]=SPEED_CENTER_X + wind_speed * sin(ang) * needle_zoom;
00376   canvasPoints->coords[3]=SPEED_CENTER_Y - wind_speed * cos(ang) * needle_zoom;
00377   gnome_canvas_item_new (speedRootItem,
00378                       gnome_canvas_line_get_type (),
00379                       "points", canvasPoints,
00380                       "fill_color_rgba", 0x6df438FF,
00381                       "width_units", (double)1,
00382                       "width_pixels", (guint) 4,
00383                       "last_arrowhead", TRUE,
00384                       "arrow_shape_a", (double) wind_speed,
00385                       "arrow_shape_b", (double) wind_speed-15,
00386                       "arrow_shape_c", (double) 5.0,
00387                       NULL);
00388 
00389   gnome_canvas_points_free(canvasPoints);
00390 
00391   /* Draw the center of the speedometer */
00392   gnome_canvas_item_new (speedRootItem,
00393                       gnome_canvas_ellipse_get_type(),
00394                       "x1", (double)SPEED_CENTER_X-5,
00395                       "y1", (double)SPEED_CENTER_Y-5,
00396                       "x2", (double)SPEED_CENTER_X+5,
00397                       "y2", (double)SPEED_CENTER_Y+5,
00398                       "fill_color_rgba", 0x6df438FF,
00399                       "outline_color", "red",
00400                       "width_units", (double)1,
00401                       NULL);
00402 
00403   tmpstr = g_strdup_printf(_("Wind speed = %d\nkilometers/hour"), (guint)wind_speed);
00404   gnome_canvas_item_new (speedRootItem,
00405                       gnome_canvas_text_get_type (),
00406                       "text", tmpstr,
00407                       "font", gc_skin_font_board_medium,
00408                       "x", (double) SPEED_CENTER_X,
00409                       "y", (double) SPEED_CENTER_Y + 110,
00410                       "anchor", GTK_ANCHOR_CENTER,
00411                       "fill_color", "white",
00412                       NULL);
00413   g_free(tmpstr);
00414 
00415 }
00416 
00417 
00418 
00419 /* ==================================== */
00420 static GnomeCanvasItem *target_create_item(GnomeCanvasGroup *parent)
00421 {
00422   int i;
00423   gchar *tmpstr;
00424   GnomeCanvasItem *item = NULL;
00425 
00426   boardRootItem = GNOME_CANVAS_GROUP(
00427                                  gnome_canvas_item_new (parent,
00428                                                      gnome_canvas_group_get_type (),
00429                                                      "x", (double) TARGET_CENTER_X,
00430                                                      "y", (double) TARGET_CENTER_Y,
00431                                                      NULL));
00432   for(i=0; i<MAX_NUMBER_OF_TARGET; i++)
00433     {
00434       if(targetDefinition[gcomprisBoard->level-1].target_width_value[i*2]>0)
00435        {
00436          item = gnome_canvas_item_new (boardRootItem,
00437                                    gnome_canvas_ellipse_get_type(),
00438                                    "x1", (double)-targetDefinition[gcomprisBoard->level-1].target_width_value[i*2],
00439                                    "y1", (double)-targetDefinition[gcomprisBoard->level-1].target_width_value[i*2],
00440                                    "x2", (double)targetDefinition[gcomprisBoard->level-1].target_width_value[i*2],
00441                                    "y2", (double)targetDefinition[gcomprisBoard->level-1].target_width_value[i*2],
00442                                    "fill_color_rgba", target_colors[i],
00443                                    "outline_color", "black",
00444                                    "width_units", (double)1,
00445                                    NULL);
00446 
00447          gnome_canvas_item_lower_to_bottom(item);
00448          gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event, NULL);
00449 
00450          /* Display the value for this target */
00451          tmpstr = g_strdup_printf("%d",
00452                                targetDefinition[gcomprisBoard->level-1].target_width_value[i*2+1]);
00453          item = gnome_canvas_item_new (boardRootItem,
00454                                    gnome_canvas_text_get_type (),
00455                                    "text", tmpstr,
00456                                    "font", gc_skin_font_board_medium,
00457                                    "x", (double) 0,
00458                                    "y", (double) targetDefinition[gcomprisBoard->level-1].target_width_value[i*2] - 10,
00459                                    "anchor", GTK_ANCHOR_CENTER,
00460                                    "fill_color", "white",
00461                                    NULL);
00462          g_free(tmpstr);
00463 
00464          gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event, NULL);
00465        }
00466     }
00467 
00468   number_of_arrow = targetDefinition[gcomprisBoard->level-1].number_of_arrow;
00469 
00470   tmpstr = g_strdup_printf(_("Distance to target = %d meters"),
00471                         targetDefinition[gcomprisBoard->level-1].target_distance);
00472   gnome_canvas_item_new (boardRootItem,
00473                       gnome_canvas_text_get_type (),
00474                       "text", tmpstr,
00475                       "font", gc_skin_font_board_medium,
00476                       "x", (double) 0,
00477                       "y", (double) BOARDHEIGHT-TARGET_CENTER_Y -45,
00478                       "anchor", GTK_ANCHOR_CENTER,
00479                       "fill_color", "white",
00480                       NULL);
00481   g_free(tmpstr);
00482 
00483   display_windspeed();
00484 
00485   return NULL;
00486 }
00487 /* ==================================== */
00488 static void game_won()
00489 {
00490   gcomprisBoard->sublevel++;
00491 
00492   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00493     /* Try the next level */
00494     gcomprisBoard->sublevel=1;
00495     gcomprisBoard->level++;
00496     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00497       gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00498       return;
00499     }
00500     gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00501   }
00502   target_next_level();
00503 }
00504 
00505 static void process_ok()
00506 {
00507   guint answer_points = atoi(answer_string);
00508 
00509   if(answer_item) {
00510 
00511     if(answer_points == user_points)
00512       {
00513        gamewon = TRUE;
00514        target_destroy_all_items();
00515        gc_bonus_display(gamewon, BONUS_SMILEY);
00516       }
00517     else
00518       {
00519        gamewon = FALSE;
00520        gc_bonus_display(gamewon, BONUS_SMILEY);
00521       }
00522   }
00523 
00524 }
00525 
00526 /*
00527  * Request score
00528  *
00529  */
00530 static void request_score()
00531 {
00532   GdkPixbuf *button_pixmap = NULL;
00533   double y_offset = 160;
00534   double x_offset = 245;
00535   gchar *tmpstr;
00536 
00537   gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK);
00538   button_pixmap = gc_skin_pixmap_load("button_large2.png");
00539   gnome_canvas_item_new (boardRootItem,
00540                       gnome_canvas_pixbuf_get_type (),
00541                       "pixbuf",  button_pixmap,
00542                       "x", x_offset,
00543                       "y", y_offset,
00544                       NULL);
00545 
00546   tmpstr =  g_strdup_printf(_("Points = %s"), "");
00547   answer_item = gnome_canvas_item_new (boardRootItem,
00548                                    gnome_canvas_text_get_type (),
00549                                    "text", tmpstr,
00550                                    "font", gc_skin_font_board_title_bold,
00551                                    "x", (double) x_offset + gdk_pixbuf_get_width(button_pixmap)/2,
00552                                    "y", (double) y_offset + gdk_pixbuf_get_height(button_pixmap)/2,
00553                                    "anchor", GTK_ANCHOR_CENTER,
00554                                    "fill_color", "white",
00555                                    NULL);
00556   g_free(tmpstr);
00557 
00558   gdk_pixbuf_unref(button_pixmap);
00559 }
00560 
00561 static void add_points(double x, double y)
00562 {
00563   guint i;
00564   double diametre;
00565 
00566   // Calculate the distance
00567   diametre = sqrt(x*x+y*y);
00568 
00569   for(i=0; i<MAX_NUMBER_OF_TARGET; i++)
00570     {
00571       if(diametre < targetDefinition[gcomprisBoard->level-1].target_width_value[i*2])
00572        {
00573          user_points += targetDefinition[gcomprisBoard->level-1].target_width_value[i*2+1];
00574          break;
00575        }
00576     }
00577 
00578 }
00579 
00580 
00581 /*
00582  * Dart animation
00583  *
00584  */
00585 static void animate_items()
00586 {
00587   if(board_paused)
00588     return;
00589 
00590   if(!animate_item)
00591     return;
00592 
00593   // Apply the wind move
00594   animate_item_x = animate_item_x + wind_speed * sin(ang);
00595   animate_item_y = animate_item_y - wind_speed * cos(ang);
00596 
00597   gnome_canvas_item_set (animate_item,
00598                       "x1", (double)animate_item_x - animate_item_size,
00599                       "y1", (double)animate_item_y - animate_item_size,
00600                       "x2", (double)animate_item_x + animate_item_size,
00601                       "y2", (double)animate_item_y + animate_item_size,
00602                       NULL);
00603 
00604   if(animate_item_size>MIN_DART_SIZE)
00605     animate_item_size--;
00606 
00607   if(animate_item_distance-- == 0)
00608     {
00609       gtk_timeout_remove (animate_id);
00610       animate_id = 0;
00611       animate_item = NULL;
00612 
00613       // Calc the point for this dart
00614       add_points(animate_item_x, animate_item_y);
00615 
00616       // Change the wind for the next target
00617       display_windspeed();
00618     }
00619 
00620 }
00621 
00622 /*
00623  *
00624  */
00625 static void launch_dart(double item_x, double item_y)
00626 {
00627 
00628   animate_item_x     = item_x;
00629   animate_item_y     = item_y;
00630   animate_item_size  = MAX_DART_SIZE;
00631   animate_item_distance     = targetDefinition[gcomprisBoard->level-1].target_distance/10;
00632 
00633   animate_item = gnome_canvas_item_new (boardRootItem,
00634                                    gnome_canvas_ellipse_get_type(),
00635                                    "x1", (double)item_x-MAX_DART_SIZE,
00636                                    "y1", (double)item_y-MAX_DART_SIZE,
00637                                    "x2", (double)item_x+MAX_DART_SIZE,
00638                                    "y2", (double)item_y+MAX_DART_SIZE,
00639                                    "fill_color_rgba", 0xFF80FFFF,
00640                                    "outline_color", "white",
00641                                    "width_units", (double)1,
00642                                    NULL);
00643 
00644   animate_id = gtk_timeout_add (200, (GtkFunction) animate_items, NULL);
00645 
00646   if(--number_of_arrow == 0)
00647     {
00648       request_score();
00649     }
00650 
00651 }
00652 
00653 /* ==================================== */
00654 static gint
00655 item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00656 {
00657   double item_x, item_y;
00658 
00659   if(board_paused)
00660     return FALSE;
00661 
00662   /* Is there already a dart on air */
00663   if(number_of_arrow == 0 || animate_item)
00664     return FALSE;
00665 
00666   switch (event->type)
00667     {
00668     case GDK_BUTTON_PRESS:
00669       switch(event->button.button)
00670        {
00671        case 1:
00672        case 2:
00673        case 3:
00674          item_x = event->button.x;
00675          item_y = event->button.y;
00676          gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00677 
00678          launch_dart(item_x, item_y);
00679 
00680          break;
00681        default:
00682          break;
00683        }
00684     default:
00685       break;
00686     }
00687   return FALSE;
00688 }