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