/* gcompris - billard.c
*
* Copyright (C) 2001, 2008 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
typedef enum {
MACH_HORZ_WALL = 0,
MACH_VERT_WALL,
MACH_HOLE,
MACH_DIAG_WALL,
MACH_BASKET_BALL,
MACH_FLYING_BALL,
} MachItemType;
struct _MachItem {
MachItemType type;
gboolean moving;
GooCanvasItem *item; /* The canvas item */
double times;
double ax, ay;
double xposo, xpos, vxo;
double yposo, ypos, vyo;
double elasticity;
double width, height;
};
typedef struct _MachItem MachItem;
static GList *item_list = NULL;
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static gint move_id = 0;
static double times_inc = 0.1;
static double gravity = 0;
static double velocity = 0.99;
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);
static GooCanvasItem *boardRootItem = NULL;
static GooCanvasItem *minigolf_create_item(GooCanvasItem *parent);
static void minigolf_destroy_all_items(void);
static void minigolf_next_level(void);
static gboolean item_event (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventButton *event,
MachItem *machItem);
static gboolean minigolf_move(GList *item_list);
static MachItem *create_machine_item(MachItemType machItemType, double x, double y);
/* The border in the image background */
#define BORDER 40
/* The inner board limit */
#define MIN_X1 60
#define MIN_X2 (BOARDWIDTH-MIN_X1)
#define MIN_Y1 65
#define MIN_Y2 (BOARDHEIGHT-30)
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
N_("Move the mouse"),
"football",
"Bruno Coudoin ",
NULL,
NULL,
NULL,
NULL,
start_board,
pause_board,
end_board,
is_our_board,
NULL,
NULL,
set_level,
NULL,
NULL,
NULL,
NULL
};
/*
* Main entry point mandatory for each Gcompris's game
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO(machpuzzle)
/*
* 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)
{
gcomprisBoard=agcomprisBoard;
gcomprisBoard->level=1;
gcomprisBoard->maxlevel=6;
gcomprisBoard->sublevel=1;
gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
gc_bar_set(GC_BAR_LEVEL);
gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
"billard/foot_background.svgz");
minigolf_next_level();
gamewon = FALSE;
pause_board(FALSE);
}
}
/* ======================================= */
static void end_board ()
{
if(gcomprisBoard!=NULL)
{
pause_board(TRUE);
minigolf_destroy_all_items();
}
gcomprisBoard = NULL;
}
/* ======================================= */
static void set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
gcomprisBoard->sublevel=1;
minigolf_next_level();
}
}
/* ======================================= */
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_ascii_strcasecmp(gcomprisBoard->type, "billard")==0)
{
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void minigolf_next_level()
{
gc_bar_set_level(gcomprisBoard);
minigolf_destroy_all_items();
gamewon = FALSE;
/* Try the next level */
minigolf_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
move_id = g_timeout_add (40, (GSourceFunc)minigolf_move, item_list);
}
/* ==================================== */
/* Destroy all the items */
static void minigolf_destroy_all_items()
{
if(boardRootItem!=NULL)
goo_canvas_item_remove(boardRootItem);
if (move_id) {
g_source_remove (move_id);
move_id = 0;
}
boardRootItem = NULL;
if(item_list)
{
int i;
for( i=0; i< g_list_length(item_list); i++)
g_free(g_list_nth_data(item_list,i));
g_list_free(item_list);
}
item_list = NULL;
}
/* ==================================== */
static GooCanvasItem *minigolf_create_item(GooCanvasItem *parent)
{
boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
NULL);
create_machine_item(MACH_HOLE, 750.0, 260.0);
create_machine_item(MACH_BASKET_BALL,
(double)g_random_int_range(60, 150),
(double)RAND(70, 400));
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)
gcomprisBoard->level = gcomprisBoard->maxlevel;
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
}
minigolf_next_level();
}
/* ==================================== */
static gboolean
item_event (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventButton *event,
MachItem *machItem)
{
double item_x, item_y;
GooCanvasBounds bounds;
double width;
item_x = event->x;
item_y = event->y;
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
item, &item_x, &item_y);
goo_canvas_item_get_bounds (item, &bounds);
if(board_paused)
return FALSE;
gc_sound_play_ogg ("sounds/scroll.wav", NULL);
width = bounds.x2 - bounds.x1;
machItem->times = 0;
machItem->yposo = machItem->ypos;
machItem->xposo = machItem->xpos;
machItem->vyo = ((item_y-bounds.y1)vxo = ((item_x-bounds.x1)type);
printf(" times = %f\n", machItem->times);
printf(" ax = %f\n", machItem->ax);
printf(" ay = %f\n", machItem->ay);
printf(" xposo = %f\n", machItem->xposo);
printf(" xpos = %f\n", machItem->xpos);
printf(" vxo = %f\n", machItem->vxo);
printf(" yposo = %f\n", machItem->yposo);
printf(" ypos = %f\n", machItem->ypos);
printf(" vyo = %f\n", machItem->vyo);
}
#endif
/* Create a machine item */
static MachItem *create_machine_item(MachItemType machItemType, double x, double y)
{
MachItem *machItem;
guint width;
guint height;
machItem = g_new (MachItem, 1);
machItem->type = machItemType;
switch (machItemType)
{
case MACH_HORZ_WALL:
width = 100;
height = 20;
machItem->moving = FALSE;
machItem->times = 0.0;
machItem->xposo = x;
machItem->xpos = x;
machItem->vxo = 0;
machItem->ax = 0;
machItem->yposo = y;
machItem->ypos = y;
machItem->vyo = 0;
machItem->ay = 0;
machItem->elasticity = 5;
machItem->item = goo_canvas_rect_new (boardRootItem,
machItem->xposo,
machItem->yposo,
width,
height,
"stroke-color", "black",
"fill_color_rgba", 0xFF10C0FF,
"line-width", (double)1,
NULL);
g_signal_connect(machItem->item,
"button_press_event",
(GCallback) item_event,
machItem);
break;
case MACH_VERT_WALL:
break;
case MACH_DIAG_WALL:
break;
case MACH_HOLE:
/* Make the hole be smaller based on the level */
width = 110 - gcomprisBoard->level*3;
machItem->moving = FALSE;
machItem->times = 0.0;
machItem->xposo = x;
machItem->xpos = x;
machItem->vxo = 0;
machItem->ax = 0;
machItem->yposo = y;
machItem->ypos = y;
machItem->vyo = 0;
machItem->ay = 0;
machItem->width = width;
machItem->height = width;
machItem->elasticity = 3;
machItem->item = goo_canvas_ellipse_new (boardRootItem,
machItem->xposo,
machItem->yposo,
width/2,
width/2,
"stroke-color-rgba", 0xEEEEEEFF,
"fill-color-rgba", 0x111111FF,
"line-width", (double)2,
NULL);
break;
case MACH_BASKET_BALL:
width = 40;
machItem->moving = TRUE;
machItem->times = 0.0;
machItem->xposo = x;
machItem->xpos = x;
machItem->vxo = 0;
machItem->ax = 0;
machItem->yposo = y;
machItem->ypos = y;
machItem->vyo = 0;
machItem->ay = gravity;
machItem->width = width;
machItem->height = width;
machItem->elasticity = 4;
machItem->item = goo_canvas_ellipse_new (boardRootItem,
machItem->xposo,
machItem->yposo,
width/2,
width/2,
"stroke-color", "black",
"fill-color", "white",
"line-width", (double)1,
NULL);
g_signal_connect(machItem->item, "button_press_event",
(GCallback) item_event,
machItem);
break;
case MACH_FLYING_BALL:
width = 40;
machItem->moving = TRUE;
machItem->times = 0.0;
machItem->xposo = x;
machItem->xpos = x;
machItem->vxo = 10;
machItem->ax = 0;
machItem->yposo = y;
machItem->ypos = y;
machItem->vyo = -5;
machItem->ay = -0.5;
machItem->width = width;
machItem->height = width;
machItem->elasticity = 1;
machItem->item = goo_canvas_ellipse_new (boardRootItem,
machItem->xposo,
machItem->yposo,
width/2,
width/2 * 1.5,
"stroke-color", "black",
"fill_color_rgba", 0xE03000FF,
"line-width", (double)1,
NULL);
g_signal_connect(machItem->item, "enter_notify_event",
(GCallback) item_event,
machItem);
break;
}
g_object_set_data(G_OBJECT(machItem->item), "mach", (gpointer)machItem);
item_list = g_list_append (item_list, machItem);
return machItem;
}
/*
* Returns true if at least 3 corners of 's' rectangle are inside 'd' rectangle
*
*/
static gint rectangle_in(double sx1, double sy1, double sx2, double sy2,
double dx1, double dy1, double dx2, double dy2)
{
guint corner_in = 0;
// printf("rectangle_in %10f %10f %10f %10f\n %10f %10f %10f %10f\n", sx1,sy1,sx2,sy2,dx1,dy1,dx2,dy2);
if(sx1>dx1 && sx1dy1 && sy1dx1 && sx2dy1 && sy2dx1 && sx2dy1 && sy1dx1 && sx1dy1 && sy2=3 ? TRUE : FALSE);
}
/* Move */
static gboolean minigolf_move(GList *item_list)
{
GooCanvasBounds bounds;
MachItem *machItem;
GooCanvasItem *item;
guint i;
gboolean collision = FALSE;
double xpos, ypos;
for(i=0; iitem;
if(machItem->moving)
{
goo_canvas_item_get_bounds(item, &bounds);
machItem->times += times_inc;
/* Collision detection */
for(j=0; jitem, &bounds2);
if(rectangle_in(bounds.x1, bounds.y1, bounds.x2, bounds.y2,
bounds2.x1, bounds2.y1, bounds2.x2, bounds2.y2))
{
//printf("!!! Collision detected with:\n");
//dump_machItem(collMachItem);
gc_sound_play_ogg ("sounds/flip.wav", NULL);
collision = TRUE;
gamewon = TRUE;
minigolf_destroy_all_items();
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
return(FALSE);
}
}
}
ypos=machItem->yposo
+ (machItem->vyo*machItem->times)
+ (.5*machItem->ay * (machItem->times*machItem->times));
/* Simulate going slower */
if(ABS(machItem->ypos-ypos)>0.3) {
machItem->vyo = machItem->vyo * velocity;
} else {
machItem->yposo = ypos;
machItem->vyo = 0;
}
xpos=machItem->xposo
+ (machItem->vxo*machItem->times)
+ (.5*machItem->ax * (machItem->times*machItem->times));
/* Simulate going slower */
if(ABS(machItem->xpos-xpos)>0.3) {
machItem->vxo = machItem->vxo * velocity;
} else {
machItem->xposo = xpos;
machItem->vxo = 0;
}
machItem->xpos=xpos;
machItem->ypos=ypos;
/* v = u + at */
machItem->vxo += (machItem->ax * machItem->times);
machItem->vyo += (machItem->ay * machItem->times);
if(machItem->ypos >= MIN_Y2 - machItem->height -1)
{
machItem->ypos = MIN_Y2 - machItem->height;
}
if(machItem->ypos < MIN_Y1)
{
machItem->ypos = MIN_Y1;
}
if(machItem->xpos < MIN_X1)
{
machItem->xpos = MIN_X1;
}
if(machItem->xpos > MIN_X2)
{
machItem->xpos = MIN_X2;
}
if (machItem->vxo != 0 || machItem->vyo != 0)
gc_item_absolute_move(item, machItem->xpos, machItem->ypos);
if((machItem->ypos>=MIN_Y2-machItem->height-BORDER
&& (bounds.y1 - machItem->ypos)<=0) || collision == TRUE)
{
machItem->vyo = machItem->vyo * -0.5;
machItem->vxo = machItem->vxo * 0.5;
machItem->times = 0;
machItem->yposo = machItem->ypos;
machItem->xposo = machItem->xpos;
/* Floor touch */
//machItem->vxo *= 0.9;
gc_sound_play_ogg ("sounds/line_end.wav", NULL);
}
if((bounds.y1<=MIN_Y1 && (bounds.y1 - machItem->ypos)>=0)
|| collision == TRUE)
{
machItem->vyo = machItem->vyo * -0.5;
machItem->vxo = machItem->vxo * 0.5;
machItem->times=0;
machItem->yposo=machItem->ypos;
machItem->xposo=machItem->xpos;
gc_sound_play_ogg ("sounds/line_end.wav", NULL);
}
// if(x1<=5 && (x1 - machItem->xpos)>0 || collision == TRUE)
if((bounds.x1<=MIN_X1 && machItem->vxo<0) || collision == TRUE)
{
machItem->vyo = machItem->vyo * 0.5;
machItem->vxo = machItem->vxo * -0.5;
machItem->times=0;
machItem->yposo=machItem->ypos;
machItem->xposo=machItem->xpos;
gc_sound_play_ogg ("sounds/line_end.wav", NULL);
}
if((bounds.x2>=MIN_X2 && machItem->vxo>0) || collision == TRUE)
{
machItem->vyo = 0.5 * machItem->vyo;
machItem->vxo = machItem->vxo * -0.5;
machItem->times=0;
machItem->yposo=machItem->ypos;
machItem->xposo=machItem->xpos;
gc_sound_play_ogg ("sounds/line_end.wav", NULL);
}
}
}
return(TRUE);
}