1 /*
2 *
3 * Copyright (c) 1994, 2002, 2003 Johannes Prix
4 * Copyright (c) 1994, 2002 Reinhard Prix
5 * Copyright (c) 2004-2010 Arthur Huillet
6 *
7 *
8 * This file is part of Freedroid
9 *
10 * Freedroid is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * Freedroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Freedroid; see the file COPYING. If not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25 /**
26 * This file contains graphics primitives, such as initialisation of SDL
27 * and video modes.
28 */
29 #define _graphics_c
30
31 #include "pngfuncs.h"
32
33 #include "system.h"
34
35 #include "defs.h"
36 #include "struct.h"
37 #include "global.h"
38 #include "proto.h"
39 #include "map.h"
40 #include "widgets/widgets.h"
41
42 static const SDL_VideoInfo *vid_info;
43
44 /**
45 * We always want to blit our own mouse cursor.
46 */
blit_mouse_cursor(void)47 void blit_mouse_cursor(void)
48 {
49 static int loaded = FALSE;
50 int i;
51 static struct image mouse_cursors[16];
52 char constructed_filename[2000];
53 int cursor_index = (-1);
54 point cursoff = { 0, 0 };
55
56 // On the first function call ever, we load the surfaces for the
57 // flags into memory.
58 //
59 if (!loaded) {
60 for (i = 0; i < 10; i++) {
61 sprintf(constructed_filename, "cursors/mouse_cursor_%04d.png", i);
62 load_image(&mouse_cursors[i], constructed_filename, NO_MOD);
63 }
64 loaded = TRUE;
65 }
66
67 switch (mouse_cursor) {
68 case MOUSE_CURSOR_SCROLL_UP:
69 cursor_index = 4;
70 cursoff.x = -12;
71 break;
72 case MOUSE_CURSOR_SCROLL_DOWN:
73 cursor_index = 5;
74 cursoff.x = -12;
75 cursoff.y = -30;
76 break;
77 case MOUSE_CURSOR_NORMAL:
78 cursor_index = 0;
79 cursoff.x = -5;
80 cursoff.y = -4;
81 break;
82 case MOUSE_CURSOR_REPAIR:
83 cursor_index = 6;
84 break;
85 case MOUSE_CURSOR_SELECT_TOOL:
86 cursor_index = 9;
87 cursoff.x = -32;
88 cursoff.y = -16;
89 break;
90 case MOUSE_CURSOR_DRAGDROP_TOOL:
91 cursor_index = 3;
92 break;
93 default:
94 error_message(__FUNCTION__, "Illegal mouse cursor encountered: %d",
95 PLEASE_INFORM | IS_FATAL, mouse_cursor);
96 break;
97 }
98
99 // Blit the mouse cursor
100 display_image_on_screen(&mouse_cursors[cursor_index],
101 GetMousePos_x() + cursoff.x, GetMousePos_y() + cursoff.y, IMAGE_NO_TRANSFO);
102
103 // Reset the mouse cursor for next frame
104 mouse_cursor = MOUSE_CURSOR_NORMAL;
105 }
106
fade(int fade_delay,int direction)107 static void fade(int fade_delay, int direction)
108 {
109 SDL_Surface* bg = NULL;
110
111 Activate_Conservative_Frame_Computation();
112 AssembleCombatPicture(SHOW_ITEMS | NO_CURSOR);
113
114 if (use_open_gl) {
115 StoreMenuBackground(0);
116 } else {
117 bg = SDL_DisplayFormat(Screen);
118 }
119
120 Uint32 now = SDL_GetTicks();
121 Uint32 start = now;
122 while (now < start + fade_delay) {
123 Uint8 fade = 255 * ((float)(now - start)) / fade_delay;
124
125 if (direction < 0)
126 fade = 255 - fade;
127
128 if (!use_open_gl) {
129 SDL_SetAlpha(bg, SDL_SRCALPHA | SDL_RLEACCEL, fade);
130 SDL_FillRect(Screen, NULL, 0);
131 SDL_BlitSurface(bg, NULL, Screen, NULL);
132 } else {
133 #ifdef HAVE_LIBGL
134 glColor4ub(fade, fade, fade, 255);
135 RestoreMenuBackground(0);
136 glColor4ub(255, 255, 255, 255);
137 #endif
138 }
139 blit_mouse_cursor();
140 our_SDL_flip_wrapper();
141 now = SDL_GetTicks();
142 }
143
144 if (!use_open_gl && bg)
145 SDL_FreeSurface(bg);
146 }
147
148 /**
149 * Effect to fade entire screen to black.
150 */
fade_out_screen(void)151 void fade_out_screen(void)
152 {
153 if (!GameConfig.do_fadings)
154 return;
155 fade(180, -1);
156 }
157
158 /**
159 * Effect to fade entire screen from black.
160 */
fade_in_screen(void)161 void fade_in_screen(void)
162 {
163 if (!GameConfig.do_fadings)
164 return;
165 fade(180, 1);
166 }
167
168 /**
169 * In the shop interface, when an item was selected that could be grouped
170 * together in inventory, we showed three mouse buttons to either buy 1,
171 * buy 10 or buy 100 or the similar thing for selling items.
172 * But now Bastian has proposed a new number selector design with a scale
173 * and a small knob to set the right number of items you wish to have and
174 * also with small buttons left and right for some fine tuning.
175 * This function is intended to handle this number selection process.
176 * It will accept the range allowed and do the complete selection process
177 * with the user until he presses 'OK' on the scale screen.
178 */
do_graphical_number_selection_in_range(int lower_range,int upper_range,int default_value,int unit_price)179 int do_graphical_number_selection_in_range(int lower_range, int upper_range, int default_value, int unit_price)
180 {
181 static struct image selection_knob = EMPTY_IMAGE;
182 int ok_button_was_pressed = FALSE;
183 int escape_button_was_pressed = FALSE;
184 int knob_start_x = UNIVERSAL_COORD_W(200);
185 int knob_end_x = UNIVERSAL_COORD_W(390);
186 if (!(upper_range - lower_range))
187 return upper_range;
188 float knob_step_size = (float)(knob_end_x - knob_start_x) / (float)(upper_range - lower_range);
189 int knob_offset_x = ceilf((float)(default_value * knob_step_size));
190 int knob_is_grabbed = FALSE;
191 int knob_at = default_value;
192 int delta = 0;
193 int old_knob_at = 0;
194 SDL_Event event;
195 SDL_Rect knob_target_rect;
196 int wait_before_repeat = 0; // 0: apply once only - 1: apply once and wait before repeat - 2: repeat after delay
197 Uint32 time_before_repeat = 0;
198
199 /* Initialize the text widget. */
200 static struct widget_text item_description;
201 widget_text_init(&item_description, "");
202 widget_set_rect(WIDGET(&item_description), UNIVERSAL_COORD_W(310), UNIVERSAL_COORD_H(180), UNIVERSAL_COORD_W(75), UNIVERSAL_COORD_H(45));
203 item_description.font = FPS_Display_Font;
204 item_description.line_height_factor = 1.0;
205
206 int old_game_status = game_status;
207
208 StoreMenuBackground(1);
209
210 if (!image_loaded(&selection_knob)) {
211 load_image(&selection_knob, "mouse_buttons/number_selector_selection_knob.png", NO_MOD);
212 }
213
214 knob_target_rect.w = selection_knob.w;
215 knob_target_rect.h = selection_knob.h;
216
217 while (!ok_button_was_pressed && !escape_button_was_pressed) {
218 RestoreMenuBackground(1);
219 blit_background("number_selector.png");
220 ShowGenericButtonFromList(NUMBER_SELECTOR_OK_BUTTON);
221 knob_target_rect.x = knob_start_x + knob_offset_x - selection_knob.w / 2;
222 knob_target_rect.y = UNIVERSAL_COORD_H(260) - selection_knob.h / 2;
223 display_image_on_screen(&selection_knob, knob_target_rect.x, knob_target_rect.y, IMAGE_NO_TRANSFO);
224
225 if (old_knob_at != knob_at) {
226 item_description.scroll_offset = -3;
227 free_autostr(item_description.text);
228 item_description.text = alloc_autostr(100);
229 autostr_append(item_description.text, "%d\n", knob_at);
230 if (unit_price) {
231 // TRANSLATORS: Used on object selector's widget to display unit price * number of objects
232 autostr_append(item_description.text, _("%d price\n"), unit_price * knob_at);
233 }
234 old_knob_at = knob_at;
235 }
236 widget_text_display(WIDGET(&item_description));
237
238 blit_mouse_cursor();
239 our_SDL_flip_wrapper();
240 SDL_framerateDelay(&SDL_FPSmanager);
241
242 while (SDL_PollEvent(&event)) {
243
244 if (event.type == SDL_QUIT) {
245 Terminate(EXIT_SUCCESS);
246 } else if (event.type == SDL_KEYDOWN) {
247 switch(event.key.keysym.sym) {
248 case SDLK_LEFT:
249 delta = -1;
250 wait_before_repeat = 0; // apply once only
251 break;
252 case SDLK_RIGHT:
253 delta = 1;
254 wait_before_repeat = 0; // apply once only
255 break;
256 case SDLK_DOWN:
257 knob_at = lower_range;
258 break;
259 case SDLK_UP:
260 knob_at = upper_range;
261 break;
262 case SDLK_RETURN:
263 ok_button_was_pressed = TRUE;
264 break;
265 default:
266 break;
267 }
268 } else if (event.type == SDL_KEYUP) {
269 delta = 0;
270 if (event.key.keysym.sym == SDLK_ESCAPE) {
271 escape_button_was_pressed = TRUE;
272 }
273 } else if (event.type == SDL_MOUSEBUTTONDOWN) {
274 if (event.button.button == SDL_BUTTON_WHEELDOWN || event.button.button == SDL_BUTTON_WHEELUP) {
275 knob_at = max(lower_range, min(upper_range, knob_at + (event.button.button == SDL_BUTTON_WHEELDOWN ? -1 : 1)));
276 } else if (event.button.button == SDL_BUTTON_LEFT) {
277 // Is event within knob's height?
278 if (abs(event.button.y - (knob_target_rect.y + knob_target_rect.h / 2)) < knob_target_rect.h) {
279 if (abs(event.button.x - (knob_target_rect.x + knob_target_rect.w / 2)) < knob_target_rect.w) {
280 knob_is_grabbed = TRUE;
281 } else if (event.button.x >= knob_start_x && event.button.x <= knob_end_x) {
282 knob_at = (event.button.x - knob_start_x) / knob_step_size;
283 knob_offset_x = (knob_at - lower_range) * knob_step_size;
284 knob_is_grabbed = TRUE;
285 }
286 }
287
288 if (MouseCursorIsOnButton(NUMBER_SELECTOR_OK_BUTTON, event.button.x, event.button.y)) {
289 ok_button_was_pressed = TRUE;
290 } else if (MouseCursorIsOnButton(NUMBER_SELECTOR_LEFT_BUTTON, event.button.x, event.button.y)) {
291 delta = -1;
292 wait_before_repeat = 1; // apply once and wait before repeat
293 } else if (MouseCursorIsOnButton(NUMBER_SELECTOR_RIGHT_BUTTON, event.button.x, event.button.y)) {
294 delta = 1;
295 wait_before_repeat = 1; // apply once and wait before repeat
296 }
297 }
298 } else if (event.type == SDL_MOUSEBUTTONUP) {
299 if (event.button.button == SDL_BUTTON_LEFT) {
300 delta = 0;
301 knob_is_grabbed = FALSE;
302 }
303 }
304
305 if (knob_is_grabbed) {
306 if (GetMousePos_x() - knob_start_x < (knob_at - lower_range) * knob_step_size) {
307 knob_at = max(lower_range, min(upper_range, (GetMousePos_x() - knob_start_x + knob_step_size) / knob_step_size));
308 } else {
309 knob_at = max(lower_range, min(upper_range, (GetMousePos_x() - knob_start_x) / knob_step_size));
310 }
311
312 if (knob_offset_x < 0) {
313 knob_offset_x = 0;
314 } else if (knob_offset_x >= knob_end_x - knob_start_x) {
315 knob_offset_x = knob_end_x - knob_start_x;
316 }
317 }
318 }
319
320 if (delta != 0) {
321 if (wait_before_repeat == 0) { // apply once only (when numkeys are used to change the value)
322 knob_at = max(lower_range, min(upper_range, knob_at + delta));
323 delta = 0;
324 } else if (wait_before_repeat == 1) { // apply once and wait before repeat
325 knob_at = max(lower_range, min(upper_range, knob_at + delta));
326 wait_before_repeat = 2; // repeat after delay
327 time_before_repeat = SDL_GetTicks() + 500; // 500ms before starting to repeat
328 } else if ((wait_before_repeat == 2) && (SDL_GetTicks() > time_before_repeat)) { // repeat apply
329 knob_at = max(lower_range, min(upper_range, knob_at + delta));
330 SDL_Delay(50); // 50ms interval between 2 repeats
331 }
332 }
333
334 knob_offset_x = (knob_at - lower_range) * knob_step_size;
335 }
336
337 game_status = old_game_status;
338 return (!escape_button_was_pressed ? knob_at : 0);
339
340 }; // int do_graphical_number_selection_in_range ( int lower_range , int upper_range )
341
342 /**
343 * Return the pixel value at (x, y).
344 *
345 * NOTE: The surface must be locked before calling this!
346 *
347 * @param surf The surface where we want to retrieve the pixel.
348 * @param x The X position of the pixel.
349 * @param y The Y position of the pixel.
350 * @return The pixel value at (x, y) on success, zero on failure.
351 */
sdl_get_pixel(SDL_Surface * surf,int x,int y)352 uint32_t sdl_get_pixel(SDL_Surface *surf, int x, int y)
353 {
354 int bpp = surf->format->BytesPerPixel;
355 uint8_t *p = (uint8_t *)surf->pixels + y * surf->pitch + x * bpp;
356
357 switch (bpp) {
358 case 1:
359 return *p;
360 break;
361 case 2:
362 return *(uint16_t *)p;
363 break;
364 case 3:
365 if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
366 return p[0] << 16 | p[1] << 8 | p[2];
367 else
368 return p[0] | p[1] << 8 | p[2] << 16;
369 break;
370 case 4:
371 return *(uint32_t *)p;
372 break;
373 default:
374 /* shouldn't happen, but avoids warnings */
375 return 0;
376 }
377 }
378
sdl_put_pixel(SDL_Surface * surf,int x,int y,uint8_t red,uint8_t green,uint8_t blue,uint8_t alpha)379 void sdl_put_pixel(SDL_Surface *surf, int x, int y, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
380 {
381 int bpp = surf->format->BytesPerPixel;
382 uint32_t color;
383 uint8_t *p;
384
385 if ((x < 0) || (y < 0) || (x >= surf->w) || (y >= surf->h))
386 return;
387
388 color = SDL_MapRGBA(surf->format, red, green, blue, alpha);
389
390 p = (Uint8 *) surf->pixels + y * surf->pitch + x * bpp;
391
392 switch (bpp) {
393 case 1:
394 *p = color;
395 break;
396 case 2:
397 *(Uint16 *) p = color;
398 break;
399 case 3:
400 if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
401 p[0] = (color >> 16) & 0xff;
402 p[1] = (color >> 8) & 0xff;
403 p[2] = color & 0xff;
404 } else {
405 p[0] = color & 0xff;
406 p[1] = (color >> 8) & 0xff;
407 p[2] = (color >> 16) & 0xff;
408 }
409 break;
410 case 4:
411 *(Uint32 *) p = color;
412 break;
413 }
414 }
415
add_val_to_component(Uint8 component,int value)416 static Uint8 add_val_to_component(Uint8 component, int value)
417 {
418 int tmp;
419
420 // Calculate the new value and check overflow/underflow.
421 tmp = component + value;
422 tmp = (tmp > 255) ? 255 : tmp;
423 tmp = (tmp < 0) ? 0 : tmp;
424
425 return (Uint8)tmp;
426 }
get_components(SDL_Surface * surf,int x,int y,uint8_t * r,uint8_t * g,uint8_t * b,uint8_t * a)427 static void get_components(SDL_Surface *surf, int x, int y, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a)
428 {
429 SDL_PixelFormat *fmt = surf->format;
430
431 SDL_LockSurface(surf);
432 Uint32 pixel = *((Uint32 *) (((Uint8 *) (surf->pixels)) + (x + y * surf->w) * surf->format->BytesPerPixel));
433 SDL_UnlockSurface(surf);
434
435 *r = (pixel >> fmt->Rshift) & 0xFF;
436 *g = (pixel >> fmt->Gshift) & 0xFF;
437 *b = (pixel >> fmt->Bshift) & 0xFF;
438 *a = (pixel >> fmt->Ashift) & 0xFF;
439 }
440
sdl_create_colored_surface(SDL_Surface * surf,float r,float g,float b,float a,int highlight)441 SDL_Surface *sdl_create_colored_surface(SDL_Surface *surf, float r, float g, float b, float a, int highlight)
442 {
443 uint8_t red, green, blue, alpha;
444 SDL_Surface *colored_surf;
445 int x, y;
446
447 colored_surf = SDL_DisplayFormatAlpha(surf);
448
449 for (y = 0; y < surf->h; y++) {
450 for (x = 0; x < surf->w; x++) {
451
452 get_components(surf, x, y, &red, &green, &blue, &alpha);
453
454 if (r != 1.0)
455 red *= r;
456 if (g != 1.0)
457 green *= g;
458 if (b != 1.0)
459 blue *= b;
460 if (a != 1.0)
461 alpha *= a;
462
463 if (highlight) {
464 red = add_val_to_component(red, 64);
465 green = add_val_to_component(green, 64);
466 blue = add_val_to_component(blue, 64);
467 }
468
469 sdl_put_pixel(colored_surf, x, y, red, green, blue, alpha);
470 }
471 }
472
473 return colored_surf;
474 }
475
476 /**
477 *
478 *
479 */
get_standard_iso_floor_tile_size(void)480 static void get_standard_iso_floor_tile_size(void)
481 {
482 // iso_miscellaneous_floor_0000 dimensions
483 #define TILE_WIDTH 136
484 #define TILE_HEIGHT 69
485
486 if (TILE_WIDTH % 2)
487 iso_floor_tile_width = TILE_WIDTH - 3;
488 else
489 iso_floor_tile_width = TILE_WIDTH - 2;
490
491 if (TILE_HEIGHT % 2)
492 iso_floor_tile_height = TILE_HEIGHT - 3;
493 else
494 iso_floor_tile_height = TILE_HEIGHT - 2;
495
496 iso_floor_tile_width_over_two = iso_floor_tile_width / 2;
497 iso_floor_tile_height_over_two = iso_floor_tile_height / 2;
498 }
499
500 /* -----------------------------------------------------------------
501 * This function does all the bitmap initialisation, so that you
502 * later have the bitmaps in perfect form in memory, ready for blitting
503 * them to the screen.
504 * ----------------------------------------------------------------- */
InitPictures(void)505 void InitPictures(void)
506 {
507 // First thing to do is get the size of a typical isometric
508 // floor tile, i.e. height and width of the corresponding graphics
509 // bitmap
510 //
511 get_standard_iso_floor_tile_size();
512
513 // Loading all these pictures might take a while...
514 // and we do not want do deal with huge frametimes, which
515 // could box the influencer out of the ship....
516 Activate_Conservative_Frame_Computation();
517
518 load_floor_tiles();
519
520 next_startup_percentage(19);
521
522 load_all_obstacles(TRUE);
523
524 if (!GameConfig.lazyload) {
525 load_all_items();
526 // maybe also load all enemy models here?
527 }
528
529 Load_Blast_Surfaces();
530
531 next_startup_percentage(5);
532
533 Load_Mouse_Move_Cursor_Surfaces();
534
535 iso_load_bullet_surfaces();
536
537 next_startup_percentage(3);
538 }
539
540 /**
541 * This function initializes the timer subsystem.
542 */
init_timer(void)543 void init_timer(void)
544 {
545 // Now SDL_TIMER is initialized here:
546 //
547 if (SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
548 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
549 Terminate(EXIT_FAILURE);
550 }
551 }
552
553 /**
554 * This function checks if the availability of OpenGL libraries (at compile
555 * time) and request of OpenGL graphics output are compatible with each
556 * other... If not, we just disable OpenGL output method...
557 */
check_open_gl_libraries_present(void)558 static void check_open_gl_libraries_present(void)
559 {
560 // Here we introduce some warning output in case open_gl output is
561 // requested while the game was compiled without having the GL libs...
562 //
563 // The solution in this case is to force open_gl switch off again and
564 // to (forcefully) print out a warning message about this!
565 //
566 if (use_open_gl) {
567 #ifndef HAVE_LIBGL
568 DebugPrintf(-100, "\n**********************************************************************\
569 \n*\
570 \n* W A R N I N G ! ! ! \
571 \n*\
572 \n* You have requested OpenGL output via command line switch (-o parameter)\
573 \n* but you (or someone else) compiled this version of FreedroidRPG without\
574 \n* having the necessary OpenGL libraries on your (his/her) system. \
575 \n*\
576 \n* FreedroidRPG will now fallback to normal SDL output (which might be a\
577 \n* lot slower than the OpenGL method.\n\
578 \n*\
579 \n* You might try setting appropriate speed optimization parameters in the\
580 \n* 'performance tweaks' menu, in case you run into speed trouble.\
581 \n*\
582 \n* If you prefer to use OpenGL output, please make sure that you have \
583 \n* libGL installed on your system and recompile FreedroidRPG.\
584 \n*\
585 \n***********************************************************************\
586 \n");
587 use_open_gl = FALSE;
588 #endif
589 }
590 }; // void check_open_gl_libraries_present ( void )
591
592 /**
593 * This function should display the driver info obtained from the OpenGL
594 * libraries. This should be in a function of it's own (like now) to
595 * make sure that the OpenGL error checks in the video mode set functions
596 * and that seem to be occurring so frequently are not coming from this
597 * chunk of code.
598 */
599 #ifdef HAVE_LIBGL
show_open_gl_driver_info(void)600 static void show_open_gl_driver_info(void)
601 {
602 // Since we want to use openGl, it might be good to check the OpenGL vendor string
603 // provided by the graphics driver. Let's see...
604 //
605 fprintf(stderr, "\n-OpenGL--------------------------------------------------------------------------");
606 fprintf(stderr, "\nVendor : %s", glGetString(GL_VENDOR));
607 open_gl_check_error_status(__FUNCTION__);
608 fprintf(stderr, "\nRenderer : %s", glGetString(GL_RENDERER));
609 open_gl_check_error_status(__FUNCTION__);
610 fprintf(stderr, "\nVersion : %s", glGetString(GL_VERSION));
611 open_gl_check_error_status(__FUNCTION__);
612 fprintf(stderr, "\nExtensions : %s", glGetString(GL_EXTENSIONS));
613 open_gl_check_error_status(__FUNCTION__);
614 fprintf(stderr, "\n\n");
615 }
616 #endif
617
618 /**
619 * This function sets the OpenGL double buffering attribute. We do this
620 * in a separate function, so that eventual errors (and bug reports) from
621 * the OpenGL error checking can be attributed to a source more easily.
622 */
623 #ifdef HAVE_LIBGL
set_double_buffering_attribute(void)624 static void set_double_buffering_attribute(void)
625 {
626 if (SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)) {
627 error_message(__FUNCTION__, "\
628 Unable to set SDL_GL_DOUBLEBUFFER attribute!", PLEASE_INFORM | IS_FATAL);
629 }
630 // Since the OpenGL stuff hasn't been initialized yet, it's normal
631 // to get an GL_INVALID_OPERATION here, if we would really do the
632 // check. So better refrain from OpenGL error checking here...
633 //
634 // open_gl_check_error_status ( __FUNCTION__ );
635 }
636 #endif
637
638 /**
639 * This function is supposed to set the video mode in the case that
640 * OpenGL is used for graphics output. The function is highly split up
641 * into subfunctions now, so that the OpenGL error checking will be more
642 * precise. Typically it's in here that most problems occur when there
643 * is a peculiar OpenGL driver used, mostly under the Windows operating
644 * system.
645 */
set_video_mode_for_open_gl(void)646 static void set_video_mode_for_open_gl(void)
647 {
648 #ifdef HAVE_LIBGL
649
650 Uint32 video_flags = 0; // flags for SDL video mode
651 int video_mode_ok_check_result;
652 int buffer_size, depth_size, red_size, green_size, blue_size, alpha_size;
653 // We need OpenGL double buffering, so we request it. If we
654 // can't get it, something must be wrong, maybe an extremely bad
655 // card/driver is present or some bad emulation. Anyway, we'll
656 // break off...
657 //
658 set_double_buffering_attribute();
659
660 // Now we start setting up the proper OpenGL flags to pass to the
661 // SDL for creating the initial output window...
662 //
663 video_flags = SDL_OPENGL; /* Enable OpenGL in SDL */
664
665 // Now according to the document http://sdldoc.csn.ul.ie/guidevideoopengl.php
666 // we do need the SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER, 1 ) and NOT
667 // this here...
668 //
669
670 if (GameConfig.fullscreen_on)
671 video_flags |= SDL_FULLSCREEN;
672 if (vid_info->hw_available)
673 video_flags |= SDL_HWSURFACE;
674 else
675 video_flags |= SDL_SWSURFACE;
676
677 if (vid_info->blit_hw)
678 video_flags |= SDL_HWACCEL;
679
680 // We have 24 bit (or 32 bit) color depth in some of the graphics used,
681 // like e.g. backgrounds produced by Basse, so we try to get close to
682 // a target color depth of 24, or 32.
683 //
684 vid_bpp = 32;
685
686 // First we check to see if the mode we wish to set is really supported. If it
687 // isn't supported, then we cancel the whole operation...
688 //
689 video_mode_ok_check_result = SDL_VideoModeOK(GameConfig.screen_width, GameConfig.screen_height, vid_bpp, video_flags);
690 switch (video_mode_ok_check_result) {
691 case 0:
692 error_message(__FUNCTION__,
693 "SDL reported that the video mode (%d x %d) mentioned above is not supported\n"
694 "To see all possible resolutions please run 'freedroidRPG -r99'\n"
695 "Resetting to current resolution (%d x %d)...",
696 NO_REPORT,
697 GameConfig.screen_width, GameConfig.screen_height,
698 vid_info->current_w, vid_info->current_h);
699 //resetting configuration file to current (desktop) settings
700 GameConfig.screen_width = vid_info->current_w;
701 GameConfig.screen_height = vid_info->current_h;
702 GameConfig.next_time_width_of_screen = GameConfig.screen_width;
703 GameConfig.next_time_height_of_screen = GameConfig.screen_height;
704 break;
705 default:
706 DebugPrintf(-4, "\nTesting if color depth %d bits is available... ", vid_bpp);
707 if (video_mode_ok_check_result == vid_bpp) {
708 DebugPrintf(-4, "YES.");
709 } else {
710 DebugPrintf(-4, "NO! \nThe closest we will get is %d bits per pixel.", video_mode_ok_check_result);
711 /*
712 ErrorMessage ( __FUNCTION__ , "\
713 SDL reported, that the video mode mentioned \nabove is not supported UNDER THE COLOR DEPTH MENTIONED ABOVE!\n\
714 We'll be using the alternate color depth given above instead...",
715 PLEASE_INFORM );
716 */
717 vid_bpp = video_mode_ok_check_result;
718 }
719 }
720
721 // Now that we know which mode to go for, we can give it a try and get the
722 // output surface we want. Of course, some extra checking will be done, so
723 // that we know that the surface we're expecting is really there...
724 //
725 Screen = SDL_SetVideoMode(GameConfig.screen_width, GameConfig.screen_height, vid_bpp, video_flags);
726 if (!Screen) {
727 fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError());
728 Terminate(EXIT_FAILURE);
729 } else {
730 // open_gl_check_error_status ( __FUNCTION__ );
731 SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &buffer_size);
732 SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &red_size);
733 SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &green_size);
734 SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blue_size);
735 SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha_size);
736 SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth_size);
737 fprintf(stderr, "\n\nvideo mode set (bpp=%d RGBA=%d%d%d%d depth=%d)",
738 buffer_size, red_size, green_size, blue_size, alpha_size, depth_size);
739 }
740
741 show_open_gl_driver_info();
742
743 safely_initialize_our_default_open_gl_parameters();
744
745 // Maybe resize the window to standard size?
746 //
747 // resizeWindow( GameConfig . screen_width, GameConfig . screen_height );
748 //
749
750 // Now under win32 we're running into problems, because there seems to be some
751 // garbage in ONE OF THE TWO BUFFERS from the double-buffering. Maybe cleaning
752 // that out solves part of the problem. Well, not all, since there is still the
753 // dialog background not visible. But anyway, let's just clear the two buffers
754 // for now...
755 //
756 clear_screen();
757 our_SDL_flip_wrapper();
758 clear_screen();
759 our_SDL_flip_wrapper();
760 #endif
761 }
762
763 /**
764 * This function initialises the video display and opens up a
765 * window for graphics display.
766 */
InitVideo(void)767 void InitVideo(void)
768 {
769 char vid_driver[81];
770 Uint32 video_flags = 0; // flags for SDL video mode
771 char fpath[PATH_MAX];
772
773 // Tell SDL to center the window once we make it
774 putenv("SDL_VIDEO_CENTERED=1");
775
776 // Let's get some info about the whole system here. Is this a windows or x11 or
777 // mac or whatever graphical environment?
778 //
779 // NOTE: This has got NOTHING to do with OpenGL and OpenGL venour or the like yet...
780 //
781 if (SDL_VideoDriverName(vid_driver, 80)) {
782 DebugPrintf(-4, "\nVideo system type: %s.", vid_driver);
783 } else {
784 fprintf(stderr, "Video driver seems not to exist or initialization failure!\nError code: %s\n", SDL_GetError());
785 Terminate(EXIT_FAILURE);
786 }
787
788 // We check if the program has been compiled with OpenGL libraries present
789 // and take care of the case OpenGL output requested when compiled without
790 // those libs...
791 //
792 check_open_gl_libraries_present();
793
794 // We note the screen resolution used.
795 //
796 DebugPrintf(-4, "\nUsing screen resolution %d x %d.\n", GameConfig.screen_width, GameConfig.screen_height);
797
798 // We query the available video configuration on this system.
799 //
800 vid_info = SDL_GetVideoInfo();
801 if (!vid_info) {
802 fprintf(stderr, "Could not obtain video info via SDL: %s\n", SDL_GetError());
803 Terminate(EXIT_FAILURE);
804 }
805
806 if (use_open_gl) {
807 set_video_mode_for_open_gl();
808 } else {
809 if (GameConfig.fullscreen_on)
810 video_flags |= SDL_FULLSCREEN;
811
812 if (!SDL_VideoModeOK(GameConfig.screen_width, GameConfig.screen_height, 32, video_flags))
813 {
814 error_message(__FUNCTION__,
815 "SDL reported that the video mode (%d x %d) mentioned above is not supported\n"
816 "To see all possible resolutions please run 'freedroidRPG -r99'\n"
817 "Resetting to current resolution (%d x %d)...",
818 NO_REPORT,
819 GameConfig.screen_width, GameConfig.screen_height,
820 vid_info->current_w, vid_info->current_h);
821 //resetting configuration file to current (desktop) settings
822 GameConfig.screen_width = vid_info->current_w;
823 GameConfig.screen_height = vid_info->current_h;
824 GameConfig.next_time_width_of_screen = GameConfig.screen_width;
825 GameConfig.next_time_height_of_screen = GameConfig.screen_height;
826 }
827 if (!(Screen = SDL_SetVideoMode(GameConfig.screen_width, GameConfig.screen_height, 0, video_flags))) {
828 fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError());
829 Terminate(EXIT_FAILURE);
830 }
831 }
832
833 vid_bpp = 32; /* start with the simplest */
834
835 // End of possibly open-gl dependent initialisation stuff...
836 //
837 if (vid_info->wm_available) { /* if there's a window-manager */
838 char window_title_string[200];
839 sprintf(window_title_string, "FreedroidRPG %s", VERSION);
840 SDL_WM_SetCaption(window_title_string, "");
841
842 if (find_file(ICON_FILE, GRAPHICS_DIR, fpath, PLEASE_INFORM)) {
843 SDL_Surface *icon = IMG_Load(fpath);
844 SDL_WM_SetIcon(icon, NULL);
845 SDL_FreeSurface(icon);
846 }
847 }
848
849 init_fonts();
850
851 blit_background("startup1.jpg");
852 our_SDL_flip_wrapper();
853
854 SDL_SetGamma(GameConfig.current_gamma_correction, GameConfig.current_gamma_correction, GameConfig.current_gamma_correction);
855
856 mouse_cursor = MOUSE_CURSOR_NORMAL;
857 SDL_ShowCursor(SDL_DISABLE);
858
859 SDL_initFramerate(&SDL_FPSmanager);
860 SDL_setFramerate(&SDL_FPSmanager, 40);
861 }
862
863 /**
864 * Draw a colored rectangle on screen with alpha blending in SDL.
865 *
866 * @param rect The rectangular area.
867 * @param r The red color value.
868 * @param g The green color value.
869 * @param b The blue color value.
870 * @param a The alpha color value.
871 */
sdl_draw_rectangle(SDL_Rect * rect,int r,int g,int b,int a)872 void sdl_draw_rectangle(SDL_Rect *rect, int r, int g, int b, int a)
873 {
874 SDL_PixelFormat *fmt = Screen->format;
875 SDL_Surface *surface;
876
877 // SDL_FillRect and SDL_BlitSurface clip the rectangle to fit on the surface, but
878 // callers of sdl_draw_rectangle expect it to be unchanged, so store it.
879 SDL_Rect old_rect = (*rect);
880
881 if (a == SDL_ALPHA_OPAQUE) {
882 // Do a rectangle fill operation if the input rectangle is opaque.
883 SDL_FillRect(Screen, rect, SDL_MapRGB(Screen->format, r, g, b));
884
885 // Restore the old rectangle.
886 (*rect) = old_rect;
887 return;
888 }
889
890 // Create an empty surface with 32 bits per pixel in video memory. This will
891 // allow SDL to take advantage of video.
892 surface = SDL_CreateRGBSurface(SDL_HWSURFACE, rect->w, rect->h, 32, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
893
894 // Perform a fast fill of the whole rectangle with color.
895 SDL_FillRect(surface, NULL, SDL_MapRGB(Screen->format, r, g, b));
896
897 // Adjust the alpha properties of a surface and active the acceleration.
898 SDL_SetAlpha(surface, SDL_SRCALPHA | SDL_RLEACCEL, a);
899
900 // Blit the surface on screen and free it.
901 SDL_BlitSurface(surface, NULL, Screen, rect);
902 SDL_FreeSurface(surface);
903
904 // Restore the old rectangle.
905 (*rect) = old_rect;
906 }
907
draw_rectangle(SDL_Rect * rect,int r,int g,int b,int a)908 void draw_rectangle(SDL_Rect *rect, int r, int g, int b, int a)
909 {
910 if (use_open_gl) {
911 gl_draw_rectangle(rect, r, g, b, a);
912 } else {
913 sdl_draw_rectangle(rect, r, g, b, a);
914 }
915 }
916
sdl_draw_quad(const int16_t vx[4],const int16_t vy[4],int r,int g,int b,int a)917 static void sdl_draw_quad(const int16_t vx[4], const int16_t vy[4], int r, int g, int b, int a)
918 {
919 filledPolygonRGBA(Screen, vx, vy, 4, r, g, b, a);
920 }
921
922 /**
923 * Pay attention when you use this function because we must not use it when you
924 * are inside a start_image_batch()/end_image_batch() operation but at the moment,
925 * we do not have the choice.
926 */
gl_draw_quad(const int16_t vx[4],const int16_t vy[4],int r,int g,int b,int a)927 static void gl_draw_quad(const int16_t vx[4], const int16_t vy[4], int r, int g, int b, int a)
928 {
929 #ifdef HAVE_LIBGL
930 glDisable(GL_TEXTURE_2D);
931 glColor4ub(r, g, b, a);
932
933 glBegin(GL_QUADS);
934 glVertex2i(vx[0], vy[0]);
935 glVertex2i(vx[1], vy[1]);
936 glVertex2i(vx[2], vy[2]);
937 glVertex2i(vx[3], vy[3]);
938 glEnd();
939
940 glColor4ub(255, 255, 255, 255);
941 glEnable(GL_TEXTURE_2D);
942 #endif
943 }
944
draw_quad(const int16_t vx[4],const int16_t vy[4],int r,int g,int b,int a)945 void draw_quad(const int16_t vx[4], const int16_t vy[4], int r, int g, int b, int a)
946 {
947 if (use_open_gl) {
948 gl_draw_quad(vx, vy, r, g, b, a);
949 } else {
950 sdl_draw_quad(vx, vy, r, g, b, a);
951 }
952 }
953
954 /**
955 * This function draws a transparent black rectangle over a specified
956 * area on the screen.
957 */
ShadowingRectangle(SDL_Surface * Surface,SDL_Rect Area)958 void ShadowingRectangle(SDL_Surface * Surface, SDL_Rect Area)
959 {
960 draw_rectangle(&Area, 0, 0, 0, 150);
961 }
962
963 /**
964 * This function draws a transparent white rectangle over a specified
965 * area on the screen.
966 */
HighlightRectangle(SDL_Surface * Surface,SDL_Rect Area)967 void HighlightRectangle(SDL_Surface * Surface, SDL_Rect Area)
968 {
969 draw_rectangle(&Area, 255, 255, 255, 100);
970 }
971
972 /*
973 * Draw an 'expanded' pixel.
974 * Used to draw thick lines.
975 */
draw_expanded_pixel(SDL_Surface * Surface,int x,int y,int xincr,int yincr,int thickness,int r,int g,int b)976 static void draw_expanded_pixel(SDL_Surface * Surface, int x, int y, int xincr, int yincr, int thickness, int r, int g, int b)
977 {
978 int i;
979
980 sdl_put_pixel(Surface, x, y, r, g, b, 255);
981
982 if (thickness <= 1)
983 return;
984 for (i = x + xincr; i != x + thickness * xincr; i += xincr) {
985 sdl_put_pixel(Surface, i, y, r, g, b, 255);
986 }
987 for (i = y + yincr; i != y + thickness * yincr; i += yincr) {
988 sdl_put_pixel(Surface, x, i, r, g, b, 255);
989 }
990 }
991
992 /**
993 * This function draws a line in SDL mode.
994 * Classical Bresenham algorithm
995 */
draw_line_sdl(SDL_Surface * Surface,int x1,int y1,int x2,int y2,int r,int g,int b,int thickness)996 static void draw_line_sdl(SDL_Surface *Surface, int x1, int y1, int x2, int y2, int r, int g, int b, int thickness)
997 {
998 if (use_open_gl)
999 return;
1000
1001 int delta_x, incr_x;
1002 int delta_y, incr_y;
1003 int error_accum;
1004
1005 // Algorithm initialization
1006
1007 delta_x = x2 - x1;
1008 incr_x = 1;
1009 if (delta_x < 0) {
1010 delta_x = -delta_x;
1011 incr_x = -1;
1012 }
1013
1014 delta_y = y2 - y1;
1015 incr_y = 1;
1016 if (delta_y < 0) {
1017 delta_y = -delta_y;
1018 incr_y = -1;
1019 }
1020 // Incremental line drawing
1021
1022 if (delta_y < delta_x) {
1023 error_accum = delta_x >> 1;
1024 while (x1 != x2) {
1025 draw_expanded_pixel(Surface, x1, y1, incr_x, incr_y, thickness, r, g, b);
1026 error_accum += delta_y;
1027 if (error_accum > delta_x) {
1028 error_accum -= delta_x;
1029 y1 += incr_y;
1030 }
1031 x1 += incr_x;
1032 }
1033 draw_expanded_pixel(Surface, x1, y1, incr_x, incr_y, thickness, r, g, b);
1034 } else {
1035 error_accum = delta_y >> 1;
1036 while (y1 != y2) {
1037 draw_expanded_pixel(Surface, x1, y1, incr_x, incr_y, thickness, r, g, b);
1038 error_accum += delta_x;
1039 if (error_accum > delta_y) {
1040 error_accum -= delta_y;
1041 x1 += incr_x;
1042 }
1043 y1 += incr_y;
1044 }
1045 draw_expanded_pixel(Surface, x1, y1, incr_x, incr_y, thickness, r, g, b);
1046 }
1047 }
1048
1049 /**
1050 * This function draws a line in OpenGL mode.
1051 */
draw_line_opengl(int x1,int y1,int x2,int y2,int r,int g,int b,int width)1052 static void draw_line_opengl(int x1, int y1, int x2, int y2, int r, int g, int b, int width)
1053 {
1054 #ifdef HAVE_LIBGL
1055 glLineWidth(width);
1056 glColor3ub(r, g, b);
1057
1058 glDisable(GL_TEXTURE_2D);
1059
1060 glBegin(GL_LINES);
1061 glVertex2i(x1, y1);
1062 glVertex2i(x2, y2);
1063 glEnd();
1064
1065 glEnable(GL_TEXTURE_2D);
1066
1067 glColor3ub(255, 255, 255);
1068 #endif
1069 }
1070
draw_line(float x1,float y1,float x2,float y2,uint8_t r,uint8_t g,uint8_t b,int width)1071 void draw_line(float x1, float y1, float x2, float y2, uint8_t r, uint8_t g, uint8_t b, int width)
1072 {
1073 if (!use_open_gl) {
1074 draw_line_sdl(Screen, x1, y1, x2, y2, r, g, b, width);
1075 } else {
1076 draw_line_opengl(x1, y1, x2, y2, r, g, b, width);
1077 }
1078 }
1079
draw_line_on_map(float x1,float y1,float x2,float y2,uint8_t r,uint8_t g,uint8_t b,int width)1080 void draw_line_on_map(float x1, float y1, float x2, float y2, uint8_t r, uint8_t g, uint8_t b, int width)
1081 {
1082 int c1, c2, r1, r2;
1083
1084 translate_map_point_to_screen_pixel(x1, y1, &r1, &c1);
1085 translate_map_point_to_screen_pixel(x2, y2, &r2, &c2);
1086
1087 draw_line(r1, c1, r2, c2, r, g, b, width);
1088 }
1089
1090 /**
1091 * This function saves a screenshot of the current game, scaled to the width number of pixels.
1092 * If width is set to 0, then no scaling is used.
1093 */
save_screenshot(const char * filename,int width)1094 void save_screenshot(const char *filename, int width)
1095 {
1096 Activate_Conservative_Frame_Computation();
1097 float scale_factor = width ? (float) width / GameConfig.screen_width : 1.0;
1098 SDL_Surface *screenshot = NULL;
1099
1100 #ifdef HAVE_LIBGL
1101 if (use_open_gl) {
1102 void *imgdata = malloc(GameConfig.screen_width * GameConfig.screen_height * 3);
1103 glPixelStorei(GL_PACK_ALIGNMENT, 1);
1104 glReadPixels(0, 0, GameConfig.screen_width, GameConfig.screen_height, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
1105
1106 // Now we need to make a real SDL surface from the raw image data we
1107 // have just extracted.
1108 //
1109 SDL_Surface *screen_surf =
1110 SDL_CreateRGBSurfaceFrom(imgdata, GameConfig.screen_width, GameConfig.screen_height, 24, 3 * GameConfig.screen_width,
1111 bmask, gmask, rmask, 0);
1112 screenshot = zoomSurface(screen_surf, scale_factor, scale_factor, 0);
1113 SDL_FreeSurface(screen_surf);
1114 free(imgdata);
1115 }
1116 #endif
1117 if (!use_open_gl)
1118 screenshot = zoomSurface(Screen, scale_factor, scale_factor, 0);
1119
1120 if (screenshot == NULL) {
1121 error_message(__FUNCTION__, "Cannot save image: %s", PLEASE_INFORM, filename);
1122 return;
1123 }
1124
1125 #ifdef HAVE_LIBGL
1126 if (use_open_gl) {
1127 flip_image_vertically(screenshot);
1128 }
1129 #endif
1130
1131 png_save_surface(filename, screenshot);
1132 SDL_FreeSurface(screenshot);
1133 }
1134
reload_graphics(void)1135 void reload_graphics(void)
1136 {
1137 free_floor_tiles();
1138 load_floor_tiles();
1139 free_obstacle_graphics();
1140 load_all_obstacles(FALSE);
1141 // Free all items graphics. Graphics will be loaded when needed.
1142 free_item_graphics();
1143 // Free all enemies graphics. Graphics for an enemy will be loaded
1144 // when the enemy is encountered.
1145 free_enemy_graphics();
1146 reload_tux_graphics();
1147 }
1148
1149 /**
1150 * Clear the screen.
1151 * This function will use either the glClear() or the SDL_FillRect()
1152 * depending on the current output method.
1153 */
clear_screen(void)1154 void clear_screen(void)
1155 {
1156 #ifdef HAVE_LIBGL
1157 if (use_open_gl) {
1158 glClear(GL_COLOR_BUFFER_BIT);
1159 return;
1160 }
1161 #endif
1162
1163 SDL_FillRect(Screen, NULL, SDL_MapRGB(Screen->format, 0, 0, 0));
1164 }
1165
1166 #undef _graphics_c
1167