1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <ctype.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <X11/Xlib.h>
25 #include <X11/Intrinsic.h>
26
27 #include <png.h>
28
29 /* utility */
30 #include "fcintl.h"
31 #include "log.h"
32 #include "mem.h"
33 #include "shared.h"
34 #include "support.h"
35
36 /* common */
37 #include "movement.h"
38 #include "unit.h"
39 #include "version.h"
40
41 /* client */
42 #include "client_main.h"
43 #include "climisc.h"
44 #include "colors.h"
45 #include "gui_main.h"
46 #include "mapview_g.h"
47 #include "options.h"
48 #include "tilespec.h"
49
50 #include "graphics.h"
51
52 struct sprite *intro_gfx_sprite;
53 struct sprite *radar_gfx_sprite;
54
55 Cursor cursors[CURSOR_LAST];
56
57 static struct sprite *ctor_sprite(Pixmap mypixmap, int width, int height);
58 static struct sprite *ctor_sprite_mask(Pixmap mypixmap, Pixmap mask,
59 int width, int height);
60
61 /**************************************************************************
62 Return whether the client supports given view type
63 **************************************************************************/
is_view_supported(enum ts_type type)64 bool is_view_supported(enum ts_type type)
65 {
66 switch (type) {
67 case TS_ISOMETRIC:
68 case TS_OVERHEAD:
69 return TRUE;
70 }
71
72 return FALSE;
73 }
74
75 /***************************************************************************
76 ...
77 ***************************************************************************/
78 #define COLOR_MOTTO_FACE "#2D71E3"
79
load_intro_gfx(void)80 void load_intro_gfx(void)
81 {
82 int tot, lin, y, w;
83 char s[64];
84 XColor face;
85 int have_face;
86 const char *motto = freeciv_motto();
87 XFontSetExtents *exts;
88 const char *radar_name;
89
90 /* metrics */
91
92 exts = XExtentsOfFontSet(main_font_set);
93 lin = exts->max_logical_extent.height;
94
95 /* get colors */
96
97 if (XParseColor(display, cmap, COLOR_MOTTO_FACE, &face)
98 && XAllocColor(display, cmap, &face)) {
99 have_face = TRUE;
100 } else {
101 face.pixel = get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel;
102 have_face = FALSE;
103 }
104
105 /* Main graphic */
106
107 intro_gfx_sprite = load_gfxfile(tileset_main_intro_filename(tileset));
108 tot = intro_gfx_sprite->width;
109
110 y = intro_gfx_sprite->height - (2 * lin);
111
112 w = XmbTextEscapement(main_font_set, motto, strlen(motto));
113 XSetForeground(display, font_gc, face.pixel);
114 XmbDrawString(display, intro_gfx_sprite->pixmap,
115 main_font_set, font_gc,
116 tot / 2 - w / 2, y,
117 motto, strlen(motto));
118
119 /* Minimap graphic */
120
121 radar_name = tileset_mini_intro_filename(tileset);
122
123 if (radar_name != NULL) {
124 radar_gfx_sprite = load_gfxfile(radar_name);
125 } else {
126 struct color *pcol = color_alloc(0, 0, 0);
127
128 radar_gfx_sprite = create_sprite(200, 75, pcol);
129
130 color_free(pcol);
131 }
132
133 tot = radar_gfx_sprite->width;
134
135 y = radar_gfx_sprite->height - (2 * lin +
136 1.5 * (exts->max_logical_extent.height + exts->max_logical_extent.y));
137
138 w = XmbTextEscapement(main_font_set, word_version(), strlen(word_version()));
139 XSetForeground(display, font_gc,
140 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
141 XmbDrawString(display, radar_gfx_sprite->pixmap,
142 main_font_set, font_gc,
143 (tot / 2 - w / 2) + 1, y + 1,
144 word_version(), strlen(word_version()));
145 XSetForeground(display, font_gc,
146 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
147 XmbDrawString(display, radar_gfx_sprite->pixmap,
148 main_font_set, font_gc,
149 tot / 2 - w / 2, y,
150 word_version(), strlen(word_version()));
151
152 y += lin;
153
154 fc_snprintf(s, sizeof(s), "%s", VERSION_STRING);
155
156 w = XmbTextEscapement(main_font_set, s, strlen(s));
157 XSetForeground(display, font_gc,
158 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
159 XmbDrawString(display, radar_gfx_sprite->pixmap,
160 main_font_set, font_gc,
161 (tot / 2 - w / 2) + 1, y + 1, s, strlen(s));
162 XSetForeground(display, font_gc,
163 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
164 XmbDrawString(display, radar_gfx_sprite->pixmap,
165 main_font_set, font_gc,
166 tot / 2 - w / 2, y, s, strlen(s));
167
168 y += lin;
169
170 w = XmbTextEscapement(main_font_set, client_string, strlen(client_string));
171 XSetForeground(display, font_gc,
172 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
173 XmbDrawString(display, radar_gfx_sprite->pixmap,
174 main_font_set, font_gc,
175 (tot / 2 - w / 2) + 1, y + 1, client_string, strlen(client_string));
176 XSetForeground(display, font_gc,
177 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
178 XmbDrawString(display, radar_gfx_sprite->pixmap,
179 main_font_set, font_gc,
180 tot / 2 - w / 2, y, client_string, strlen(client_string));
181
182 /* free colors */
183
184 if (have_face) {
185 XFreeColors(display, cmap, &(face.pixel), 1, 0);
186 }
187
188 /* done */
189
190 return;
191 }
192
193 /****************************************************************************
194 Create a new sprite by cropping and taking only the given portion of
195 the image.
196 ****************************************************************************/
crop_sprite(struct sprite * source,int x,int y,int width,int height,struct sprite * mask,int mask_offset_x,int mask_offset_y,float scale,bool smooth)197 struct sprite *crop_sprite(struct sprite *source,
198 int x, int y, int width, int height,
199 struct sprite *mask, int mask_offset_x, int mask_offset_y,
200 float scale, bool smooth)
201 {
202 Pixmap mypixmap, mymask;
203 GC plane_gc;
204
205 mypixmap = XCreatePixmap(display, root_window,
206 width, height, display_depth);
207 XCopyArea(display, source->pixmap, mypixmap, civ_gc,
208 x, y, width, height, 0, 0);
209
210 if (source->has_mask) {
211 mymask = XCreatePixmap(display, root_window, width, height, 1);
212
213 plane_gc = XCreateGC(display, mymask, 0, NULL);
214 XCopyArea(display, source->mask, mymask, plane_gc,
215 x, y, width, height, 0, 0);
216 XFreeGC(display, plane_gc);
217
218 if (mask) {
219 XGCValues values;
220
221 values.function = GXand;
222
223 plane_gc = XCreateGC(display, mymask, GCFunction, &values);
224 XCopyArea(display, mask->mask, mymask, plane_gc,
225 x - mask_offset_x, y - mask_offset_y, width, height, 0, 0);
226 XFreeGC(display, plane_gc);
227 }
228
229 return ctor_sprite_mask(mypixmap, mymask, width, height);
230 } else if (mask) {
231 mymask = XCreatePixmap(display, root_window, width, height, 1);
232
233 plane_gc = XCreateGC(display, mymask, 0, NULL);
234 XCopyArea(display, source->mask, mymask, plane_gc,
235 x, y, width, height, 0, 0);
236 XFreeGC(display, plane_gc);
237 return ctor_sprite_mask(mypixmap, mymask, width, height);
238 } else {
239 return ctor_sprite(mypixmap, width, height);
240 }
241 }
242
243 /****************************************************************************
244 Create a sprite with the given height, width and color.
245 ****************************************************************************/
create_sprite(int width,int height,struct color * pcolor)246 struct sprite *create_sprite(int width, int height, struct color *pcolor)
247 {
248 struct sprite *plrcolor;
249
250 fc_assert_ret_val(width > 0, NULL);
251 fc_assert_ret_val(height > 0, NULL);
252 fc_assert_ret_val(pcolor != NULL, NULL);
253
254 {
255 /* FIXME: I do not know why it works but the code below allows the creation
256 * of the needed player color sprites. */
257 fc_assert_ret_val(tileset != NULL, NULL);
258 struct sprite *psprite_dummy = get_basic_fog_sprite(tileset);
259 Pixmap mypixmap, mymask;
260 GC plane_gc;
261
262 mypixmap = XCreatePixmap(display, root_window, width, height,
263 display_depth);
264 mymask = XCreatePixmap(display, root_window, width, height, 1);
265 plane_gc = XCreateGC(display, mymask, 0, NULL);
266 XCopyArea(display, psprite_dummy->mask, mymask, plane_gc,
267 0, 0, width, height, 0, 0);
268 XFreeGC(display, plane_gc);
269 plrcolor = ctor_sprite_mask(mypixmap, mymask, width, height);
270 }
271
272 XSetForeground(display, fill_bg_gc, pcolor->color.pixel);
273 XFillRectangle(display, plrcolor->pixmap, fill_bg_gc, 0, 0, width, height);
274
275 return plrcolor;
276 }
277
278 /****************************************************************************
279 Find the dimensions of the sprite.
280 ****************************************************************************/
get_sprite_dimensions(struct sprite * sprite,int * width,int * height)281 void get_sprite_dimensions(struct sprite *sprite, int *width, int *height)
282 {
283 *width = sprite->width;
284 *height = sprite->height;
285 }
286
287 /***************************************************************************
288 ...
289 ***************************************************************************/
load_cursors(void)290 void load_cursors(void)
291 {
292 enum cursor_type cursor;
293 XColor white, black;
294 struct sprite *sprite;
295 int hot_x, hot_y;
296
297 white.pixel = get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel;
298 black.pixel = get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel;
299 XQueryColor(display, cmap, &white);
300 XQueryColor(display, cmap, &black);
301
302 for (cursor = 0; cursor < CURSOR_LAST; cursor++) {
303 sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y, 0);
304
305 /* FIXME: this is entirely wrong. It should be rewritten using
306 * XcursorImageLoadCursor. See gdkcursor-x11.c in the GTK sources for
307 * examples. */
308 cursors[cursor] = XCreatePixmapCursor(display,
309 sprite->mask, sprite->mask,
310 &white, &black, hot_x, hot_y);
311 }
312 }
313
314 /***************************************************************************
315 ...
316 ***************************************************************************/
ctor_sprite(Pixmap mypixmap,int width,int height)317 static struct sprite *ctor_sprite(Pixmap mypixmap, int width, int height)
318 {
319 struct sprite *mysprite=fc_malloc(sizeof(struct sprite));
320 mysprite->pixmap=mypixmap;
321 mysprite->width=width;
322 mysprite->height=height;
323 mysprite->ncols = 0;
324 mysprite->pcolorarray = NULL;
325 mysprite->has_mask=0;
326 return mysprite;
327 }
328
329 /***************************************************************************
330 ...
331 ***************************************************************************/
ctor_sprite_mask(Pixmap mypixmap,Pixmap mask,int width,int height)332 static struct sprite *ctor_sprite_mask(Pixmap mypixmap, Pixmap mask,
333 int width, int height)
334 {
335 struct sprite *mysprite=fc_malloc(sizeof(struct sprite));
336 mysprite->pixmap=mypixmap;
337 mysprite->mask=mask;
338
339 mysprite->width=width;
340 mysprite->height=height;
341 mysprite->pcolorarray = NULL;
342 mysprite->has_mask=1;
343 return mysprite;
344 }
345
346 /***************************************************************************
347 Returns the filename extensions the client supports
348 Order is important.
349 ***************************************************************************/
gfx_fileextensions(void)350 const char **gfx_fileextensions(void)
351 {
352 static const char *ext[] =
353 {
354 "png",
355 NULL
356 };
357
358 return ext;
359 }
360
361 /***************************************************************************
362 Converts an image to a pixmap...
363 ***************************************************************************/
image2pixmap(XImage * xi)364 static Pixmap image2pixmap(XImage *xi)
365 {
366 Pixmap ret;
367 XGCValues values;
368 GC gc;
369
370 ret = XCreatePixmap(display, root_window, xi->width, xi->height, xi->depth);
371
372 values.foreground = 1;
373 values.background = 0;
374 gc = XCreateGC(display, ret, GCForeground | GCBackground, &values);
375
376 XPutImage(display, ret, gc, xi, 0, 0, 0, 0, xi->width, xi->height);
377 XFreeGC(display, gc);
378 return ret;
379 }
380
381 /***************************************************************************
382 ...
383 ***************************************************************************/
load_gfxfile(const char * filename)384 struct sprite *load_gfxfile(const char *filename)
385 {
386 png_structp pngp;
387 png_infop infop;
388 png_int_32 width, height, x, y;
389 FILE *fp;
390 int npalette, ntrans;
391 png_colorp palette;
392 png_bytep trans;
393 png_bytep buf, pb;
394 png_uint_32 stride;
395 unsigned long *pcolorarray;
396 bool *ptransarray;
397 struct sprite *mysprite;
398 XImage *xi;
399 int has_mask;
400 png_byte color_type;
401 png_byte alpha;
402 bool pixel, reported;
403
404 fp = fc_fopen(filename, "rb");
405 if (!fp) {
406 log_fatal("Failed reading PNG file: \"%s\"", filename);
407 exit(EXIT_FAILURE);
408 }
409
410 pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
411 if (!pngp) {
412 log_fatal("Failed creating PNG struct");
413 exit(EXIT_FAILURE);
414 }
415
416 infop = png_create_info_struct(pngp);
417 if (!infop) {
418 log_fatal("Failed creating PNG struct");
419 exit(EXIT_FAILURE);
420 }
421
422 if (setjmp(png_jmpbuf(pngp))) {
423 log_fatal("Failed while reading PNG file: \"%s\"", filename);
424 exit(EXIT_FAILURE);
425 }
426
427 png_init_io(pngp, fp);
428
429 png_set_strip_16(pngp);
430 png_set_packing(pngp);
431
432 png_read_info(pngp, infop);
433 width = png_get_image_width(pngp, infop);
434 height = png_get_image_height(pngp, infop);
435 color_type = png_get_color_type(pngp, infop);
436
437 if (color_type == PNG_COLOR_TYPE_PALETTE) {
438 if (png_get_PLTE(pngp, infop, &palette, &npalette)) {
439 int i;
440 XColor *mycolors;
441
442 pcolorarray = fc_malloc(npalette * sizeof(*pcolorarray));
443
444 mycolors = fc_malloc(npalette * sizeof(*mycolors));
445
446 for (i = 0; i < npalette; i++) {
447 mycolors[i].red = palette[i].red << 8;
448 mycolors[i].green = palette[i].green << 8;
449 mycolors[i].blue = palette[i].blue << 8;
450 }
451
452 alloc_colors(mycolors, npalette);
453
454 for (i = 0; i < npalette; i++) {
455 pcolorarray[i] = mycolors[i].pixel;
456 }
457
458 free(mycolors);
459 } else {
460 log_fatal("PNG file has no palette: \"%s\"", filename);
461 exit(EXIT_FAILURE);
462 }
463
464 has_mask = png_get_tRNS(pngp, infop, &trans, &ntrans, NULL);
465
466 if (has_mask) {
467 int i;
468
469 ptransarray = fc_calloc(npalette, sizeof(*ptransarray));
470
471 reported = FALSE;
472 for (i = 0; i < ntrans; i++) {
473 if (trans[i] < npalette) {
474 ptransarray[trans[i]] = TRUE;
475 } else if (!reported) {
476 log_verbose("PNG: Transparent array entry is out of palette: "
477 "\"%s\"", filename);
478 reported = TRUE;
479 }
480 }
481 } else {
482 ptransarray = NULL;
483 }
484
485 } else {
486 pcolorarray = NULL;
487 ptransarray = NULL;
488 npalette = 0;
489 if ((color_type == PNG_COLOR_TYPE_RGB_ALPHA)
490 || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) {
491 has_mask = 1;
492 } else {
493 has_mask = 0;
494 }
495 ntrans = 0;
496 }
497
498 png_read_update_info(pngp, infop);
499
500 {
501 png_bytep *row_pointers;
502
503 stride = png_get_rowbytes(pngp, infop);
504 buf = fc_malloc(stride * height);
505
506 row_pointers = fc_malloc(height * sizeof(png_bytep));
507
508 for (y = 0, pb = buf; y < height; y++, pb += stride) {
509 row_pointers[y] = pb;
510 }
511
512 png_read_image(pngp, row_pointers);
513 png_read_end(pngp, infop);
514 fclose(fp);
515
516 free(row_pointers);
517 if (infop != NULL) {
518 png_destroy_read_struct(&pngp, &infop, (png_infopp)NULL);
519 } else {
520 log_error("PNG info struct is NULL (non-fatal): \"%s\"", filename);
521 png_destroy_read_struct(&pngp, (png_infopp)NULL, (png_infopp)NULL);
522 }
523 }
524
525 mysprite = fc_malloc(sizeof(*mysprite));
526
527
528 xi = XCreateImage(display, DefaultVisual(display, screen_number),
529 display_depth, ZPixmap, 0, NULL, width, height, 32, 0);
530 xi->data = fc_calloc(xi->bytes_per_line * xi->height, 1);
531
532 pb = buf;
533 for (y = 0; y < height; y++) {
534 for (x = 0; x < width; x++) {
535 if (pcolorarray) {
536 XPutPixel(xi, x, y, pcolorarray[pb[x]]);
537 } else {
538 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
539 XPutPixel(xi, x, y,
540 (pb[2 * x] << 16) + (pb[2 * x] << 8)
541 + pb[2 * x]);
542 } else {
543 if (has_mask) {
544 XPutPixel(xi, x, y,
545 (pb[4 * x] << 16) + (pb[4 * x + 1] << 8)
546 + pb[4 * x + 2]);
547 } else {
548 XPutPixel(xi, x, y,
549 (pb[3 * x] << 16) + (pb[3 * x + 1] << 8)
550 + pb[3 * x + 2]);
551 }
552 }
553 }
554 }
555 pb += stride;
556 }
557 mysprite->pixmap = image2pixmap(xi);
558 XDestroyImage(xi);
559
560 if (has_mask) {
561 XImage *xm;
562
563 xm = XCreateImage(display, DefaultVisual(display, screen_number),
564 1, XYBitmap, 0, NULL, width, height, 8, 0);
565 xm->data = fc_calloc(xm->bytes_per_line * xm->height, 1);
566
567 pb = buf;
568 for (y = 0; y < height; y++) {
569 for (x = 0; x < width; x++) {
570 if (ptransarray) {
571 XPutPixel(xm, x, y, !ptransarray[pb[x]]);
572 } else {
573 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
574 alpha = pb[2 * x + 1];
575 } else {
576 alpha = pb[4 * x + 3];
577 }
578 if (alpha > 204) {
579 pixel = FALSE;
580 } else if (alpha > 153) {
581 if ((y + x * 2) % 4 == 0) {
582 pixel = TRUE;
583 } else {
584 pixel = FALSE;
585 }
586 } else if (alpha > 102) {
587 if ((y + x) % 2 == 0) {
588 pixel = TRUE;
589 } else {
590 pixel = FALSE;
591 }
592 } else if (alpha > 51) {
593 if ((y + x * 2) % 4 == 0) {
594 pixel = FALSE;
595 } else {
596 pixel = TRUE;
597 }
598 } else {
599 pixel = TRUE;
600 }
601 XPutPixel(xm, x, y, !pixel);
602 }
603 }
604 pb += stride;
605 }
606 mysprite->mask = image2pixmap(xm);
607 XDestroyImage(xm);
608 }
609
610 mysprite->has_mask = has_mask;
611 mysprite->width = width;
612 mysprite->height = height;
613 mysprite->pcolorarray = pcolorarray;
614 mysprite->ncols = npalette;
615
616 if (ptransarray) {
617 free(ptransarray);
618 }
619 free(buf);
620 return mysprite;
621 }
622
623 /***************************************************************************
624 Deletes a sprite. These things can use a lot of memory.
625 ***************************************************************************/
free_sprite(struct sprite * s)626 void free_sprite(struct sprite *s)
627 {
628 XFreePixmap(display, s->pixmap);
629 if (s->has_mask) {
630 XFreePixmap(display, s->mask);
631 }
632 if (s->pcolorarray) {
633 free_colors(s->pcolorarray, s->ncols);
634 free(s->pcolorarray);
635 s->pcolorarray = NULL;
636 }
637 free(s);
638 }
639
640 /***************************************************************************
641 ...
642 ***************************************************************************/
create_overlay_unit(const struct unit_type * punittype)643 Pixmap create_overlay_unit(const struct unit_type *punittype)
644 {
645 Pixmap pm;
646 enum color_std bg_color;
647
648 pm=XCreatePixmap(display, root_window,
649 tileset_full_tile_width(tileset), tileset_full_tile_height(tileset), display_depth);
650
651 /* Give tile a background color, based on the type of unit */
652 /* Should there be colors like COLOR_MAPVIEW_LAND etc? -ev */
653 switch (unit_color_type(punittype)) {
654 case UNIT_BG_LAND:
655 bg_color = COLOR_OVERVIEW_LAND;
656 break;
657 case UNIT_BG_SEA:
658 bg_color = COLOR_OVERVIEW_OCEAN;
659 break;
660 case UNIT_BG_HP_LOSS:
661 case UNIT_BG_AMPHIBIOUS:
662 bg_color = COLOR_OVERVIEW_MY_UNIT;
663 break;
664 case UNIT_BG_FLYING:
665 bg_color = COLOR_OVERVIEW_ENEMY_CITY;
666 break;
667 default:
668 bg_color = COLOR_OVERVIEW_UNKNOWN;
669 break;
670 }
671 XSetForeground(display, fill_bg_gc,
672 get_color(tileset, bg_color)->color.pixel);
673 XFillRectangle(display, pm, fill_bg_gc, 0,0,
674 tileset_full_tile_width(tileset), tileset_full_tile_height(tileset));
675
676 /* If we're using flags, put one on the tile */
677 if (!gui_options.solid_color_behind_units) {
678 struct sprite *flag = get_nation_flag_sprite(tileset,
679 nation_of_player(client.conn.playing));
680
681 XSetClipOrigin(display, civ_gc, 0,0);
682 XSetClipMask(display, civ_gc, flag->mask);
683 XCopyArea(display, flag->pixmap, pm, civ_gc, 0, 0,
684 flag->width,flag->height, 0,0);
685 XSetClipMask(display, civ_gc, None);
686 }
687
688 /* Finally, put a picture of the unit in the tile */
689 /* if (i < utype_count()) */ {
690 struct sprite *s = get_unittype_sprite(tileset, punittype,
691 direction8_invalid(), TRUE);
692
693 XSetClipOrigin(display,civ_gc,0,0);
694 XSetClipMask(display,civ_gc,s->mask);
695 XCopyArea(display, s->pixmap, pm, civ_gc,
696 0,0, s->width,s->height, 0,0 );
697 XSetClipMask(display,civ_gc,None);
698 }
699 return(pm);
700 }
701
702
703 /***************************************************************************
704 This function is so that packhand.c can be gui-independent, and
705 not have to deal with Sprites itself.
706 ***************************************************************************/
free_intro_radar_sprites(void)707 void free_intro_radar_sprites(void)
708 {
709 if (intro_gfx_sprite) {
710 free_sprite(intro_gfx_sprite);
711 intro_gfx_sprite=NULL;
712 }
713 if (radar_gfx_sprite) {
714 free_sprite(radar_gfx_sprite);
715 radar_gfx_sprite=NULL;
716 }
717 }
718