1
/* gcompris - canal_lock.c
3
* Copyright (C) 2001 Bruno Coudoin
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
#define ANIMATE_SPEED 30
25
#define CANAL_COLOR 0x0010FFFF
26
#define LOCK_COLOR 0x8cc679FF
27
#define LOCK_COLOR_H 0x71A65FFF
28
#define CANALLOCK_COLOR 0xd1cd0cFF
29
#define CANALLOCK_COLOR_H 0xf1ed1cFF
30
#define GROUND_COLOR 0x9b5f5aFF
32
#define LEFT_CANAL_HEIGHT 100
33
#define LEFT_CANAL_WIDTH 325
34
#define RIGHT_CANAL_HEIGHT 200
35
#define RIGHT_CANAL_WIDTH 325
36
#define MIDDLE_CANAL_WIDTH (BOARDWIDTH - RIGHT_CANAL_WIDTH - LEFT_CANAL_WIDTH)
39
#define LOCK_HEIGHT_MAX (RIGHT_CANAL_HEIGHT + 40)
40
#define LOCK_HEIGHT_MIN 60
42
#define SUBCANAL_BASE_LINE (BASE_LINE + 80)
43
#define SUBCANAL_HEIGHT 40
45
#define CANALLOCK_WIDTH 30
46
#define CANALLOCK_HEIGHT_MAX SUBCANAL_HEIGHT
47
#define CANALLOCK_HEIGHT_MIN 15
49
static GcomprisBoard *gcomprisBoard = NULL;
50
static gboolean board_paused = TRUE;
52
static void start_board (GcomprisBoard *agcomprisBoard);
53
static void pause_board (gboolean pause);
54
static void end_board (void);
55
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
56
static void set_level (guint level);
58
static void game_won(void);
61
static gboolean animation;
63
static GnomeCanvasGroup *boardRootItem = NULL;
65
static GnomeCanvasItem *lock_left_item = NULL;
66
static GnomeCanvasItem *lock_right_item = NULL;
68
static GnomeCanvasItem *canallock_left_item = NULL;
69
static GnomeCanvasItem *canallock_right_item = NULL;
71
static GnomeCanvasItem *canal_left_item = NULL;
72
static GnomeCanvasItem *canal_middle_item = NULL;
73
static GnomeCanvasItem *canal_right_item = NULL;
75
static GnomeCanvasItem *tuxboat_item = NULL;
76
static double tuxboat_width;
78
#define BOAT_POS_LEFT 1
79
#define BOAT_POS_MIDDLE 2
80
#define BOAT_POS_RIGHT 3
82
static guint boat_position;
83
static gboolean right_way;
84
static gboolean lock_left_up;
85
static gboolean lock_right_up;
86
static gboolean lock_water_low;
87
static gboolean canallock_left_up;
88
static gboolean canallock_right_up;
90
static double timer_item_x1, timer_item_y1, timer_item_x2, timer_item_y2;
91
static double timer_item_limit_y, timer_item_limit_x;
92
static GnomeCanvasItem *timer_item;
93
static gint timer_step_y1, timer_step_x1;
95
static GnomeCanvasItem *canal_lock_create_item(GnomeCanvasGroup *parent);
96
static void canal_lock_destroy_all_items(void);
97
static void canal_lock_next_level(void);
98
static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
99
static gboolean animate_step();
100
static void update_water();
101
static void toggle_lock(GnomeCanvasItem *item);
103
/* Description of this plugin */
104
static BoardPlugin menu_bp =
108
"Operate a canal lock",
109
"Tux is in trouble in his ship. He needs to take it through a lock",
110
"Bruno Coudoin <bruno.coudoin@free.fr>",
129
* Main entry point mandatory for each Gcompris's game
130
* ---------------------------------------------------
134
GET_BPLUGIN_INFO(canal_lock)
137
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
140
static void pause_board (gboolean pause)
142
if(gcomprisBoard==NULL)
145
if(gamewon == TRUE && pause == FALSE) /* the game is won */
150
board_paused = pause;
155
static void start_board (GcomprisBoard *agcomprisBoard)
158
if(agcomprisBoard!=NULL)
160
gcomprisBoard=agcomprisBoard;
161
gcomprisBoard->level=1;
162
gcomprisBoard->maxlevel=2;
163
gcomprisBoard->sublevel=1;
164
gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
166
canal_lock_next_level();
176
/* ======================================= */
177
static void end_board ()
179
// If we don't stop animation, there may be a segfault if leaving while the animation is running
181
gtk_timeout_remove (timer_id);
186
if(gcomprisBoard!=NULL)
189
canal_lock_destroy_all_items();
191
gcomprisBoard = NULL;
194
/* ======================================= */
195
static void set_level (guint level)
198
if(gcomprisBoard!=NULL)
200
gcomprisBoard->level=level;
201
gcomprisBoard->sublevel=1;
202
canal_lock_next_level();
205
/* ======================================= */
206
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
210
if(g_strcasecmp(gcomprisBoard->type, "canal_lock")==0)
212
/* Set the plugin entry */
213
gcomprisBoard->plugin=&menu_bp;
221
/*-------------------------------------------------------------------------------*/
222
/*-------------------------------------------------------------------------------*/
223
/* set initial values for the next level */
224
static void canal_lock_next_level()
227
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
228
"canal_lock/canal_lock_bg.png");
230
gc_bar_set_level(gcomprisBoard);
232
canal_lock_destroy_all_items();
235
/* Original state of the lock */
236
boat_position = BOAT_POS_LEFT;
239
lock_right_up = TRUE;
240
lock_water_low = TRUE;
241
canallock_left_up = TRUE;
242
canallock_right_up = TRUE;
244
/* Try the next level */
245
canal_lock_create_item(gnome_canvas_root(gcomprisBoard->canvas));
247
/* ==================================== */
248
/* Destroy all the items */
249
static void canal_lock_destroy_all_items()
251
if(boardRootItem!=NULL)
252
gtk_object_destroy (GTK_OBJECT(boardRootItem));
254
boardRootItem = NULL;
256
/* ==================================== */
257
static GnomeCanvasItem *canal_lock_create_item(GnomeCanvasGroup *parent)
259
GdkPixbuf *pixmap = NULL;
261
boardRootItem = GNOME_CANVAS_GROUP(
262
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
263
gnome_canvas_group_get_type (),
270
pixmap = gc_pixmap_load("gcompris/misc/tuxboat.png");
272
tuxboat_item = gnome_canvas_item_new (boardRootItem,
273
gnome_canvas_pixbuf_get_type (),
275
"x", (double) (LEFT_CANAL_WIDTH - gdk_pixbuf_get_width(pixmap)) / 2,
276
"y", (double) BASE_LINE - LEFT_CANAL_HEIGHT - gdk_pixbuf_get_height(pixmap)*0.9,
278
gtk_signal_connect(GTK_OBJECT(tuxboat_item), "event",
279
(GtkSignalFunc) item_event,
281
gtk_signal_connect(GTK_OBJECT(tuxboat_item), "event",
282
(GtkSignalFunc) gc_item_focus_event,
284
tuxboat_width = gdk_pixbuf_get_width(pixmap);
285
gdk_pixbuf_unref(pixmap);
287
/* This is the ground canal */
288
gnome_canvas_item_new (boardRootItem,
289
gnome_canvas_rect_get_type (),
291
"y1", (double) BASE_LINE,
292
"x2", (double) BOARDWIDTH,
293
"y2", (double) BOARDHEIGHT,
294
"fill_color_rgba", GROUND_COLOR,
295
"width_units", (double) 0,
298
/* This is the left canal */
299
canal_left_item = gnome_canvas_item_new (boardRootItem,
300
gnome_canvas_rect_get_type (),
302
"y1", (double) BASE_LINE - LEFT_CANAL_HEIGHT,
303
"x2", (double) LEFT_CANAL_WIDTH,
304
"y2", (double) BASE_LINE,
305
"fill_color_rgba", CANAL_COLOR,
306
"width_units", (double) 0,
309
/* This is the middle canal */
310
canal_middle_item = gnome_canvas_item_new (boardRootItem,
311
gnome_canvas_rect_get_type (),
312
"x1", (double) LEFT_CANAL_WIDTH,
313
"y1", (double) BASE_LINE - LEFT_CANAL_HEIGHT,
314
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH,
315
"y2", (double) BASE_LINE,
316
"fill_color_rgba", CANAL_COLOR,
317
"width_units", (double) 0,
320
/* This is the right canal */
321
canal_right_item = gnome_canvas_item_new (boardRootItem,
322
gnome_canvas_rect_get_type (),
323
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH,
324
"y1", (double) BASE_LINE - RIGHT_CANAL_HEIGHT,
325
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + RIGHT_CANAL_WIDTH,
326
"y2", (double) BASE_LINE,
327
"fill_color_rgba", CANAL_COLOR,
328
"width_units", (double) 0,
331
/* This is the left lock */
332
lock_left_item = gnome_canvas_item_new (boardRootItem,
333
gnome_canvas_rect_get_type (),
334
"x1", (double) LEFT_CANAL_WIDTH - LOCK_WIDTH / 2,
335
"y1", (double) BASE_LINE - LOCK_HEIGHT_MAX,
336
"x2", (double) LEFT_CANAL_WIDTH + LOCK_WIDTH / 2,
337
"y2", (double) BASE_LINE,
338
"fill_color_rgba", LOCK_COLOR,
339
"width_units", (double) 0,
341
gtk_signal_connect(GTK_OBJECT(lock_left_item), "event",
342
(GtkSignalFunc) item_event,
345
/* This is the right lock */
346
lock_right_item = gnome_canvas_item_new (boardRootItem,
347
gnome_canvas_rect_get_type (),
348
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH - LOCK_WIDTH / 2,
349
"y1", (double) BASE_LINE - LOCK_HEIGHT_MAX,
350
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + LOCK_WIDTH / 2,
351
"y2", (double) BASE_LINE,
352
"fill_color_rgba", LOCK_COLOR,
353
"width_units", (double) 0,
355
gtk_signal_connect(GTK_OBJECT(lock_right_item), "event",
356
(GtkSignalFunc) item_event,
359
/* This is the water conduit under the canal */
360
gnome_canvas_item_new (boardRootItem,
361
gnome_canvas_rect_get_type (),
362
"x1", (double) LEFT_CANAL_WIDTH/2,
363
"y1", (double) SUBCANAL_BASE_LINE - SUBCANAL_HEIGHT,
364
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + RIGHT_CANAL_WIDTH / 2 + SUBCANAL_HEIGHT,
365
"y2", (double) SUBCANAL_BASE_LINE,
366
"fill_color_rgba", CANAL_COLOR,
367
"width_units", (double) 0,
371
gnome_canvas_item_new (boardRootItem,
372
gnome_canvas_rect_get_type (),
373
"x1", (double) LEFT_CANAL_WIDTH/2,
374
"y1", (double) BASE_LINE,
375
"x2", (double) LEFT_CANAL_WIDTH/2 + SUBCANAL_HEIGHT,
376
"y2", (double) SUBCANAL_BASE_LINE,
377
"fill_color_rgba", CANAL_COLOR,
378
"width_units", (double) 0,
382
gnome_canvas_item_new (boardRootItem,
383
gnome_canvas_rect_get_type (),
384
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH/2 - SUBCANAL_HEIGHT/2,
385
"y1", (double) BASE_LINE,
386
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH/2 + SUBCANAL_HEIGHT/2,
387
"y2", (double) SUBCANAL_BASE_LINE,
388
"fill_color_rgba", CANAL_COLOR,
389
"width_units", (double) 0,
393
gnome_canvas_item_new (boardRootItem,
394
gnome_canvas_rect_get_type (),
395
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + RIGHT_CANAL_WIDTH/2,
396
"y1", (double) BASE_LINE,
397
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + RIGHT_CANAL_WIDTH/2 + SUBCANAL_HEIGHT,
398
"y2", (double) SUBCANAL_BASE_LINE,
399
"fill_color_rgba", CANAL_COLOR,
400
"width_units", (double) 0,
403
/* And to finish, the 2 canal locks */
404
canallock_left_item =
405
gnome_canvas_item_new (boardRootItem,
406
gnome_canvas_rect_get_type (),
407
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH * 0.1,
408
"y1", (double) SUBCANAL_BASE_LINE - SUBCANAL_HEIGHT,
409
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH * 0.1 + LOCK_WIDTH / 2,
410
"y2", (double) SUBCANAL_BASE_LINE,
411
"fill_color_rgba", CANALLOCK_COLOR,
412
"width_units", (double) 0,
414
gtk_signal_connect(GTK_OBJECT(canallock_left_item), "event",
415
(GtkSignalFunc) item_event,
418
canallock_right_item =
419
gnome_canvas_item_new (boardRootItem,
420
gnome_canvas_rect_get_type (),
421
"x1", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH * 0.9,
422
"y1", (double) SUBCANAL_BASE_LINE - SUBCANAL_HEIGHT,
423
"x2", (double) LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH * 0.9 + LOCK_WIDTH / 2,
424
"y2", (double) SUBCANAL_BASE_LINE,
425
"fill_color_rgba", CANALLOCK_COLOR,
426
"width_units", (double) 0,
428
gtk_signal_connect(GTK_OBJECT(canallock_right_item), "event",
429
(GtkSignalFunc) item_event,
436
/* ==================================== */
437
static void game_won()
439
gcomprisBoard->sublevel++;
441
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
442
/* Try the next level */
443
gcomprisBoard->sublevel=1;
444
gcomprisBoard->level++;
445
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
446
gc_bonus_end_display(GC_BOARD_FINISHED_RANDOM);
449
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
451
canal_lock_next_level();
454
/* ==================================== */
455
/* Move the boat to the next possible position */
456
static void move_boat()
459
/* If there is already an animation do nothing else set animation to avoid deadlock */
464
if(boat_position == BOAT_POS_LEFT && !lock_left_up)
466
boat_position = BOAT_POS_MIDDLE;
467
timer_item_limit_x = LEFT_CANAL_WIDTH + (MIDDLE_CANAL_WIDTH - tuxboat_width)/2;
470
else if(boat_position == BOAT_POS_MIDDLE && !lock_left_up)
472
boat_position = BOAT_POS_LEFT;
473
timer_item_limit_x = (LEFT_CANAL_WIDTH - tuxboat_width)/2;
476
else if(boat_position == BOAT_POS_MIDDLE && !lock_right_up)
478
boat_position = BOAT_POS_RIGHT;
479
timer_item_limit_x = LEFT_CANAL_WIDTH + MIDDLE_CANAL_WIDTH + (RIGHT_CANAL_WIDTH - tuxboat_width)/2;
482
else if(boat_position == BOAT_POS_RIGHT && !lock_right_up)
484
boat_position = BOAT_POS_MIDDLE;
485
timer_item_limit_x = LEFT_CANAL_WIDTH + (MIDDLE_CANAL_WIDTH - tuxboat_width)/2;
490
/* No possible move */
491
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
496
gc_sound_play_ogg ("sounds/eraser2.wav", NULL);
498
gnome_canvas_item_get_bounds(tuxboat_item, &timer_item_x1, &timer_item_y1,
499
&timer_item_x2, &timer_item_y2);
501
timer_item = tuxboat_item;
504
timer_id = gtk_timeout_add (ANIMATE_SPEED, (GtkFunction) animate_step, NULL);
507
/* ==================================== */
508
/* Update the water level if necessary */
509
static void update_water()
511
gboolean status = TRUE;
513
gint min = LEFT_CANAL_HEIGHT;
515
/* If there is already an animation do nothing else set animation to avoid deadlock */
520
if((!canallock_left_up && !lock_water_low) ||
521
(!canallock_right_up && lock_water_low))
523
status = !lock_water_low;
524
lock_water_low = !lock_water_low;
525
y1 = BASE_LINE - RIGHT_CANAL_HEIGHT;
529
/* The water level is correct */
534
gnome_canvas_item_get_bounds(canal_middle_item, &timer_item_x1, &timer_item_y1,
535
&timer_item_x2, &timer_item_y2);
537
timer_item = canal_middle_item;
538
timer_item_limit_y = (status ? timer_item_y2 - min :
540
timer_step_y1 = (status ? 2 : -2);
543
timer_id = gtk_timeout_add (ANIMATE_SPEED, (GtkFunction) animate_step, NULL);
546
/* ==================================== */
547
/* Toggle the given lock */
548
static void toggle_lock(GnomeCanvasItem *item)
550
gboolean status = TRUE;
553
guint animate_speed = 0;
555
/* If there is already an animation do nothing else set animation to avoid deadlock */
560
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
562
gnome_canvas_item_get_bounds(item, &timer_item_x1, &timer_item_y1,
563
&timer_item_x2, &timer_item_y2);
565
if(item == lock_left_item)
567
status = lock_left_up;
568
lock_left_up = !lock_left_up;
569
y1 = BASE_LINE - LOCK_HEIGHT_MAX;
570
min = LOCK_HEIGHT_MIN;
571
animate_speed = ANIMATE_SPEED;
573
else if(item == lock_right_item)
575
status = lock_right_up;
576
lock_right_up = !lock_right_up;
577
y1 = BASE_LINE - LOCK_HEIGHT_MAX;
578
min = LOCK_HEIGHT_MIN;
579
animate_speed = ANIMATE_SPEED;
581
else if(item == canallock_left_item)
583
status = canallock_left_up;
584
canallock_left_up = !canallock_left_up;
585
y1 = SUBCANAL_BASE_LINE - SUBCANAL_HEIGHT;
586
min = CANALLOCK_HEIGHT_MIN;
587
animate_speed = ANIMATE_SPEED;
589
else if(item == canallock_right_item)
591
status = canallock_right_up;
592
canallock_right_up = !canallock_right_up;
593
y1 = SUBCANAL_BASE_LINE - SUBCANAL_HEIGHT;
594
min = CANALLOCK_HEIGHT_MIN;
595
animate_speed = ANIMATE_SPEED;
599
timer_item_limit_y = (status ? timer_item_y2 - min :
601
timer_step_y1 = (status ? 2 : -2);
604
timer_id = gtk_timeout_add (animate_speed, (GtkFunction) animate_step, NULL);
609
/* ==================================== */
610
static gboolean animate_step()
616
timer_item_x1 += timer_step_x1;
617
timer_item_y1 += timer_step_y1;
619
if(GNOME_IS_CANVAS_PIXBUF(timer_item))
620
gnome_canvas_item_set(timer_item,
624
else if(GNOME_IS_CANVAS_RECT(timer_item))
625
gnome_canvas_item_set(timer_item,
630
/* Special case for raising/lowering the boat */
631
if(boat_position==BOAT_POS_MIDDLE && timer_item==canal_middle_item)
633
double item_x1, item_y1, item_x2, item_y2;
635
gnome_canvas_item_get_bounds(tuxboat_item, &item_x1, &item_y1,
638
gnome_canvas_item_set(tuxboat_item,
639
"y", item_y1 + timer_step_y1,
643
if((timer_item_y1 >= timer_item_limit_y && timer_step_y1 > 0) ||
644
(timer_item_y1 <= timer_item_limit_y && timer_step_y1 < 0))
646
gtk_timeout_remove (timer_id);
651
else if((timer_item_x1 >= timer_item_limit_x && timer_step_x1 > 0) ||
652
(timer_item_x1 <= timer_item_limit_x && timer_step_x1 < 0))
654
gtk_timeout_remove (timer_id);
660
gnome_canvas_update_now(gcomprisBoard->canvas);
665
/* ==================================== */
666
/* Highlight the given item */
667
static void hightlight(GnomeCanvasItem *item, gboolean status)
671
/* This is an image, not a rectangle */
672
if(item == tuxboat_item)
675
if(item == lock_left_item ||
676
item == lock_right_item)
678
color = (status ? LOCK_COLOR_H : LOCK_COLOR);
680
else if (item == canallock_left_item ||
681
item == canallock_right_item)
683
color = (status ? CANALLOCK_COLOR_H : CANALLOCK_COLOR);
687
gnome_canvas_item_set(item,
688
"fill_color_rgba", color,
693
/* ==================================== */
695
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
697
double item_x, item_y;
698
item_x = event->button.x;
699
item_y = event->button.y;
700
gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
707
case GDK_ENTER_NOTIFY:
708
hightlight(item, TRUE);
710
case GDK_LEAVE_NOTIFY:
711
hightlight(item, FALSE);
713
case GDK_BUTTON_PRESS:
715
/* If there is already an animation do nothing */
719
if(item == lock_left_item)
721
if(lock_water_low && canallock_right_up)
724
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
727
else if(item == lock_right_item)
729
if(!lock_water_low && canallock_left_up)
732
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
734
else if(item == canallock_left_item && canallock_right_up)
739
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
741
else if(item == canallock_right_item && canallock_left_up)
746
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
748
else if(item == tuxboat_item)
754
gc_sound_play_ogg ("sounds/crash.ogg", NULL);
765
canal_lock_destroy_all_items();
766
gc_bonus_display(gamewon, GC_BONUS_SMILEY);