]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/CGraphics.cpp
Make functions const where possible.
[quix0rs-blobwars.git] / src / CGraphics.cpp
1 /*
2 Copyright (C) 2004 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "headers.h"
22
23 Graphics::Graphics()
24 {
25         for (int i = 0 ; i < MAX_TILES ; i++)
26         {
27                 tile[i] = NULL;
28         }
29
30         background = NULL;
31         infoMessage = NULL;
32
33         fontSize = 0;
34         
35         medalMessageTimer = 0;
36         medalType = 0;
37
38         currentLoading = 0;
39
40         screenShotNumber = 0;
41         takeRandomScreenShots = false;
42
43         waterAnim = 201;
44         slimeAnim = 208;
45         lavaAnim = 215;
46 }
47
48 void Graphics::free()
49 {
50         debug(("graphics.free: Background\n"));
51         if (background != NULL)
52         {
53                 SDL_FreeSurface(background);
54         }
55         debug(("graphics.free: Background - Done\n"));
56
57         background = NULL;
58
59         debug(("graphics.free: Tiles\n"));
60         for (int i = 0 ; i < MAX_TILES ; i++)
61         {
62                 if (tile[i] != NULL)
63                 {
64                         SDL_FreeSurface(tile[i]);
65                         tile[i] = NULL;
66                 }
67         }
68         debug(("graphics.free: Tiles - Done\n"));
69
70         debug(("graphics.free: Sprites\n"));
71         Sprite *sprite = (Sprite*)spriteList.getHead();
72         while (sprite->next != NULL)
73         {
74                 sprite = (Sprite*)sprite->next;
75                 //debug(("graphics.free: Sprites Sprite::Free - %s\n", sprite->name));
76                 sprite->free();
77         }
78         debug(("graphics.free: Sprites Clear()\n"));
79         spriteList.clear();
80         debug(("graphics.free: Sprites - Done\n"));
81 }
82
83 void Graphics::destroy()
84 {
85         free();
86
87         for (int i = 0 ; i < 5 ; i++)
88         {
89                 if (font[i])
90                 {
91                         TTF_CloseFont(font[i]);
92                 }
93         }
94         
95         if (medalMessage != NULL)
96         {
97                 SDL_FreeSurface(medalMessage);
98         }
99
100         if (fadeBlack)
101         {
102                 SDL_FreeSurface(fadeBlack);
103         }
104
105         if (infoBar)
106         {
107                 SDL_FreeSurface(infoBar);
108         }
109         
110         for (int i = 0 ; i < 4 ; i++)
111         {
112                 if (medal[i] != NULL)
113                 {
114                         SDL_FreeSurface(medal[i]);
115                         medal[i] = NULL;
116                 }
117         }
118 }
119
120 void Graphics::registerEngine(Engine *engine)
121 {
122         this->engine = engine;
123 }
124
125 void Graphics::mapColors()
126 {
127         red = SDL_MapRGB(screen->format, 0xff, 0x00, 0x00);
128         yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);
129         green = SDL_MapRGB(screen->format, 0x00, 0xff, 0x00);
130         darkGreen = SDL_MapRGB(screen->format, 0x00, 0x77, 0x00);
131         skyBlue = SDL_MapRGB(screen->format, 0x66, 0x66, 0xff);
132         blue = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);
133         cyan = SDL_MapRGB(screen->format, 0x00, 0x99, 0xff);
134         white = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff);
135         lightGrey = SDL_MapRGB(screen->format, 0xcc, 0xcc, 0xcc);
136         grey = SDL_MapRGB(screen->format, 0x88, 0x88, 0x88);
137         darkGrey = SDL_MapRGB(screen->format, 0x33, 0x33, 0x33);
138         black = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
139
140         fontForeground.r = fontForeground.g = fontForeground.b = 0xff;
141         fontBackground.r = fontBackground.g = fontBackground.b = 0x00;
142
143         fontForeground.unused = fontBackground.unused = 0;
144
145         fadeBlack = alphaRect(640, 480, 0x00, 0x00, 0x00);
146
147         infoBar = alphaRect(640, 25, 0x00, 0x00, 0x00);
148         
149         medalMessage = NULL;
150 }
151
152 Sprite *Graphics::getSpriteHead()
153 {
154         return (Sprite*)spriteList.getHead();
155 }
156
157 void Graphics::setTransparent(SDL_Surface *sprite)
158 {
159         SDL_SetColorKey(sprite, (SDL_SRCCOLORKEY|SDL_RLEACCEL), SDL_MapRGB(sprite->format, 0, 0, 0));
160 }
161
162 bool Graphics::canShowMedalMessage() const
163 {
164         return (medalMessageTimer <= 0);
165 }
166
167 void Graphics::updateScreen()
168 {
169         if (medalMessageTimer > 0)
170         {
171                 int padding = 0;
172                 
173                 medalMessageTimer--;
174                 
175                 if (medalType >= 0)
176                 {
177                         padding = 18;
178                 }
179                 
180                 drawRect(screen->w - (medalMessage->w + 5 + padding), 5, medalMessage->w + padding - 2, 20, grey, screen);
181                 drawRect(screen->w - (medalMessage->w + 5 + padding - 1), 6, medalMessage->w + padding - 4, 18, black, screen);
182                 blit(medalMessage, screen->w - (medalMessage->w + 5), 7, screen, false);
183                 
184                 if (medalType >= 0)
185                 {
186                         blit(medal[medalType], screen->w - (medalMessage->w + 5 + 16), 7, screen, false);
187                 }
188         }
189         
190         SDL_Flip(screen);
191         SDL_Delay(1);
192
193         if (takeRandomScreenShots)
194         {
195                 if ((Math::prand() % 500) == 0)
196                 {
197                         snprintf(screenshot, sizeof screenshot, "screenshots/screenshot%.3d.bmp", screenShotNumber);
198                         SDL_SaveBMP(screen, screenshot);
199                         screenShotNumber++;
200                 }
201
202                 SDL_Delay(16);
203         }
204
205         if (engine->keyState[SDLK_F12])
206         {
207                 snprintf(screenshot, sizeof screenshot, "screenshots/screenshot%.3d.bmp", screenShotNumber);
208                 SDL_SaveBMP(screen, screenshot);
209                 screenShotNumber++;
210
211                 engine->keyState[SDLK_F12] = 0;
212         }
213
214         if ((engine->keyState[SDLK_F10]) || ((engine->keyState[SDLK_RETURN]) && (engine->keyState[SDLK_LALT])))
215         {
216                 SDL_WM_ToggleFullScreen(screen);
217                 engine->fullScreen = !engine->fullScreen;
218
219                 engine->keyState[SDLK_F10] = engine->keyState[SDLK_LALT] = engine->keyState[SDLK_RETURN] = 0;
220         }
221 }
222
223 void Graphics::delay(int time)
224 {
225         unsigned long then = SDL_GetTicks();
226
227         engine->keyState[SDLK_ESCAPE] = 0;
228
229         while (true)
230         {
231                 updateScreen();
232                 
233                 if (SDL_GetTicks() >= then + time)
234                 {
235                         break;
236                 }
237
238                 engine->getInput();
239                 
240                 /*
241                 if (engine->keyState[SDLK_ESCAPE])
242                 {
243                         break;
244                 }
245                 */
246         }
247 }
248
249 void Graphics::RGBtoHSV(float r, float g, float b, float *h, float *s, float *v)
250 {
251         float mn, mx, delta;
252         mn = min(min(r, g), b);
253         mx = max(max(r, g), b);
254         *v = mx;
255         delta = mx - mn;
256
257         if (mx != 0)
258         {
259                 *s = delta / mx;
260         }
261         else
262         {
263                 *s = 0;
264                 *h = -1;
265                 return;
266         }
267
268         if (r == mx)
269         {
270                 *h = (g - b) / delta;
271         }
272         else if (g == mx)
273         {
274                 *h = 2 + (b - r) / delta;
275         }
276         else
277         {
278                 *h = 4 + (r - g) / delta;
279         }
280
281         *h *= 60;
282
283         if (*h < 0)
284         {
285                 *h += 360;
286         }
287 }
288
289 void Graphics::HSVtoRGB(float *r, float *g, float *b, float h, float s, float v)
290 {
291         int i;
292         float f, p, q, t;
293         if (s == 0)
294         {
295                 *r = *g = *b = v;
296                 return;
297         }
298
299         h /= 60;
300         i = (int)(h);
301         f = h - i;
302         p = v * (1 - s);
303         q = v * (1 - s * f);
304         t = v * (1 - s * (1 - f));
305
306         switch (i)
307         {
308                 case 0:
309                         *r = v;
310                         *g = t;
311                         *b = p;
312                         break;
313
314                 case 1:
315                         *r = q;
316                         *g = v;
317                         *b = p;
318                         break;
319
320                 case 2:
321                         *r = p;
322                         *g = v;
323                         *b = t;
324                         break;
325
326                 case 3:
327                         *r = p;
328                         *g = q;
329                         *b = v;
330                         break;
331
332                 case 4:
333                         *r = t;
334                         *g = p;
335                         *b = v;
336                         break;
337
338                 default:
339                         *r = v;
340                         *g = p;
341                         *b = q;
342                         break;
343         }
344 }
345
346 SDL_Surface *Graphics::loadImage(const char *filename, bool srcalpha)
347 {
348         SDL_Surface *image, *newImage;
349
350         #if USEPAK
351                 if (!engine->unpack(filename, PAK_IMG))
352                         showErrorAndExit(ERR_FILE, filename);
353                 image = IMG_Load_RW(engine->sdlrw, 1);
354         #else
355                 image = IMG_Load(filename);
356         #endif
357
358         if (!image)
359                 showErrorAndExit(ERR_FILE, filename);
360
361         newImage = SDL_DisplayFormat(image);
362
363         if (newImage)
364         {
365                 SDL_FreeSurface(image);
366         }
367         else
368         {
369                 // This happens when we are loading the window icon image
370                 newImage = image;
371         }
372
373         if(srcalpha)
374                 SDL_SetAlpha(newImage, SDL_SRCALPHA, 255);
375         else
376                 setTransparent(newImage);
377
378         return newImage;
379 }
380
381 SDL_Surface *Graphics::loadImage(const char *filename, int hue, int sat, int value)
382 {
383         SDL_Surface *image, *newImage;
384
385         #if USEPAK
386                 if (!engine->unpack(filename, PAK_IMG))
387                         showErrorAndExit(ERR_FILE, filename);
388                 image = IMG_Load_RW(engine->sdlrw, 1);
389         #else
390                 image = IMG_Load(filename);
391         #endif
392
393         if (!image)
394                 showErrorAndExit(ERR_FILE, filename);
395
396         if ((hue != 0) || (sat != 0) || (value != 0))
397         {
398                 if (image->format->BitsPerPixel != 8)
399                 {
400                         debug(("WARNING: Could not set Hue for '%s'! Not an 8 bit image!\n", filename));
401                 }
402                 else
403                 {
404                         SDL_Color *color;
405                         float r, g, b, h, s, v;
406
407                         if (image->format->palette->colors != NULL)
408                         {
409                                 for (int i = 1 ; i < image->format->palette->ncolors ; i++)
410                                 {
411                                         color = &image->format->palette->colors[i];
412
413                                         r = (int)color->r;
414                                         g = (int)color->g;
415                                         b = (int)color->b;
416
417                                         RGBtoHSV(r, g, b, &h, &s, &v);
418
419                                         h += hue;
420                                         s += sat;
421                                         v += value;
422
423                                         HSVtoRGB(&r, &g, &b, h, s, v);
424
425                                         color->r = (int)r;
426                                         color->g = (int)g;
427                                         color->b = (int)b;
428
429                                 }
430                         }
431                 }
432         }
433
434         newImage = SDL_DisplayFormat(image);
435
436         if (newImage)
437         {
438                 SDL_FreeSurface(image);
439         }
440         else
441         {
442                 // This happens when we are loading the window icon image
443                 newImage = image;
444         }
445
446         setTransparent(newImage);
447
448         return newImage;
449 }
450
451 SDL_Surface *Graphics::quickSprite(const char *name, SDL_Surface *image)
452 {
453         Sprite *sprite = addSprite(name);
454         sprite->setFrame(0, image, 60);
455
456         return sprite->getCurrentFrame();
457 }
458
459 void Graphics::fade(int amount)
460 {
461         SDL_SetAlpha(fadeBlack, SDL_SRCALPHA|SDL_RLEACCEL, amount);
462         blit(fadeBlack, 0, 0, screen, false);
463 }
464
465 void Graphics::fadeToBlack()
466 {
467         int start = 0;
468
469         while (start < 50)
470         {
471                 SDL_SetAlpha(fadeBlack, SDL_SRCALPHA|SDL_RLEACCEL, start);
472                 blit(fadeBlack, 0, 0, screen, false);
473                 delay(60);
474                 start++;
475         }
476 }
477
478 void Graphics::loadMapTiles(const char *baseDir)
479 {
480         bool found, autoAlpha;
481         char filename[255];
482         filename[0] = 0;
483
484         autoAlpha = false;
485         
486         if (strcmp(baseDir, "gfx/common") == 0)
487         {
488                 autoAlpha = true;
489         }
490
491         #if !USEPAK
492         FILE *fp;
493         #endif
494
495         for (int i = 1 ; i < MAX_TILES ; i++)
496         {
497                 found = true;
498
499                 snprintf(filename, sizeof filename, "%s/%d.png", baseDir, i);
500
501                 #if USEPAK
502                 
503                 if (!engine->getPak()->fileExists(filename))
504                         continue;
505
506                 #else
507
508                 fp = fopen(filename, "rb");
509                 if (!fp)
510                         continue;
511                 fclose(fp);
512
513                 #endif
514
515                 if (found)
516                 {
517                         tile[i] = loadImage(filename);
518
519                         if (autoAlpha)
520                         {
521                                 if ((i < MAP_EXITSIGN) || (i >= MAP_WATERANIM))
522                                 {
523                                         SDL_SetAlpha(tile[i], SDL_SRCALPHA|SDL_RLEACCEL, 130);
524                                 }
525                         }
526                         else
527                         {
528                                 if (i < MAP_DECORATION)
529                                 {
530                                         SDL_SetColorKey(tile[i], 0, SDL_MapRGB(tile[i]->format, 0, 0, 0));
531                                 }
532                         }
533                 }
534         }
535 }
536
537 /*
538 Note : We need to search for the right >>> PIXEL SIZE <<< and NOT point size!!
539 If a user has a resolution other than approximately 72dpi then
540 they will get a small or larger font and this won't work. This might look
541 weird since we'll load and delete multiple fonts, but it works...
542 */
543 void Graphics::loadFont(int i, const char *filename, int pixelSize)
544 {
545         int minx, maxx, miny, maxy, advance;
546         
547         debug(("Attempting to load a font with pixel size of %d...\n", pixelSize));
548         
549         if (font[i])
550         {
551                 debug(("Freeing Font %d first...\n", i));
552                 TTF_CloseFont(font[i]);
553         }
554         
555         char tempPath[PATH_MAX];
556         
557         snprintf(tempPath, sizeof tempPath, "%sfont.ttf", engine->userHomeDirectory);
558
559         bool found = false;
560         int size = 0;
561         
562         while (!found)
563         {
564                 if (font[i])
565                 {
566                         TTF_CloseFont(font[i]);
567                 }
568                 
569                 #if USEPAK
570                         font[i] = TTF_OpenFont(tempPath, ++size);
571                 #else
572                         font[i] = TTF_OpenFont("data/vera.ttf", ++size);
573                 #endif
574         
575                 if (!font[i])
576                 {
577                         engine->reportFontFailure();
578                 }
579                 
580                 TTF_GlyphMetrics(font[i], '8', &minx, &maxx, &miny, &maxy, &advance);
581                 
582                 // great! we have an exact match
583                 if (maxx == pixelSize)
584                 {
585                         break;
586                 }
587                 
588                 // we've overshot, so we'll use the previous size!
589                 if (maxx > pixelSize)
590                 {
591                         TTF_CloseFont(font[i]);
592                         
593                         #if USEPAK
594                                 font[i] = TTF_OpenFont(tempPath, size - 1);
595                         #else
596                                 font[i] = TTF_OpenFont("data/vera.ttf", size - 1);
597                         #endif
598                                         
599                         TTF_GlyphMetrics(font[i], '8', &minx, &maxx, &miny, &maxy, &advance);
600                         
601                         break;
602                 }
603                 
604                 if (size >= 100)
605                 {
606                         debug(("Pixel size has exceeded 99 pixels! I'm giving up!\n"));
607                         engine->reportFontFailure();
608                 }
609         }
610         
611         TTF_SetFontStyle(font[i], TTF_STYLE_NORMAL);
612         
613         debug(("Got a match for font size %d - Nearest = %d\n", pixelSize, maxx));
614 }
615
616 Sprite *Graphics::addSprite(const char *name)
617 {
618         Sprite *sprite = new Sprite;
619         strlcpy(sprite->name, name, sizeof sprite->name);
620
621         spriteList.add(sprite);
622
623         return sprite;
624 }
625
626 Sprite *Graphics::getSprite(const char *name, bool required)
627 {
628         Sprite *sprite = (Sprite*)spriteList.getHead();
629
630         while (sprite->next != NULL)
631         {
632                 sprite = (Sprite*)sprite->next;
633                 
634                 if (strcmp(sprite->name, name) == 0)
635                 {
636                         return sprite;
637                 }
638         }
639
640         if (required)
641                 showErrorAndExit("The requested sprite '%s' does not exist", name);
642
643         return NULL;
644 }
645
646 void Graphics::animateSprites()
647 {
648         Sprite *sprite = (Sprite*)spriteList.getHead();
649
650         while (sprite->next != NULL)
651         {
652                 sprite = (Sprite*)sprite->next;
653
654                 sprite->animate();
655         }
656
657         if ((engine->getFrameLoop() % 8) == 0)
658         {
659                 Math::wrapInt(&(++waterAnim), 201, 204);
660                 Math::wrapInt(&(++slimeAnim), 207, 212);
661                 Math::wrapInt(&(++lavaAnim), 214, 220);
662         }
663 }
664
665 int Graphics::getWaterAnim() const
666 {
667         return waterAnim;
668 }
669
670 int Graphics::getSlimeAnim() const
671 {
672         return slimeAnim;
673 }
674
675 int Graphics::getLavaAnim() const
676 {
677         return lavaAnim;
678 }
679
680 int Graphics::getLavaAnim(int current)
681 {
682         if ((engine->getFrameLoop() % 8) == 0)
683                 return Math::rrand(214, 220);
684
685         return current;
686 }
687
688 void Graphics::loadBackground(const char *filename)
689 {
690         if (background != NULL)
691                 SDL_FreeSurface(background);
692
693         if (strcmp(filename, "@none@") == 0)
694                 return;
695
696         background = loadImage(filename);
697
698         SDL_SetColorKey(background, 0, SDL_MapRGB(background->format, 0, 0, 0));
699 }
700
701 void Graphics::putPixel(int x, int y, Uint32 pixel, SDL_Surface *dest)
702 {
703         if ((x < 0) || (x > 639) || (y < 0) || (y > 479))
704                 return;
705
706         int bpp = dest->format->BytesPerPixel;
707         /* Here p is the address to the pixel we want to set */
708         Uint8 *p = (Uint8 *)dest->pixels + y * dest->pitch + x * bpp;
709
710         switch(bpp)
711         {
712                 case 1:
713                         *p = pixel;
714                         break;
715
716                 case 2:
717                         *(Uint16 *)p = pixel;
718                         break;
719
720                 case 3:
721                         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
722                         {
723                                 p[0] = (pixel >> 16) & 0xff;
724                                 p[1] = (pixel >> 8) & 0xff;
725                                 p[2] = pixel & 0xff;
726                         }
727                         else
728                         {
729                                 p[0] = pixel & 0xff;
730                                 p[1] = (pixel >> 8) & 0xff;
731                                 p[2] = (pixel >> 16) & 0xff;
732                         }
733                         break;
734
735                 case 4:
736                         *(Uint32 *)p = pixel;
737                         break;
738         }
739 }
740
741 Uint32 Graphics::getPixel(SDL_Surface *surface, int x, int y)
742 {
743         if ((x < 0) || (x > (surface->w - 1)) || (y < 0) || (y > (surface->h - 1)))
744                 return 0;
745
746         int bpp = surface->format->BytesPerPixel;
747         Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
748
749         switch(bpp) {
750         case 1:
751                 return *p;
752
753         case 2:
754                 return *(Uint16 *)p;
755
756         case 3:
757                 if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
758                                 return p[0] << 16 | p[1] << 8 | p[2];
759                 else
760                                 return p[0] | p[1] << 8 | p[2] << 16;
761
762         case 4:
763                 return *(Uint32 *)p;
764
765         default:
766                 return 0;       /* shouldn't happen, but avoids warnings */
767         }
768 }
769
770 void Graphics::drawLine(float startX, float startY, float endX, float endY, int color, SDL_Surface *dest)
771 {
772         lock(screen);
773         
774         float dx, dy;
775         
776         Math::calculateSlope(startX, startY, endX, endY, &dx, &dy);
777
778         while (true)
779         {
780                 putPixel((int)startX, (int)startY, color, dest);
781
782                 if ((int)startX == (int)endX)
783                         break;
784
785                 startX -= dx;
786                 startY -= dy;
787         }
788
789         unlock(screen);
790 }
791
792 void Graphics::blit(SDL_Surface *image, int x, int y, SDL_Surface *dest, bool centered)
793 {
794         if (!image)
795         {
796                 showErrorAndExit("graphics::blit() - NULL pointer", SDL_GetError());
797         }
798
799         if ((x < -image->w) || (x > 640 + image->w))
800                 return;
801
802         if ((y < -image->h) || (y > 480 + image->h))
803                 return;
804
805         // Set up a rectangle to draw to
806         gRect.x = x;
807         gRect.y = y;
808         if (centered)
809         {
810                 gRect.x -= (image->w / 2);
811                 gRect.y -= (image->h / 2);
812         }
813
814         gRect.w = image->w;
815         gRect.h = image->h;
816
817         /* Blit onto the screen surface */
818         if (SDL_BlitSurface(image, NULL, dest, &gRect) < 0)
819                 showErrorAndExit("graphics::blit() - %s", SDL_GetError());
820 }
821
822 void Graphics::drawBackground()
823 {
824         if (background != NULL)
825                 blit(background, 0, 0, screen, false);
826         else
827                 SDL_FillRect(screen, NULL, black);
828 }
829
830 void Graphics::drawBackground(SDL_Rect *r)
831 {
832         if (r->x < 0) r->x = 0;
833         if (r->y < 0) r->y = 0;
834         if (r->x + r->w > 639) r->w = 640 - r->x;
835         if (r->y + r->h > 639) r->h = 480 - r->y;
836
837         if (SDL_BlitSurface(background, r, screen, r) < 0)
838                 showErrorAndExit("graphics::blit() - %s", SDL_GetError());
839 }
840
841 void Graphics::drawRect(int x, int y, int w, int h, int color, SDL_Surface *dest)
842 {
843         gRect.x = x;
844         gRect.y = y;
845         gRect.w = w;
846         gRect.h = h;
847
848         SDL_FillRect(dest, &gRect, color);
849 }
850
851 void Graphics::drawRect(int x, int y, int w, int h, int color, int borderColor, SDL_Surface *dest)
852 {
853         drawRect(x - 1, y - 1, w + 2, h + 2, borderColor, dest);
854         drawRect(x, y, w, h, color, dest);
855 }
856
857 void Graphics::setFontColor(int red, int green, int blue, int red2, int green2, int blue2)
858 {
859         fontForeground.r = red;
860         fontForeground.g = green;
861         fontForeground.b = blue;
862
863         fontBackground.r = red2;
864         fontBackground.g = green2;
865         fontBackground.b = blue2;
866
867         fontForeground.unused = fontBackground.unused = 0;
868 }
869
870 void Graphics::setFontSize(int size)
871 {
872         fontSize = size;
873         Math::limitInt(&fontSize, 0, 4);
874 }
875
876 SDL_Surface *Graphics::getString(const char *in, bool transparent)
877 {
878         SDL_Surface *text = TTF_RenderUTF8_Shaded(font[fontSize], in, fontForeground, fontBackground);
879
880         if (!text)
881         {
882                 text = TTF_RenderUTF8_Shaded(font[fontSize], "FONT_ERROR", fontForeground, fontBackground);
883         }
884
885         if (transparent)
886                 setTransparent(text);
887
888         return text;
889 }
890
891 void Graphics::drawString(const char *in, int x, int y, int alignment, SDL_Surface *dest)
892 {
893         bool center = false;
894
895         SDL_Surface *text = TTF_RenderUTF8_Shaded(font[fontSize], in, fontForeground, fontBackground);
896
897         if (!text)
898                 text = TTF_RenderUTF8_Shaded(font[fontSize], "FONT_ERROR", fontForeground, fontBackground);
899
900         if (!text)
901                 return;
902
903         setTransparent(text);
904
905         if (alignment == TXT_RIGHT) x -= text->w;
906         if (alignment == TXT_CENTERED) center = true;
907
908         blit(text, x, y, dest, center);
909         SDL_FreeSurface(text);
910 }
911
912 void Graphics::drawString(const char *in, int x, int y, int alignment, SDL_Surface *dest, SurfaceCache &cache)
913 {
914         bool center = false;
915
916         if(!cache.text || strcmp(in, cache.text)) {
917                 if(cache.surface)
918                         SDL_FreeSurface(cache.surface);
919
920                 if(cache.text)
921                         ::free(cache.text);
922
923                 cache.text = strdup(in);
924
925                 cache.surface = TTF_RenderUTF8_Shaded(font[fontSize], in, fontForeground, fontBackground);
926
927                 if (!cache.surface)
928                         cache.surface = TTF_RenderUTF8_Shaded(font[fontSize], "FONT_ERROR", fontForeground, fontBackground);
929
930                 if(!cache.surface)
931                         return;
932
933                 setTransparent(cache.surface);
934         }
935
936         if (alignment == TXT_RIGHT) x -= cache.surface->w;
937         if (alignment == TXT_CENTERED) center = true;
938
939         blit(cache.surface, x, y, dest, center);
940 }
941
942 void Graphics::clearChatString()
943 {
944         chatString[0] = 0;
945 }
946
947 void Graphics::createChatString(const char *in)
948 {
949         strlcat(chatString, " ", sizeof chatString);
950         strlcat(chatString, in, sizeof chatString);
951 }
952
953 void Graphics::drawChatString(SDL_Surface *surface, int y)
954 {
955         char *word = strtok(chatString, " ");
956         char wordWithSpace[100];
957         
958         int r, g, b;
959
960         int x = 10;
961         int surfaceWidth = surface->w - 10;
962
963         SDL_Surface *wordSurface;
964
965         while (word)
966         {
967                 if (strcmp(word, "<RGB>") == 0)
968                 {
969                         r = atoi(strtok(NULL, " "));
970                         g = atoi(strtok(NULL, " "));
971                         b = atoi(strtok(NULL, " "));
972
973                         if ((!r) && (!g) && (!b))
974                         {
975                                 debug(("Parse Error in Text Color (%d:%d:%d)!!\n", r, g, b));
976                                 exit(1);
977                         }
978
979                         setFontColor(r, g, b, 0, 0, 0);
980
981                         word = strtok(NULL, " ");
982
983                         continue;
984                 }
985
986                 snprintf(wordWithSpace, sizeof wordWithSpace, "%s ", word);
987
988                 wordSurface = getString(wordWithSpace, false);
989
990                 if (x + wordSurface->w > surfaceWidth)
991                 {
992                         y += (int)(wordSurface->h * 1.5) ;
993                         x = 10;
994                 }
995
996                 blit(wordSurface, x, y, surface, false);
997
998                 x += wordSurface->w;
999
1000                 SDL_FreeSurface(wordSurface);
1001
1002                 word = strtok(NULL, " ");
1003         }
1004 }
1005
1006 void Graphics::showMedalMessage(int type, const char *in)
1007 {
1008         char message[1024];
1009         
1010         if (medalMessage != NULL)
1011         {
1012                 SDL_FreeSurface(medalMessage);
1013         }
1014         
1015         setFontSize(0);
1016         
1017         switch (type)
1018         {
1019                 // Bronze
1020                 case 1:
1021                         setFontColor(0xA6, 0x7D, 0x3D, 0x00, 0x00, 0x00);
1022                         break;
1023                 
1024                 // Silver
1025                 case 2:
1026                         setFontColor(0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00);
1027                         break;
1028                         
1029                 // Gold
1030                 case 3:
1031                         setFontColor(0xFF, 0xCC, 0x33, 0x00, 0x00, 0x00);
1032                         break;
1033                         
1034                 // Ruby
1035                 case 4:
1036                         setFontColor(0xFF, 0x11, 0x55, 0x00, 0x00, 0x00);
1037                         break;
1038         }
1039         
1040         medalType = type - 1; // for indexing on the image
1041         if (type != -1)
1042         {
1043                 snprintf(message, sizeof message, "  Medal Earned - %s  ", in);
1044                 medalMessage = getString(message, true);
1045         }
1046         else
1047         {
1048                 snprintf(message, sizeof message, "  %s  ", in);
1049                 medalMessage = getString(message, true);
1050         }
1051         medalMessageTimer = (5 * 60);
1052 }
1053
1054 void Graphics::drawWidgetRect(int x, int y, int w, int h)
1055 {
1056         drawRect(x - 5, y - 4, w + 10, h + 8, white, screen);
1057         drawRect(x - 4, y - 3, w + 8, h + 6, black, screen);
1058         drawRect(x - 3, y - 2, w + 6, h + 4, green, screen);
1059 }
1060
1061 SDL_Surface *Graphics::createSurface(int width, int height)
1062 {
1063         SDL_Surface *surface, *newImage;
1064
1065         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
1066
1067         if (surface == NULL)
1068                 showErrorAndExit("CreateRGBSurface failed: %s\n", SDL_GetError());
1069
1070         newImage = SDL_DisplayFormat(surface);
1071
1072         SDL_FreeSurface(surface);
1073
1074         return newImage;
1075 }
1076
1077 SDL_Surface *Graphics::alphaRect(int width, int height, Uint8 red, Uint8 green, Uint8 blue)
1078 {
1079         SDL_Surface *surface = createSurface(width, height);
1080
1081         SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, red, green, blue));
1082
1083         SDL_SetAlpha(surface, SDL_SRCALPHA|SDL_RLEACCEL, 130);
1084
1085         return surface;
1086 }
1087
1088 void Graphics::colorize(SDL_Surface *image, int red, int green, int blue)
1089 {
1090         SDL_Surface *alpha = alphaRect(image->w, image->h, red, green, blue);
1091
1092         blit(alpha, 0, 0, image, false);
1093
1094         SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL), SDL_MapRGB(image->format, red / 2, green / 2, blue / 2));
1095 }
1096
1097 void Graphics::lock(SDL_Surface *surface)
1098 {
1099         /* Lock the screen for direct access to the pixels */
1100         if (SDL_MUSTLOCK(surface))
1101         {
1102                 if (SDL_LockSurface(surface) < 0 )
1103                 {
1104                         showErrorAndExit("Could not lock surface", "");
1105                 }
1106         }
1107 }
1108
1109 void Graphics::unlock(SDL_Surface *surface)
1110 {
1111         if (SDL_MUSTLOCK(surface))
1112         {
1113                 SDL_UnlockSurface(surface);
1114         }
1115 }
1116
1117 void Graphics::resetLoading()
1118 {
1119         currentLoading = 0;
1120 }
1121
1122 void Graphics::showLoading(int amount, int max)
1123 {
1124         #if USEPAK
1125         max *= 4;
1126
1127         if (max > 398)
1128                 max = 398;
1129
1130         Math::limitInt(&(currentLoading += amount), 0, max);
1131
1132         drawRect(120, 420, 400, 10, black, white, screen);
1133         drawRect(121, 421, currentLoading, 8, red, screen);
1134
1135         SDL_UpdateRect(screen, 120, 420, 400, 10);
1136         #endif
1137 }
1138
1139 void Graphics::showLicenseErrorAndExit()
1140 {
1141         setFontSize(3); setFontColor(0xff, 0x00, 0x00, 0x00, 0x00, 0x00);
1142         drawString("License Agreement Missing", 320, 50, true, screen);
1143         
1144         setFontSize(1); setFontColor(0xff, 0xff, 0xff, 0x00, 0x00, 0x00);
1145
1146         drawString("The GNU General Public License was not found.", 320, 180, true, screen);
1147         drawString("It could either not be properly loaded or has been removed.", 320, 220, true, screen);
1148         drawString("Blob Wars : Metal Blob Solid will not run with the license missing.", 320, 260, true, screen);
1149         
1150         drawString("Blob Wars : Metal Blob Solid will now exit", 320, 420, true, screen);
1151         drawString("Press Escape to continue", 320, 450, true, screen);
1152
1153         engine->flushInput();
1154
1155         while (true)
1156         {
1157                 updateScreen();
1158                 engine->getInput();
1159                 if (engine->keyState[SDLK_ESCAPE])
1160                         exit(1);
1161                 SDL_Delay(16);
1162         }
1163 }
1164
1165 void Graphics::showErrorAndExit(const char *error, const char *param)
1166 {
1167         SDL_FillRect(screen, NULL, black);
1168
1169         if (strcmp(param, "LICENSE") == 0)
1170         {
1171                 showLicenseErrorAndExit();
1172         }
1173
1174         char message[256];
1175         snprintf(message, sizeof message, error, param);
1176
1177         setFontSize(3); setFontColor(0xff, 0x00, 0x00, 0x00, 0x00, 0x00);
1178         drawString("An unforseen error has occurred", 320, 50, true, screen);
1179         setFontSize(1); setFontColor(0xff, 0xff, 0xff, 0x00, 0x00, 0x00);
1180         drawString(message, 320, 90, true, screen);
1181
1182         drawString("You may wish to try the following,", 50, 150, false, screen);
1183
1184         setFontSize(0);
1185         drawString("1) Try reinstalling the game.", 75, 190, false, screen);
1186         drawString("2) Ensure you have SDL 1.2.5 or greater installed.", 75, 210, false, screen);
1187         drawString("3) Ensure you have the latest versions of additional required SDL libraries.", 75, 230, false, screen);
1188         drawString("4) Install using an RPM if you originally built the game from source", 75, 250, false, screen);
1189         drawString("or try building from source if you installed using an RPM.", 75, 270, false, screen);
1190         drawString("5) Visit http://www.parallelrealities.co.uk/blobWars.php and check for updates.", 75, 290, false, screen);
1191
1192         setFontSize(1);
1193
1194         drawString("If problems persist contact Parallel Realities. Please be aware however that we will not", 320, 360, true, screen);
1195         drawString("be able to assist in cases where the code or data has been modified.", 320, 380, true, screen);
1196
1197         drawString("Blob Wars : Metal Blob Solid will now exit", 320, 420, true, screen);
1198         drawString("Press Escape to continue", 320, 450, true, screen);
1199
1200         engine->flushInput();
1201
1202         while (true)
1203         {
1204                 updateScreen();
1205                 engine->getInput();
1206                 if (engine->keyState[SDLK_ESCAPE])
1207                 {
1208                         exit(1);
1209                 }
1210                 SDL_Delay(16);
1211         }
1212 }
1213
1214 void Graphics::showRootWarning()
1215 {
1216         setFontSize(3); setFontColor(0xff, 0x00, 0x00, 0x00, 0x00, 0x00);
1217         drawString("CAUTION - RUNNING AS ROOT USER!", 320, 50, true, screen);
1218         
1219         setFontSize(1); setFontColor(0xff, 0xff, 0xff, 0x00, 0x00, 0x00);
1220
1221         drawString("WARNING - You appear to be running the game as the root user!", 320, 180, true, screen);
1222         drawString("This is not recommended and is it strongly advised that you do not run", 320, 220, true, screen);
1223         drawString("the game as root. You may still continue but consider running as regular user in future!", 320, 260, true, screen);
1224         
1225         drawString("Press Space to Exit", 320, 420, true, screen);
1226         drawString("Press Escape to Continue", 320, 450, true, screen);
1227
1228         engine->flushInput();
1229
1230         while (true)
1231         {
1232                 updateScreen();
1233                 engine->getInput();
1234                 
1235                 if (engine->keyState[SDLK_ESCAPE])
1236                 {
1237                         return;
1238                 }
1239                 else if (engine->keyState[SDLK_SPACE])
1240                 {
1241                         exit(0);
1242                 }
1243
1244                 SDL_Delay(16);
1245         }
1246 }