1
/* gcompris - wordsgame.c
3
* Time-stamp: <2002/04/14 00:29:36 bruno>
5
* Copyright (C) 2000 Bruno Coudoin
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
#include "gcompris/gcompris.h"
27
#define SOUNDLISTFILE PACKAGE
28
#define MAXWORDSLENGTH 50
30
static GList *item_list = NULL;
31
static GList *item2del_list = NULL;
33
static GcomprisBoard *gcomprisBoard = NULL;
35
static gint dummy_id = 0;
36
static gint drop_items_id = 0;
38
/* Hash table of all displayed letters */
39
static GHashTable *words_table= NULL;
44
GnomeCanvasItem *rootitem;
45
GnomeCanvasItem *overwriteItem;
48
static LettersItem *currentFocus = NULL;
50
static void start_board (GcomprisBoard *agcomprisBoard);
51
static void pause_board (gboolean pause);
52
static void end_board (void);
53
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
54
static void set_level (guint level);
55
gint key_press(guint keyval);
57
static GnomeCanvasItem *wordsgame_create_item(GnomeCanvasGroup *parent);
58
static gint wordsgame_drop_items (GtkWidget *widget, gpointer data);
59
static gint wordsgame_move_items (GtkWidget *widget, gpointer data);
60
static void wordsgame_destroy_item(LettersItem *item);
61
static void wordsgame_destroy_items(void);
62
static void wordsgame_destroy_all_items(void);
63
static void wordsgame_next_level(void);
64
static void wordsgame_add_new_item(void);
66
static void player_win(LettersItem *item);
67
static void player_loose(void);
68
static LettersItem *item_find_by_title (const gchar *title);
69
static char *get_random_word(void);
70
static void wordsgame_check_focus (char *key,
72
LettersItem **user_data);
74
static guint32 fallSpeed = 0;
75
static double speed = 0.0;
77
/* Description of this plugin */
83
N_("Fully type the falling words before they reach the ground"),
84
"Bruno Coudoin <bruno.coudoin@free.fr>",
101
* Main entry point mandatory for each Gcompris's game
102
* ---------------------------------------------------
107
*get_bplugin_info(void)
113
* in : boolean TRUE = PAUSE : FALSE = UNPAUSE
116
static void pause_board (gboolean pause)
119
if(gcomprisBoard==NULL)
125
gtk_timeout_remove (dummy_id);
129
gtk_timeout_remove (drop_items_id);
136
drop_items_id = gtk_timeout_add (100,
137
(GtkFunction) wordsgame_drop_items, NULL);
140
dummy_id = gtk_timeout_add (100, (GtkFunction) wordsgame_move_items, NULL);
147
static void start_board (GcomprisBoard *agcomprisBoard)
150
if(agcomprisBoard!=NULL)
152
gcomprisBoard=agcomprisBoard;
154
gcompris_set_background(gnome_canvas_root(gcomprisBoard->canvas), "gcompris/gcompris-bg.jpg");
157
gcomprisBoard->level = 1;
158
gcomprisBoard->maxlevel = 6;
159
gcomprisBoard->sublevel = 1;
160
gcompris_bar_set(GCOMPRIS_BAR_LEVEL);
162
wordsgame_next_level();
166
gboolean words_table_foreach_remove (char *key,
168
LettersItem *user_data)
171
free(value->overword);
179
if(gcomprisBoard!=NULL)
182
gcompris_score_end();
183
wordsgame_destroy_all_items();
186
g_hash_table_foreach_remove (words_table,
187
(GHRFunc)words_table_foreach_remove,
189
g_hash_table_destroy (words_table);
193
gcomprisBoard = NULL;
197
set_level (guint level)
200
if(gcomprisBoard!=NULL)
202
gcomprisBoard->level=level;
203
wordsgame_next_level();
207
static void wordsgame_check_focus (char *key,
209
LettersItem **user_data)
211
LettersItem *usrdata = *user_data;
213
if(usrdata->rootitem!=NULL)
216
if(key[0]==usrdata->word[0])
224
gint key_press(guint keyval)
231
/* Add some filter for control and shift key */
248
case GDK_Mode_switch:
249
case GDK_dead_circumflex:
284
sprintf(str, "%c", keyval);
286
if(currentFocus==NULL)
288
LettersItem *searchitem;
290
searchitem = malloc(sizeof(LettersItem));
292
/* Try to see if this letter matches the first one of any words */
293
searchitem->word = (char *)&str;
294
searchitem->rootitem=NULL;
295
searchitem->overword="";
297
g_hash_table_foreach (words_table, (GHFunc) wordsgame_check_focus, &searchitem);
300
if(searchitem->rootitem!=NULL)
302
currentFocus=searchitem;
310
if(currentFocus!=NULL)
312
if(currentFocus->rootitem!=NULL)
316
/* Check this is the correct letter */
317
currentChar = currentFocus->word[strlen(currentFocus->overword)];
319
if(currentChar==str[0])
321
/* Increment the overword */
322
snprintf(currentFocus->overword,
323
strlen(currentFocus->overword)+2,
324
"%s", currentFocus->word);
326
gnome_canvas_item_set (currentFocus->overwriteItem,
327
"text", currentFocus->overword,
330
if(strlen(currentFocus->overword)==strlen(currentFocus->word))
333
player_win(item_find_by_title(currentFocus->word));
340
/* It is a loose : unselect the word and defocus */
341
currentFocus->overword[0]='\0';
342
gnome_canvas_item_set (currentFocus->overwriteItem,
343
"text", currentFocus->overword,
353
/* Anyway kid you clicked on the wrong key */
361
is_our_board (GcomprisBoard *gcomprisBoard)
365
if(g_strcasecmp(gcomprisBoard->type, "wordsgame")==0)
367
/* Set the plugin entry */
368
gcomprisBoard->plugin=&menu_bp;
377
/*-------------------------------------------------------------------------------*/
378
/*-------------------------------------------------------------------------------*/
379
/*-------------------------------------------------------------------------------*/
380
/*-------------------------------------------------------------------------------*/
382
/* set initial values for the next level */
383
static void wordsgame_next_level()
386
gcomprisBoard->number_of_sublevel = 10 +
387
((gcomprisBoard->level-1) * 5);
388
gcompris_score_start(SCORESTYLE_NOTE,
389
gcomprisBoard->width - 220,
390
gcomprisBoard->height - 50,
391
gcomprisBoard->number_of_sublevel);
393
gcompris_bar_set_level(gcomprisBoard);
394
gcompris_score_set(gcomprisBoard->sublevel);
396
wordsgame_destroy_all_items();
402
/* Increase speed only after 5 levels */
403
if(gcomprisBoard->level>5)
405
fallSpeed=7000-gcomprisBoard->level*200;
411
static void remove_old_word(LettersItem *item)
413
/* Remove old word */
414
g_hash_table_remove (words_table, (item->word));
415
/* The items are freed by player_win */
417
free(item->overword);
421
static void wordsgame_move_item(LettersItem *item)
423
double x1, y1, x2, y2;
425
gnome_canvas_item_move(item->rootitem, 0, 2.0);
427
gnome_canvas_item_get_bounds (item->rootitem,
433
if(y1>gcomprisBoard->height) {
434
item2del_list = g_list_append (item2del_list, item);
439
static void wordsgame_destroy_item(LettersItem *item)
442
item_list = g_list_remove (item_list, item);
443
item2del_list = g_list_remove (item2del_list, item);
444
gtk_object_destroy (GTK_OBJECT(item->rootitem));
445
remove_old_word(item);
448
/* Destroy items that falls out of the canvas */
449
static void wordsgame_destroy_items()
453
while(g_list_length(item2del_list)>0)
455
item = g_list_nth_data(item2del_list, 0);
456
wordsgame_destroy_item(item);
460
/* Destroy all the items */
461
static void wordsgame_destroy_all_items()
465
if(item_list == NULL)
468
while(g_list_length(item_list)>0)
470
item = g_list_nth_data(item_list, 0);
471
wordsgame_destroy_item(item);
476
* This does the moves of the game items on the play canvas
479
static gint wordsgame_move_items (GtkWidget *widget, gpointer data)
481
g_list_foreach (item_list, (GFunc) wordsgame_move_item, NULL);
483
/* Destroy items that falls out of the canvas */
484
wordsgame_destroy_items();
486
dummy_id = gtk_timeout_add (speed,
487
(GtkFunction) wordsgame_move_items, NULL);
492
static GnomeCanvasItem *wordsgame_create_item(GnomeCanvasGroup *parent)
494
GnomeCanvasItem *item2;
495
LettersItem *lettersItem;
498
lettersItem = malloc(sizeof(LettersItem));
502
words_table= g_hash_table_new (g_str_hash, g_str_equal);
505
/* Load a gdk font */
506
gdk_font = gdk_font_load (FONT_BOARD_BIG);
508
/* Beware, since we put the words in a hash table, we do not allow the same
509
letter to be displayed two times */
511
lettersItem->word = get_random_word();
512
} while(item_find_by_title(lettersItem->word)!=NULL);
514
/* fill up the overword with zeros */
515
lettersItem->overword=calloc(strlen(lettersItem->word), 1);
517
lettersItem->rootitem = \
518
gnome_canvas_item_new (parent,
519
gnome_canvas_group_get_type (),
520
"x", (double)(rand()%(gcomprisBoard->width-
521
(guint)(gdk_string_width(gdk_font,
522
lettersItem->word)))),
523
"y", (double) -gdk_string_height(gdk_font, lettersItem->word),
526
/* To 'erase' words, I create 2 times the text item. One is empty now */
527
/* It will be filled each time the user enters the right key */
529
gnome_canvas_item_new (GNOME_CANVAS_GROUP(lettersItem->rootitem),
530
gnome_canvas_text_get_type (),
531
"text", lettersItem->word,
532
"font_gdk", gdk_font,
535
"anchor", GTK_ANCHOR_NW,
536
"fill_color", "white",
539
lettersItem->overwriteItem = \
540
gnome_canvas_item_new (GNOME_CANVAS_GROUP(lettersItem->rootitem),
541
gnome_canvas_text_get_type (),
543
"font_gdk", gdk_font,
546
"anchor", GTK_ANCHOR_NW,
547
"fill_color", "green",
550
item_list = g_list_append (item_list, lettersItem);
552
/* Add word to hash table of all falling words. */
553
g_hash_table_insert (words_table, lettersItem->word, lettersItem);
555
return (lettersItem->rootitem);
558
static void wordsgame_add_new_item()
560
wordsgame_create_item(gnome_canvas_root(gcomprisBoard->canvas));
564
* This is called on a low frequency and is used to drop new items
567
static gint wordsgame_drop_items (GtkWidget *widget, gpointer data)
569
wordsgame_add_new_item();
571
drop_items_id = gtk_timeout_add (fallSpeed,
572
(GtkFunction) wordsgame_drop_items, NULL);
576
static void player_win(LettersItem *item)
579
wordsgame_destroy_item(item);
580
gcompris_play_sound (SOUNDLISTFILE, "gobble");
582
gcomprisBoard->sublevel++;
583
gcompris_score_set(gcomprisBoard->sublevel);
585
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel)
587
/* Try the next level */
588
gcomprisBoard->level++;
589
gcomprisBoard->sublevel = 1;
590
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
591
board_finished(BOARD_FINISHED_RANDOM);
594
wordsgame_next_level();
595
gcompris_play_sound (SOUNDLISTFILE, "bonus");
599
/* Drop a new item now to speed up the game */
600
if(g_list_length(item_list)==0)
603
/* Remove pending new item creation to sync the falls */
604
gtk_timeout_remove (drop_items_id);
608
drop_items_id = gtk_timeout_add (0,
609
(GtkFunction) wordsgame_drop_items, NULL);
615
static void player_loose()
617
gcompris_play_sound (SOUNDLISTFILE, "crash");
621
item_find_by_title (const gchar *title)
626
return g_hash_table_lookup (words_table, title);
629
static FILE *get_wordfile(char *locale)
632
FILE *wordsfd = NULL;
634
/* First Try to find a file matching the level and the locale */
635
filename = g_strdup_printf("%s%s%d.%.2s",
636
PACKAGE_DATA_DIR, "/wordsgame/wordslevel",
637
gcomprisBoard->level, locale);
638
g_message("Trying to open file %s ", filename);
639
wordsfd = fopen (filename, "r");
643
/* Second Try to find a file matching the 'max' and the locale */
644
sprintf(filename, "%s%s%.2s",
645
PACKAGE_DATA_DIR, "/wordsgame/wordslevelmax.",
647
g_message("Trying to open file %s ", filename);
649
wordsfd = fopen (filename, "r");
657
* Return a random word from a set of text file depending on
658
* the current level and language
660
static char *get_random_word()
666
str = malloc(MAXWORDSLENGTH);
668
wordsfd = get_wordfile(gcompris_get_locale());
672
/* Try to open the english locale by default */
673
wordsfd = get_wordfile("en");
675
/* Too bad, even english is not there. Check your Install */
677
g_error("Cannot open file %s : Check your GCompris install", strerror(errno));
680
fseek (wordsfd, 0L, SEEK_END);
681
size = ftell (wordsfd);
684
fseek(wordsfd, i, SEEK_SET);
686
/* Read 2 times so that we are sure to sync on an end of line */
687
fgets(str, MAXWORDSLENGTH, wordsfd);
688
if(ftell(wordsfd)==size)
690
fgets(str, MAXWORDSLENGTH, wordsfd);
692
/* Chop the return */
693
str[strlen(str)-1]='\0';
701
/* Local Variables: */
703
/* eval:(load-library "time-stamp") */
704
/* eval:(make-local-variable 'write-file-hooks) */
705
/* eval:(add-hook 'write-file-hooks 'time-stamp) */
706
/* eval:(setq time-stamp-format '(time-stamp-yyyy/mm/dd time-stamp-hh:mm:ss user-login-name)) */