1
/* gcompris - traffic.c
3
* Copyright (C) 2002, 2008 Bruno Coudoin
5
* Based on the original code from Geoff Reedy <vader21@imsa.edu>
6
* Copyright (C) 2000 Geoff Reedy
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 3 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, see <http://www.gnu.org/licenses/>.
26
#include "gcompris/gcompris.h"
28
#define SOUNDLISTFILE PACKAGE
30
#define DATAFILE "TrafficData"
33
static GcomprisBoard *gcomprisBoard = NULL;
34
static gboolean board_paused = TRUE;
36
static GooCanvasItem *allcars = NULL;
37
static gboolean dragging = FALSE;
38
static double start_x, start_y;
44
static void start_board (GcomprisBoard *agcomprisBoard);
45
static void pause_board (gboolean pause);
46
static void end_board (void);
47
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
48
static void set_level (guint level);
50
static void game_won(void);
51
static void repeat(void);
53
static GooCanvasItem *boardRootItem = NULL;
55
static GooCanvasItem *traffic_create_item(GooCanvasItem *parent);
56
static void traffic_destroy_all_items(void);
57
static void traffic_next_level(void);
59
typedef struct _car car;
60
typedef struct _jam jam;
65
#define CAR_ORIENT_NS 0
66
#define CAR_ORIENT_EW 1
71
gchar color_string[50];
72
GooCanvasItem *canvasgroup;
79
#define MAX_NUMBER_OF_CARS 20
80
car *cars[MAX_NUMBER_OF_CARS];
83
static gboolean on_button_press (GooCanvasItem *item,
84
GooCanvasItem *target,
85
GdkEventButton *event,
87
static gboolean on_button_release (GooCanvasItem *item,
88
GooCanvasItem *target,
89
GdkEventButton *event,
91
static gboolean on_motion_notify (GooCanvasItem *item,
92
GooCanvasItem *target,
93
GdkEventMotion *event,
96
static gboolean load_level(guint level, guint card);
98
static jam current_card ={0,0,0,{NULL}};
100
static void draw_grid (GooCanvasItem *rootBorder);
101
static gint cars_from_strv(char *strv);
103
/* Description of this plugin */
104
static BoardPlugin menu_bp =
108
"A sliding block puzzle game",
110
"Bruno Coudoin <bruno.coudoin@free.fr>",
129
* Main entry point mandatory for each Gcompris's game
130
* ---------------------------------------------------
134
GET_BPLUGIN_INFO(traffic)
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)
157
if(agcomprisBoard!=NULL)
159
gcomprisBoard=agcomprisBoard;
160
gcomprisBoard->level=1;
161
gcomprisBoard->maxlevel=8;
162
gcomprisBoard->sublevel=1;
163
gcomprisBoard->number_of_sublevel=5; /* Go to next level after this number of 'play' */
164
gc_score_start(SCORESTYLE_NOTE,
167
gcomprisBoard->number_of_sublevel);
169
gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT);
171
gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
172
"traffic/traffic-bg.jpg");
174
traffic_next_level();
180
/* ======================================= */
181
static void end_board ()
183
if(gcomprisBoard!=NULL)
187
traffic_destroy_all_items();
189
gcomprisBoard = NULL;
192
/* ======================================= */
193
static void set_level (guint level)
196
if(gcomprisBoard!=NULL)
198
gcomprisBoard->level=level;
199
gcomprisBoard->sublevel=1;
200
traffic_next_level();
203
/* ======================================= */
204
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
208
if(g_strcasecmp(gcomprisBoard->type, "traffic")==0)
210
/* Set the plugin entry */
211
gcomprisBoard->plugin=&menu_bp;
220
* Repeat let the user restart the current level
223
static void repeat (){
225
traffic_destroy_all_items();
227
/* Try the next level */
228
traffic_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
231
/*-------------------------------------------------------------------------------*/
232
/*-------------------------------------------------------------------------------*/
233
/* set initial values for the next level */
238
gc_bar_set_level(gcomprisBoard);
240
traffic_destroy_all_items();
243
gc_score_set(gcomprisBoard->sublevel);
245
/* Try the next level */
246
traffic_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
249
/* ==================================== */
250
/* Destroy all the items */
252
traffic_destroy_all_items()
256
if(boardRootItem!=NULL)
257
goo_canvas_item_remove(boardRootItem);
258
boardRootItem = NULL;
260
for (i=0; i<current_card.num_cars; i++)
262
g_free(current_card.cars[i]);
264
current_card.num_cars = 0;
267
/* ==================================== */
268
static GooCanvasItem *
269
traffic_create_item(GooCanvasItem *parent)
271
GooCanvasItem *borderItem = NULL;
274
goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
278
goo_canvas_group_new (boardRootItem,
280
goo_canvas_item_translate(borderItem, OFSET_X, OFSET_Y);
282
draw_grid(borderItem);
284
allcars = goo_canvas_group_new (borderItem, NULL);
285
goo_canvas_item_translate(allcars, 11, 11);
287
g_object_set_data(G_OBJECT(allcars),
288
"whatami", (gpointer)"allcars");
290
/* Ready now, let's go */
291
load_level(gcomprisBoard->level, gcomprisBoard->sublevel);
295
/* ==================================== */
299
gcomprisBoard->sublevel++;
301
if(gcomprisBoard->sublevel > gcomprisBoard->number_of_sublevel)
303
/* Try the next level */
304
gcomprisBoard->sublevel=1;
305
gcomprisBoard->level++;
306
if(gcomprisBoard->level>gcomprisBoard->maxlevel)
307
gcomprisBoard->level = gcomprisBoard->maxlevel;
309
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
311
traffic_next_level();
317
draw_grid(GooCanvasItem *rootBorder)
319
GooCanvasItem *grid_group;
320
int xlooper, ylooper;
323
goo_canvas_group_new (rootBorder,
326
goo_canvas_item_translate(rootBorder, 10, 10);
328
g_object_set_data(G_OBJECT(grid_group),
329
"whatami", (gpointer)"grid_group");
331
goo_canvas_item_lower(grid_group, NULL);
333
for (xlooper=0; xlooper<6; xlooper++)
334
for (ylooper=0; ylooper<6; ylooper++)
335
g_object_set_data(G_OBJECT(
336
goo_canvas_rect_new(grid_group,
341
"fill-color-rgba", NULL,
342
"stroke-color", "white",
345
"whatami",(gpointer)"grid square");;
350
draw_car(car *thiscar)
352
GooCanvasItem *car_group;
353
GooCanvasItem *car_rect;
355
g_object_set_data(G_OBJECT(allcars),"whatami",(gpointer)"allcars");
357
car_group = goo_canvas_group_new(allcars,
359
goo_canvas_item_translate(car_group,
360
40.0 * thiscar->x - 10,
361
40.0 * thiscar->y - 10);
363
car_rect = goo_canvas_rect_new(car_group,
366
(thiscar->orient?40.0*thiscar->size:40.0)-2,
367
(thiscar->orient?40.0:40.0*thiscar->size)-2,
368
"fill_color_rgba", thiscar->color,
369
"stroke-color", "white",
373
g_signal_connect(car_group,"button_press_event",
374
GTK_SIGNAL_FUNC(on_button_press), (gpointer)thiscar);
375
g_signal_connect (car_group, "button_release_event",
376
(GtkSignalFunc) on_button_release, (gpointer)thiscar);
377
g_signal_connect (car_group, "motion_notify_event",
378
(GtkSignalFunc) on_motion_notify, (gpointer)thiscar);
380
g_object_set_data(G_OBJECT(car_group), "car", (gpointer)thiscar);
381
g_object_set_data(G_OBJECT(car_group), "whatami", (gpointer)"car_group");
382
g_object_set_data(G_OBJECT(car_rect), "whatami", (gpointer)"car_rect");
389
for (whichcar=0;whichcar<myjam->num_cars;whichcar++)
390
draw_car(myjam->cars[whichcar]);
394
on_motion_notify (GooCanvasItem *item,
395
GooCanvasItem *target,
396
GdkEventMotion *event,
400
double small_x, big_x, small_y, big_y;
402
double item_x, item_y;
403
GooCanvasItem *atdest = NULL;
404
car *othercar = NULL;
406
canvas = goo_canvas_item_get_canvas (item);
410
goo_canvas_convert_to_item_space(goo_canvas_item_get_canvas(item),
411
goo_canvas_item_get_parent(item),
414
if (dragging && (event->state & GDK_BUTTON1_MASK))
416
switch (thiscar->orient) {
421
big_x=40*thiscar->size-1;
424
goo_canvas_convert_from_item_space(canvas,
425
item, &small_x, &small_y);
426
goo_canvas_convert_from_item_space(canvas,
427
item, &big_x, &big_y);
429
dy = CLAMP(item_y - start_y, -39, 39);
430
dx = CLAMP(item_x - start_x, -39, 39);
432
if (thiscar->goal && big_x==250+OFSET_X)
434
gc_canvas_item_ungrab(item,event->time);
436
"visibility", GOO_CANVAS_ITEM_INVISIBLE,
441
gc_bonus_display(gamewon, GC_BONUS_SMILEY);
444
if (small_x+dx < 11+OFSET_X)
446
dx = 11-small_x+OFSET_X;
448
else if (big_x+dx > 250+OFSET_X)
450
dx = 250-big_x+OFSET_X;
453
if ((hit<0) != (dx<0)) { hit=0;}
461
atdest = goo_canvas_get_item_at(canvas,
462
big_x+dx, small_y+20, TRUE);
464
othercar = (car*)g_object_get_data(G_OBJECT(goo_canvas_item_get_parent(atdest)),
476
atdest=goo_canvas_get_item_at(canvas,
477
small_x+dx-1,small_y+20, TRUE);
479
othercar=(car*)g_object_get_data(G_OBJECT(goo_canvas_item_get_parent(atdest)),
491
goo_canvas_item_translate(item, dx, 0);
499
big_y=40*thiscar->size-1;
501
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
504
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
508
dy = CLAMP(item_y - start_y, -39, 39);
509
dx = CLAMP(item_x - start_x, -39, 39);
511
if (small_y+dy<11+OFSET_Y)
513
dy=11-small_y+OFSET_Y;
515
else if (big_y+dy>250+OFSET_Y)
517
dy=250-big_y+OFSET_Y;
520
if ((hit<0)!=(dy<0)) { hit=0; }
525
atdest = goo_canvas_get_item_at(gcomprisBoard->canvas,
530
othercar=(car*)g_object_get_data(G_OBJECT(goo_canvas_item_get_parent(atdest)),
539
atdest=goo_canvas_get_item_at(gcomprisBoard->canvas,
540
small_x+20,small_y+dy-1, TRUE);
542
othercar=(car*)g_object_get_data(G_OBJECT(goo_canvas_item_get_parent(atdest)),
552
goo_canvas_item_translate(item, 0, dy);
560
on_button_release (GooCanvasItem *item,
561
GooCanvasItem *target,
562
GdkEventButton *event,
568
canvas = goo_canvas_item_get_canvas (item);
571
g_print ("received 'button-release' signal\n");
576
double even_vals_x[]={11+OFSET_X,51+OFSET_X,91+OFSET_X,131+
577
OFSET_X,171+OFSET_X,211+OFSET_X,HUGE_VAL};
578
double even_vals_y[]={11+OFSET_Y,51+OFSET_Y,91+OFSET_Y,131+
579
OFSET_Y,171+OFSET_Y,211+OFSET_Y,HUGE_VAL};
583
goo_canvas_convert_from_item_space(canvas, item,
586
for (ptr=even_vals_x;*ptr<x;ptr++);
592
for (ptr=even_vals_y;*ptr<y;ptr++);
598
goo_canvas_item_translate(item, dx, dy);
599
gc_canvas_item_ungrab(item, event->time);
608
on_button_press (GooCanvasItem *item,
609
GooCanvasItem *target,
610
GdkEventButton *event,
614
double item_x, item_y;
617
canvas = goo_canvas_item_get_canvas (item);
621
goo_canvas_convert_to_item_space(goo_canvas_item_get_canvas(item),
622
goo_canvas_item_get_parent(item),
628
if (thiscar->orient==CAR_ORIENT_NS)
629
cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
631
cursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
633
gc_canvas_item_grab(item,
634
GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
638
gdk_cursor_unref(cursor);
647
load_level(guint level, guint sublevel)
651
current_card.level = level;
652
current_card.card = sublevel;
654
car_strv = DataList[(level-1) * gcomprisBoard->number_of_sublevel + (sublevel-1)];
656
current_card.num_cars = cars_from_strv(car_strv);
658
if(current_card.num_cars == -1)
659
g_error("In loading dataset for traffic activity");
661
draw_jam(¤t_card);
666
/* Returns the number of cars
667
* I took the formatting from
668
* http://www.javascript-games.org/puzzle/rushhour/
671
cars_from_strv(char *strv)
675
int number_of_cars = 0;
676
gboolean more_car = TRUE;
680
current_card.cars[number_of_cars] = (car *)g_new(car, 1);
681
ccar = current_card.cars[number_of_cars];
683
/* By default, not a goal car */
688
if (sscanf(strv,"%c%c%c",
693
/* Point to the next car */
701
if (id == 'O' || id == 'P' || id == 'Q' || id == 'R') ccar->size = 3;
708
if (x == 'A') ccar->x = 0;
709
else if (x == 'B') ccar->x = 1;
710
else if (x == 'C') ccar->x = 2;
711
else if (x == 'D') ccar->x = 3;
712
else if (x == 'E') ccar->x = 4;
713
else if (x == 'F') ccar->x = 5;
718
if (y == 'A') ccar->x = 0;
719
else if (y == 'B') ccar->x = 1;
720
else if (y == 'C') ccar->x = 2;
721
else if (y == 'D') ccar->x = 3;
722
else if (y == 'E') ccar->x = 4;
723
else if (y == 'F') ccar->x = 5;
728
ccar->color = 0xFF0000FF;
731
else if (id == 'A') ccar->color = 0x80FF80FF;
732
else if (id == 'B') ccar->color = 0xC0C000FF;
733
else if (id == 'C') ccar->color = 0x8080FFFF;
734
else if (id == 'D') ccar->color = 0xFF80FFFF;
735
else if (id == 'E') ccar->color = 0xC00000FF;
736
else if (id == 'F') ccar->color = 0x008000FF;
737
else if (id == 'G') ccar->color = 0xC0C0C0FF;
738
else if (id == 'H') ccar->color = 0x6000efFF;
739
else if (id == 'I') ccar->color = 0xFFFF00FF;
740
else if (id == 'J') ccar->color = 0xFFA801FF;
741
else if (id == 'K') ccar->color = 0x00FF00FF;
742
else if (id == 'O') ccar->color = 0xFFFF00FF;
743
else if (id == 'P') ccar->color = 0xFF80FFFF;
744
else if (id == 'Q') ccar->color = 0x0000FFFF;
745
else if (id == 'R') ccar->color = 0x00FFFFFF;
748
return number_of_cars;