Back to index

gcompris  8.2.2
planegame.c
Go to the documentation of this file.
00001 /* gcompris - planegame.c
00002  *
00003  * Time-stamp: <2006/08/21 23:34:38 bruno>
00004  *
00005  * Copyright (C) 2000 Bruno Coudoin
00006  *
00007  *   This program is free software; you can redistribute it and/or modify
00008  *   it under the terms of the GNU General Public License as published by
00009  *   the Free Software Foundation; either version 2 of the License, or
00010  *   (at your option) any later version.
00011  *
00012  *   This program is distributed in the hope that it will be useful,
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *   GNU General Public License for more details.
00016  *
00017  *   You should have received a copy of the GNU General Public License
00018  *   along with this program; if not, write to the Free Software
00019  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 #include "gcompris/gcompris.h"
00023 
00024 #define SOUNDLISTFILE PACKAGE
00025 
00026 static GList *item_list = NULL;
00027 static GList *item2del_list = NULL;
00028 
00029 static GcomprisBoard *gcomprisBoard = NULL;
00030 
00031 static gint dummy_id = 0;
00032 static gint drop_items_id = 0;
00033 
00034 static GnomeCanvasItem *planeitem = NULL;
00035 static gint plane_x, plane_y;
00036 static gint planespeed_x, planespeed_y;
00037 
00038 #define MAXSPEED 7
00039 
00040 /* These are the index for managing the game rule */
00041 static gint plane_target, plane_last_target;
00042 
00043 typedef struct {
00044   gint number;
00045   GnomeCanvasItem *rootitem;
00046 } CloudItem;
00047 
00048 
00049 static void start_board (GcomprisBoard *agcomprisBoard);
00050 static void pause_board (gboolean pause);
00051 static void end_board (void);
00052 static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
00053 static void set_level (guint level);
00054 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
00055 
00056 static GnomeCanvasItem *planegame_create_item(GnomeCanvasGroup *parent);
00057 static gint planegame_drop_items (GtkWidget *widget, gpointer data);
00058 static gint planegame_move_items (GtkWidget *widget, gpointer data);
00059 static void planegame_destroy_item(CloudItem *clouditem);
00060 static void planegame_destroy_items(void);
00061 static void planegame_destroy_all_items(void);
00062 static void setup_item(GnomeCanvasItem *item);
00063 static void planegame_next_level(void);
00064 
00065 static  guint32              fallSpeed = 0;
00066 static  double               speed = 0.0;
00067 static  double               imageZoom = 0.0;
00068 
00069 /* Description of this plugin */
00070 static BoardPlugin menu_bp =
00071 {
00072    NULL,
00073    NULL,
00074    N_("Numbers in Order"),
00075    N_("Move the plane to catch the clouds in the correct order"),
00076    "Bruno Coudoin <bruno.coudoin@free.fr>",
00077    NULL,
00078    NULL,
00079    NULL,
00080    NULL,
00081    start_board,
00082    pause_board,
00083    end_board,
00084    is_our_board,
00085    key_press,
00086    NULL,
00087    set_level,
00088    NULL,
00089    NULL,
00090    NULL,
00091    NULL
00092 };
00093 
00094 /*
00095  * Main entry point mandatory for each Gcompris's game
00096  * ---------------------------------------------------
00097  *
00098  */
00099 
00100 GET_BPLUGIN_INFO(planegame)
00101 
00102 /*
00103  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00104  *
00105  */
00106 static void pause_board (gboolean pause)
00107 {
00108   if(gcomprisBoard==NULL)
00109     return;
00110 
00111   if(pause)
00112     {
00113       if (dummy_id) {
00114        gtk_timeout_remove (dummy_id);
00115        dummy_id = 0;
00116       }
00117       if (drop_items_id) {
00118        gtk_timeout_remove (drop_items_id);
00119        drop_items_id = 0;
00120       }
00121     }
00122   else
00123     {
00124       if(!drop_items_id) {
00125        drop_items_id = gtk_timeout_add (1000,
00126                                     (GtkFunction) planegame_drop_items, NULL);
00127       }
00128       if(!dummy_id) {
00129        dummy_id = gtk_timeout_add (1000, (GtkFunction) planegame_move_items, NULL);
00130       }
00131     }
00132 }
00133 
00134 /*
00135  */
00136 static void start_board (GcomprisBoard *agcomprisBoard)
00137 {
00138 
00139   if(agcomprisBoard!=NULL)
00140     {
00141       gcomprisBoard=agcomprisBoard;
00142 
00143       /* disable im_context */
00144       gcomprisBoard->disable_im_context = TRUE;
00145 
00146       gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "images/scenery3_background.png");
00147 
00148 
00149       /* set initial values for this level */
00150       gcomprisBoard->level = 1;
00151       gcomprisBoard->maxlevel = 2;
00152       gc_bar_set(GC_BAR_LEVEL);
00153 
00154       planegame_next_level();
00155 
00156       pause_board(FALSE);
00157 
00158     }
00159 
00160 }
00161 
00162 static void
00163 end_board ()
00164 {
00165 
00166   if(gcomprisBoard!=NULL)
00167     {
00168       pause_board(TRUE);
00169       gc_score_end();
00170       planegame_destroy_all_items();
00171       gcomprisBoard->level = 1;       // Restart this game to zero
00172     }
00173   gcomprisBoard = NULL;
00174 }
00175 
00176 static void
00177 set_level (guint level)
00178 {
00179 
00180   if(gcomprisBoard!=NULL)
00181     {
00182       gcomprisBoard->level=level;
00183       planegame_next_level();
00184     }
00185 }
00186 
00187 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
00188 {
00189 
00190   if(!gcomprisBoard)
00191     return FALSE;
00192 
00193   /* Add some filter for control and shift key */
00194   switch (keyval)
00195     {
00196       /* Avoid all this keys to be interpreted by this game */
00197     case GDK_Shift_L:
00198     case GDK_Shift_R:
00199     case GDK_Control_L:
00200     case GDK_Control_R:
00201     case GDK_Caps_Lock:
00202     case GDK_Shift_Lock:
00203     case GDK_Meta_L:
00204     case GDK_Meta_R:
00205     case GDK_Alt_L:
00206     case GDK_Alt_R:
00207     case GDK_Super_L:
00208     case GDK_Super_R:
00209     case GDK_Hyper_L:
00210     case GDK_Hyper_R:
00211     case GDK_Num_Lock:
00212       return FALSE;
00213     case GDK_KP_Enter:
00214     case GDK_Return:
00215       return TRUE;
00216     case GDK_Right:
00217       if(planespeed_x < MAXSPEED)
00218        planespeed_x++;
00219       return TRUE;
00220     case GDK_Left:
00221       if(planespeed_x > -MAXSPEED)
00222        planespeed_x--;
00223       return TRUE;
00224     case GDK_Up:
00225       if(planespeed_y > -MAXSPEED)
00226       planespeed_y--;
00227       return TRUE;
00228     case GDK_Down:
00229       if(planespeed_y < MAXSPEED)
00230       planespeed_y++;
00231       return TRUE;
00232     }
00233   return TRUE;
00234 }
00235 
00236 gboolean
00237 is_our_board (GcomprisBoard *gcomprisBoard)
00238 {
00239   if (gcomprisBoard)
00240     {
00241       if(g_strcasecmp(gcomprisBoard->type, "planegame")==0)
00242        {
00243          /* Set the plugin entry */
00244          gcomprisBoard->plugin=&menu_bp;
00245 
00246          return TRUE;
00247        }
00248     }
00249   return FALSE;
00250 }
00251 
00252 
00253 /*-------------------------------------------------------------------------------*/
00254 /*-------------------------------------------------------------------------------*/
00255 /*-------------------------------------------------------------------------------*/
00256 /*-------------------------------------------------------------------------------*/
00257 
00258 /* set initial values for the next level */
00259 static void planegame_next_level()
00260 {
00261   GdkPixbuf *pixmap = NULL;
00262 
00263   gc_bar_set_level(gcomprisBoard);
00264 
00265   planegame_destroy_all_items();
00266 
00267   /* Try the next level */
00268   speed=100+(40/(gcomprisBoard->level));
00269   fallSpeed=10000-gcomprisBoard->level*200;
00270   /* Make the images tend to 0.5 ratio */
00271   imageZoom=0.3+(0.5/(gcomprisBoard->level));
00272 
00273   /* Setup and Display the plane */
00274   planespeed_y = 0;
00275   planespeed_x = 0;
00276   pixmap = gc_pixmap_load("gcompris/misc/tuxhelico.png");
00277   plane_x = 50;
00278   plane_y = 300;
00279   planeitem = gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00280                                  gnome_canvas_pixbuf_get_type (),
00281                                  "pixbuf", pixmap,
00282                                  "x", (double) plane_x,
00283                                  "y", (double) plane_y,
00284                                  "width", (double) gdk_pixbuf_get_width(pixmap)*imageZoom,
00285                                  "height", (double) gdk_pixbuf_get_height(pixmap)*imageZoom,
00286                                  "width_set", TRUE,
00287                                  "height_set", TRUE,
00288                                  NULL);
00289   gdk_pixbuf_unref(pixmap);
00290 
00291   /* Game rules */
00292   plane_target = 1;
00293   plane_last_target = 10;
00294 
00295   gcomprisBoard->number_of_sublevel=plane_last_target;
00296 
00297   gcomprisBoard->sublevel=plane_target;
00298 
00299   if(gcomprisBoard->level>1)
00300     {
00301       /* No scoring after level 1 */
00302       gc_score_end();
00303     }
00304   else
00305     {
00306       gc_score_start(SCORESTYLE_NOTE,
00307                         gcomprisBoard->width - 220,
00308                         gcomprisBoard->height - 50,
00309                         gcomprisBoard->number_of_sublevel);
00310       gc_score_set(gcomprisBoard->sublevel);
00311     }
00312 }
00313 
00314 #define ISIN(x1, y1, px1, py1, px2, py2) (x1>px1 && x1<px2 && y1>py1 && y2<py2 ? TRUE : FALSE)
00315 
00316 static void planegame_cloud_colision(CloudItem *clouditem)
00317 {
00318   double px1, px2, py1, py2;
00319   double x1, x2, y1, y2;
00320   GnomeCanvasItem *item;
00321 
00322   if(clouditem==NULL)
00323     return;
00324 
00325   item = clouditem->rootitem;
00326 
00327   gnome_canvas_item_get_bounds(planeitem,  &px1, &py1, &px2, &py2);
00328   gnome_canvas_item_get_bounds(item,  &x1, &y1, &x2, &y2);
00329 
00330   if(
00331      ISIN(x1, y1, px1, py1, px2, py2) ||
00332      ISIN(x2, y1, px1, py1, px2, py2) ||
00333      ISIN(x1, y2, px1, py1, px2, py2) ||
00334      ISIN(x2, y2, px1, py1, px2, py2)
00335      )
00336     {
00337       if(plane_target == clouditem->number)
00338        {
00339          gc_sound_play_ogg ("sounds/gobble.ogg", NULL);
00340          item2del_list = g_list_append (item2del_list, clouditem);
00341          plane_target++;
00342 
00343          if(gcomprisBoard->level==1)
00344            {
00345              gc_score_set(plane_target);
00346            }
00347 
00348          if(plane_target==plane_last_target)
00349            {
00350              /* Try the next level */
00351              gcomprisBoard->level++;
00352              if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00353               gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00354               return;
00355              }
00356              planegame_next_level();
00357              gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00358            }
00359        }
00360     }
00361 }
00362 
00363 /* Move the plane */
00364 static void planegame_move_plane(GnomeCanvasItem *item)
00365 {
00366   if(plane_x>gcomprisBoard->width-150 && planespeed_x>0)
00367     planespeed_x=0;
00368 
00369   if(plane_x<0 && planespeed_x<0)
00370     planespeed_x=0;
00371 
00372   if(plane_y>gcomprisBoard->height-50 && planespeed_y>0)
00373     planespeed_y=0;
00374 
00375   if(plane_y<10 && planespeed_y<0)
00376     planespeed_y=0;
00377 
00378   gnome_canvas_item_move(item, (double)planespeed_x, (double)planespeed_y);
00379   plane_x+=planespeed_x;
00380   plane_y+=planespeed_y;
00381 }
00382 
00383 static void planegame_move_item(CloudItem *clouditem)
00384 {
00385   double x1, y1, x2, y2;
00386   GnomeCanvasItem *item = clouditem->rootitem;
00387 
00388   gnome_canvas_item_move(item, -2.0, 0.0);
00389 
00390   gnome_canvas_item_get_bounds    (item,
00391                                &x1,
00392                                &y1,
00393                                &x2,
00394                                &y2);
00395 
00396   if(x2<0) {
00397     item2del_list = g_list_append (item2del_list, clouditem);
00398   }
00399 
00400 }
00401 
00402 static void planegame_destroy_item(CloudItem *clouditem)
00403 {
00404   GnomeCanvasItem *item = clouditem->rootitem;
00405 
00406   item_list = g_list_remove (item_list, clouditem);
00407   item2del_list = g_list_remove (item2del_list, clouditem);
00408   gtk_object_destroy (GTK_OBJECT(item));
00409 
00410   g_free(clouditem);
00411 }
00412 
00413 /* Destroy items that falls out of the canvas */
00414 static void planegame_destroy_items()
00415 {
00416   CloudItem *clouditem;
00417 
00418   while(g_list_length(item2del_list)>0)
00419     {
00420       clouditem = g_list_nth_data(item2del_list, 0);
00421       planegame_destroy_item(clouditem);
00422     }
00423 }
00424 
00425 /* Destroy all the items */
00426 static void planegame_destroy_all_items()
00427 {
00428   CloudItem *clouditem;
00429 
00430   while(g_list_length(item_list)>0)
00431     {
00432       clouditem = g_list_nth_data(item_list, 0);
00433       planegame_destroy_item(clouditem);
00434     }
00435 
00436   if(planeitem)
00437     {
00438       gtk_object_destroy (GTK_OBJECT(planeitem));
00439       planeitem = NULL;
00440     }
00441 }
00442 
00443 /*
00444  * This does the moves of the game items on the play canvas
00445  *
00446  */
00447 static gint planegame_move_items (GtkWidget *widget, gpointer data)
00448 {
00449   g_list_foreach (item_list, (GFunc) planegame_move_item, NULL);
00450   g_list_foreach (item_list, (GFunc) planegame_cloud_colision, NULL);
00451 
00452   /* Destroy items that falls out of the canvas */
00453   planegame_destroy_items();
00454 
00455   /* move the plane */
00456   planegame_move_plane(planeitem);
00457   dummy_id = gtk_timeout_add (speed,
00458                            (GtkFunction) planegame_move_items, NULL);
00459 
00460   return(FALSE);
00461 }
00462 
00463 static GnomeCanvasItem *planegame_create_item(GnomeCanvasGroup *parent)
00464 {
00465   GdkPixbuf *pixmap = NULL;
00466   GnomeCanvasItem *itemgroup;
00467   char *number = NULL;
00468   int i, min;
00469   CloudItem *clouditem;
00470 
00471   /* Random cloud number */
00472   if(rand()%2==0)
00473     {
00474       /* Put the target */
00475       i = plane_target;
00476     }
00477   else
00478     {
00479       min = MAX(1, plane_target - 1);
00480       i   = min + rand()%(plane_target - min + 3);
00481     }
00482   number = g_strdup_printf("%d", i);
00483 
00484   pixmap = gc_pixmap_load("gcompris/misc/cloud.png");
00485 
00486   itemgroup = \
00487     gnome_canvas_item_new (parent,
00488                         gnome_canvas_group_get_type (),
00489                         "x", (double) gcomprisBoard->width,
00490                         "y", (double)(rand()%(gcomprisBoard->height-
00491                                            (guint)(gdk_pixbuf_get_height(pixmap)*
00492                                                   imageZoom))),
00493                         NULL);
00494 
00495 
00496   gnome_canvas_item_new (GNOME_CANVAS_GROUP(itemgroup),
00497                       gnome_canvas_pixbuf_get_type (),
00498                       "pixbuf", pixmap,
00499                       "x", (double) -gdk_pixbuf_get_width(pixmap)*imageZoom/2,
00500                       "y", (double) -gdk_pixbuf_get_height(pixmap)*imageZoom/2,
00501                       "width", (double) gdk_pixbuf_get_width(pixmap)*imageZoom,
00502                       "height", (double) gdk_pixbuf_get_height(pixmap)*imageZoom,
00503                       "width_set", TRUE,
00504                       "height_set", TRUE,
00505                       NULL);
00506   gdk_pixbuf_unref(pixmap);
00507 
00508 
00509   gnome_canvas_item_new (GNOME_CANVAS_GROUP(itemgroup),
00510                       gnome_canvas_text_get_type (),
00511                       "text", number,
00512                       "font", gc_skin_font_board_big,
00513                       "x", (double) 0,
00514                       "y", (double) 0,
00515                       "fill_color", "red",
00516                       NULL);
00517 
00518   /* The plane is always on top */
00519   gnome_canvas_item_raise_to_top(planeitem);
00520 
00521   clouditem = g_malloc(sizeof(CloudItem));
00522   clouditem->rootitem = itemgroup;
00523   clouditem->number   = i;
00524 
00525   item_list = g_list_append (item_list, clouditem);
00526 
00527   g_free (number);
00528 
00529   return (itemgroup);
00530 }
00531 
00532 static void planegame_add_new_item()
00533 {
00534   setup_item (planegame_create_item(gnome_canvas_root(gcomprisBoard->canvas)));
00535 }
00536 
00537 /*
00538  * This is called on a low frequency and is used to drop new items
00539  *
00540  */
00541 static gint planegame_drop_items (GtkWidget *widget, gpointer data)
00542 {
00543   planegame_add_new_item();
00544 
00545   drop_items_id = gtk_timeout_add (fallSpeed,
00546                                (GtkFunction) planegame_drop_items, NULL);
00547   return (FALSE);
00548 }
00549 
00550 static gint
00551 item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00552 {
00553    static double x, y;
00554    double new_x, new_y;
00555    GdkCursor *fleur;
00556    static int dragging;
00557    double item_x, item_y;
00558 
00559   if(!gcomprisBoard)
00560     return FALSE;
00561 
00562    item_x = event->button.x;
00563    item_y = event->button.y;
00564    gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00565 
00566    switch (event->type)
00567      {
00568      case GDK_BUTTON_PRESS:
00569        switch(event->button.button)
00570          {
00571          case 1:
00572            if (event->button.state & GDK_SHIFT_MASK)
00573              {
00574                x = item_x;
00575                y = item_y;
00576 
00577                fleur = gdk_cursor_new(GDK_FLEUR);
00578                gc_canvas_item_grab(item,
00579                                       GDK_POINTER_MOTION_MASK |
00580                                       GDK_BUTTON_RELEASE_MASK,
00581                                       fleur,
00582                                       event->button.time);
00583                gdk_cursor_destroy(fleur);
00584                dragging = TRUE;
00585              }
00586            break;
00587 
00588          default:
00589            break;
00590          }
00591        break;
00592 
00593      case GDK_MOTION_NOTIFY:
00594        if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
00595          {
00596            new_x = item_x;
00597            new_y = item_y;
00598 
00599            gnome_canvas_item_move(item, new_x - x, new_y - y);
00600            x = new_x;
00601            y = new_y;
00602          }
00603        break;
00604 
00605      case GDK_BUTTON_RELEASE:
00606        if(dragging)
00607         {
00608           gc_canvas_item_ungrab(item, event->button.time);
00609           dragging = FALSE;
00610         }
00611        break;
00612 
00613      default:
00614        break;
00615      }
00616 
00617    return FALSE;
00618  }
00619 
00620 static void
00621 setup_item(GnomeCanvasItem *item)
00622 {
00623   gtk_signal_connect(GTK_OBJECT(item), "event",
00624                    (GtkSignalFunc) item_event,
00625                    NULL);
00626 }