1 /**
2  * @file texts.c
3  * @brief Handle texts PAUSE, LEVEL, GAME OVER, PAUSE, enemy's name
4  * @created 1999-09-05
5  * @date 2012-08-26
6  */
7 /*
8  * copyright (c) 1998-2015 TLK Games all rights reserved
9  * $Id: texts.c,v 1.17 2012/08/26 17:09:14 gurumeditation Exp $
10  *
11  * Powermanga is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Powermanga is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA  02110-1301, USA.
25  */
26 #include "config.h"
27 #include "powermanga.h"
28 #include "tools.h"
29 #include "images.h"
30 #include "config_file.h"
31 #include "display.h"
32 #include "electrical_shock.h"
33 #include "enemies.h"
34 #include "shots.h"
35 #include "gfx_wrapper.h"
36 #include "guardians.h"
37 #include "log_recorder.h"
38 #include "meteors_phase.h"
39 #include "menu.h"
40 #include "spaceship.h"
41 #include "sprites_string.h"
42 #include "texts.h"
43 const Sint32 POS_Y_GAME = 179;
44 const Sint32 POS_Y_OVER = 222;
45 
46 /** New player's score value */
47 Sint32 player_score;
48 /** Old player's score value */
49 static Sint32 old_player_score;
50 /** Level number: from 0 to 41 */
51 Sint32 num_level = -1;
52 /** If TRUE refresh player's score points */
53 bool is_player_score_displayed = FALSE;
54 
55 static bool texts_load (void);
56 
57 /** Strings loaded from "texts/text_en.txt" or "texts/text_fr.txt" file */
58 static char **texts_loaded = NULL;
59 static Uint32 texts_loaded_count = 0;
60 typedef enum
61 {
62   TEXT_LEVEL,
63   TEXT_PAUSE,
64   TEXT_GAME,
65   TEXT_OVER,
66   TEXT_NUMOFLINES
67 }
68 TEXT_ENUM;
69 
70 static sprite_string_struct *text_pause = NULL;
71 static sprite_string_struct *text_level = NULL;
72 static sprite_string_struct *text_game = NULL;
73 static sprite_string_struct *text_over = NULL;
74 static sprite_string_struct *text_score = NULL;
75 static sprite_string_struct *text_enemy_name = NULL;
76 static Uint32 text_gameover_case = 0;
77 
78 /**
79  * First initialization, load strings file, create structures for texts like
80  * "PAUSE", "LEVEL", "GAME OVER"
81  * @return TRUE if it completed successfully or FALSE otherwise
82  */
83 bool
texts_init_once(void)84 texts_init_once (void)
85 {
86   Uint32 i;
87   /* load game texts */
88   if (!texts_load ())
89     {
90       return FALSE;
91     }
92 
93   /* create structures for PAUSE text */
94   text_pause =
95     sprites_string_new (texts_loaded[TEXT_PAUSE], 0, FONT_BIG, 0, 0);
96   if (text_pause == NULL)
97     {
98       LOG_ERR ("creation of 'PAUSE' sprites string failed");
99       return FALSE;
100     }
101   sprite_string_centerx (text_pause, (float) (77 * pixel_size),
102                          2 * pixel_size);
103   sprite_string_init_anim (text_pause, 250, 1000, FALSE, 0);
104   text_pause->space = text_pause->sprites_chars[0].font_size + 1 * pixel_size;
105 
106   /* create structures for LEVEL text */
107   text_level =
108     sprites_string_new (texts_loaded[TEXT_LEVEL], 3, FONT_GAME, 0, 0);
109   if (text_level == NULL)
110     {
111       LOG_ERR ("creation of 'LEVEL' sprites string failed");
112       return FALSE;
113     }
114   sprite_string_init_anim (text_level, 500, 1000, TRUE, 0);
115 
116   /* create structures for "GAME" text */
117   text_game = sprites_string_new (texts_loaded[TEXT_GAME], 0, FONT_BIG, 0, 0);
118   if (text_game == NULL)
119     {
120       LOG_ERR ("creation of 'GAME' sprites string failed");
121       return FALSE;
122     }
123   sprite_string_set_center (text_game, -1, POS_Y_GAME);
124   sprite_string_init_anim (text_game, 1, 6, FALSE, FONTS_MAX_OF_IMAGES * 5);
125   text_game->delay_next_char = FONTS_MAX_OF_IMAGES * 6;
126   text_game->delay_next_char_count = text_game->delay_next_char;
127 
128   /* create structures for "OVER" text */
129   text_over = sprites_string_new (texts_loaded[TEXT_OVER], 0, FONT_BIG, 0, 0);
130   if (text_over == NULL)
131     {
132       LOG_ERR ("creation of 'OVER' sprites string failed!");
133       return FALSE;
134     }
135   sprite_string_set_center (text_over, -1, POS_Y_OVER);
136   sprite_string_init_anim (text_over, 1, 6, FALSE, FONTS_MAX_OF_IMAGES * 5);
137   text_over->delay_next_char = FONTS_MAX_OF_IMAGES * 6;
138   text_over->delay_add_to_last = 0;
139 
140   /* create structures for the player's score */
141   text_score =
142     sprites_string_new ("00000000", 0, FONT_SCORE,
143                         (float) (68 * pixel_size), 0);
144   if (text_score == NULL)
145     {
146       LOG_ERR ("creation of score sprites string failed!");
147       return FALSE;
148     }
149   sprite_string_init_anim (text_score, 500, 1000, TRUE, 0);
150   for (i = 0; i < text_score->num_of_chars; i++)
151     {
152       text_score->sprites_chars[i].anim_speed_inc =
153         (350 + text_score->num_of_chars * 25) - 25 * i;
154     }
155   sprite_string_coords (text_score);
156 
157   /* create structures for the enemy name in congratulations */
158   text_enemy_name =
159     sprites_string_new ("                                      ", 0,
160                         FONT_GAME, 0, 0);
161   if (text_enemy_name == NULL)
162     {
163       LOG_ERR ("creation of congratulations sprites string failed!");
164       return FALSE;
165     }
166   sprite_string_init_anim (text_enemy_name, 1, 6, TRUE, 0);
167   return TRUE;
168 }
169 
170 /**
171  * Release sprites strings structures and other strings
172  */
173 void
texts_free(void)174 texts_free (void)
175 {
176   Uint32 i;
177 
178   /* free text loaded from "texts/text_en.txt" file */
179   if (texts_loaded != NULL)
180     {
181       for (i = 0; i < texts_loaded_count; i++)
182         {
183           if (texts_loaded[i] != NULL)
184             {
185               free_memory (texts_loaded[i]);
186               texts_loaded[i] = NULL;
187             }
188         }
189       free_memory ((char *) texts_loaded);
190       texts_loaded = NULL;
191     }
192 
193   if (text_level != NULL)
194     {
195       sprites_string_delete (text_level);
196       text_level = NULL;
197     }
198   if (text_pause != NULL)
199     {
200       sprites_string_delete (text_pause);
201       text_pause = NULL;
202     }
203   if (text_game != NULL)
204     {
205       sprites_string_delete (text_game);
206       text_game = NULL;
207     }
208   if (text_over != NULL)
209     {
210       sprites_string_delete (text_over);
211       text_over = NULL;
212     }
213   if (text_score != NULL)
214     {
215       sprites_string_delete (text_score);
216       text_score = NULL;
217     }
218   if (text_enemy_name != NULL)
219     {
220       sprites_string_delete (text_enemy_name);
221       text_enemy_name = NULL;
222     }
223 }
224 
225 /**
226  * Initialize a sprites string with a enemy's name in the
227  * congratulations phase
228  * @param name a enemy's name
229  */
230 void
text_enemy_name_init(const char * name)231 text_enemy_name_init (const char *name)
232 {
233   if (!sprite_string_set_char (text_enemy_name, name))
234     {
235       return;
236     }
237   /* enable animation of each char */
238   sprite_string_set_anim (text_enemy_name);
239   /* center the string horizontally at the screen */
240   sprite_string_centerx (text_enemy_name, 0, 0);
241 }
242 
243 /**
244  * Draw name of the enemy
245  * @param offsetx
246  * @param ycoord y coordinate of the first char
247  * @param restart TRUE if the animation of a char restarts when it is
248  * finished
249  */
250 void
text_enemy_name_draw(Sint32 offsetx,Sint32 ycoord,bool restart)251 text_enemy_name_draw (Sint32 offsetx, Sint32 ycoord, bool restart)
252 {
253   text_enemy_name->offset_x = (float) offsetx;
254   text_enemy_name->coord_y = (float) ycoord;
255   /* set coordinates for each chars */
256   sprite_string_coords (text_enemy_name);
257   sprite_string_anim (text_enemy_name, restart);
258   sprite_string_draw (text_enemy_name);
259 }
260 
261 /**
262  * Display score number
263  */
264 void
text_draw_score(void)265 text_draw_score (void)
266 {
267   bool is_updated = FALSE;
268   if (player_score != old_player_score)
269     {
270       sprite_string_set_anim (text_score);
271       sprintf (text_score->string, "%08d", player_score);
272       sprite_chars_to_image (text_score);
273       old_player_score = player_score;
274       is_updated = TRUE;
275     }
276   sprite_string_anim (text_score, FALSE);
277   if (text_score->at_least_one_char_changed || is_updated)
278     {
279       sprite_string_draw (text_score);
280       /* refresh player's score into the top panel */
281       is_player_score_displayed = TRUE;
282     }
283 }
284 
285 /**
286  * Initialize the appearance of the "LEVEL n" chars if it is
287  * not active, and check if the text is centered horizontally
288  * on the screen
289  * @return "LEVEL n" string is located at the center of the screen
290  */
291 bool
text_level_move(Sint32 level_nu)292 text_level_move (Sint32 level_nu)
293 {
294   if (!text_level->is_move)
295     {
296       text_level->is_move = TRUE;
297       sprintf (text_level->string, "%s %d", texts_loaded[TEXT_LEVEL],
298                level_nu + 1);
299       text_level->num_of_chars = strlen (text_level->string);
300       sprite_chars_to_image (text_level);
301       text_level->offset_x = 128.0;
302       text_level->offset_y = 0.0;
303       sprite_string_center (text_level);
304       /* set the first image of each char */
305       sprite_string_restart_anim (text_level);
306       /* enable animation of each char */
307       sprite_string_set_anim (text_level);
308     }
309   if (text_level->offset_y == 0.0 && text_level->offset_x == 0.0)
310     {
311       /* "LEVEL n" string is located at the center of the screen */
312       return TRUE;
313     }
314   else
315     {
316       return FALSE;
317     }
318 }
319 
320 /**
321  * Move and draw "LEVEL N" chars
322  */
323 void
text_level_draw(void)324 text_level_draw (void)
325 {
326   if (!text_level->is_move)
327     {
328       return;
329     }
330 
331   /* meteors or guardian phase? */
332   if (meteor_activity || guardian->number > 0)
333     {
334       /* horizontal displacement, toward left */
335       text_level->offset_x -= 2 * pixel_size;
336       if (text_level->offset_x < 0.0)
337         {
338           text_level->offset_x = 0.0;
339         }
340     }
341   else
342     {
343       sprite_string_set_anim (text_level);
344       text_level->offset_y += (float) (0.4 * pixel_size);
345       if (text_level->offset_y > 100 * pixel_size)
346         {
347           text_level->is_move = FALSE;
348         }
349     }
350   sprite_string_coords (text_level);
351   sprite_string_anim (text_level, FALSE);
352   sprite_string_draw (text_level);
353 }
354 
355 /**
356  * Initialize the appearance of the "GAME OVER" chars
357  */
358 void
text_gameover_init(void)359 text_gameover_init (void)
360 {
361   sprite_string_init_rotation (text_game, 64, 0, 0, 120);
362   sprite_string_init_rotation (text_over, 0, 0, 0, 120);
363   /* set the first image of each char */
364   sprite_string_restart_anim (text_game);
365   sprite_string_restart_anim (text_over);
366   /* enable animation of each char */
367   sprite_string_set_anim (text_game);
368   sprite_string_set_anim (text_over);
369   text_game->is_move = TRUE;
370   text_gameover_case = 0;
371 }
372 
373 /**
374  * Draw and move "GAME OVER" chars
375  */
376 bool
text_gameover_draw(void)377 text_gameover_draw (void)
378 {
379   bool is_finished = FALSE;
380   switch (text_gameover_case)
381     {
382       /* GAME OVER appearing in progress */
383     case 0:
384       is_finished = TRUE;
385       if (!sprites_string_rotation_dec (text_game, 2, 2, 0))
386         {
387           is_finished = FALSE;
388         }
389       if (!sprites_string_rotation_dec (text_over, 2, 2, 0))
390         {
391           is_finished = FALSE;
392         }
393       sprite_string_anim (text_game, TRUE);
394       sprite_string_anim (text_over, TRUE);
395       if (is_finished)
396         {
397           text_gameover_case = 1;
398           is_finished = FALSE;
399         }
400       break;
401 
402     case 1:
403       is_finished = FALSE;
404       if (sprite_string_anim (text_game, FALSE) >= 0)
405         {
406           is_finished = TRUE;
407         }
408       if (sprite_string_anim (text_over, FALSE) >= 0)
409         {
410           is_finished = TRUE;
411         }
412       if (is_finished)
413         {
414           text_gameover_case = 2;
415         }
416       break;
417 
418       /* animation of each char separately, one by one  */
419     case 2:
420       is_finished = TRUE;
421       if (text_game->is_move)
422         {
423           if (sprite_string_anim_one (text_game))
424             {
425               text_game->is_move = FALSE;
426               text_over->is_move = TRUE;
427             }
428         }
429       else
430         {
431           if (sprite_string_anim_one (text_over))
432             {
433               text_over->is_move = FALSE;
434               text_game->is_move = TRUE;
435             }
436         }
437     }
438 
439   if (menu_status != MENU_OFF)
440     {
441       text_game->coord_y =
442         (float) (POS_Y_GAME + menu_coord_y - (offscreen_clipsize +
443                                               offscreen_height_visible));
444       text_over->coord_y =
445         (float) (POS_Y_OVER + menu_coord_y - (offscreen_clipsize +
446                                               offscreen_height_visible));
447     }
448   sprite_string_coords (text_game);
449   sprite_string_coords (text_over);
450   sprite_string_draw (text_game);
451   sprite_string_draw (text_over);
452   return is_finished;
453 }
454 
455 /**
456  * Display "PAUSE" chars sprites
457  */
458 void
text_pause_draw(void)459 text_pause_draw (void)
460 {
461   sprite_string_anim_one (text_pause);
462   sprite_string_draw (text_pause);
463 }
464 
465 /**
466  * Initialize player score
467  */
468 void
texts_init(void)469 texts_init (void)
470 {
471   player_score = 0;
472   old_player_score = -1;
473 }
474 
475 /**
476  * Load game texts files
477  * @return TRUE if it completed successfully or FALSE otherwise
478  */
479 bool
texts_load(void)480 texts_load (void)
481 {
482   Uint32 offset, char_count, start, line_num, filesize;
483   char c, *str, *filedata;
484   filedata = loadfile_with_lang ("texts/text_%s.txt", &filesize);
485   if (filedata == NULL)
486     {
487       return FALSE;
488     }
489 
490   /*
491    * caclulate the number of lines
492    */
493   offset = 0;
494   texts_loaded_count = 0;
495   while (offset < filesize)
496     {
497       if (filedata[offset++] == '\n')
498         {
499           texts_loaded_count++;
500         }
501     }
502   texts_loaded =
503     (char **) memory_allocation (texts_loaded_count * sizeof (char *));
504   if (texts_loaded == NULL)
505     {
506       LOG_ERR ("not enough memory to allocate 'texts_loaded'!");
507       return FALSE;
508     }
509 
510   /*
511    * read line by line
512    */
513   offset = 0;
514   start = offset;
515   char_count = 0;
516   line_num = 0;
517   while (offset < filesize)
518     {
519       c = filedata[offset++];
520       if (c == '\n')
521         {
522           if (char_count > 0)
523             {
524               str = memory_allocation (char_count * sizeof (char) + 1);
525               if (str == NULL)
526                 {
527                   LOG_ERR ("not enough memory to allocate 'str'!");
528                   return FALSE;
529                 }
530               texts_loaded[line_num] = str;
531               while (start < offset)
532                 {
533                   if (filedata[start] > ' ')
534                     {
535                       *(str++) = filedata[start];
536                     }
537                   start++;
538                 }
539             }
540           line_num++;
541           start = offset;
542           char_count = 0;
543         }
544       else
545         {
546           if (c >= ' ')
547             {
548               char_count++;
549             }
550         }
551     }
552   free_memory (filedata);
553   return TRUE;
554 }
555