1
/* gcompris - railroad.c
3
* Copyright (C) 2001 Pascal Georges
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, see <http://www.gnu.org/licenses/>.
19
#include "gcompris/gcompris.h"
21
#define SOUNDLISTFILE PACKAGE
23
static GcomprisBoard *gcomprisBoard = NULL;
24
static gboolean board_paused = TRUE;
26
static void start_board (GcomprisBoard *agcomprisBoard);
27
static void pause_board (gboolean pause);
28
static void end_board (void);
29
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
30
static void set_level (guint level);
32
static void process_ok(void);
33
static void game_won(void);
34
static void repeat(void);
35
static void animate_model(void);
36
static gboolean animate_step(void);
40
#define NUMBER_OF_SUBLEVELS 3
41
#define NUMBER_OF_LEVELS 3
42
#define MODEL_MAX_SIZE NUMBER_OF_LEVELS+1
44
static const int line[] = { 100,180,260,340, 420, 500};
45
static gboolean animation_pending;
46
static gint animation_count = 0;
48
static GnomeCanvasGroup *boardRootItem = NULL;
50
static GnomeCanvasGroup *allwagonsRootItem = NULL;
51
static GnomeCanvasGroup *modelRootItem = NULL;
52
static GnomeCanvasGroup *answerRootItem = NULL;
54
static GList * listPixmapEngines = NULL;
55
static GList * listPixmapWagons = NULL;
57
// ==========================================
58
// In all the lists below, 0 is the LEFTmost vehicle|
59
// ==========================================
60
// contains the list of vehicles to be found.
61
static GnomeCanvasItem *item_model[MODEL_MAX_SIZE];
62
// contains the list of vehicles proposed by child.
63
static GList *item_answer_list = NULL;
64
// contains the list of vehicles proposed by child.
65
static GList *int_answer_list = NULL;
66
// contains the list of vehicles to be found
67
static GList *int_model_list = NULL;
69
static int model_size = 0;
72
static GnomeCanvasItem *railroad_create_item(GnomeCanvasGroup *parent);
73
static void railroad_destroy_all_items(void);
74
static void railroad_next_level(void);
75
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
76
static gint answer_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
78
static void reposition_model(void);
79
static void reposition_answer(void);
81
// helper function because g_list_free does not actually reset a list
82
static GList * reset_list(GList * list);
83
static void reset_all_lists(void);
85
/* Description of this plugin */
86
static BoardPlugin menu_bp =
91
N_("Build a train according to the model"),
92
"Pascal Georges pascal.georges1@free.fr>",
111
* Main entry point mandatory for each Gcompris's game
112
* ---------------------------------------------------
116
GET_BPLUGIN_INFO(railroad)
119
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
122
static void pause_board (gboolean pause)
124
if(gcomprisBoard==NULL)
127
if(gamewon == TRUE && pause == FALSE) /* the game is won */
132
board_paused = pause;
135
/* ======================================= */
136
static void start_board (GcomprisBoard *agcomprisBoard)
140
GdkPixbuf *pixmap = NULL;
142
if(agcomprisBoard!=NULL)
144
gcomprisBoard=agcomprisBoard;
145
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
146
"opt/railroad-bg.png");
148
for (i=0; i<ENGINES; i++) {
149
str = g_strdup_printf("railroad/loco%d.png", i+1);
150
pixmap = gc_pixmap_load(str);
151
listPixmapEngines = g_list_append(listPixmapEngines, pixmap);
155
for (i=0; i<WAGONS; i++) {
156
str = g_strdup_printf("railroad/wagon%d.png", i+1);
157
pixmap = gc_pixmap_load(str);
158
listPixmapWagons = g_list_append(listPixmapWagons, pixmap);
162
animation_pending = FALSE;
163
gcomprisBoard->level=1;
164
gcomprisBoard->maxlevel=NUMBER_OF_LEVELS;
165
gcomprisBoard->sublevel=1;
166
gcomprisBoard->number_of_sublevel=NUMBER_OF_SUBLEVELS; /* Go to next level after this number of 'play' */
167
gc_score_start(SCORESTYLE_NOTE,
168
gcomprisBoard->width - 220,
169
gcomprisBoard->height - 50,
170
gcomprisBoard->number_of_sublevel);
173
str = gc_skin_image_get("button_reload.png");
174
pixmap = gc_pixmap_load(str);
177
gc_bar_set_repeat_icon(pixmap);
178
gdk_pixbuf_unref(pixmap);
179
gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK|GC_BAR_REPEAT_ICON);
181
gc_bar_set(GC_BAR_LEVEL|GC_BAR_OK|GC_BAR_REPEAT);
184
railroad_next_level();
190
/* ======================================= */
191
static void end_board ()
193
GdkPixbuf * pixmap = NULL;
194
// If we don't end animation, there may be a segfault if leaving while the animation is pending
196
gtk_timeout_remove (timer_id);
200
if(gcomprisBoard!=NULL)
204
railroad_destroy_all_items();
206
while(g_list_length(listPixmapEngines)>0) {
207
pixmap = g_list_nth_data(listPixmapEngines, 0);
208
listPixmapEngines = g_list_remove (listPixmapEngines, pixmap);
209
gdk_pixbuf_unref(pixmap);
212
while(g_list_length(listPixmapWagons)>0) {
213
pixmap = g_list_nth_data(listPixmapWagons, 0);
214
listPixmapWagons = g_list_remove (listPixmapWagons, pixmap);
215
gdk_pixbuf_unref(pixmap);
219
gcomprisBoard = NULL;
222
/* ======================================= */
223
static void set_level (guint level)
226
if(gcomprisBoard!=NULL)
228
gcomprisBoard->level=level;
229
gcomprisBoard->sublevel=1;
230
railroad_next_level();
233
/* ======================================= */
234
gboolean is_our_board (GcomprisBoard *gcomprisBoard)
238
if(g_strcasecmp(gcomprisBoard->type, "railroad")==0)
240
/* Set the plugin entry */
241
gcomprisBoard->plugin=&menu_bp;
249
/* ======================================= */
250
static void repeat ()
252
if(gcomprisBoard!=NULL && !animation_pending) {
253
gnome_canvas_item_hide(GNOME_CANVAS_ITEM(answerRootItem));
254
gnome_canvas_item_show(GNOME_CANVAS_ITEM(modelRootItem));
255
gnome_canvas_item_hide(GNOME_CANVAS_ITEM(allwagonsRootItem));
261
/* ==================================== */
262
/* set initial values for the next level */
263
static void railroad_next_level()
265
gc_bar_set_level(gcomprisBoard);
268
// I have big troubles with the GList API : the worst I have ever seen !
269
g_assert(g_list_length(item_answer_list) == 0 && g_list_length(int_answer_list) == 0 && g_list_length(int_model_list) == 0);
273
railroad_destroy_all_items();
275
gc_score_set(gcomprisBoard->sublevel);
277
/* Try the next level */
278
railroad_create_item(gnome_canvas_root(gcomprisBoard->canvas));
280
/* ==================================== */
281
/* Destroy all the items */
282
static void railroad_destroy_all_items()
284
if(boardRootItem!=NULL)
285
gtk_object_destroy (GTK_OBJECT(boardRootItem));
287
boardRootItem = NULL;
289
/* ==================================== */
290
static GnomeCanvasItem *railroad_create_item(GnomeCanvasGroup *parent)
292
int xOffset = 0, yOffset = 0;
294
GdkPixbuf * pixmap = NULL;
295
GnomeCanvasItem *item;
297
boardRootItem = GNOME_CANVAS_GROUP(
298
gnome_canvas_item_new (parent,
299
gnome_canvas_group_get_type (),
303
/* Create a root group for the answer */
304
answerRootItem = GNOME_CANVAS_GROUP(
305
gnome_canvas_item_new (boardRootItem,
306
gnome_canvas_group_get_type (),
311
// Create the vehicules
312
allwagonsRootItem = GNOME_CANVAS_GROUP(
313
gnome_canvas_item_new (boardRootItem,
314
gnome_canvas_group_get_type (),
319
for (i=0; i<ENGINES+WAGONS; i++) {
321
pixmap = g_list_nth_data(listPixmapEngines, i);
323
pixmap = g_list_nth_data(listPixmapWagons, i-ENGINES);
325
if ( (xOffset + gdk_pixbuf_get_width(pixmap)) >= gcomprisBoard->width) {
329
yOffset = line[l] - gdk_pixbuf_get_height(pixmap);
331
item = gnome_canvas_item_new (allwagonsRootItem,
332
gnome_canvas_pixbuf_get_type (),
334
"x", (double) xOffset,
335
"y", (double) yOffset,
337
xOffset += gdk_pixbuf_get_width(pixmap);
339
gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event,
344
gnome_canvas_item_hide(GNOME_CANVAS_ITEM(allwagonsRootItem));
346
// construct the model to be recognized
347
modelRootItem = GNOME_CANVAS_GROUP(
348
gnome_canvas_item_new (boardRootItem,
349
gnome_canvas_group_get_type (),
356
model_size = gcomprisBoard->level +1; // engine + cars
357
// First the cars, depending of the level
358
for (i=0; i<model_size-1; i++) {
359
r = g_random_int_range( 0, WAGONS);
360
g_assert( r >=0 && r < WAGONS);
361
// keep track of the answer
362
int_model_list = g_list_append(int_model_list, GINT_TO_POINTER(r+ENGINES));
363
pixmap = g_list_nth_data(listPixmapWagons, r);
364
g_assert(i >= 0 && i<MODEL_MAX_SIZE);
365
item_model[i] =gnome_canvas_item_new (modelRootItem,
366
gnome_canvas_pixbuf_get_type (),
368
"x", (double) xOffset,
369
"y", (double) yOffset - gdk_pixbuf_get_height(pixmap),
371
xOffset += gdk_pixbuf_get_width(pixmap);
375
r = g_random_int_range( 0, ENGINES);
376
g_assert( r >=0 && r < ENGINES);
377
// keep track of the answer
378
int_model_list = g_list_append(int_model_list, GINT_TO_POINTER(r));
379
pixmap = g_list_nth_data(listPixmapEngines, r);
380
item_model[model_size-1] =gnome_canvas_item_new (modelRootItem,
381
gnome_canvas_pixbuf_get_type (),
383
"x", (double) xOffset,
384
"y", (double) yOffset - gdk_pixbuf_get_height(pixmap),
391
/* ==================================== */
392
static void game_won()
394
gcomprisBoard->sublevel++;
396
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
397
/* Try the next level */
398
gcomprisBoard->sublevel=1;
399
gcomprisBoard->level++;
400
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
401
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
404
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
406
railroad_next_level();
409
/* ==================================== */
410
static void process_ok()
417
g_warning("l answer = %d\tl model = %d\n", g_list_length(int_answer_list), g_list_length(int_model_list));
418
if (g_list_length(int_answer_list) != g_list_length(int_model_list))
421
for (i=0; i<g_list_length(int_answer_list); i++) {
422
if ( GPOINTER_TO_INT(g_list_nth_data(int_answer_list,i)) != GPOINTER_TO_INT(g_list_nth_data(int_model_list,i))) {
423
printf("pour i= %d --> diff�rent\n", i);
429
g_warning("answer:\n");
430
for (i=0; i<g_list_length(int_answer_list); i++)
431
g_warning(" i = \t%d val = \t%d\n", i, GPOINTER_TO_INT(g_list_nth_data(int_answer_list,i)) );
432
g_warning("model:\n");
433
for (i=0; i<g_list_length(int_model_list); i++)
434
g_warning(" i = \t%d val = \t%d\n", i, GPOINTER_TO_INT(g_list_nth_data(int_model_list,i)) );
436
gc_bonus_display(gamewon, GC_BONUS_FLOWER);
438
/* ==================================== */
439
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data) {
440
double item_x, item_y;
442
GdkPixbuf * pixmap = NULL;
444
GnomeCanvasItem * local_item;
445
double dx1, dy1, dx2, dy2;
446
item_number = GPOINTER_TO_INT(data);
448
// we don't allow any input until train is gone
449
if (animation_pending)
452
item_x = event->button.x;
453
item_y = event->button.y;
454
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
461
case GDK_BUTTON_PRESS:
462
g_warning("GDK_BUTTON_PRESS item %d\tlength answer = %d\n",item_number,g_list_length(item_answer_list));
463
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
465
for (i=0; i<g_list_length(item_answer_list); i++) {
466
gnome_canvas_item_get_bounds(g_list_nth_data(item_answer_list,i), &dx1, &dy1, &dx2, &dy2);
469
if (item_number < ENGINES)
470
pixmap = g_list_nth_data(listPixmapEngines, item_number);
472
pixmap = g_list_nth_data(listPixmapWagons, item_number-ENGINES);
474
local_item =gnome_canvas_item_new (answerRootItem,
475
gnome_canvas_pixbuf_get_type (),
477
"x", (double) xOffset,
478
"y", (double) line[0] - gdk_pixbuf_get_height(pixmap),
480
item_answer_list = g_list_append(item_answer_list, local_item);
481
int_answer_list = g_list_append(int_answer_list,GINT_TO_POINTER(item_number));
482
// printf("added %d to int_answer_list\n", item_number);
483
gtk_signal_connect(GTK_OBJECT(local_item), "event", (GtkSignalFunc) answer_event, GINT_TO_POINTER( g_list_length(item_answer_list)-1 ));
491
/* ==================================== */
492
/* Used to delete a vehicule at the top (the proposed answer) */
493
static gint answer_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data) {
494
double item_x, item_y;
496
GnomeCanvasItem *local_item;
497
item_number = GPOINTER_TO_INT(data);
498
// we don't allow any input until train is gone
499
if (animation_pending)
502
item_x = event->button.x;
503
item_y = event->button.y;
504
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
511
case GDK_BUTTON_PRESS:
512
gc_sound_play_ogg ("sounds/smudge.wav", NULL);
513
g_warning("Deleting %d\n",item_number);
514
local_item = g_list_nth_data(item_answer_list,item_number);
515
item_answer_list = g_list_remove( item_answer_list, local_item );
516
// gtk_signal_disconnect(GTK_OBJECT(local_item), (GtkSignalFunc) answer_event, NULL);
517
gtk_object_destroy (GTK_OBJECT(local_item));
518
int_answer_list = g_list_remove(int_answer_list, g_list_nth_data(int_answer_list, item_number) );
520
// setup the signals for the cars at the right side of the deleted object
521
for (i=item_number; i<g_list_length(item_answer_list); i++) {
522
local_item = g_list_nth_data(item_answer_list, i);
523
gtk_signal_disconnect_by_func(GTK_OBJECT(local_item), (GtkSignalFunc) answer_event, GINT_TO_POINTER( i+1 ));
524
gtk_signal_connect(GTK_OBJECT(local_item),"event", (GtkSignalFunc) answer_event, GINT_TO_POINTER( i ));
533
/* ==================================== */
534
static void reposition_answer() {
535
double dx1, dy1, dx2, dy2;
538
GnomeCanvasItem * item = NULL;
543
g_warning("+++ reposition_answer\n");
544
for (i=0; i<g_list_length(item_answer_list); i++) {
545
item = g_list_nth_data(item_answer_list,i);
546
gnome_canvas_item_get_bounds(item, &dx1, &dy1, &dx2, &dy2);
547
gnome_canvas_item_move(item, xOffset-dx1, line[0]-dy2);
551
/* ==================================== */
552
static void reposition_model() {
553
double dx1, dy1, dx2, dy2;
556
GnomeCanvasItem * item = NULL;
561
g_warning("+++ reposition_model\n");
562
gnome_canvas_item_move(GNOME_CANVAS_ITEM(modelRootItem), 0, 0);
563
for (i=0; i<model_size; i++) {
564
item = item_model[i];
565
gnome_canvas_item_get_bounds(item, &dx1, &dy1, &dx2, &dy2);
566
gnome_canvas_item_move(item, xOffset-dx1, line[0]-dy2);
571
/* ==================================== */
572
static gboolean animate_step() {
575
// this defines how the train waits before start
576
#define MODEL_PAUSE 30
577
// printf("+++animate_step %d \n",animation_count);
587
if (animation_count < MODEL_PAUSE)
590
if (animation_count >= 160+MODEL_PAUSE) {
592
gtk_timeout_remove (timer_id);
595
animation_pending = FALSE;
596
gnome_canvas_item_hide(GNOME_CANVAS_ITEM(modelRootItem));
597
/* Move back the model to its 0 position */
598
gnome_canvas_item_set(GNOME_CANVAS_ITEM(modelRootItem),
602
gnome_canvas_item_show(GNOME_CANVAS_ITEM(allwagonsRootItem));
603
gnome_canvas_item_show(GNOME_CANVAS_ITEM(answerRootItem));
607
step = (double) (animation_count-MODEL_PAUSE) / 50.0;
610
gnome_canvas_item_move(GNOME_CANVAS_ITEM(modelRootItem), step, 0.0);
614
/* ==================================== */
615
static void animate_model() {
616
animation_pending = TRUE;
619
gc_sound_play_ogg( "sounds/train.wav", NULL );
621
// warning : if timeout is too low, the model will not be displayed
622
timer_id = gtk_timeout_add (100, (GtkFunction) animate_step, NULL);
624
/* ==================================== */
625
static void reset_all_lists(void) {
626
GnomeCanvasItem *item;
628
int_model_list = reset_list(int_model_list);
629
int_answer_list = reset_list(int_answer_list);
631
while(g_list_length(item_answer_list)>0) {
632
item = g_list_nth_data(item_answer_list, 0);
633
item_answer_list = g_list_remove (item_answer_list, item);
635
// gtk_object_destroy (GTK_OBJECT(item));
639
/* ==================================== */
640
static GList * reset_list(GList * list) {
641
while (g_list_length(list) > 0)
642
list = g_list_remove(list, g_list_nth_data(list,0));