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