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