/* gcompris - minigolf.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
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;
GnomeCanvasItem *item; /* The canvas item */
double x1, y1, x2, y2; /* Bounding of the 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 GnomeCanvasGroup *boardRootItem = NULL;
static GnomeCanvasItem *minigolf_create_item(GnomeCanvasGroup *parent);
static void minigolf_destroy_all_items(void);
static void minigolf_next_level(void);
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, MachItem *machItem);
static void 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);
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_strcasecmp(gcomprisBoard->type, "machpuzzle")==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_set_background(gnome_canvas_root(gcomprisBoard->canvas),
"opt/foot_background.png");
gc_bar_set_level(gcomprisBoard);
minigolf_destroy_all_items();
gamewon = FALSE;
/* Try the next level */
minigolf_create_item(gnome_canvas_root(gcomprisBoard->canvas));
move_id = gtk_timeout_add (40, (GtkFunction) minigolf_move, item_list);
}
/* ==================================== */
/* Destroy all the items */
static void minigolf_destroy_all_items()
{
if(boardRootItem!=NULL)
gtk_object_destroy (GTK_OBJECT(boardRootItem));
if (move_id) {
gtk_timeout_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 GnomeCanvasItem *minigolf_create_item(GnomeCanvasGroup *parent)
{
boardRootItem = GNOME_CANVAS_GROUP(
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
gnome_canvas_group_get_type (),
"x", (double) 0,
"y", (double) 0,
NULL));
create_machine_item(MACH_HOLE, 730.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) { // the current board is finished : bail out
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
return;
}
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
}
minigolf_next_level();
}
/* ==================================== */
static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, MachItem *machItem)
{
double item_x, item_y;
double x1, y1, x2, y2;
double width;
item_x = event->button.x;
item_y = event->button.y;
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
gnome_canvas_item_get_bounds (item,
&x1,
&y1,
&x2,
&y2);
if(board_paused)
return FALSE;
switch (event->type)
{
case GDK_BUTTON_PRESS:
gc_sound_play_ogg ("sounds/scroll.wav", NULL);
width = x2-x1;
// machItem->vyo = (y1 - machItem->ypos) * machItem->elasticity;
machItem->times = 0;
machItem->yposo = machItem->ypos;
machItem->xposo = machItem->xpos;
machItem->vyo = ((item_y-y1)vxo = ((item_x-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 = gnome_canvas_item_new (boardRootItem,
gnome_canvas_rect_get_type (),
"x1", (double) machItem->xposo,
"y1", (double) machItem->yposo,
"x2", (double) machItem->xposo + width,
"y2", (double) machItem->yposo + height,
"outline_color", "black",
"fill_color_rgba", 0xFF10C0FF,
"width_units", (double)1,
NULL);
gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
(GtkSignalFunc) 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 - width/2;
machItem->xpos = x - width/2;
machItem->vxo = 0;
machItem->ax = 0;
machItem->yposo = y - width/2;
machItem->ypos = y - width/2;
machItem->vyo = 0;
machItem->ay = 0;
machItem->width = width;
machItem->height = width;
machItem->elasticity = 3;
machItem->item = gnome_canvas_item_new (boardRootItem,
gnome_canvas_ellipse_get_type (),
"x1", (double) machItem->xposo,
"y1", (double) machItem->yposo,
"x2", (double) machItem->xposo + width,
"y2", (double) machItem->yposo + width,
"outline_color_rgba", 0xEEEEEEFF,
"fill_color_rgba", 0x111111FF,
"width_units", (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 = gnome_canvas_item_new (boardRootItem,
gnome_canvas_ellipse_get_type (),
"x1", (double) machItem->xposo,
"y1", (double) machItem->yposo,
"x2", (double) machItem->xposo + width,
"y2", (double) machItem->yposo + width,
"outline_color", "black",
"fill_color", "white",
"width_units", (double)1,
NULL);
gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
(GtkSignalFunc) 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 = gnome_canvas_item_new (boardRootItem,
gnome_canvas_ellipse_get_type (),
"x1", (double) machItem->xposo,
"y1", (double) machItem->yposo,
"x2", (double) machItem->xposo + width,
"y2", (double) machItem->yposo + width * 1.5,
"outline_color", "black",
"fill_color_rgba", 0xE03000FF,
"width_units", (double)1,
NULL);
gtk_signal_connect(GTK_OBJECT(machItem->item), "event",
(GtkSignalFunc) item_event,
machItem);
break;
}
gtk_object_set_data(GTK_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 void minigolf_move(GList *item_list)
{
double x1, y1, x2, y2;
MachItem *machItem;
GnomeCanvasItem *item;
guint i;
gboolean collision = FALSE;
double xpos, ypos;
for(i=0; iitem;
if(machItem->moving)
{
gnome_canvas_item_get_bounds(item, &x1, &y1, &x2, &y2);
machItem->times += times_inc;
/* Collision detection */
for(j=0; jxpos,
collMachItem->ypos,
collMachItem->xpos + collMachItem->width,
collMachItem->ypos + collMachItem->height))
{
//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;
}
}
}
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;
}
gc_item_absolute_move(item, machItem->xpos, machItem->ypos);
if((machItem->ypos>=MIN_Y2-machItem->height-BORDER && (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((y1<=MIN_Y1 && (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((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((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);
}
}
}
}