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