Back to index

gcompris  8.2.2
machpuzzle.c
Go to the documentation of this file.
00001 /* gcompris - minigolf.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 typedef enum {
00025   MACH_HORZ_WALL = 0,
00026   MACH_VERT_WALL,
00027   MACH_HOLE,
00028   MACH_DIAG_WALL,
00029   MACH_BASKET_BALL,
00030   MACH_FLYING_BALL,
00031 } MachItemType;
00032 
00033 struct _MachItem {
00034   MachItemType          type;
00035   gboolean       moving;
00036   GnomeCanvasItem *item;           /* The canvas item      */
00037   double         x1, y1, x2, y2;   /* Bounding of the item */
00038   double         times;
00039   double         ax, ay;
00040   double         xposo, xpos, vxo;
00041   double         yposo, ypos, vyo;
00042   double         elasticity;
00043   double         width, height;
00044 };
00045 
00046 typedef struct _MachItem MachItem;
00047 
00048 static GList *item_list = NULL;
00049 
00050 static GcomprisBoard *gcomprisBoard = NULL;
00051 static gboolean board_paused = TRUE;
00052 static gint move_id = 0;
00053 static double        times_inc  = 0.1;
00054 static double        gravity = 0;
00055 static double        velocity = 0.99;
00056 static void    start_board (GcomprisBoard *agcomprisBoard);
00057 static void    pause_board (gboolean pause);
00058 static void    end_board (void);
00059 static gboolean       is_our_board (GcomprisBoard *gcomprisBoard);
00060 static void    set_level (guint level);
00061 static int     gamewon;
00062 static void    game_won(void);
00063 
00064 static GnomeCanvasGroup *boardRootItem = NULL;
00065 
00066 static GnomeCanvasItem      *minigolf_create_item(GnomeCanvasGroup *parent);
00067 static void           minigolf_destroy_all_items(void);
00068 static void           minigolf_next_level(void);
00069 static gint           item_event(GnomeCanvasItem *item, GdkEvent *event, MachItem *machItem);
00070 static void           minigolf_move(GList *item_list);
00071 
00072 static MachItem             *create_machine_item(MachItemType machItemType, double x, double y);
00073 
00074 /* The border in the image background */
00075 #define BORDER 40
00076 
00077 /* The inner board limit */
00078 #define MIN_X1       60
00079 #define MIN_X2       (BOARDWIDTH-MIN_X1)
00080 #define MIN_Y1       65
00081 #define MIN_Y2       (BOARDHEIGHT-30)
00082 
00083 /* Description of this plugin */
00084 static BoardPlugin menu_bp =
00085   {
00086     NULL,
00087     NULL,
00088     N_("Move the mouse"),
00089     "football",
00090     "Bruno Coudoin <bruno.coudoin@free.fr>",
00091     NULL,
00092     NULL,
00093     NULL,
00094     NULL,
00095     start_board,
00096     pause_board,
00097     end_board,
00098     is_our_board,
00099     NULL,
00100     NULL,
00101     set_level,
00102     NULL,
00103     NULL,
00104     NULL,
00105     NULL
00106   };
00107 
00108 /*
00109  * Main entry point mandatory for each Gcompris's game
00110  * ---------------------------------------------------
00111  *
00112  */
00113 
00114 GET_BPLUGIN_INFO(machpuzzle)
00115 
00116 /*
00117  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00118  *
00119  */
00120 static void pause_board (gboolean pause)
00121 {
00122   if(gcomprisBoard==NULL)
00123     return;
00124 
00125   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00126     {
00127       game_won();
00128     }
00129 
00130   board_paused = pause;
00131 }
00132 
00133 /*
00134  */
00135 static void start_board (GcomprisBoard *agcomprisBoard)
00136 {
00137 
00138   if(agcomprisBoard!=NULL)
00139     {
00140       gcomprisBoard=agcomprisBoard;
00141       gcomprisBoard->level=1;
00142       gcomprisBoard->maxlevel=6;
00143       gcomprisBoard->sublevel=1;
00144       gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
00145       gc_bar_set(GC_BAR_LEVEL);
00146 
00147       minigolf_next_level();
00148 
00149       gamewon = FALSE;
00150       pause_board(FALSE);
00151     }
00152 }
00153 /* ======================================= */
00154 static void end_board ()
00155 {
00156   if(gcomprisBoard!=NULL)
00157     {
00158       pause_board(TRUE);
00159       minigolf_destroy_all_items();
00160     }
00161   gcomprisBoard = NULL;
00162 }
00163 
00164 /* ======================================= */
00165 static void set_level (guint level)
00166 {
00167 
00168   if(gcomprisBoard!=NULL)
00169     {
00170       gcomprisBoard->level=level;
00171       gcomprisBoard->sublevel=1;
00172       minigolf_next_level();
00173     }
00174 }
00175 /* ======================================= */
00176 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00177 {
00178   if (gcomprisBoard)
00179     {
00180       if(g_strcasecmp(gcomprisBoard->type, "machpuzzle")==0)
00181        {
00182          /* Set the plugin entry */
00183          gcomprisBoard->plugin=&menu_bp;
00184 
00185          return TRUE;
00186        }
00187     }
00188   return FALSE;
00189 }
00190 
00191 /*-------------------------------------------------------------------------------*/
00192 /*-------------------------------------------------------------------------------*/
00193 /* set initial values for the next level */
00194 static void minigolf_next_level()
00195 {
00196 
00197   gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),"images/foot_background.png");
00198 
00199   gc_bar_set_level(gcomprisBoard);
00200 
00201   minigolf_destroy_all_items();
00202   gamewon = FALSE;
00203 
00204   /* Try the next level */
00205   minigolf_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00206 
00207   move_id = gtk_timeout_add (40, (GtkFunction) minigolf_move, item_list);
00208 
00209 }
00210 /* ==================================== */
00211 /* Destroy all the items */
00212 static void minigolf_destroy_all_items()
00213 {
00214   if(boardRootItem!=NULL)
00215     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00216 
00217   if (move_id) {
00218     gtk_timeout_remove (move_id);
00219     move_id = 0;
00220   }
00221 
00222   boardRootItem = NULL;
00223 
00224   if(item_list)
00225   {
00226       int i;
00227       for( i=0; i< g_list_length(item_list); i++)
00228           g_free(g_list_nth_data(item_list,i));
00229       g_list_free(item_list);
00230   }
00231   item_list = NULL;
00232 }
00233 /* ==================================== */
00234 static GnomeCanvasItem *minigolf_create_item(GnomeCanvasGroup *parent)
00235 {
00236 
00237   boardRootItem = GNOME_CANVAS_GROUP(
00238                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00239                                                      gnome_canvas_group_get_type (),
00240                                                      "x", (double) 0,
00241                                                      "y", (double) 0,
00242 
00243                                                      NULL));
00244 
00245   create_machine_item(MACH_HOLE, 730.0, 260.0);
00246   create_machine_item(MACH_BASKET_BALL, (double)RAND(60, 150), (double)RAND(60, 400));
00247 
00248 
00249   return NULL;
00250 }
00251 /* ==================================== */
00252 static void game_won()
00253 {
00254   gcomprisBoard->sublevel++;
00255 
00256   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00257     /* Try the next level */
00258     gcomprisBoard->sublevel=1;
00259     gcomprisBoard->level++;
00260     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00261       gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00262       return;
00263     }
00264     gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00265   }
00266   minigolf_next_level();
00267 }
00268 
00269 /* ==================================== */
00270 static gint
00271 item_event(GnomeCanvasItem *item, GdkEvent *event, MachItem *machItem)
00272 {
00273   double item_x, item_y;
00274   double x1, y1, x2, y2;
00275   double width;
00276   item_x = event->button.x;
00277   item_y = event->button.y;
00278   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00279   gnome_canvas_item_get_bounds    (item,
00280                                &x1,
00281                                &y1,
00282                                &x2,
00283                                &y2);
00284 
00285   if(board_paused)
00286     return FALSE;
00287 
00288    switch (event->type)
00289      {
00290 
00291      case GDK_BUTTON_PRESS:
00292        width = x2-x1;
00293 
00294        //       machItem->vyo   = (y1 - machItem->ypos) * machItem->elasticity;
00295        machItem->times = 0;
00296        machItem->yposo = machItem->ypos;
00297        machItem->xposo = machItem->xpos;
00298 
00299        machItem->vyo = ((item_y-y1)<width/2?(width/2-(item_y-y1))*20:-1*(width/2-(y2-item_y))*20);
00300        machItem->vxo = ((item_x-x1)<width/2?(width/2-(item_x-x1))*20:-1*(width/2-(x2-item_x))*20);
00301        break;
00302 
00303      default:
00304        break;
00305      }
00306 
00307 
00308    return FALSE;
00309 }
00310 
00311 #if 0
00312 static void dump_machItem(MachItem *machItem)
00313 {
00314 
00315   printf("Type              = %d\n", machItem->type);
00316   printf("        times = %f\n", machItem->times);
00317   printf("           ax = %f\n", machItem->ax);
00318   printf("           ay = %f\n", machItem->ay);
00319   printf("        xposo = %f\n", machItem->xposo);
00320   printf("         xpos = %f\n", machItem->xpos);
00321   printf("          vxo = %f\n", machItem->vxo);
00322   printf("        yposo = %f\n", machItem->yposo);
00323   printf("         ypos = %f\n", machItem->ypos);
00324   printf("          vyo = %f\n", machItem->vyo);
00325 
00326 }
00327 #endif
00328 
00329 /* Create a machine item */
00330 static MachItem *create_machine_item(MachItemType machItemType, double x, double y)
00331 {
00332   MachItem           *machItem;
00333   guint                      width;
00334   guint                      height;
00335 
00336   machItem = g_new (MachItem, 1);
00337 
00338   machItem->type     = machItemType;
00339 
00340   switch (machItemType)
00341     {
00342     case MACH_HORZ_WALL:
00343       width = 100;
00344       height = 20;
00345       machItem->moving      = FALSE;
00346       machItem->times   = 0.0;
00347 
00348       machItem->xposo       = x;
00349       machItem->xpos = x;
00350       machItem->vxo  = 0;
00351       machItem->ax   = 0;
00352 
00353       machItem->yposo       = y;
00354       machItem->ypos = y;
00355       machItem->vyo  = 0;
00356       machItem->ay   = 0;
00357 
00358       machItem->elasticity  = 5;
00359 
00360       machItem->item = gnome_canvas_item_new (boardRootItem,
00361                                          gnome_canvas_rect_get_type (),
00362                                          "x1", (double) machItem->xposo,
00363                                          "y1", (double) machItem->yposo,
00364                                          "x2", (double) machItem->xposo + width,
00365                                          "y2", (double) machItem->yposo + height,
00366                                          "outline_color", "black",
00367                                          "fill_color_rgba", 0xFF10C0FF,
00368                                          "width_units", (double)1,
00369                                          NULL);
00370 
00371       gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
00372                       (GtkSignalFunc) item_event,
00373                       machItem);
00374       break;
00375     case MACH_VERT_WALL:
00376       break;
00377     case MACH_DIAG_WALL:
00378       break;
00379     case MACH_HOLE:
00380       /* Make the hole be smaller based on the level */
00381       width = 110 - gcomprisBoard->level*3;
00382       machItem->moving      = FALSE;
00383       machItem->times   = 0.0;
00384 
00385       machItem->xposo       = x - width/2;
00386       machItem->xpos = x - width/2;
00387       machItem->vxo  = 0;
00388       machItem->ax   = 0;
00389 
00390       machItem->yposo       = y - width/2;
00391       machItem->ypos = y - width/2;
00392       machItem->vyo  = 0;
00393       machItem->ay   = 0;
00394 
00395       machItem->width       = width;
00396       machItem->height      = width;
00397 
00398       machItem->elasticity  = 3;
00399 
00400       machItem->item = gnome_canvas_item_new (boardRootItem,
00401                                          gnome_canvas_ellipse_get_type (),
00402                                          "x1", (double) machItem->xposo,
00403                                          "y1", (double) machItem->yposo,
00404                                          "x2", (double) machItem->xposo + width,
00405                                          "y2", (double) machItem->yposo + width,
00406                                          "outline_color_rgba", 0xEEEEEEFF,
00407                                          "fill_color_rgba", 0x111111FF,
00408                                          "width_units", (double)2,
00409                                          NULL);
00410 
00411       break;
00412     case MACH_BASKET_BALL:
00413       width = 40;
00414       machItem->moving      = TRUE;
00415       machItem->times   = 0.0;
00416 
00417       machItem->xposo       = x;
00418       machItem->xpos = x;
00419       machItem->vxo  = 0;
00420       machItem->ax   = 0;
00421 
00422       machItem->yposo       = y;
00423       machItem->ypos = y;
00424       machItem->vyo  = 0;
00425       machItem->ay   = gravity;
00426 
00427       machItem->width       = width;
00428       machItem->height      = width;
00429 
00430       machItem->elasticity  = 4;
00431 
00432       machItem->item = gnome_canvas_item_new (boardRootItem,
00433                                          gnome_canvas_ellipse_get_type (),
00434                                          "x1", (double) machItem->xposo,
00435                                          "y1", (double) machItem->yposo,
00436                                          "x2", (double) machItem->xposo + width,
00437                                          "y2", (double) machItem->yposo + width,
00438                                          "outline_color", "black",
00439                                          "fill_color", "white",
00440                                          "width_units", (double)1,
00441                                          NULL);
00442 
00443       gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
00444                       (GtkSignalFunc) item_event,
00445                       machItem);
00446 
00447       break;
00448     case MACH_FLYING_BALL:
00449       width = 40;
00450       machItem->moving      = TRUE;
00451       machItem->times   = 0.0;
00452 
00453       machItem->xposo       = x;
00454       machItem->xpos = x;
00455       machItem->vxo  = 10;
00456       machItem->ax   = 0;
00457 
00458       machItem->yposo       = y;
00459       machItem->ypos = y;
00460       machItem->vyo  = -5;
00461       machItem->ay   = -0.5;
00462 
00463       machItem->width       = width;
00464       machItem->height      = width;
00465 
00466       machItem->elasticity  = 1;
00467 
00468       machItem->item = gnome_canvas_item_new (boardRootItem,
00469                                          gnome_canvas_ellipse_get_type (),
00470                                          "x1", (double) machItem->xposo,
00471                                          "y1", (double) machItem->yposo,
00472                                          "x2", (double) machItem->xposo + width,
00473                                          "y2", (double) machItem->yposo + width * 1.5,
00474                                          "outline_color", "black",
00475                                          "fill_color_rgba", 0xE03000FF,
00476                                          "width_units", (double)1,
00477                                          NULL);
00478 
00479       gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
00480                       (GtkSignalFunc) item_event,
00481                       machItem);
00482       break;
00483     }
00484 
00485   gtk_object_set_data(GTK_OBJECT(machItem->item),"mach",(gpointer)machItem);
00486 
00487   item_list = g_list_append (item_list, machItem);
00488 
00489   return machItem;
00490 }
00491 
00492 /*
00493  * Returns true if at least 3 corners of 's' rectangle are inside 'd' rectangle
00494  *
00495  */
00496 static gint rectangle_in(double sx1, double sy1, double sx2, double sy2,
00497                       double dx1, double dy1, double dx2, double dy2)
00498 {
00499   guint corner_in = 0;
00500   //  printf("rectangle_in %10f %10f %10f %10f\n             %10f %10f %10f %10f\n", sx1,sy1,sx2,sy2,dx1,dy1,dx2,dy2);
00501 
00502   if(sx1>dx1 && sx1<dx2 && sy1>dy1 && sy1<dy2)
00503     corner_in++;
00504 
00505   if(sx2>dx1 && sx2<dx2 && sy2>dy1 && sy2<dy2)
00506     corner_in++;
00507 
00508   if(sx2>dx1 && sx2<dx2 && sy1>dy1 && sy1<dy2)
00509     corner_in++;
00510 
00511   if(sx1>dx1 && sx1<dx2 && sy2>dy1 && sy2<dy2)
00512     corner_in++;
00513 
00514   return (corner_in>=3 ? TRUE : FALSE);
00515 }
00516 
00517 /* Move */
00518 static void minigolf_move(GList *item_list)
00519 {
00520   double              x1, y1, x2, y2;
00521   MachItem           *machItem;
00522   GnomeCanvasItem    *item;
00523   guint                      i;
00524   gboolean            collision = FALSE;
00525   double               xpos, ypos;
00526 
00527   for(i=0; i<g_list_length(item_list); i++)
00528     {
00529       gint j;
00530 
00531       machItem = g_list_nth_data(item_list, i);
00532 
00533       //      dump_machItem(machItem);
00534       item = machItem->item;
00535 
00536       if(machItem->moving)
00537        {
00538 
00539          gnome_canvas_item_get_bounds(item, &x1, &y1, &x2, &y2);
00540 
00541          machItem->times += times_inc;
00542 
00543          /* Collision detection */
00544          for(j=0; j<g_list_length(item_list); j++)
00545            {
00546              MachItem             *collMachItem;
00547 
00548              collMachItem = g_list_nth_data(item_list, j);
00549 
00550              //printf("Checking coll detec item %d\n", j);
00551 
00552              if(collMachItem != machItem) {
00553 
00554               if(rectangle_in(x1, y1, x2, y2,
00555                             collMachItem->xpos,
00556                             collMachItem->ypos,
00557                             collMachItem->xpos + collMachItem->width,
00558                             collMachItem->ypos + collMachItem->height))
00559                 {
00560                   //printf("!!! Collision detected with:\n");
00561                   //dump_machItem(collMachItem);
00562                   collision = TRUE;
00563 
00564                   gamewon = TRUE;
00565                   minigolf_destroy_all_items();
00566                   gc_bonus_display(gamewon, BONUS_SMILEY);
00567                   return;
00568                 }
00569              }
00570            }
00571 
00572          ypos=machItem->yposo
00573            + (machItem->vyo*machItem->times)
00574            + (.5*machItem->ay * (machItem->times*machItem->times));
00575 
00576          /* Simulate going slower */
00577          if(ABS(machItem->ypos-ypos)>0.3) {
00578            machItem->vyo = machItem->vyo * velocity;
00579          } else {
00580            machItem->yposo = ypos;
00581            machItem->vyo = 0;
00582          }
00583 
00584          xpos=machItem->xposo
00585            + (machItem->vxo*machItem->times)
00586            + (.5*machItem->ax * (machItem->times*machItem->times));
00587 
00588          /* Simulate going slower */
00589          if(ABS(machItem->xpos-xpos)>0.3) {
00590            machItem->vxo = machItem->vxo * velocity;
00591          } else {
00592            machItem->xposo = xpos;
00593            machItem->vxo = 0;
00594          }
00595 
00596          machItem->xpos=xpos;
00597          machItem->ypos=ypos;
00598 
00599          /* v = u + at */
00600          machItem->vxo += (machItem->ax * machItem->times);
00601          machItem->vyo += (machItem->ay * machItem->times);
00602 
00603          if(machItem->ypos >= MIN_Y2 - machItem->height -1)
00604            machItem->ypos = MIN_Y2 - machItem->height;
00605 
00606          if(machItem->ypos < MIN_Y1)
00607            machItem->ypos = MIN_Y1;
00608 
00609          if(machItem->xpos < MIN_X1)
00610            machItem->xpos = MIN_X1;
00611 
00612          if(machItem->xpos > MIN_X2)
00613            machItem->xpos = MIN_X2;
00614 
00615 
00616          gc_item_absolute_move(item, machItem->xpos, machItem->ypos);
00617 
00618          if((machItem->ypos>=MIN_Y2-machItem->height-BORDER && (y1 - machItem->ypos)<=0) || collision == TRUE)
00619            {
00620              machItem->vyo   = machItem->vyo * -0.5;
00621              machItem->vxo   = machItem->vxo * 0.5;
00622              machItem->times = 0;
00623              machItem->yposo = machItem->ypos;
00624              machItem->xposo = machItem->xpos;
00625 
00626              /* Floor touch */
00627              //machItem->vxo *= 0.9;
00628            }
00629 
00630          if((y1<=MIN_Y1 && (y1 - machItem->ypos)>=0) || collision == TRUE)
00631            {
00632              machItem->vyo   = machItem->vyo * -0.5;
00633              machItem->vxo   = machItem->vxo * 0.5;
00634              machItem->times=0;
00635              machItem->yposo=machItem->ypos;
00636              machItem->xposo=machItem->xpos;
00637            }
00638 
00639          //     if(x1<=5 && (x1 - machItem->xpos)>0 || collision == TRUE)
00640          if((x1<=MIN_X1 && machItem->vxo<0) || collision == TRUE)
00641            {
00642              machItem->vyo   = machItem->vyo * 0.5;
00643              machItem->vxo   = machItem->vxo * -0.5;
00644              machItem->times=0;
00645              machItem->yposo=machItem->ypos;
00646              machItem->xposo=machItem->xpos;
00647            }
00648 
00649          if((x2>=MIN_X2 && machItem->vxo>0) || collision == TRUE)
00650            {
00651              machItem->vyo = 0.5 * machItem->vyo;
00652              machItem->vxo = machItem->vxo * -0.5;
00653              machItem->times=0;
00654              machItem->yposo=machItem->ypos;
00655              machItem->xposo=machItem->xpos;
00656            }
00657 
00658        }
00659 
00660     }
00661 
00662 }
00663