/* gcompris - planegame.c
*
* Copyright (C) 2000 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
static GList *item_list = NULL;
static GList *item2del_list = NULL;
static gboolean board_paused = TRUE;
static GcomprisBoard *gcomprisBoard = NULL;
static gint dummy_id = 0;
static gint drop_items_id = 0;
static GnomeCanvasItem *planeitem = NULL;
static gint plane_x, plane_y;
static gint planespeed_x, planespeed_y;
#define MAXSPEED 7
/* These are the index for managing the game rule */
static gint plane_target, plane_last_target;
typedef struct {
gint number;
GnomeCanvasItem *rootitem;
} CloudItem;
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 gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
static GnomeCanvasItem *planegame_create_item(GnomeCanvasGroup *parent);
static gint planegame_drop_items (GtkWidget *widget, gpointer data);
static gint planegame_move_items (GtkWidget *widget, gpointer data);
static void planegame_destroy_item(CloudItem *clouditem);
static void planegame_destroy_items(void);
static void planegame_destroy_all_items(void);
static void setup_item(GnomeCanvasItem *item);
static void planegame_next_level(void);
static guint32 fallSpeed = 0;
static double speed = 0.0;
static double imageZoom = 0.0;
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
N_("Numbers in Order"),
N_("Move the plane to catch the clouds in the correct order"),
"Bruno Coudoin ",
NULL,
NULL,
NULL,
NULL,
start_board,
pause_board,
end_board,
is_our_board,
key_press,
NULL,
set_level,
NULL,
NULL,
NULL,
NULL
};
/*
* Main entry point mandatory for each Gcompris's game
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO(planegame)
/*
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
*
*/
static void pause_board (gboolean pause)
{
if(gcomprisBoard==NULL)
return;
if(pause)
{
if (dummy_id) {
gtk_timeout_remove (dummy_id);
dummy_id = 0;
}
if (drop_items_id) {
gtk_timeout_remove (drop_items_id);
drop_items_id = 0;
}
}
else
{
if(!drop_items_id) {
drop_items_id = gtk_timeout_add (1000,
(GtkFunction) planegame_drop_items, NULL);
}
if(!dummy_id) {
dummy_id = gtk_timeout_add (1000, (GtkFunction) planegame_move_items, NULL);
}
}
board_paused = pause;
}
/*
*/
static void start_board (GcomprisBoard *agcomprisBoard)
{
if(agcomprisBoard!=NULL)
{
gcomprisBoard=agcomprisBoard;
/* disable im_context */
gcomprisBoard->disable_im_context = TRUE;
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), "opt/scenery3_background.png");
/* set initial values for this level */
gcomprisBoard->level = 1;
gcomprisBoard->maxlevel = 2;
gc_bar_set(GC_BAR_LEVEL);
planegame_next_level();
pause_board(FALSE);
}
}
static void
end_board ()
{
if(gcomprisBoard!=NULL)
{
pause_board(TRUE);
gc_score_end();
planegame_destroy_all_items();
gcomprisBoard->level = 1; // Restart this game to zero
}
gcomprisBoard = NULL;
}
static void
set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
planegame_next_level();
}
}
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
{
if(board_paused || !gcomprisBoard)
return FALSE;
/* Add some filter for control and shift key */
switch (keyval)
{
/* Avoid all this keys to be interpreted by this game */
case GDK_Shift_L:
case GDK_Shift_R:
case GDK_Control_L:
case GDK_Control_R:
case GDK_Caps_Lock:
case GDK_Shift_Lock:
case GDK_Meta_L:
case GDK_Meta_R:
case GDK_Alt_L:
case GDK_Alt_R:
case GDK_Super_L:
case GDK_Super_R:
case GDK_Hyper_L:
case GDK_Hyper_R:
case GDK_Num_Lock:
return FALSE;
case GDK_KP_Enter:
case GDK_Return:
return TRUE;
case GDK_Right:
if(planespeed_x < MAXSPEED)
planespeed_x++;
return TRUE;
case GDK_Left:
if(planespeed_x > -MAXSPEED)
planespeed_x--;
return TRUE;
case GDK_Up:
if(planespeed_y > -MAXSPEED)
planespeed_y--;
return TRUE;
case GDK_Down:
if(planespeed_y < MAXSPEED)
planespeed_y++;
return TRUE;
}
return TRUE;
}
gboolean
is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_strcasecmp(gcomprisBoard->type, "planegame")==0)
{
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void planegame_next_level()
{
GdkPixbuf *pixmap = NULL;
gc_bar_set_level(gcomprisBoard);
planegame_destroy_all_items();
/* Try the next level */
speed=100+(40/(gcomprisBoard->level));
fallSpeed=10000-gcomprisBoard->level*200;
/* Make the images tend to 0.5 ratio */
imageZoom=0.3+(0.5/(gcomprisBoard->level));
/* Setup and Display the plane */
planespeed_y = 0;
planespeed_x = 0;
pixmap = gc_pixmap_load("gcompris/misc/tuxhelico.png");
plane_x = 50;
plane_y = 300;
planeitem = gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
gnome_canvas_pixbuf_get_type (),
"pixbuf", pixmap,
"x", (double) plane_x,
"y", (double) plane_y,
"width", (double) gdk_pixbuf_get_width(pixmap)*imageZoom,
"height", (double) gdk_pixbuf_get_height(pixmap)*imageZoom,
"width_set", TRUE,
"height_set", TRUE,
NULL);
gdk_pixbuf_unref(pixmap);
/* Game rules */
plane_target = 1;
plane_last_target = 10;
gcomprisBoard->number_of_sublevel=plane_last_target;
gcomprisBoard->sublevel=plane_target;
if(gcomprisBoard->level>1)
{
/* No scoring after level 1 */
gc_score_end();
}
else
{
gc_score_start(SCORESTYLE_NOTE,
gcomprisBoard->width - 220,
gcomprisBoard->height - 50,
gcomprisBoard->number_of_sublevel);
gc_score_set(gcomprisBoard->sublevel);
}
}
#define ISIN(x1, y1, px1, py1, px2, py2) (x1>px1 && x1py1 && y2rootitem;
gnome_canvas_item_get_bounds(planeitem, &px1, &py1, &px2, &py2);
gnome_canvas_item_get_bounds(item, &x1, &y1, &x2, &y2);
if(
ISIN(x1, y1, px1, py1, px2, py2) ||
ISIN(x2, y1, px1, py1, px2, py2) ||
ISIN(x1, y2, px1, py1, px2, py2) ||
ISIN(x2, y2, px1, py1, px2, py2)
)
{
if(plane_target == clouditem->number)
{
gc_sound_play_ogg ("sounds/gobble.wav", NULL);
item2del_list = g_list_append (item2del_list, clouditem);
plane_target++;
if(gcomprisBoard->level==1)
{
gc_score_set(plane_target);
}
if(plane_target==plane_last_target)
{
/* Try the next level */
gcomprisBoard->level++;
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
return;
}
planegame_next_level();
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
}
}
}
}
/* Move the plane */
static void planegame_move_plane(GnomeCanvasItem *item)
{
if(plane_x>gcomprisBoard->width-150 && planespeed_x>0)
planespeed_x=0;
if(plane_x<0 && planespeed_x<0)
planespeed_x=0;
if(plane_y>gcomprisBoard->height-50 && planespeed_y>0)
planespeed_y=0;
if(plane_y<10 && planespeed_y<0)
planespeed_y=0;
gnome_canvas_item_move(item, (double)planespeed_x, (double)planespeed_y);
plane_x+=planespeed_x;
plane_y+=planespeed_y;
}
static void planegame_move_item(CloudItem *clouditem)
{
double x1, y1, x2, y2;
GnomeCanvasItem *item = clouditem->rootitem;
gnome_canvas_item_move(item, -2.0, 0.0);
gnome_canvas_item_get_bounds (item,
&x1,
&y1,
&x2,
&y2);
if(x2<0) {
item2del_list = g_list_append (item2del_list, clouditem);
}
}
static void planegame_destroy_item(CloudItem *clouditem)
{
GnomeCanvasItem *item = clouditem->rootitem;
item_list = g_list_remove (item_list, clouditem);
item2del_list = g_list_remove (item2del_list, clouditem);
gtk_object_destroy (GTK_OBJECT(item));
g_free(clouditem);
}
/* Destroy items that falls out of the canvas */
static void planegame_destroy_items()
{
CloudItem *clouditem;
while(g_list_length(item2del_list)>0)
{
clouditem = g_list_nth_data(item2del_list, 0);
planegame_destroy_item(clouditem);
}
}
/* Destroy all the items */
static void planegame_destroy_all_items()
{
CloudItem *clouditem;
while(g_list_length(item_list)>0)
{
clouditem = g_list_nth_data(item_list, 0);
planegame_destroy_item(clouditem);
}
if(planeitem)
{
gtk_object_destroy (GTK_OBJECT(planeitem));
planeitem = NULL;
}
}
/*
* This does the moves of the game items on the play canvas
*
*/
static gint planegame_move_items (GtkWidget *widget, gpointer data)
{
g_list_foreach (item_list, (GFunc) planegame_move_item, NULL);
g_list_foreach (item_list, (GFunc) planegame_cloud_colision, NULL);
/* Destroy items that falls out of the canvas */
planegame_destroy_items();
/* move the plane */
planegame_move_plane(planeitem);
dummy_id = gtk_timeout_add (speed,
(GtkFunction) planegame_move_items, NULL);
return(FALSE);
}
static GnomeCanvasItem *planegame_create_item(GnomeCanvasGroup *parent)
{
GdkPixbuf *pixmap = NULL;
GnomeCanvasItem *itemgroup;
char *number = NULL;
int i, min;
CloudItem *clouditem;
/* Random cloud number */
if(g_random_int()%2==0)
{
/* Put the target */
i = plane_target;
}
else
{
min = MAX(1, plane_target - 1);
i = min + g_random_int()%(plane_target - min + 3);
}
number = g_strdup_printf("%d", i);
pixmap = gc_pixmap_load("gcompris/misc/cloud.png");
itemgroup = \
gnome_canvas_item_new (parent,
gnome_canvas_group_get_type (),
"x", (double) gcomprisBoard->width,
"y", (double)(g_random_int()%(gcomprisBoard->height-
(guint)(gdk_pixbuf_get_height(pixmap)*
imageZoom))),
NULL);
gnome_canvas_item_new (GNOME_CANVAS_GROUP(itemgroup),
gnome_canvas_pixbuf_get_type (),
"pixbuf", pixmap,
"x", (double) -gdk_pixbuf_get_width(pixmap)*imageZoom/2,
"y", (double) -gdk_pixbuf_get_height(pixmap)*imageZoom/2,
"width", (double) gdk_pixbuf_get_width(pixmap)*imageZoom,
"height", (double) gdk_pixbuf_get_height(pixmap)*imageZoom,
"width_set", TRUE,
"height_set", TRUE,
NULL);
gdk_pixbuf_unref(pixmap);
gnome_canvas_item_new (GNOME_CANVAS_GROUP(itemgroup),
gnome_canvas_text_get_type (),
"text", number,
"font", gc_skin_font_board_big,
"x", (double) 0,
"y", (double) 0,
"fill_color", "red",
NULL);
/* The plane is always on top */
gnome_canvas_item_raise_to_top(planeitem);
clouditem = g_malloc(sizeof(CloudItem));
clouditem->rootitem = itemgroup;
clouditem->number = i;
item_list = g_list_append (item_list, clouditem);
g_free (number);
return (itemgroup);
}
static void planegame_add_new_item()
{
setup_item (planegame_create_item(gnome_canvas_root(gcomprisBoard->canvas)));
}
/*
* This is called on a low frequency and is used to drop new items
*
*/
static gint planegame_drop_items (GtkWidget *widget, gpointer data)
{
planegame_add_new_item();
drop_items_id = gtk_timeout_add (fallSpeed,
(GtkFunction) planegame_drop_items, NULL);
return (FALSE);
}
static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
static double x, y;
double new_x, new_y;
GdkCursor *fleur;
static int dragging;
double item_x, item_y;
if(!gcomprisBoard)
return FALSE;
item_x = event->button.x;
item_y = event->button.y;
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
switch (event->type)
{
case GDK_BUTTON_PRESS:
switch(event->button.button)
{
case 1:
if (event->button.state & GDK_SHIFT_MASK)
{
x = item_x;
y = item_y;
fleur = gdk_cursor_new(GDK_FLEUR);
gc_canvas_item_grab(item,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
fleur,
event->button.time);
gdk_cursor_destroy(fleur);
dragging = TRUE;
}
break;
default:
break;
}
break;
case GDK_MOTION_NOTIFY:
if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
{
new_x = item_x;
new_y = item_y;
gnome_canvas_item_move(item, new_x - x, new_y - y);
x = new_x;
y = new_y;
}
break;
case GDK_BUTTON_RELEASE:
if(dragging)
{
gc_canvas_item_ungrab(item, event->button.time);
dragging = FALSE;
}
break;
default:
break;
}
return FALSE;
}
static void
setup_item(GnomeCanvasItem *item)
{
gtk_signal_connect(GTK_OBJECT(item), "event",
(GtkSignalFunc) item_event,
NULL);
}