naev 0.12.5
intro.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
12#include "SDL.h"
13
14#include "naev.h"
16
17#include "intro.h"
18
19#include "array.h"
20#include "conf.h"
21#include "font.h"
22#include "log.h"
23#include "menu.h"
24#include "music.h"
25#include "ndata.h"
26#include "nstring.h"
27#include "toolkit.h"
28
29#define INTRO_SPEED 30.
30#define SIDE_MARGIN 100.
31#define IMAGE_WIDTH 300.
32
34typedef struct intro_img_t_ {
35 glTexture *tex; /* Image. */
36 double x, y, w, h; /* Position. */
37 glColour c; /* Alpha channel for fading. */
38 double fade_rate; /* Positive for fade-in, negative for fade-out. */
40
42typedef enum intro_opcode_t_ {
47
49typedef struct intro_cmd_t_ {
50 intro_opcode_t opcode;
51 const char *operand;
53
54/*
55 * State.
56 */
58 NULL;
59static char *intro_buf = NULL;
62static int has_side_gfx = 0; /* Determines how wide to make the text. */
63
64/*
65 * Prototypes.
66 */
67static int intro_load( const char *text );
68static void intro_cleanup( void );
69static int intro_event_handler( int *stop, double *offset, double *vel );
70static void initialize_image( intro_img_t *img );
71static void load_image( intro_img_t *img, const char *img_file );
72static void intro_fade_image_in( intro_img_t *side, intro_img_t *transition,
73 const char *img_file );
74static int intro_draw_text( char **const sb_list, int sb_size, int sb_index,
75 double offset, double line_height );
76
80static int intro_load( const char *text )
81{
82 size_t intro_size;
83 char *cur_line, *rest_of_file;
84
85 has_side_gfx = 0;
86
87 /* Load text. */
88 intro_buf = ndata_read( text, &intro_size );
89
90 /* Create intro font. */
91 gl_fontInit( &intro_font, _( FONT_MONOSPACE_PATH ), conf.font_size_intro,
92 FONT_PATH_PREFIX, 0 );
93
95 rest_of_file = intro_buf;
96 while ( rest_of_file ) {
97 cur_line = rest_of_file;
98 rest_of_file = strchr( cur_line, '\n' );
99 /* If there's a next line, split the string and point rest_of_file to it.
100 */
101 if ( rest_of_file != NULL ) {
102 /* Check for CRLF endings -- if present, zero both parts. */
103 if ( rest_of_file > cur_line && *( rest_of_file - 1 ) == '\r' )
104 *( rest_of_file - 1 ) = '\0';
105 *rest_of_file++ = '\0';
106 }
107
108 if ( strncmp( cur_line, "[fadein ", 8 ) == 0 ) {
110 array_back( intro_cmds ).operand = &cur_line[8];
111 /* Zap the closing square-bracket. */
112 cur_line[strlen( cur_line ) - 1] = '\0';
113 /* Mark that there are graphics. */
114 has_side_gfx = 1;
115 } else if ( strncmp( cur_line, "[fadeout]", 9 ) == 0 )
117 else {
118 array_grow( &intro_cmds ).opcode = INTRO_TEXT;
119 /* Translate the line if it's not the empty string. */
120 array_back( intro_cmds ).operand =
121 ( cur_line[0] == '\0' ? cur_line : _( cur_line ) );
122 }
123 }
124
125 return 0;
126}
127
131static void intro_cleanup( void )
132{
133 /* Free memory. */
135 intro_cmds = NULL;
136 free( intro_buf );
137 intro_buf = NULL;
139}
140
146static void initialize_image( intro_img_t *img )
147{
148 img->tex = NULL;
149 img->c.r = 1.0;
150 img->c.g = 1.0;
151 img->c.b = 1.0;
152 img->c.a = 1.0;
153 img->fade_rate = 0.0;
154}
155
162static void load_image( intro_img_t *img, const char *img_file )
163{
164 img->tex = gl_newImage( img_file, 0 );
165 img->w = MIN( img->tex->w, IMAGE_WIDTH );
166 img->h = img->tex->h * img->w / img->tex->w;
167 img->x = ( IMAGE_WIDTH + SIDE_MARGIN - img->w ) / 2.0;
168 img->y = (double)SCREEN_H / 2.0 - ( img->h / 2.0 );
169 img->c.a = 0.0;
170 img->fade_rate = 0.1;
171}
172
180static void intro_fade_image_in( intro_img_t *side, intro_img_t *transition,
181 const char *img_file )
182{
183 if ( NULL == side->tex )
184 load_image( side, img_file ); /* Simple fade-in. */
185 else {
186 /*
187 * Transition or on-deck. The difference is whether one image is
188 * replacing the other (transition), or whether the first must fade out
189 * completely before the second comes in (on-deck).
190 *
191 * We can determine which is the case by seeing whether [fadeout] has been
192 * called. I.e., is side->fade_rate < 0?
193 */
194 if ( NULL != transition->tex ) {
195 /* Scrolling is happening faster than fading... */
196 WARN( _( "Intro scrolling too fast!" ) );
197 gl_freeTexture( transition->tex );
198 }
199 load_image( transition, img_file );
200 if ( side->fade_rate < 0.0 )
201 transition->fade_rate = 0.0; /* put an image on deck. */
202 else {
203 /* transition. */
204 side->fade_rate = -0.1; /* begin fading out. */
205 side->c.a = 0.99;
206 }
207 }
208}
209
216static int intro_event_handler( int *stop, double *offset, double *vel )
217{
218 SDL_Event event; /* user key-press, mouse-push, etc. */
219 while ( SDL_PollEvent( &event ) ) {
220 if ( event.type == SDL_QUIT ) {
221 if ( naev_isQuit() || menu_askQuit() ) {
222 naev_quit();
223 return 1;
224 }
225 }
226
227 if ( event.type == SDL_WINDOWEVENT &&
228 event.window.event == SDL_WINDOWEVENT_RESIZED ) {
229 naev_resize();
230 continue;
231 }
232
233 if ( event.type != SDL_KEYDOWN )
234 continue;
235
236 /* Escape skips directly. */
237 if ( event.key.keysym.sym == SDLK_ESCAPE ) {
238 *stop = 1;
239 break;
240 }
241
242 /* Slow down the text. */
243 else if ( event.key.keysym.sym == SDLK_UP ) {
244 *vel -= 8.0;
245 if ( *vel < 0.0 )
246 *vel = 0.0;
247 }
248
249 /* Speed up the text. */
250 else if ( event.key.keysym.sym == SDLK_DOWN ) {
251 *vel += 8.0;
252 if ( *vel > 100.0 )
253 *vel = 100.0;
254 }
255
256 /* Jump down. */
257 else if ( ( event.key.keysym.sym == SDLK_SPACE ) ||
258 ( event.key.keysym.sym == SDLK_RETURN ) )
259 *offset += 100;
260
261 /* Jump up. */
262 else if ( event.key.keysym.sym == SDLK_BACKSPACE )
263 *offset -= 100;
264
265 /* User is clearly flailing on keyboard. */
266 else
267 *vel = 16.;
268 }
269 return 0;
270}
271
280static int intro_draw_text( char **const sb_list, int sb_size, int sb_index,
281 double offset, double line_height )
282{
283 double x, y; /* render position. */
284 int i;
285 register int stop = 1;
286
287 x = SIDE_MARGIN;
288 if ( has_side_gfx )
289 x += IMAGE_WIDTH;
290
291 i = sb_index;
292 y = SCREEN_H + offset - line_height;
293 do {
294 if ( sb_list[i] != NULL ) {
295 stop = 0;
296 if ( y < 2 * line_height ) {
297 glColour fadedColour = cFontGreen;
298 fadedColour.a = y / ( (double)line_height * 2. );
299 gl_printRaw( &intro_font, x, y, &fadedColour, -1, sb_list[i] );
300 } else if ( y > SCREEN_H - 2 * line_height ) {
301 glColour fadedColour = cFontGreen;
302 fadedColour.a =
303 ( (double)SCREEN_H - y ) / ( (double)line_height * 2. );
304 gl_printRaw( &intro_font, x, y, &fadedColour, -1, sb_list[i] );
305 } else
306 gl_printRaw( &intro_font, x, y, &cFontGreen, -1, sb_list[i] );
307 }
308
309 y -= line_height;
310 i = ( i + 1 ) % sb_size;
311 } while ( i != sb_index );
312
313 return stop;
314}
315
323int intro_display( const char *text, const char *mus )
324{
325 double offset; /* distance from bottom of the top line. */
326 double line_height; /* # pixels per line. */
327 int lines_per_screen; /* max appearing lines on the screen. */
328 char **sb_arr; /* array of lines to render. */
329 int sb_index; /* Position in the line array. */
330 double vel = 16.; /* velocity: speed of text. */
331 int stop = 0; /* stop the intro. */
332 unsigned int tcur, tlast, tlag; /* timers. */
333 double delta; /* time diff from last render to this one. */
334 int cmd_index = 0; /* index into the big list of intro lines. */
335 intro_img_t side_image; /* image to go along with the text. */
336 intro_img_t transition; /* image for transitioning. */
338 iter; /* the renderable lines coming from the current text directive. */
339
340 /* Load the introduction. */
341 if ( intro_load( text ) < 0 )
342 return -1;
343
344 /* Change music to intro music. */
345 if ( mus != NULL )
346 music_choose( mus );
347
348 /* We need to clear key repeat to avoid infinite loops. */
350
351 /* Do a few calculations to figure out how many lines can be present on the
352 screen at any given time. */
353 line_height = (double)intro_font.h * 1.3;
354 lines_per_screen = (int)( SCREEN_H / line_height + 1.5 ); /* round up + 1 */
355
356 /* Initialize the lines. */
358 SCREEN_W - 2 * SIDE_MARGIN - IMAGE_WIDTH );
359 (void)gl_printLineIteratorNext( &iter );
360 sb_arr = calloc( lines_per_screen, sizeof( char *) );
361 sb_index = 0;
362
363 /* Force the first line to be loaded immediately. */
364 offset = line_height;
365
366 /* Unset the side image. */
367 initialize_image( &side_image );
368 initialize_image( &transition );
369
370 tlast = SDL_GetTicks();
371 while ( !stop ) {
372 tcur = SDL_GetTicks();
373 delta = (double)( tcur - tlast ) / 1000.;
374 tlast = tcur;
375
376 /* Increment position. */
377 offset += vel * delta;
378 while ( !( offset < line_height ) ) {
379 /* One line has scrolled off, and another one on. */
380 if ( gl_printLineIteratorNext( &iter ) ) {
381 free( sb_arr[sb_index] );
382 sb_arr[sb_index] =
383 strndup( &iter.text[iter.l_begin], iter.l_end - iter.l_begin );
384 offset -= line_height;
385 sb_index = ( sb_index + 1 ) % lines_per_screen;
386 } else if ( cmd_index < array_size( intro_cmds ) ) {
387 switch ( intro_cmds[cmd_index].opcode ) {
388 case INTRO_TEXT:
390 intro_cmds[cmd_index].operand,
391 iter.width );
392 break;
393 case INTRO_FADEIN:
394 intro_fade_image_in( &side_image, &transition,
395 intro_cmds[cmd_index].operand );
396 break;
397 case INTRO_FADEOUT:
398 if ( NULL == side_image.tex ) {
399 WARN( _( "Tried to fade out without an image." ) );
400 break;
401 }
402 side_image.fade_rate = -0.1;
403 side_image.c.a = 0.99;
404 break;
405 }
406 ++cmd_index;
407 } else {
408 free( sb_arr[sb_index] );
409 sb_arr[sb_index] = NULL;
410 offset -= line_height;
411 sb_index = ( sb_index + 1 ) % lines_per_screen;
412 }
413 } /* while (offset > line_height) */
414
415 /* Fade the side image. */
416 if ( side_image.tex != NULL && side_image.c.a < 1.0 ) {
417 side_image.c.a += delta * vel * side_image.fade_rate;
418
419 if ( transition.tex != NULL && transition.fade_rate > 0.0 )
420 transition.c.a += delta * vel * transition.fade_rate;
421
422 if ( side_image.c.a > 1.0 ) {
423 /* Faded in... */
424 side_image.c.a = 1.0;
425 side_image.fade_rate = 0.0;
426 } else if ( side_image.c.a < 0.0 ) {
427 /* Faded out... */
428 gl_freeTexture( side_image.tex );
429 if ( transition.tex != NULL ) {
430 side_image.tex = transition.tex;
431 side_image.c.a = transition.c.a;
432 side_image.w = transition.w;
433 side_image.h = transition.h;
434 side_image.y = transition.y;
435 side_image.fade_rate = 0.1;
436 transition.tex = NULL;
437 transition.c.a = 1.0;
438 } else {
439 side_image.c.a = 1.0;
440 side_image.tex = NULL;
441 side_image.fade_rate = 0.0;
442 }
443 }
444 }
445
446 /* Clear stuff. */
447 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
448
449 /* Only thing we actually care about updating is music. */
450 music_update( delta );
451
452 /* Draw text. */
453 stop = intro_draw_text( sb_arr, lines_per_screen, sb_index, offset,
454 line_height );
455
456 if ( NULL != side_image.tex )
457 /* Draw the image next to the text. */
458 gl_renderScale( side_image.tex, side_image.x, side_image.y,
459 side_image.w, side_image.h, &side_image.c );
460
461 if ( NULL != transition.tex && transition.c.a > 0.0 )
462 /* Draw the image in transition. */
463 gl_renderScale( transition.tex, transition.x, transition.y,
464 transition.w, transition.h, &transition.c );
465
466 /* Display stuff. */
467 SDL_GL_SwapWindow( gl_screen.window );
468
469 tlag = SDL_GetTicks() - tcur;
470 if ( tlag < 25 ) /* No need to burn CPU. Note: swapping may involve a wait
471 for vblank; 25 is 1.5 frames @60fps. */
472 SDL_Delay( 25 - tlag );
473
474 /* Handle user events. */
475 if ( intro_event_handler( &stop, &offset, &vel ) )
476 break;
477
478 } /* while (!stop) */
479
480 /* free malloc'd memory. */
481 free( sb_arr );
482 gl_freeTexture( side_image.tex );
483 gl_freeTexture( transition.tex );
484
485 /* Stop music, normal music will start shortly after. */
486 music_stop( 0 );
487
488 /* Clean up after the introduction. */
490
491 return 0;
492}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_back(ptr_array)
Returns the last element in the array.
Definition array.h:228
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int gl_printLineIteratorNext(glPrintLineIterator *iter)
Updates iter with the next line's information.
Definition font.c:550
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition font.c:646
void gl_freeFont(glFont *font)
Frees a loaded font. Caution: its glFontStash still has a slot in avail_fonts. At the time of writing...
Definition font.c:1881
void gl_printLineIteratorInit(glPrintLineIterator *iter, const glFont *ft_font, const char *text, int width)
Initialize an iterator object for breaking text into lines.
Definition font.c:528
int gl_fontInit(glFont *font, const char *fname, const unsigned int h, const char *prefix, unsigned int flags)
Initializes a font.
Definition font.c:1670
static void initialize_image(intro_img_t *img)
Initialize an intro_img_t to default values.
Definition intro.c:146
static intro_cmd_t * intro_cmds
Definition intro.c:57
intro_opcode_t
The possible display operations.
Definition intro.c:42
@ INTRO_TEXT
Definition intro.c:43
@ INTRO_FADEIN
Definition intro.c:44
@ INTRO_FADEOUT
Definition intro.c:45
static int intro_draw_text(char **const sb_list, int sb_size, int sb_index, double offset, double line_height)
Draw intro text onto the screen.
Definition intro.c:280
static int intro_event_handler(int *stop, double *offset, double *vel)
Handle user events (mouse clicks, key presses, etc.).
Definition intro.c:216
#define SIDE_MARGIN
Definition intro.c:30
static int intro_load(const char *text)
Loads the intro stuff.
Definition intro.c:80
#define IMAGE_WIDTH
Definition intro.c:31
static void load_image(intro_img_t *img, const char *img_file)
Initialize an intro_img_t to default values.
Definition intro.c:162
static void intro_cleanup(void)
Cleans up the intro stuff.
Definition intro.c:131
int intro_display(const char *text, const char *mus)
Displays the introduction sequence.
Definition intro.c:323
static char * intro_buf
Definition intro.c:59
static void intro_fade_image_in(intro_img_t *side, intro_img_t *transition, const char *img_file)
Fade an image in.
Definition intro.c:180
static glFont intro_font
Definition intro.c:61
Handles the important game menus.
int menu_askQuit(void)
Menu to ask if player really wants to quit.
Definition menu.c:701
int music_choose(const char *situation)
Actually runs the music stuff, based on situation.
Definition music.c:426
int music_stop(int disable)
Stops the loaded music.
Definition music.c:267
void music_update(double dt)
Updates the music.
Definition music.c:68
void naev_quit(void)
Flags naev to quit.
Definition naev.c:145
void naev_resize(void)
Wrapper for gl_resize that handles non-GL reinitialization.
Definition naev.c:874
int naev_isQuit(void)
Get if Naev is trying to quit.
Definition naev.c:153
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
char * strndup(const char *s, size_t n)
Return a pointer to a new string, which is a duplicate of the string s (or, if necessary,...
Definition nstring.c:69
glInfo gl_screen
Definition opengl.c:47
void gl_renderScale(const glTexture *texture, double bx, double by, double bw, double bh, const glColour *c)
Blits a texture scaling it.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
Represents a font in memory.
Definition font.h:17
The state of a line iteration. This matches the process of rendering text into an on-screen box: An e...
Definition font.h:44
const char * text
Definition font.h:45
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
A display command (operation code and operand if applicable).
Definition intro.c:49
Intro Image: to be displayed to the side of the scrolling.
Definition intro.c:34
void toolkit_clearKey(void)
Clears the registered keys.
Definition toolkit.c:2051