text: #include "Graphics.h"
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <utf8/unchecked.h>
  
  #include "Entity.h"
  #include "FileSystemUtils.h"
  #include "Map.h"
  #include "Music.h"
  #include "Screen.h"
  #include "UtilityClass.h"
  #include "editor.h"
  
  void Graphics::init()
  {
      grphx.init();
  
      flipmode = false;
      setRect(tiles_rect, 0, 0, 8, 8);
      setRect(sprites_rect, 0, 0, 32, 32);
      setRect(bfont_rect, 0, 0, 8, 8);
      setRect(bg_rect, 0, 0, 320, 240);
      setRect(footerrect, 0, 230, 320, 10);
      setRect(prect, 0, 0, 4, 4);
      setRect(line_rect, 0, 0, 0, 0);
      setRect(tele_rect, 0, 0, 96, 96);
  
      // We initialise a few things
  
      linestate = 0;
  
      trinketcolset = false;
  
      showcutscenebars = false;
      cutscenebarspos = 0;
      oldcutscenebarspos = 0;
      notextoutline = false;
  
      flipmode = false;
      setflipmode = false;
  
      // Background inits
      for (int i = 0; i < numstars; i++) {
          SDL_Rect s = { Sint16(fRandom() * 320), Sint16(fRandom() * 240), 2, 2 };
          int s2 = 4 + (fRandom() * 4);
          stars[i] = s;
          starsspeed[i] = s2;
      }
  
      for (int i = 0; i < numbackboxes; i++) {
          SDL_Rect bb;
          int bvx = 0;
          int bvy = 0;
          if (fRandom() * 100 > 50) {
              bvx = 9 - (fRandom() * 19);
              if (bvx > -6 && bvx < 6)
                  bvx = 6;
              bvx = bvx * 1.5;
              setRect(bb, fRandom() * 320, fRandom() * 240, 32, 12);
          } else {
              bvy = 9 - (fRandom() * 19);
              if (bvy > -6 && bvy < 6)
                  bvy = 6;
              bvy = bvy * 1.5;
              setRect(bb, fRandom() * 320, fRandom() * 240, 12, 32);
          }
          float bint = 0.5 + ((fRandom() * 100) / 200);
          backboxes[i] = bb;
          backboxvx[i] = bvx;
          backboxvy[i] = bvy;
          backboxint[i] = bint;
      }
      backoffset = 0;
      backgrounddrawn = false;
  
      warpskip = 0;
      warpfcol = 0x000000;
      warpbcol = 0x000000;
  
      spcol = 0;
      spcoldel = 0;
      rcol = 0;
  
      crewframe = 0;
      crewframedelay = 4;
      menuoffset = 0;
      oldmenuoffset = 0;
      resumegamemode = false;
  
      // Fading stuff
      SDL_memset(fadebars, 0, sizeof(fadebars));
  
      fadeamount = 0;
      oldfadeamount = 0;
      fademode = 0;
  
      // initialize everything else to zero
      backBuffer = NULL;
      bcol = 0;
      bcol2 = 0;
      ct = colourTransform();
      foot_rect = SDL_Rect();
      foregrounddrawn = false;
      foregroundBuffer = NULL;
      backgrounddrawn = false;
      images_rect = SDL_Rect();
      m = 0;
      linedelay = 0;
      menubuffer = NULL;
      screenbuffer = NULL;
      tempBuffer = NULL;
      tl = point();
      towerbuffer = NULL;
      towerbuffer_lerp = NULL;
      footerbuffer = NULL;
      ghostbuffer = NULL;
      trinketr = 0;
      trinketg = 0;
      trinketb = 0;
      warprect = SDL_Rect();
  
      translucentroomname = false;
      showmousecursor = true;
  
      alpha = 1.0f;
  
      screenshake_x = 0;
      screenshake_y = 0;
  
      col_crewred = 0x00000000;
      col_crewyellow = 0x00000000;
      col_crewgreen = 0x00000000;
      col_crewcyan = 0x00000000;
      col_crewblue = 0x00000000;
      col_crewpurple = 0x00000000;
      col_crewinactive = 0x00000000;
      col_clock = 0x00000000;
      col_trinket = 0x00000000;
      col_tr = 0;
      col_tg = 0;
      col_tb = 0;
  
      kludgeswnlinewidth = false;
  }
  
  int Graphics::font_idx(uint32_t ch)
  {
      if (font_positions.size() > 0) {
          std::map<int, int>::iterator iter = font_positions.find(ch);
          if (iter == font_positions.end()) {
              iter = font_positions.find('?');
              if (iter == font_positions.end()) {
                  puts("font.txt missing fallback character!");
                  exit(1);
              }
          }
          return iter->second;
      } else {
          return ch;
      }
  }
  
  void Graphics::drawspritesetcol(int x, int y, int t, int c)
  {
      if (!INBOUNDS(t, sprites)) {
          return;
      }
      SDL_Rect rect;
      setRect(rect, x, y, sprites_rect.w, sprites_rect.h);
      setcol(c);
  
      BlitSurfaceColoured(sprites[t], NULL, backBuffer, &rect, ct);
  }
  
  void Graphics::updatetitlecolours()
  {
      setcol(15);
      col_crewred = ct.colour;
      setcol(14);
      col_crewyellow = ct.colour;
      setcol(13);
      col_crewgreen = ct.colour;
      setcol(0);
      col_crewcyan = ct.colour;
      setcol(16);
      col_crewblue = ct.colour;
      setcol(20);
      col_crewpurple = ct.colour;
      setcol(19);
      col_crewinactive = ct.colour;
  
      setcol(18);
      col_clock = ct.colour;
      setcol(18);
      col_trinket = ct.colour;
  }
  
  #define PROCESS_TILESHEET_CHECK_ERROR(tilesheet, tile_square)                  \
      if (grphx.im_##tilesheet->w % tile_square != 0 ||                          \
          grphx.im_##tilesheet->h % tile_square != 0) {                          \
          const char* error =                                                    \
              "Error: %s.png dimensions not exact multiples of %i!";             \
          char message[128];                                                     \
          SDL_snprintf(                                                          \
              message, sizeof(message), error, #tilesheet, tile_square);         \
                                                                                 \
          const char* error_title = "Error with %s.png";                         \
          char message_title[128];                                               \
          SDL_snprintf(                                                          \
              message_title, sizeof(message_title), error_title, #tilesheet);    \
                                                                                 \
          puts(message);                                                         \
                                                                                 \
          SDL_ShowSimpleMessageBox(                                              \
              SDL_MESSAGEBOX_ERROR, message_title, message, NULL);               \
                                                                                 \
          exit(1);                                                               \
      }
  
  #define PROCESS_TILESHEET_RENAME(tilesheet, vector, tile_square, extra_code)   \
      PROCESS_TILESHEET_CHECK_ERROR(tilesheet, tile_square)                      \
                                                                                 \
      for (int j = 0; j < grphx.im_##tilesheet->h / tile_square; j++) {          \
          for (int i = 0; i < grphx.im_##tilesheet->w / tile_square; i++) {      \
              SDL_Surface* temp = GetSubSurface(grphx.im_##tilesheet,            \
                                                i * tile_square,                 \
                                                j * tile_square,                 \
                                                tile_square,                     \
                                                tile_square);                    \
              vector.push_back(temp);                                            \
                                                                                 \
              extra_code                                                         \
          }                                                                      \
      }
  
  #define PROCESS_TILESHEET(tilesheet, tile_square, extra_code)                  \
      PROCESS_TILESHEET_RENAME(tilesheet, tilesheet, tile_square, extra_code)
  
  void Graphics::Makebfont()
  {
      PROCESS_TILESHEET(bfont, 8, {
          SDL_Surface* TempFlipped = FlipSurfaceVerticle(temp);
          flipbfont.push_back(TempFlipped);
      })
  
      unsigned char* charmap = NULL;
      size_t length;
      FILESYSTEM_loadFileToMemory("graphics/font.txt", &charmap, &length);
      if (charmap != NULL) {
          unsigned char* current = charmap;
          unsigned char* end = charmap + length;
          int pos = 0;
          while (current != end) {
              int codepoint = utf8::unchecked::next(current);
              font_positions[codepoint] = pos;
              ++pos;
          }
          FILESYSTEM_freeMemory(&charmap);
      }
  }
  
  int Graphics::bfontlen(uint32_t ch)
  {
      if (ch < 32) {
          return 6;
      } else {
          return 8;
      }
  }
  
  void Graphics::MakeTileArray()
  {
      PROCESS_TILESHEET(tiles, 8, )
      PROCESS_TILESHEET(tiles2, 8, )
      PROCESS_TILESHEET(tiles3, 8, )
      PROCESS_TILESHEET(entcolours, 8, )
  }
  
  void Graphics::maketelearray()
  {
      PROCESS_TILESHEET_RENAME(teleporter, tele, 96, )
  }
  
  void Graphics::MakeSpriteArray()
  {
      PROCESS_TILESHEET(sprites, 32, )
      PROCESS_TILESHEET(flipsprites, 32, )
  }
  
  #undef PROCESS_TILESHEET
  #undef PROCESS_TILESHEET_RENAME
  #undef PROCESS_TILESHEET_CHECK_ERROR
  
  void Graphics::map_tab(int opt,
                         const std::string& text,
                         bool selected /*= false*/)
  {
      int x = opt * 80 + 40 - len(text) / 2;
      if (selected) {
          Print(x - 8, 220, "[" + text + "]", 196, 196, 255 - help.glow);
      } else {
          Print(x, 220, text, 64, 64, 64);
      }
  }
  
  void Graphics::map_option(int opt,
                            int num_opts,
                            const std::string& text,
                            bool selected /*= false*/)
  {
      int x = 80 + opt * 32;
      int y = 136; // start from middle of menu
  
      int yoff = -(num_opts * 12) / 2; // could be simplified to -num_opts * 6,
                                       // this conveys my intent better though
      yoff += opt * 12;
  
      if (flipmode) {
          y -= yoff; // going down, which in Flip Mode means going up
          y -= 40;
      } else {
          y += yoff; // going up
      }
  
      if (selected) {
          std::string text_upper(text);
          for (size_t i = 0; i < text_upper.length(); i++) {
              text_upper[i] = SDL_toupper(text_upper[i]);
          }
          Print(x - 16, y, "[ " + text_upper + " ]", 196, 196, 255 - help.glow);
      } else {
          Print(x, y, text, 96, 96, 96);
      }
  }
  
  void Graphics::Print(int _x,
                       int _y,
                       std::string _s,
                       int r,
                       int g,
                       int b,
                       bool cen /*= false*/)
  {
      return PrintAlpha(_x, _y, _s, r, g, b, 255, cen);
  }
  
  void Graphics::PrintAlpha(int _x,
                            int _y,
                            std::string _s,
                            int r,
                            int g,
                            int b,
                            int a,
                            bool cen /*= false*/)
  {
      std::vector<SDL_Surface*>& font = flipmode ? flipbfont : bfont;
  
      r = clamp(r, 0, 255);
      g = clamp(g, 0, 255);
      b = clamp(b, 0, 255);
      a = clamp(a, 0, 255);
  
      ct.colour = getRGBA(r, g, b, a);
  
      if (cen)
          _x = ((160) - (len(_s) / 2));
      int bfontpos = 0;
      int curr;
      int idx;
      std::string::iterator iter = _s.begin();
      while (iter != _s.end()) {
          curr = utf8::unchecked::next(iter);
          point tpoint;
          tpoint.x = _x + bfontpos;
          tpoint.y = _y;
  
          SDL_Rect fontRect = bfont_rect;
          fontRect.x = tpoint.x;
          fontRect.y = tpoint.y;
  
          idx = font_idx(curr);
          if (INBOUNDS(idx, font)) {
              BlitSurfaceColoured(font[idx], NULL, backBuffer, &fontRect, ct);
          }
          bfontpos += bfontlen(curr);
      }
  }
  
  void Graphics::bigprint(int _x,
                          int _y,
                          std::string _s,
                          int r,
                          int g,
                          int b,
                          bool cen,
                          int sc)
  {
      std::vector<SDL_Surface*>& font = flipmode ? flipbfont : bfont;
  
      r = clamp(r, 0, 255);
      g = clamp(g, 0, 255);
      b = clamp(b, 0, 255);
  
      ct.colour = getRGB(r, g, b);
  
      if (cen) {
          _x = std::max(160 - (int((len(_s) / 2.0) * sc)), 0);
      }
  
      int bfontpos = 0;
      int curr;
      int idx;
      std::string::iterator iter = _s.begin();
      while (iter != _s.end()) {
          curr = utf8::unchecked::next(iter);
  
          /*
          point tpoint;
          tpoint.x = _x + bfontpos;
          tpoint.y = _y;
  
          SDL_Rect fontRect = bfont_rect;
          fontRect.x = tpoint.x ;
          fontRect.y = tpoint.y ;
          */
  
          idx = font_idx(curr);
          if (INBOUNDS(idx, font)) {
              SDL_Surface* tempPrint =
                  ScaleSurface(font[idx], font[idx]->w * sc, font[idx]->h * sc);
              SDL_Rect printrect = { static_cast<Sint16>((_x) + bfontpos),
                                     static_cast<Sint16>(_y),
                                     static_cast<Sint16>((bfont_rect.w * sc) + 1),
                                     static_cast<Sint16>((bfont_rect.h * sc) +
                                                         1) };
              BlitSurfaceColoured(tempPrint, NULL, backBuffer, &printrect, ct);
              SDL_FreeSurface(tempPrint);
          }
          bfontpos += bfontlen(curr) * sc;
      }
  }
  
  int Graphics::len(std::string t)
  {
      int bfontpos = 0;
      std::string::iterator iter = t.begin();
      while (iter != t.end()) {
          int cur = utf8::unchecked::next(iter);
          bfontpos += bfontlen(cur);
      }
      return bfontpos;
  }
  
  void Graphics::PrintOff(int _x,
                          int _y,
                          std::string _s,
                          int r,
                          int g,
                          int b,
                          bool cen /*= false*/)
  {
      PrintOffAlpha(_x, _y, _s, r, g, b, 255, cen);
  }
  
  void Graphics::PrintOffAlpha(int _x,
                               int _y,
                               std::string _s,
                               int r,
                               int g,
                               int b,
                               int a,
                               bool cen /*= false*/)
  {
      std::vector<SDL_Surface*>& font = flipmode ? flipbfont : bfont;
  
      r = clamp(r, 0, 255);
      g = clamp(g, 0, 255);
      b = clamp(b, 0, 255);
      a = clamp(a, 0, 255);
  
      ct.colour = getRGB(r, g, b);
  
      if (cen)
          _x = ((160) - (len(_s) / 2)) + _x;
      int bfontpos = 0;
      int idx;
      std::string::iterator iter = _s.begin();
      while (iter != _s.end()) {
          int curr = utf8::unchecked::next(iter);
          point tpoint;
          tpoint.x = _x + bfontpos;
          tpoint.y = _y;
  
          SDL_Rect fontRect = bfont_rect;
          fontRect.x = tpoint.x;
          fontRect.y = tpoint.y;
  
          idx = font_idx(curr);
          if (INBOUNDS(idx, font)) {
              BlitSurfaceColoured(font[idx], NULL, backBuffer, &fontRect, ct);
          }
          bfontpos += bfontlen(curr);
      }
  }
  
  void Graphics::bprint(int x,
                        int y,
                        std::string t,
                        int r,
                        int g,
                        int b,
                        bool cen /*= false*/)
  {
      bprintalpha(x, y, t, r, g, b, 255, cen);
  }
  
  void Graphics::bprintalpha(int x,
                             int y,
                             std::string t,
                             int r,
                             int g,
                             int b,
                             int a,
                             bool cen /*= false*/)
  {
      if (!notextoutline) {
          PrintAlpha(x, y - 1, t, 0, 0, 0, a, cen);
          if (cen) {
              PrintOffAlpha(-1, y, t, 0, 0, 0, a, cen);
              PrintOffAlpha(1, y, t, 0, 0, 0, a, cen);
          } else {
              PrintAlpha(x - 1, y, t, 0, 0, 0, a, cen);
              PrintAlpha(x + 1, y, t, 0, 0, 0, a, cen);
          }
          PrintAlpha(x, y + 1, t, 0, 0, 0, a, cen);
      }
  
      PrintAlpha(x, y, t, r, g, b, a, cen);
  }
  
  void Graphics::RPrint(int _x,
                        int _y,
                        std::string _s,
                        int r,
                        int g,
                        int b,
                        bool cen /*= false*/)
  {
      std::vector<SDL_Surface*>& font = flipmode ? flipbfont : bfont;
  
      r = clamp(r, 0, 255);
      g = clamp(g, 0, 255);
      b = clamp(b, 0, 255);
      ct.colour = getRGB(r, g, b);
  
      if (cen)
          _x = ((308) - (_s.length() / 2));
      int bfontpos = 0;
      int curr;
      int idx;
      std::string::iterator iter = _s.begin();
      while (iter != _s.end()) {
          curr = utf8::unchecked::next(iter);
          point tpoint;
          tpoint.x = _x + bfontpos;
          tpoint.y = _y;
  
          SDL_Rect fontRect = bfont_rect;
          fontRect.x = tpoint.x;
          fontRect.y = tpoint.y;
  
          idx = font_idx(curr);
          if (INBOUNDS(idx, font)) {
              BlitSurfaceColoured(font[idx], NULL, backBuffer, &fontRect, ct);
          }
          bfontpos += bfontlen(curr);
      }
  }
  
  void Graphics::printcrewname(int x, int y, int t)
  {
      // Print the name of crew member t in the right colour
      switch (t) {
          case 0:
              Print(x, y, "Viridian", 16, 240, 240, false);
              break;
          case 1:
              Print(x, y, "Violet", 240, 16, 240, false);
              break;
          case 2:
              Print(x, y, "Vitellary", 240, 240, 16, false);
              break;
          case 3:
              Print(x, y, "Vermilion", 240, 16, 16, false);
              break;
          case 4:
              Print(x, y, "Verdigris", 16, 240, 16, false);
              break;
          case 5:
              Print(x, y, "Victoria", 16, 16, 240, false);
              break;
      }
  }
  
  void Graphics::printcrewnamedark(int x, int y, int t)
  {
      // Print the name of crew member t as above, but in black and white
      switch (t) {
          case 0:
              Print(x, y, "Viridian", 128, 128, 128, false);
              break;
          case 1:
              Print(x, y, "Violet", 128, 128, 128, false);
              break;
          case 2:
              Print(x, y, "Vitellary", 128, 128, 128, false);
              break;
          case 3:
              Print(x, y, "Vermilion", 128, 128, 128, false);
              break;
          case 4:
              Print(x, y, "Verdigris", 128, 128, 128, false);
              break;
          case 5:
              Print(x, y, "Victoria", 128, 128, 128, false);
              break;
      }
  }
  
  void Graphics::printcrewnamestatus(int x, int y, int t)
  {
      // Print the status of crew member t in the right colour
      switch (t) {
          case 0:
              Print(x, y, "(that's you!)", 12, 140, 140, false);
              break;
          case 1:
              Print(x, y, "Rescued!", 140, 12, 140, false);
              break;
          case 2:
              Print(x, y, "Rescued!", 140, 140, 12, false);
              break;
          case 3:
              Print(x, y, "Rescued!", 140, 12, 12, false);
              break;
          case 4:
              Print(x, y, "Rescued!", 12, 140, 12, false);
              break;
          case 5:
              Print(x, y, "Rescued!", 12, 12, 140, false);
              break;
      }
  }
  
  void Graphics::drawsprite(int x, int y, int t, int r, int g, int b)
  {
      SDL_Rect rect = { Sint16(x), Sint16(y), sprites_rect.w, sprites_rect.h };
      setcolreal(getRGB(r, g, b));
      BlitSurfaceColoured(sprites[t], NULL, backBuffer, &rect, ct);
  }
  
  void Graphics::drawsprite(int x, int y, int t, Uint32 c)
  {
      SDL_Rect rect = { Sint16(x), Sint16(y), sprites_rect.w, sprites_rect.h };
      setcolreal(c);
      BlitSurfaceColoured(sprites[t], NULL, backBuffer, &rect, ct);
  }
  
  void Graphics::drawtile(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles)) {
          WHINE_ONCE("drawtile() out-of-bounds!")
          return;
      }
  
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
  
  #if !defined(NO_CUSTOM_LEVELS)
      if (t >= 14 && t <= 17 &&
          (!FILESYSTEM_assetsmounted || ed.onewaycol_override)) {
          colourTransform thect = { ed.getonewaycol() };
          BlitSurfaceTinted(tiles[t], NULL, backBuffer, &rect, thect);
      } else
  #endif
      {
          BlitSurfaceStandard(tiles[t], NULL, backBuffer, &rect);
      }
  }
  
  void Graphics::drawtile2(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles2)) {
          WHINE_ONCE("drawtile2() out-of-bounds!")
          return;
      }
  
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
  
  #if !defined(NO_CUSTOM_LEVELS)
      if (t >= 14 && t <= 17 &&
          (!FILESYSTEM_assetsmounted || ed.onewaycol_override)) {
          colourTransform thect = { ed.getonewaycol() };
          BlitSurfaceTinted(tiles2[t], NULL, backBuffer, &rect, thect);
      } else
  #endif
      {
          BlitSurfaceStandard(tiles2[t], NULL, backBuffer, &rect);
      }
  }
  
  void Graphics::drawtile3(int x,
                           int y,
                           int t,
                           int off,
                           int height_subtract /*= 0*/)
  {
      t += off * 30;
      if (!INBOUNDS(t, tiles3)) {
          WHINE_ONCE("drawtile3() out-of-bounds!")
          return;
      }
      SDL_Rect src_rect = { 0, 0, tiles_rect.w, tiles_rect.h - height_subtract };
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
      BlitSurfaceStandard(tiles3[t], &src_rect, backBuffer, &rect);
  }
  
  void Graphics::drawentcolours(int x, int y, int t)
  {
      if (!INBOUNDS(t, entcolours)) {
          WHINE_ONCE("drawentcolours() out-of-bounds!")
          return;
      }
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
      BlitSurfaceStandard(entcolours[t], NULL, backBuffer, &rect);
  }
  
  void Graphics::drawtowertile(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles2)) {
          WHINE_ONCE("drawtowertile() out-of-bounds!")
          return;
      }
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
      BlitSurfaceStandard(tiles2[t], NULL, towerbuffer, &rect);
  }
  
  void Graphics::drawtowertile3(int x, int y, int t, int off)
  {
      t += off * 30;
      if (!INBOUNDS(t, tiles3)) {
          WHINE_ONCE("drawtowertile3() out-of-bounds!")
          return;
      }
      SDL_Rect rect = { Sint16(x), Sint16(y), tiles_rect.w, tiles_rect.h };
      BlitSurfaceStandard(tiles3[t], NULL, towerbuffer, &rect);
  }
  
  void Graphics::drawgui()
  {
      // Draw all the textboxes to the screen
      for (size_t i = 0; i < textbox.size(); i++) {
          // This routine also updates textbox colors
          float tl_lerp = lerp(textbox[i].prev_tl, textbox[i].tl);
          textbox[i].setcol(textbox[i].tr * tl_lerp,
                            textbox[i].tg * tl_lerp,
                            textbox[i].tb * tl_lerp);
  
          if (textbox[i].r == 0 && textbox[i].g == 0 && textbox[i].b == 0) {
              if (flipmode) {
                  for (size_t j = 0; j < textbox[i].line.size(); j++) {
                      Print(textbox[i].xp + 8,
                            textbox[i].yp + (textbox[i].line.size() * 8) -
                                (j * 8),
                            textbox[i].line[j],
                            196,
                            196,
                            255 - help.glow);
                  }
              } else {
                  for (size_t j = 0; j < textbox[i].line.size(); j++) {
                      Print(textbox[i].xp + 8,
                            textbox[i].yp + 8 + (j * 8),
                            textbox[i].line[j],
                            196,
                            196,
                            255 - help.glow);
                  }
              }
          } else {
  
              FillRect(backBuffer,
                       textbox[i].textrect,
                       textbox[i].r / 6,
                       textbox[i].g / 6,
                       textbox[i].b / 6);
  
              drawcoloredtile(textbox[i].xp,
                              textbox[i].yp,
                              40,
                              textbox[i].r,
                              textbox[i].g,
                              textbox[i].b);
              drawcoloredtile(textbox[i].xp + textbox[i].w - 8,
                              textbox[i].yp,
                              42,
                              textbox[i].r,
                              textbox[i].g,
                              textbox[i].b);
              drawcoloredtile(textbox[i].xp,
                              textbox[i].yp + textbox[i].h - 8,
                              45,
                              textbox[i].r,
                              textbox[i].g,
                              textbox[i].b);
              drawcoloredtile(textbox[i].xp + textbox[i].w - 8,
                              textbox[i].yp + textbox[i].h - 8,
                              47,
                              textbox[i].r,
                              textbox[i].g,
                              textbox[i].b);
              for (int k = 0; k < textbox[i].lw; k++) {
                  drawcoloredtile(textbox[i].xp + 8 + (k * 8),
                                  textbox[i].yp,
                                  41,
                                  textbox[i].r,
                                  textbox[i].g,
                                  textbox[i].b);
                  drawcoloredtile(textbox[i].xp + 8 + (k * 8),
                                  textbox[i].yp + textbox[i].h - 8,
                                  46,
                                  textbox[i].r,
                                  textbox[i].g,
                                  textbox[i].b);
              }
              for (size_t k = 0; k < textbox[i].line.size(); k++) {
                  drawcoloredtile(textbox[i].xp,
                                  textbox[i].yp + 8 + (k * 8),
                                  43,
                                  textbox[i].r,
                                  textbox[i].g,
                                  textbox[i].b);
                  drawcoloredtile(textbox[i].xp + textbox[i].w - 8,
                                  textbox[i].yp + 8 + (k * 8),
                                  44,
                                  textbox[i].r,
                                  textbox[i].g,
                                  textbox[i].b);
              }
  
              if (flipmode) {
                  for (size_t j = 0; j < textbox[i].line.size(); j++) {
                      Print(textbox[i].xp + 8,
                            textbox[i].yp + (textbox[i].line.size() * 8) -
                                (j * 8),
                            textbox[i].line[j],
                            textbox[i].r,
                            textbox[i].g,
                            textbox[i].b);
                  }
              } else {
                  for (size_t j = 0; j < textbox[i].line.size(); j++) {
                      Print(textbox[i].xp + 8,
                            textbox[i].yp + 8 + (j * 8),
                            textbox[i].line[j],
                            textbox[i].r,
                            textbox[i].g,
                            textbox[i].b);
                  }
              }
          }
  
          // Only draw special images when fully opaque
          // This prevents flashes of special images during delta frames
          bool drawspecial = textbox[i].tl >= 1.0;
  
          if ((textbox[i].yp == 12 || textbox[i].yp == 180) &&
              textbox[i].r == 165 && drawspecial) {
              if (flipmode) {
                  drawimage(5, 0, 180, true);
              } else {
                  drawimage(0, 0, 12, true);
              }
          } else if ((textbox[i].yp == 12 || textbox[i].yp == 180) &&
                     textbox[i].g == 165 && drawspecial) {
              if (flipmode) {
                  drawimage(6, 0, 180, true);
              } else {
                  drawimage(4, 0, 12, true);
              }
          }
          if (flipmode) {
              if (textbox[i].r == 175 && textbox[i].g == 175 && drawspecial) {
                  // purple guy
                  drawsprite(80 - 6,
                             64 + 48 + 4,
                             6,
                             220 - help.glow / 4 - int(fRandom() * 20),
                             120 - help.glow / 4,
                             210 - help.glow / 4);
              } else if (textbox[i].r == 175 && textbox[i].b == 175 &&
                         drawspecial) {
                  // red guy
                  drawsprite(80 - 6,
                             64 + 48 + 4,
                             6,
                             255 - help.glow / 8,
                             70 - help.glow / 4,
                             70 - help.glow / 4);
              } else if (textbox[i].r == 175 && drawspecial) {
                  // green guy
                  drawsprite(80 - 6,
                             64 + 48 + 4,
                             6,
                             120 - help.glow / 4 - int(fRandom() * 20),
                             220 - help.glow / 4,
                             120 - help.glow / 4);
              } else if (textbox[i].g == 175 && drawspecial) {
                  // yellow guy
                  drawsprite(80 - 6,
                             64 + 48 + 4,
                             6,
                             220 - help.glow / 4 - int(fRandom() * 20),
                             210 - help.glow / 4,
                             120 - help.glow / 4);
              } else if (textbox[i].b == 175 && drawspecial) {
                  // blue guy
                  drawsprite(80 - 6,
                             64 + 48 + 4,
                             6,
                             75,
                             75,
                             255 - help.glow / 4 - int(fRandom() * 20));
              }
          } else {
              if (textbox[i].r == 175 && textbox[i].g == 175 && drawspecial) {
                  // purple guy
                  drawsprite(80 - 6,
                             64 + 32 + 4,
                             0,
                             220 - help.glow / 4 - int(fRandom() * 20),
                             120 - help.glow / 4,
                             210 - help.glow / 4);
              } else if (textbox[i].r == 175 && textbox[i].b == 175 &&
                         drawspecial) {
                  // red guy
                  drawsprite(80 - 6,
                             64 + 32 + 4,
                             0,
                             255 - help.glow / 8,
                             70 - help.glow / 4,
                             70 - help.glow / 4);
              } else if (textbox[i].r == 175 && drawspecial) {
                  // green guy
                  drawsprite(80 - 6,
                             64 + 32 + 4,
                             0,
                             120 - help.glow / 4 - int(fRandom() * 20),
                             220 - help.glow / 4,
                             120 - help.glow / 4);
              } else if (textbox[i].g == 175 && drawspecial) {
                  // yellow guy
                  drawsprite(80 - 6,
                             64 + 32 + 4,
                             0,
                             220 - help.glow / 4 - int(fRandom() * 20),
                             210 - help.glow / 4,
                             120 - help.glow / 4);
              } else if (textbox[i].b == 175 && drawspecial) {
                  // blue guy
                  drawsprite(80 - 6,
                             64 + 32 + 4,
                             0,
                             75,
                             75,
                             255 - help.glow / 4 - int(fRandom() * 20));
              }
          }
      }
  }
  
  void Graphics::updatetextboxes()
  {
      for (size_t i = 0; i < textbox.size(); i++) {
          textbox[i].update();
  
          if (textbox[i].tm == 2 && textbox[i].tl <= 0.5) {
              textbox.erase(textbox.begin() + i);
              i--;
              continue;
          }
      }
  }
  
  void Graphics::drawimagecol(int t,
                              int xp,
                              int yp,
                              int r = 0,
                              int g = 0,
                              int b = 0,
                              bool cent /*= false*/)
  {
      if (!INBOUNDS(t, images)) {
          return;
      }
      SDL_Rect trect;
      if (r + g + b != 0) {
          RGBf(r, g, b);
      }
  
      point tpoint;
      if (cent) {
          tpoint.x = 160 - int(images[t]->w / 2);
          tpoint.y = yp;
          trect.x = tpoint.x;
          trect.y = tpoint.y;
          trect.w = images[t]->w;
          trect.h = images[t]->h;
          BlitSurfaceColoured(images[t], NULL, backBuffer, &trect, ct);
  
      } else {
          trect.x = xp;
          trect.y = yp;
          trect.w = images[t]->w;
          trect.h = images[t]->h;
          BlitSurfaceColoured(images[t], NULL, backBuffer, &trect, ct);
      }
  }
  
  void Graphics::drawimage(int t, int xp, int yp, bool cent /*=false*/)
  {
      if (!INBOUNDS(t, images)) {
          return;
      }
  
      SDL_Rect trect;
      if (cent) {
          trect.x = 160 - int(images[t]->w / 2);
          trect.y = yp;
          trect.w = images[t]->w;
          trect.h = images[t]->h;
          BlitSurfaceStandard(images[t], NULL, backBuffer, &trect);
      } else {
  
          trect.x = xp;
          trect.y = yp;
          trect.w = images[t]->w;
          trect.h = images[t]->h;
  
          BlitSurfaceStandard(images[t], NULL, backBuffer, &trect);
      }
  }
  
  void Graphics::drawpartimage(int t, int xp, int yp, int wp, int hp)
  {
      if (!INBOUNDS(t, images)) {
          return;
      }
  
      SDL_Rect trect;
  
      trect.x = xp;
      trect.y = yp;
      trect.w = wp;
      trect.h = hp;
  
      SDL_Rect trect2;
  
      trect2.x = 0;
      trect2.y = 0;
      trect2.w = wp;
      trect2.h = hp;
  
      BlitSurfaceStandard(images[t], &trect2, backBuffer, &trect);
  }
  
  void Graphics::cutscenebars()
  {
      int usethispos = lerp(oldcutscenebarspos, cutscenebarspos);
      if (showcutscenebars) {
          FillRect(backBuffer, 0, 0, usethispos, 16, 0x000000);
          FillRect(backBuffer, 360 - usethispos, 224, usethispos, 16, 0x000000);
      } else if (cutscenebarspos > 0) // disappearing
      {
          // draw
          FillRect(backBuffer, 0, 0, usethispos, 16, 0x000000);
          FillRect(backBuffer, 360 - usethispos, 224, usethispos, 16, 0x000000);
      }
  }
  
  void Graphics::cutscenebarstimer()
  {
      oldcutscenebarspos = cutscenebarspos;
      if (showcutscenebars) {
          cutscenebarspos += 25;
          cutscenebarspos = std::min(cutscenebarspos, 361);
      } else if (cutscenebarspos > 0) {
          // disappearing
          cutscenebarspos -= 25;
          cutscenebarspos = std::max(cutscenebarspos, 0);
      }
  }
  
  void Graphics::drawcrewman(int x,
                             int y,
                             int t,
                             bool act,
                             bool noshift /*=false*/)
  {
      if (!act) {
          if (noshift) {
              if (flipmode) {
                  drawsprite(x, y, 14, col_crewinactive);
              } else {
                  drawsprite(x, y, 12, col_crewinactive);
              }
          } else {
              if (flipmode) {
                  drawsprite(x - 8, y, 14, col_crewinactive);
              } else {
                  drawsprite(x - 8, y, 12, col_crewinactive);
              }
          }
      } else {
          if (flipmode)
              crewframe += 6;
  
          switch (t) {
              case 0:
                  drawsprite(x, y, crewframe, col_crewcyan);
                  break;
              case 1:
                  drawsprite(x, y, crewframe, col_crewpurple);
                  break;
              case 2:
                  drawsprite(x, y, crewframe, col_crewyellow);
                  break;
              case 3:
                  drawsprite(x, y, crewframe, col_crewred);
                  break;
              case 4:
                  drawsprite(x, y, crewframe, col_crewgreen);
                  break;
              case 5:
                  drawsprite(x, y, crewframe, col_crewblue);
                  break;
          }
  
          if (flipmode)
              crewframe -= 6;
      }
  }
  
  void Graphics::drawpixeltextbox(int x,
                                  int y,
                                  int w,
                                  int h,
                                  int w2,
                                  int h2,
                                  int r,
                                  int g,
                                  int b,
                                  int xo,
                                  int yo)
  {
      // given these parameters, draw a textbox with a pixel width
  
      // madrect.x = x; madrect.y = y; madrect.w = w; madrect.h = h;
      FillRect(backBuffer, x, y, w, h, r / 6, g / 6, b / 6);
  
      for (int k = 0; k < w2 - 2; k++) {
          drawcoloredtile(x + 8 - xo + (k * 8), y, 41, r, g, b);
          drawcoloredtile(x + 8 - xo + (k * 8), y + (h)-8, 46, r, g, b);
      }
  
      for (int k = 0; k < h2 - 2; k++) {
          drawcoloredtile(x, y + 8 - yo + (k * 8), 43, r, g, b);
          drawcoloredtile(x + (w)-8, y + 8 - yo + (k * 8), 44, r, g, b);
      }
  
      drawcoloredtile(x, y, 40, r, g, b);
      drawcoloredtile(x + (w)-8, y, 42, r, g, b);
      drawcoloredtile(x, y + (h)-8, 45, r, g, b);
      drawcoloredtile(x + (w)-8, y + (h)-8, 47, r, g, b);
  }
  
  void Graphics::drawcustompixeltextbox(int x,
                                        int y,
                                        int w,
                                        int h,
                                        int w2,
                                        int h2,
                                        int r,
                                        int g,
                                        int b,
                                        int xo,
                                        int yo)
  {
      // given these parameters, draw a textbox with a pixel width
  
      FillRect(backBuffer, x, y, w, h, r / 6, g / 6, b / 6);
  
      for (int k = 0; k < w2 - 2; k++) {
          drawcoloredtile(x + 8 - xo + (k * 8), y, 41, r, g, b);
          drawcoloredtile(x + 8 - xo + (k * 8), y + (h)-8, 46, r, g, b);
      }
  
      drawcoloredtile(x + (w)-16, y, 41, r, g, b);
      drawcoloredtile(x + (w)-16, y + (h)-8, 46, r, g, b);
      drawcoloredtile(x + (w)-24, y, 41, r, g, b);
      drawcoloredtile(x + (w)-24, y + (h)-8, 46, r, g, b);
  
      for (int k = 0; k < h2 - 2; k++) {
          drawcoloredtile(x, y + 8 - yo + (k * 8), 43, r, g, b);
          drawcoloredtile(x + (w)-8, y + 8 - yo + (k * 8), 44, r, g, b);
      }
  
      drawcoloredtile(x, y + (h)-16, 43, r, g, b);
      drawcoloredtile(x + (w)-8, y + (h)-16, 44, r, g, b);
      drawcoloredtile(x, y + (h)-24, 43, r, g, b);
      drawcoloredtile(x + (w)-8, y + (h)-24, 44, r, g, b);
  
      drawcoloredtile(x, y, 40, r, g, b);
      drawcoloredtile(x + (w)-8, y, 42, r, g, b);
      drawcoloredtile(x, y + (h)-8, 45, r, g, b);
      drawcoloredtile(x + (w)-8, y + (h)-8, 47, r, g, b);
  }
  
  void Graphics::drawtextbox(int x, int y, int w, int h, int r, int g, int b)
  {
      // given these parameters, draw a textbox
      FillRect(backBuffer, x, y, w * 8, h * 8, r / 6, g / 6, b / 6);
  
      drawcoloredtile(x, y, 40, r, g, b);
      drawcoloredtile(x + (w * 8) - 8, y, 42, r, g, b);
      drawcoloredtile(x, y + (h * 8) - 8, 45, r, g, b);
      drawcoloredtile(x + (w * 8) - 8, y + (h * 8) - 8, 47, r, g, b);
  
      for (int k = 0; k < w - 2; k++) {
          drawcoloredtile(x + 8 + (k * 8), y, 41, r, g, b);
          drawcoloredtile(x + 8 + (k * 8), y + (h * 8) - 8, 46, r, g, b);
      }
  
      for (int k = 0; k < h - 2; k++) {
          drawcoloredtile(x, y + 8 + (k * 8), 43, r, g, b);
          drawcoloredtile(x + (w * 8) - 8, y + 8 + (k * 8), 44, r, g, b);
      }
  }
  
  void Graphics::textboxactive()
  {
      // Remove all but the most recent textbox
      for (int i = 0; i < (int)textbox.size(); i++) {
          if (m != i)
              textbox[i].remove();
      }
  }
  
  void Graphics::textboxremovefast()
  {
      // Remove all textboxes
      for (size_t i = 0; i < textbox.size(); i++) {
          textbox[i].removefast();
      }
  }
  
  void Graphics::textboxremove()
  {
      // Remove all textboxes
      for (size_t i = 0; i < textbox.size(); i++) {
          textbox[i].remove();
      }
  }
  
  void Graphics::textboxtimer(int t)
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxtimer() out-of-bounds!");
          return;
      }
  
      textbox[m].timer = t;
  }
  
  void Graphics::addline(std::string t)
  {
      if (!INBOUNDS(m, textbox)) {
          puts("addline() out-of-bounds!");
          return;
      }
  
      textbox[m].addline(t);
  }
  
  void Graphics::textboxadjust()
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxadjust() out-of-bounds!");
          return;
      }
  
      textbox[m].adjust();
  }
  
  void Graphics::createtextbox(std::string t,
                               int xp,
                               int yp,
                               int r /*= 255*/,
                               int g /*= 255*/,
                               int b /*= 255*/)
  {
      m = textbox.size();
  
      if (m < 20) {
          textboxclass text;
          text.line.push_back(t);
          text.xp = xp;
          int length = utf8::unchecked::distance(t.begin(), t.end());
          if (xp == -1)
              text.xp = 160 - (((length / 2) + 1) * 8);
          text.yp = yp;
          text.initcol(r, g, b);
          text.resize();
          textbox.push_back(text);
      }
  }
  
  void Graphics::drawfade()
  {
      int usethisamount = lerp(oldfadeamount, fadeamount);
      if ((fademode == 1) || (fademode == 4)) {
          FillRect(backBuffer, 0, 0, backBuffer->w, backBuffer->h, 0x000000);
      } else if (fademode == 3) {
          for (size_t i = 0; i < SDL_arraysize(fadebars); i++) {
              FillRect(
                  backBuffer, fadebars[i], i * 16, usethisamount, 16, 0x000000);
          }
      } else if (fademode == 5) {
          for (size_t i = 0; i < SDL_arraysize(fadebars); i++) {
              FillRect(backBuffer,
                       fadebars[i] - usethisamount,
                       i * 16,
                       500,
                       16,
                       0x000000);
          }
      }
  }
  
  void Graphics::processfade()
  {
      oldfadeamount = fadeamount;
      if (fademode > 1) {
          if (fademode == 2) {
              // prepare fade out
              for (size_t i = 0; i < SDL_arraysize(fadebars); i++) {
                  fadebars[i] = -int(fRandom() * 12) * 8;
              }
              fadeamount = 0;
              fademode = 3;
          } else if (fademode == 3) {
              fadeamount += 24;
              if (fadeamount > 416) {
                  fademode = 1; // faded
              }
          } else if (fademode == 4) {
              // prepare fade in
              for (size_t i = 0; i < SDL_arraysize(fadebars); i++) {
                  fadebars[i] = 320 + int(fRandom() * 12) * 8;
              }
              fadeamount = 416;
              fademode = 5;
          } else if (fademode == 5) {
              fadeamount -= 24;
              if (fadeamount <= 0) {
                  fademode = 0; // normal
              }
          }
      }
  }
  
  void Graphics::drawmenu(int cr, int cg, int cb, bool levelmenu /*= false*/)
  {
      for (size_t i = 0; i < game.menuoptions.size(); i++) {
          MenuOption& opt = game.menuoptions[i];
  
          int fr, fg, fb;
          if (opt.active) {
              // Color it normally
              fr = cr;
              fg = cg;
              fb = cb;
          } else {
              // Color it gray
              fr = 128;
              fg = 128;
              fb = 128;
          }
  
          int x = i * game.menuspacing + game.menuxoff;
          int y = 140 + i * 12 + game.menuyoff;
  
          if (levelmenu) {
              if (game.menuoptions.size() - i <= 3) {
                  // We're on "next page", "previous page", or "return to menu".
                  // Draw them separated by a bit
                  y += 8;
              } else {
                  // Get out of the way of the level descriptions
                  y += 4;
              }
          }
  
          char tempstring[Game::menutextbytes];
          SDL_strlcpy(tempstring, opt.text, sizeof(tempstring));
  
          char buffer[Game::menutextbytes];
          if ((int)i == game.currentmenuoption) {
              if (opt.active) {
                  // Uppercase the text
                  // FIXME: This isn't UTF-8 aware!
                  size_t templen = SDL_strlen(tempstring);
                  for (size_t ii = 0; ii < templen; ii++) {
                      tempstring[ii] = SDL_toupper(tempstring[ii]);
                  }
              }
  
              // Add brackets
              SDL_snprintf(buffer, sizeof(buffer), "[ %s ]", tempstring);
              // Account for brackets
              x -= 16;
          } else {
              SDL_strlcpy(buffer, tempstring, sizeof(buffer));
          }
  
          Print(x, y, buffer, fr, fg, fb);
      }
  }
  
  void Graphics::drawcoloredtile(int x, int y, int t, int r, int g, int b)
  {
      if (!INBOUNDS(t, tiles)) {
          return;
      }
      setcolreal(getRGB(r, g, b));
  
      SDL_Rect rect;
      setRect(rect, x, y, tiles_rect.w, tiles_rect.h);
      BlitSurfaceColoured(tiles[t], NULL, backBuffer, &rect, ct);
  }
  
  bool Graphics::Hitest(SDL_Surface* surface1,
                        point p1,
                        SDL_Surface* surface2,
                        point p2)
  {
  
      // find rectangle where they intersect:
  
      int r1_left = p1.x;
      int r1_right = r1_left + surface1->w;
      int r2_left = p2.x;
      int r2_right = r2_left + surface2->w;
  
      int r1_bottom = p1.y;
      int r1_top = p1.y + surface1->h;
      int r2_bottom = p2.y;
      int r2_top = p2.y + surface2->h;
  
      SDL_Rect rect1 = { p1.x, p1.y, surface1->w, surface1->h };
      SDL_Rect rect2 = { p2.x, p2.y, surface2->w, surface2->h };
      bool intersection = help.intersects(rect1, rect2);
  
      if (intersection) {
          int r3_left = SDL_max(r1_left, r2_left);
          int r3_top = SDL_min(r1_top, r2_top);
          int r3_right = SDL_min(r1_right, r2_right);
          int r3_bottom = SDL_max(r1_bottom, r2_bottom);
  
          // for every pixel inside rectangle
          for (int x = r3_left; x < r3_right; x++) {
              for (int y = r3_bottom; y < r3_top; y++) {
                  Uint32 pixel1 = ReadPixel(surface1, x - p1.x, y - p1.y);
                  Uint32 pixel2 = ReadPixel(surface2, x - p2.x, y - p2.y);
                  if ((pixel1 & 0x000000FF) && (pixel2 & 0x000000FF)) {
                      return true;
                  }
              }
          }
      }
      return false;
  }
  
  void Graphics::drawgravityline(int t)
  {
      if (obj.entities[t].life == 0) {
          switch (linestate) {
              case 0:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(200 - 20, 200 - 20, 200 - 20));
                  break;
              case 1:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(225 - 30, 245 - 30, 245 - 30));
                  break;
              case 2:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(245 - 30, 245 - 30, 225 - 30));
                  break;
              case 3:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(164 - 10, 200 - 20, 200 - 20));
                  break;
              case 4:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(224 - 20, 255 - 30, 196 - 20));
                  break;
              case 5:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(205 - 20, 235 - 30, 196 - 20));
                  break;
              case 6:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(164 - 10, 164 - 10, 164 - 10));
                  break;
              case 7:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(225 - 30, 245 - 30, 205 - 20));
                  break;
              case 8:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(205 - 20, 255 - 30, 225 - 30));
                  break;
              case 9:
                  FillRect(backBuffer,
                           line_rect,
                           getRGB(245 - 30, 245 - 30, 245 - 30));
                  break;
          }
      } else {
          FillRect(backBuffer, line_rect, getRGB(96, 96, 96));
      }
  }
  
  void Graphics::drawtrophytext()
  {
      int temp, temp2, temp3;
  
      if (obj.trophytext < 15) {
          int usethismult = lerp(obj.oldtrophytext, obj.trophytext);
          temp = (196 * usethismult) / 15;
          temp2 = (196 * usethismult) / 15;
          temp3 = ((255 - help.glow) * usethismult) / 15;
      } else {
          temp = 196;
          temp2 = 196;
          temp3 = 255 - help.glow;
      }
      switch (obj.trophytype) {
          case 1:
              Print(-1, 6, "SPACE STATION 1 MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 2:
              Print(-1, 6, "LABORATORY MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 3:
              Print(-1, 6, "THE TOWER MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 4:
              Print(-1, 6, "SPACE STATION 2 MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 5:
              Print(-1, 6, "WARP ZONE MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 6:
              Print(-1, 6, "FINAL LEVEL MASTERED", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Obtain a V Rank in this Time Trial",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 7:
              Print(-1, 6, "GAME COMPLETE", temp, temp2, temp3, true);
              Print(-1, 16, "Complete the game", temp, temp2, temp3, true);
              break;
          case 8:
              Print(-1, 6, "FLIP MODE COMPLETE", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Complete the game in flip mode",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 9:
              Print(-1,
                    11,
                    "Win with less than 50 deaths",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 10:
              Print(-1,
                    11,
                    "Win with less than 100 deaths",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 11:
              Print(-1,
                    11,
                    "Win with less than 250 deaths",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 12:
              Print(-1,
                    11,
                    "Win with less than 500 deaths",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 13:
              Print(-1,
                    11,
                    "Last 5 seconds on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 14:
              Print(-1,
                    11,
                    "Last 10 seconds on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 15:
              Print(-1,
                    11,
                    "Last 15 seconds on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 16:
              Print(-1,
                    11,
                    "Last 20 seconds on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 17:
              Print(-1,
                    11,
                    "Last 30 seconds on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 18:
              Print(-1,
                    11,
                    "Last 1 minute on the Super Gravitron",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
          case 20:
              Print(-1, 6, "MASTER OF THE UNIVERSE", temp, temp2, temp3, true);
              Print(-1,
                    16,
                    "Complete the game in no death mode",
                    temp,
                    temp2,
                    temp3,
                    true);
              break;
      }
  }
  
  void Graphics::drawentities()
  {
      point tpoint;
  
      SDL_Rect drawRect;
  
  #if !defined(NO_CUSTOM_LEVELS)
      // Special case for gray Warp Zone tileset!
      int room = game.roomx - 100 + (game.roomy - 100) * ed.maxwidth;
      bool custom_gray = room >= 0 && room < 400 && ed.level[room].tileset == 3 &&
                         ed.level[room].tilecol == 6;
  #else
      bool custom_gray = false;
  #endif
  
      std::vector<SDL_Surface*>* tilesvec;
      if (map.custommode && !map.finalmode) {
          tilesvec = &entcolours;
      } else {
          tilesvec = &tiles;
      }
  
      std::vector<SDL_Surface*>* spritesvec;
      if (flipmode) {
          spritesvec = &flipsprites;
      } else {
          spritesvec = &sprites;
      }
  
      int yoff;
      if (map.towermode) {
          yoff = lerp(map.oldypos, map.ypos);
      } else {
          yoff = 0;
      }
  
      for (int i = obj.entities.size() - 1; i >= 0; i--) {
          if (obj.entities[i].invis) {
              continue;
          }
  
          int xp = lerp(obj.entities[i].oldxp, obj.entities[i].xp);
          int yp = lerp(obj.entities[i].oldyp, obj.entities[i].yp);
  
          switch (obj.entities[i].size) {
              case 0: {
                  // Sprites
                  if (!INBOUNDS(obj.entities[i].drawframe, (*spritesvec))) {
                      continue;
                  }
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  setcolreal(obj.entities[i].realcol);
  
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured((*spritesvec)[obj.entities[i].drawframe],
                                      NULL,
                                      backBuffer,
                                      &drawRect,
                                      ct);
  
                  // screenwrapping!
                  point wrappedPoint;
                  bool wrapX = false;
                  bool wrapY = false;
  
                  wrappedPoint.x = tpoint.x;
                  if (tpoint.x < 0) {
                      wrapX = true;
                      wrappedPoint.x += 320;
                  } else if (tpoint.x > 300) {
                      wrapX = true;
                      wrappedPoint.x -= 320;
                  }
  
                  wrappedPoint.y = tpoint.y;
                  if (tpoint.y < 0) {
                      wrapY = true;
                      wrappedPoint.y += 230;
                  } else if (tpoint.y > 210) {
                      wrapY = true;
                      wrappedPoint.y -= 230;
                  }
  
                  bool isInWrappingAreaOfTower =
                      map.towermode && !map.minitowermode && map.ypos >= 500 &&
                      map.ypos <= 5000;
                  if (wrapX && (map.warpx || isInWrappingAreaOfTower)) {
                      drawRect = sprites_rect;
                      drawRect.x += wrappedPoint.x;
                      drawRect.y += tpoint.y;
                      BlitSurfaceColoured(
                          (*spritesvec)[obj.entities[i].drawframe],
                          NULL,
                          backBuffer,
                          &drawRect,
                          ct);
                  }
                  if (wrapY && map.warpy) {
                      drawRect = sprites_rect;
                      drawRect.x += tpoint.x;
                      drawRect.y += wrappedPoint.y;
                      BlitSurfaceColoured(
                          (*spritesvec)[obj.entities[i].drawframe],
                          NULL,
                          backBuffer,
                          &drawRect,
                          ct);
                  }
                  if (wrapX && wrapY && map.warpx && map.warpy) {
                      drawRect = sprites_rect;
                      drawRect.x += wrappedPoint.x;
                      drawRect.y += wrappedPoint.y;
                      BlitSurfaceColoured(
                          (*spritesvec)[obj.entities[i].drawframe],
                          NULL,
                          backBuffer,
                          &drawRect,
                          ct);
                  }
                  break;
              }
              case 1:
                  // Tiles
                  if (!INBOUNDS(obj.entities[i].drawframe, tiles)) {
                      continue;
                  }
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  drawRect = tiles_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceStandard(tiles[obj.entities[i].drawframe],
                                      NULL,
                                      backBuffer,
                                      &drawRect);
                  break;
              case 2:
              case 8: {
                  // Special: Moving platform, 4 tiles or 8 tiles
                  if (!INBOUNDS(obj.entities[i].drawframe, (*tilesvec))) {
                      continue;
                  }
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  int thiswidth = 4;
                  if (obj.entities[i].size == 8) {
                      thiswidth = 8;
                  }
                  for (int ii = 0; ii < thiswidth; ii++) {
                      drawRect = tiles_rect;
                      drawRect.x += tpoint.x;
                      drawRect.y += tpoint.y;
                      drawRect.x += 8 * ii;
                      if (custom_gray) {
                          colourTransform temp_ct;
                          temp_ct.colour = 0xFFFFFFFF;
                          BlitSurfaceTinted(
                              (*tilesvec)[obj.entities[i].drawframe],
                              NULL,
                              backBuffer,
                              &drawRect,
                              temp_ct);
                      } else {
                          BlitSurfaceStandard(
                              (*tilesvec)[obj.entities[i].drawframe],
                              NULL,
                              backBuffer,
                              &drawRect);
                      }
                  }
                  break;
              }
              case 3: // Big chunky pixels!
                  prect.x = xp;
                  prect.y = yp - yoff;
                  FillRect(backBuffer, prect, obj.entities[i].realcol);
                  break;
              case 4: // Small pickups
                  setcolreal(obj.entities[i].realcol);
                  drawhuetile(xp, yp - yoff, obj.entities[i].tile);
                  break;
              case 5: // Horizontal Line
              {
                  int oldw = obj.entities[i].w;
                  if ((game.swngame == 3 || kludgeswnlinewidth) &&
                      obj.getlineat(84 - 32) == i) {
                      oldw -= 24;
                  }
                  line_rect.x = xp;
                  line_rect.y = yp - yoff;
                  line_rect.w = lerp(oldw, obj.entities[i].w);
                  line_rect.h = 1;
                  drawgravityline(i);
                  break;
              }
              case 6: // Vertical Line
                  line_rect.x = xp;
                  line_rect.y = yp - yoff;
                  line_rect.w = 1;
                  line_rect.h = obj.entities[i].h;
                  drawgravityline(i);
                  break;
              case 7: // Teleporter
                  drawtele(xp,
                           yp - yoff,
                           obj.entities[i].drawframe,
                           obj.entities[i].realcol);
                  break;
                  // case 8:    // Special: Moving platform, 8 tiles
                  // Note: This code is in the 4-tile code
                  break;
              case 9: // Really Big Sprite! (2x2)
                  if (!INBOUNDS(obj.entities[i].drawframe, (*spritesvec))) {
                      continue;
                  }
                  setcolreal(obj.entities[i].realcol);
  
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
  
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured((*spritesvec)[obj.entities[i].drawframe],
                                      NULL,
                                      backBuffer,
                                      &drawRect,
                                      ct);
  
                  tpoint.x = xp + 32;
                  tpoint.y = yp - yoff;
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured(
                      (*spritesvec)[obj.entities[i].drawframe + 1],
                      NULL,
                      backBuffer,
                      &drawRect,
                      ct);
  
                  tpoint.x = xp;
                  tpoint.y = yp + 32 - yoff;
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured(
                      (*spritesvec)[obj.entities[i].drawframe + 12],
                      NULL,
                      backBuffer,
                      &drawRect,
                      ct);
  
                  tpoint.x = xp + 32;
                  tpoint.y = yp + 32 - yoff;
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured(
                      (*spritesvec)[obj.entities[i].drawframe + 13],
                      NULL,
                      backBuffer,
                      &drawRect,
                      ct);
                  break;
              case 10: // 2x1 Sprite
                  if (!INBOUNDS(obj.entities[i].drawframe, (*spritesvec))) {
                      continue;
                  }
                  setcolreal(obj.entities[i].realcol);
  
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured((*spritesvec)[obj.entities[i].drawframe],
                                      NULL,
                                      backBuffer,
                                      &drawRect,
                                      ct);
  
                  tpoint.x = xp + 32;
                  tpoint.y = yp - yoff;
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured(
                      (*spritesvec)[obj.entities[i].drawframe + 1],
                      NULL,
                      backBuffer,
                      &drawRect,
                      ct);
                  break;
              case 11: // The fucking elephant
                  setcolreal(obj.entities[i].realcol);
                  drawimagecol(3, xp, yp - yoff);
                  break;
              case 12: // Regular sprites that don't wrap
                  if (!INBOUNDS(obj.entities[i].drawframe, (*spritesvec))) {
                      continue;
                  }
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  setcolreal(obj.entities[i].realcol);
                  //
                  drawRect = sprites_rect;
                  drawRect.x += tpoint.x;
                  drawRect.y += tpoint.y;
                  BlitSurfaceColoured((*spritesvec)[obj.entities[i].drawframe],
                                      NULL,
                                      backBuffer,
                                      &drawRect,
                                      ct);
  
                  // if we're outside the screen, we need to draw indicators
  
                  if (obj.entities[i].xp < -20 && obj.entities[i].vx > 0) {
                      if (obj.entities[i].xp < -100) {
                          tpoint.x = -5 + (int((-obj.entities[i].xp) / 10));
                      } else {
                          tpoint.x = 5;
                      }
  
                      tpoint.y = tpoint.y + 4;
  
                      drawRect = tiles_rect;
                      drawRect.x += tpoint.x;
                      drawRect.y += tpoint.y;
                      BlitSurfaceColoured(
                          tiles[1167], NULL, backBuffer, &drawRect, ct);
  
                  } else if (obj.entities[i].xp > 340 && obj.entities[i].vx < 0) {
                      if (obj.entities[i].xp > 420) {
                          tpoint.x = 320 - (int((obj.entities[i].xp - 320) / 10));
                      } else {
                          tpoint.x = 310;
                      }
  
                      tpoint.y = tpoint.y + 4;
                      //
  
                      drawRect = tiles_rect;
                      drawRect.x += tpoint.x;
                      drawRect.y += tpoint.y;
                      BlitSurfaceColoured(
                          tiles[1166], NULL, backBuffer, &drawRect, ct);
                  }
                  break;
              case 13: {
                  // Special for epilogue: huge hero!
                  if (!INBOUNDS(obj.entities[i].drawframe, (*spritesvec))) {
                      continue;
                  }
  
                  tpoint.x = xp;
                  tpoint.y = yp - yoff;
                  setcolreal(obj.entities[i].realcol);
                  SDL_Rect drawRect = { Sint16(obj.entities[i].xp),
                                        Sint16(obj.entities[i].yp - yoff),
                                        Sint16(sprites_rect.x * 6),
                                        Sint16(sprites_rect.y * 6) };
                  SDL_Surface* TempSurface =
                      ScaleSurface((*spritesvec)[obj.entities[i].drawframe],
                                   6 * sprites_rect.w,
                                   6 * sprites_rect.h);
                  BlitSurfaceColoured(
                      TempSurface, NULL, backBuffer, &drawRect, ct);
                  SDL_FreeSurface(TempSurface);
  
                  break;
              }
          }
      }
  }
  
  void Graphics::drawbackground(int t)
  {
      int temp = 0;
  
      switch (t) {
          case 1:
              // Starfield
              FillRect(backBuffer, 0x00000);
              for (int i = 0; i < numstars; i++) {
                  stars[i].w = 2;
                  stars[i].h = 2;
                  SDL_Rect star_rect = stars[i];
                  star_rect.x = lerp(star_rect.x + starsspeed[i], star_rect.x);
                  if (starsspeed[i] <= 6) {
                      FillRect(backBuffer, star_rect, getRGB(0x22, 0x22, 0x22));
                  } else {
                      FillRect(backBuffer, star_rect, getRGB(0x55, 0x55, 0x55));
                  }
              }
              break;
          case 2:
              // Lab
              switch (rcol) {
                      // Akward ordering to match tileset
                  case 0:
                      bcol2 = RGBflip(0, 16 * backboxint[0], 16 * backboxint[0]);
                      break; // Cyan
                  case 1:
                      bcol2 = RGBflip(16 * backboxint[0], 0, 0);
                      break; // Red
                  case 2:
                      bcol2 = RGBflip(16 * backboxint[0], 0, 16 * backboxint[0]);
                      break; // Purple
                  case 3:
                      bcol2 = RGBflip(0, 0, 16 * backboxint[0]);
                      break; // Blue
                  case 4:
                      bcol2 = RGBflip(16 * backboxint[0], 16 * backboxint[0], 0);
                      break; // Yellow
                  case 5:
                      bcol2 = RGBflip(0, 16 * backboxint[0], 0);
                      break; // Green
                  case 6:
                      // crazy case
                      switch (spcol) {
                          case 0:
                              bcol2 = RGBflip(
                                  0, 16 * backboxint[0], 16 * backboxint[0]);
                              break; // Cyan
                          case 1:
                              bcol2 = RGBflip(0,
                                              (spcoldel + 1) * backboxint[0],
                                              16 * backboxint[0]);
                              break; // Cyan
                          case 2:
                              bcol2 = RGBflip(0, 0, 16 * backboxint[0]);
                              break; // Blue
                          case 3:
                              bcol2 = RGBflip((16 - spcoldel) * backboxint[0],
                                              0,
                                              16 * backboxint[0]);
                              break; // Blue
                          case 4:
                              bcol2 = RGBflip(
                                  16 * backboxint[0], 0, 16 * backboxint[0]);
                              break; // Purple
                          case 5:
                              bcol2 = RGBflip(16 * backboxint[0],
                                              0,
                                              (spcoldel + 1) * backboxint[0]);
                              break; // Purple
                          case 6:
                              bcol2 = RGBflip(16 * backboxint[0], 0, 0);
                              break; // Red
                          case 7:
                              bcol2 = RGBflip(16 * backboxint[0],
                                              (16 - spcoldel) * backboxint[0],
                                              0);
                              break; // Red
                          case 8:
                              bcol2 = RGBflip(
                                  16 * backboxint[0], 16 * backboxint[0], 0);
                              break; // Yellow
                          case 9:
                              bcol2 = RGBflip((spcoldel + 1) * backboxint[0],
                                              16 * backboxint[0],
                                              0);
                              break; // Yellow
                          case 10:
                              bcol2 = RGBflip(0, 16 * backboxint[0], 0);
                              break; // Green
                          case 11:
                              bcol2 = RGBflip(0,
                                              16 * backboxint[0],
                                              (16 - spcoldel) * backboxint[0]);
                              break; // Green
                      }
                      break;
              }
              FillRect(backBuffer, bcol2);
  
              for (int i = 0; i < numbackboxes; i++) {
                  switch (rcol) {
                          // Akward ordering to match tileset
                      case 0:
                          bcol = RGBflip(
                              16, 128 * backboxint[0], 128 * backboxint[0]);
                          break; // Cyan
                      case 1:
                          bcol = RGBflip(128 * backboxint[0], 16, 16);
                          break; // Red
                      case 2:
                          bcol = RGBflip(
                              128 * backboxint[0], 16, 128 * backboxint[0]);
                          break; // Purple
                      case 3:
                          bcol = RGBflip(16, 16, 128 * backboxint[0]);
                          break; // Blue
                      case 4:
                          bcol = RGBflip(
                              128 * backboxint[0], 128 * backboxint[0], 16);
                          break; // Yellow
                      case 5:
                          bcol = RGBflip(16, 128 * backboxint[0], 16);
                          break; // Green
                      case 6:
                          // crazy case
                          switch (spcol) {
                              case 0:
                                  bcol = RGBflip(16,
                                                 128 * backboxint[0],
                                                 128 * backboxint[0]);
                                  break; // Cyan
                              case 1:
                                  bcol = RGBflip(16,
                                                 ((spcoldel + 1) * 8) *
                                                     backboxint[0],
                                                 128 * backboxint[0]);
                                  break; // Cyan
                              case 2:
                                  bcol = RGBflip(16, 16, 128 * backboxint[0]);
                                  break; // Blue
                              case 3:
                                  bcol = RGBflip((128 - (spcoldel * 8)) *
                                                     backboxint[0],
                                                 16,
                                                 128 * backboxint[0]);
                                  break; // Blue
                              case 4:
                                  bcol = RGBflip(128 * backboxint[0],
                                                 16,
                                                 128 * backboxint[0]);
                                  break; // Purple
                              case 5:
                                  bcol = RGBflip(128 * backboxint[0],
                                                 16,
                                                 ((spcoldel + 1) * 8) *
                                                     backboxint[0]);
                                  break; // Purple
                              case 6:
                                  bcol = RGBflip(128 * backboxint[0], 16, 16);
                                  break; // Red
                              case 7:
                                  bcol = RGBflip(128 * backboxint[0],
                                                 (128 - (spcoldel * 8)) *
                                                     backboxint[0],
                                                 16);
                                  break; // Red
                              case 8:
                                  bcol = RGBflip(128 * backboxint[0],
                                                 128 * backboxint[0],
                                                 16);
                                  break; // Yellow
                              case 9:
                                  bcol = RGBflip(((spcoldel + 1) * 8) *
                                                     backboxint[0],
                                                 128 * backboxint[0],
                                                 16);
                                  break; // Yellow
                              case 10:
                                  bcol = RGBflip(16, 128 * backboxint[0], 16);
                                  break; // Green
                              case 11:
                                  bcol = RGBflip(16,
                                                 128 * backboxint[0],
                                                 (128 - (spcoldel * 8)) *
                                                     backboxint[0]);
                                  break; // Green
                          }
                          break;
                  }
  
                  SDL_Rect backboxrect = backboxes[i];
                  backboxrect.x =
                      lerp(backboxes[i].x - backboxvx[i], backboxes[i].x);
                  backboxrect.y =
                      lerp(backboxes[i].y - backboxvy[i], backboxes[i].y);
  
                  FillRect(backBuffer, backboxrect, bcol);
                  backboxrect.x += 1;
                  backboxrect.y += 1;
                  backboxrect.w -= 2;
                  backboxrect.h -= 2;
                  FillRect(backBuffer, backboxrect, bcol2);
              }
              break;
          case 3: // Warp zone (horizontal)
              FillRect(backBuffer, 0x000000);
              BlitSurfaceStandard(towerbuffer, NULL, towerbuffer_lerp, NULL);
              ScrollSurface(towerbuffer_lerp, lerp(0, -3), 0);
              BlitSurfaceStandard(towerbuffer_lerp, NULL, backBuffer, NULL);
              break;
          case 4: // Warp zone (vertical)
              FillRect(backBuffer, 0x000000);
              SDL_BlitSurface(towerbuffer, NULL, towerbuffer_lerp, NULL);
              ScrollSurface(towerbuffer_lerp, 0, lerp(0, -3));
              SDL_BlitSurface(towerbuffer_lerp, NULL, backBuffer, NULL);
              break;
          case 5:
              // Warp zone, central
              switch (rcol) {
                      // Akward ordering to match tileset
                  case 0:
                      warpbcol = RGBflip(0x0A, 0x10, 0x0E);
                      warpfcol = RGBflip(0x10, 0x22, 0x21);
                      break; // Cyan
                  case 1:
                      warpbcol = RGBflip(0x11, 0x09, 0x0B);
                      warpfcol = RGBflip(0x22, 0x10, 0x11);
                      break; // Red
                  case 2:
                      warpbcol = RGBflip(0x0F, 0x0A, 0x10);
                      warpfcol = RGBflip(0x22, 0x10, 0x22);
                      break; // Purple
                  case 3:
                      warpbcol = RGBflip(0x0A, 0x0B, 0x10);
                      warpfcol = RGBflip(0x10, 0x10, 0x22);
                      break; // Blue
                  case 4:
                      warpbcol = RGBflip(0x10, 0x0D, 0x0A);
                      warpfcol = RGBflip(0x22, 0x1E, 0x10);
                      break; // Yellow
                  case 5:
                      warpbcol = RGBflip(0x0D, 0x10, 0x0A);
                      warpfcol = RGBflip(0x14, 0x22, 0x10);
                      break; // Green
                  case 6:
                      warpbcol = RGBflip(0x0A, 0x0A, 0x0A);
                      warpfcol = RGBflip(0x12, 0x12, 0x12);
                      break; // Gray
                  default:
                      warpbcol = RGBflip(0xFF, 0xFF, 0xFF);
                      warpfcol = RGBflip(0xFF, 0xFF, 0xFF);
              }
  
              for (int i = 10; i >= 0; i--) {
                  temp = (i << 4) + lerp(backoffset - 1, backoffset);
                  setwarprect(160 - temp, 120 - temp, temp * 2, temp * 2);
                  if (i % 2 == warpskip) {
                      FillRect(backBuffer, warprect, warpbcol);
                  } else {
                      FillRect(backBuffer, warprect, warpfcol);
                  }
              }
              break;
          case 6:
              // Final Starfield
              FillRect(backBuffer, 0x000000);
              for (int i = 0; i < numstars; i++) {
                  stars[i].w = 2;
                  stars[i].h = 2;
                  SDL_Rect star_rect = stars[i];
                  star_rect.y = lerp(star_rect.y + starsspeed[i], star_rect.y);
                  if (starsspeed[i] <= 8) {
                      FillRect(backBuffer, star_rect, getRGB(0x22, 0x22, 0x22));
                  } else {
                      FillRect(backBuffer, star_rect, getRGB(0x55, 0x55, 0x55));
                  }
              }
              break;
          case 7:
              // Static, unscrolling section of the tower
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      drawtile3(i * 8, j * 8, map.tower.backat(i, j, 200), 15);
                  }
              }
              break;
          case 8:
              // Static, unscrolling section of the tower
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      drawtile3(i * 8, j * 8, map.tower.backat(i, j, 200), 10);
                  }
              }
              break;
          case 9:
              // Static, unscrolling section of the tower
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      drawtile3(i * 8, j * 8, map.tower.backat(i, j, 600), 0);
                  }
              }
              break;
          default:
              FillRect(backBuffer, 0x000000);
  
              break;
      }
  }
  
  void Graphics::updatebackground(int t)
  {
      switch (t) {
          case 1:
              // Starfield
              for (int i = 0; i < numstars; i++) {
                  stars[i].w = 2;
                  stars[i].h = 2;
                  stars[i].x -= Sint16(starsspeed[i]);
                  if (stars[i].x < -10) {
                      stars[i].x += 340;
                      stars[i].y = int(fRandom() * 240);
                      stars[i].w = 2;
                      starsspeed[i] = 4 + int(fRandom() * 4);
                  }
              }
              break;
          case 2:
              // Lab
              if (rcol == 6) {
                  // crazy caze
                  spcoldel--;
                  if (spcoldel <= 0) {
                      spcoldel = 15;
                      spcol++;
                      if (spcol >= 12)
                          spcol = 0;
                  }
              }
              for (int i = 0; i < numbackboxes; i++) {
                  backboxes[i].x += backboxvx[i];
                  backboxes[i].y += backboxvy[i];
                  if (backboxes[i].x < -40) {
                      backboxes[i].x = 320;
                      backboxes[i].y = fRandom() * 240;
                  }
                  if (backboxes[i].x > 320) {
                      backboxes[i].x = -32;
                      backboxes[i].y = fRandom() * 240;
                  }
                  if (backboxes[i].y < -40) {
                      backboxes[i].y = 240;
                      backboxes[i].x = fRandom() * 320;
                  }
                  if (backboxes[i].y > 260) {
                      backboxes[i].y = -32;
                      backboxes[i].x = fRandom() * 320;
                  }
              }
              break;
          case 3: // Warp zone (horizontal)
          {
              int temp = 680 + (rcol * 3);
              backoffset += 3;
              if (backoffset >= 16)
                  backoffset -= 16;
  
              if (backgrounddrawn) {
                  ScrollSurface(towerbuffer, -3, 0);
                  for (int j = 0; j < 15; j++) {
                      for (int i = 0; i < 2; i++) {
                          drawtowertile(317 - backoffset + (i * 16),
                                        (j * 16),
                                        temp + 40); // 20*16 = 320
                          drawtowertile(317 - backoffset + (i * 16) + 8,
                                        (j * 16),
                                        temp + 41);
                          drawtowertile(317 - backoffset + (i * 16),
                                        (j * 16) + 8,
                                        temp + 80);
                          drawtowertile(317 - backoffset + (i * 16) + 8,
                                        (j * 16) + 8,
                                        temp + 81);
                      }
                  }
              } else {
                  // draw the whole thing for the first time!
                  backoffset = 0;
                  FillRect(towerbuffer, 0x000000);
                  for (int j = 0; j < 15; j++) {
                      for (int i = 0; i < 21; i++) {
                          drawtowertile(
                              (i * 16) - backoffset - 3, (j * 16), temp + 40);
                          drawtowertile(
                              (i * 16) - backoffset + 8 - 3, (j * 16), temp + 41);
                          drawtowertile(
                              (i * 16) - backoffset - 3, (j * 16) + 8, temp + 80);
                          drawtowertile((i * 16) - backoffset + 8 - 3,
                                        (j * 16) + 8,
                                        temp + 81);
                      }
                  }
                  backgrounddrawn = true;
              }
              break;
          }
          case 4: // Warp zone (vertical)
          {
              int temp = 760 + (rcol * 3);
              backoffset += 3;
              if (backoffset >= 16)
                  backoffset -= 16;
  
              if (backgrounddrawn) {
                  ScrollSurface(towerbuffer, 0, -3);
                  for (int j = 0; j < 2; j++) {
                      for (int i = 0; i < 21; i++) {
                          drawtowertile((i * 16),
                                        237 - backoffset + (j * 16),
                                        temp + 40); // 14*17=240 - 3
                          drawtowertile((i * 16) + 8,
                                        237 - backoffset + (j * 16),
                                        temp + 41);
                          drawtowertile((i * 16),
                                        237 - backoffset + (j * 16) + 8,
                                        temp + 80);
                          drawtowertile((i * 16) + 8,
                                        237 - backoffset + (j * 16) + 8,
                                        temp + 81);
                      }
                  }
              } else {
                  // draw the whole thing for the first time!
                  backoffset = 0;
                  FillRect(towerbuffer, 0x000000);
                  for (int j = 0; j < 16; j++) {
                      for (int i = 0; i < 21; i++) {
                          drawtowertile(
                              (i * 16), (j * 16) - backoffset - 3, temp + 40);
                          drawtowertile(
                              (i * 16) + 8, (j * 16) - backoffset - 3, temp + 41);
                          drawtowertile(
                              (i * 16), (j * 16) - backoffset + 8 - 3, temp + 80);
                          drawtowertile((i * 16) + 8,
                                        (j * 16) - backoffset + 8 - 3,
                                        temp + 81);
                      }
                  }
                  backgrounddrawn = true;
              }
              break;
          }
          case 5:
              // Warp zone, central
  
              backoffset += 1;
              if (backoffset >= 16) {
                  backoffset -= 16;
                  warpskip = (warpskip + 1) % 2;
              }
              break;
          case 6:
              // Final Starfield
              for (int i = 0; i < numstars; i++) {
                  stars[i].w = 2;
                  stars[i].h = 2;
                  stars[i].y -= starsspeed[i];
                  if (stars[i].y < -10) {
                      stars[i].y += 260;
                      stars[i].x = fRandom() * 320;
                      starsspeed[i] = 5 + (fRandom() * 5);
                  }
              }
              break;
      }
  }
  
  void Graphics::drawmap()
  {
      if (!foregrounddrawn) {
          FillRect(foregroundBuffer, 0x00000000);
          if (map.tileset == 0) {
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      if (map.contents[i + map.vmult[j]] > 0)
                          drawforetile(
                              i * 8, j * 8, map.contents[i + map.vmult[j]]);
                  }
              }
          } else if (map.tileset == 1) {
              for (int jt = 0; jt < 30; jt++) {
                  for (int it = 0; it < 40; it++) {
                      if (map.contents[it + map.vmult[jt]] > 0)
                          drawforetile2(
                              it * 8, jt * 8, map.contents[it + map.vmult[jt]]);
                  }
              }
          } else if (map.tileset == 2) {
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      if (map.contents[i + map.vmult[j]] > 0)
                          drawforetile3(i * 8,
                                        j * 8,
                                        map.contents[i + map.vmult[j]],
                                        map.rcol);
                  }
              }
          }
          foregrounddrawn = true;
      }
      SDL_BlitSurface(foregroundBuffer, NULL, backBuffer, NULL);
  }
  
  void Graphics::drawfinalmap()
  {
      if (!foregrounddrawn) {
          FillRect(foregroundBuffer, 0x00000000);
          if (map.tileset == 0) {
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      if ((map.contents[i + map.vmult[j]]) > 0)
                          drawforetile(i * 8, j * 8, map.finalat(i, j));
                  }
              }
          } else if (map.tileset == 1) {
              for (int j = 0; j < 30; j++) {
                  for (int i = 0; i < 40; i++) {
                      if ((map.contents[i + map.vmult[j]]) > 0)
                          drawforetile2(i * 8, j * 8, map.finalat(i, j));
                  }
              }
          }
          foregrounddrawn = true;
      }
  
      SDL_BlitSurface(foregroundBuffer, NULL, backBuffer, NULL);
  }
  
  void Graphics::drawtowermap()
  {
      int temp;
      int yoff = lerp(map.oldypos, map.ypos);
      for (int j = 0; j < 31; j++) {
          for (int i = 0; i < 40; i++) {
              temp = map.tower.at(i, j, yoff);
              if (temp > 0)
                  drawtile3(i * 8, (j * 8) - (yoff % 8), temp, map.colstate);
          }
      }
  }
  
  void Graphics::drawtowerspikes()
  {
      int spikeleveltop = lerp(map.oldspikeleveltop, map.spikeleveltop);
      int spikelevelbottom = lerp(map.oldspikelevelbottom, map.spikelevelbottom);
      for (int i = 0; i < 40; i++) {
          drawtile3(i * 8, -8 + spikeleveltop, 9, map.colstate);
          drawtile3(i * 8,
                    230 - spikelevelbottom,
                    8,
                    map.colstate,
                    8 - spikelevelbottom);
      }
  }
  
  void Graphics::drawtowerbackground()
  {
      FillRect(backBuffer, 0x000000);
      SDL_BlitSurface(towerbuffer, NULL, towerbuffer_lerp, NULL);
      ScrollSurface(towerbuffer_lerp, 0, lerp(0, -map.bscroll));
      SDL_BlitSurface(towerbuffer_lerp, NULL, backBuffer, NULL);
  }
  
  void Graphics::updatetowerbackground()
  {
      int temp;
  
      if (map.bypos < 0)
          map.bypos += 120 * 8;
  
      if (map.tdrawback) {
          int off = map.scrolldir == 0 ? 0 : map.bscroll;
          // Draw the whole thing; needed for every colour cycle!
          for (int j = 0; j < 32; j++) {
              for (int i = 0; i < 40; i++) {
                  temp = map.tower.backat(i, j, map.bypos);
                  drawtowertile3(
                      i * 8, (j * 8) - (map.bypos % 8) - off, temp, map.colstate);
              }
          }
  
          map.tdrawback = false;
      } else {
          // just update the bottom
          ScrollSurface(towerbuffer, 0, -map.bscroll);
          if (map.scrolldir == 0) {
              for (int i = 0; i < 40; i++) {
                  temp = map.tower.backat(i, 0, map.bypos);
                  drawtowertile3(i * 8, -(map.bypos % 8), temp, map.colstate);
              }
          } else {
              for (int i = 0; i < 40; i++) {
                  temp = map.tower.backat(i, 29, map.bypos);
                  drawtowertile3(i * 8,
                                 29 * 8 - (map.bypos % 8) - map.bscroll,
                                 temp,
                                 map.colstate);
                  temp = map.tower.backat(i, 30, map.bypos);
                  drawtowertile3(i * 8,
                                 30 * 8 - (map.bypos % 8) - map.bscroll,
                                 temp,
                                 map.colstate);
                  temp = map.tower.backat(i, 31, map.bypos);
                  drawtowertile3(i * 8,
                                 31 * 8 - (map.bypos % 8) - map.bscroll,
                                 temp,
                                 map.colstate);
                  temp = map.tower.backat(i, 32, map.bypos);
                  drawtowertile3(i * 8,
                                 32 * 8 - (map.bypos % 8) - map.bscroll,
                                 temp,
                                 map.colstate);
              }
          }
      }
  }
  
  void Graphics::setcol(int t)
  {
      int temp;
  
      // Setup predefinied colours as per our zany palette
      switch (t) {
              // Player Normal
          case 0:
              ct.colour = getRGB(160 - help.glow / 2 - (fRandom() * 20),
                                 200 - help.glow / 2,
                                 220 - help.glow);
              break;
              // Player Hurt
          case 1:
              ct.colour = getRGB(196 - (fRandom() * 64), 10, 10);
              break;
              // Enemies and stuff
          case 2:
              ct.colour = getRGB(225 - (help.glow / 2), 75, 30);
              break;
          case 3: // Trinket
              if (!trinketcolset) {
                  trinketr = 200 - (fRandom() * 64);
                  trinketg = 200 - (fRandom() * 128);
                  trinketb = 164 + (fRandom() * 60);
                  trinketcolset = true;
              }
              ct.colour = getRGB(trinketr, trinketg, trinketb);
              break;
          case 4: // Inactive savepoint
              temp = (help.glow / 2) + (fRandom() * 8);
              ct.colour = getRGB(80 + temp, 80 + temp, 80 + temp);
              break;
          case 5: // Active savepoint
              ct.colour = getRGB(164 + (fRandom() * 64),
                                 164 + (fRandom() * 64),
                                 255 - (fRandom() * 64));
              break;
          case 6: // Enemy : Red
              ct.colour = getRGB(
                  250 - help.glow / 2, 60 - help.glow / 2, 60 - help.glow / 2);
              break;
          case 7: // Enemy : Green
              ct.colour = getRGB(100 - help.glow / 2 - (fRandom() * 30),
                                 250 - help.glow / 2,
                                 100 - help.glow / 2 - (fRandom() * 30));
              break;
          case 8: // Enemy : Purple
              ct.colour = getRGB(250 - help.glow / 2,
                                 20,
                                 128 - help.glow / 2 + (fRandom() * 30));
              break;
          case 9: // Enemy : Yellow
              ct.colour = getRGB(250 - help.glow / 2, 250 - help.glow / 2, 20);
              break;
          case 10: // Warp point (white)
              ct.colour = getRGB(255 - (fRandom() * 64),
                                 255 - (fRandom() * 64),
                                 255 - (fRandom() * 64));
              break;
          case 11: // Enemy : Cyan
              ct.colour = getRGB(20, 250 - help.glow / 2, 250 - help.glow / 2);
              break;
          case 12: // Enemy : Blue
              ct.colour = getRGB(
                  90 - help.glow / 2, 90 - help.glow / 2, 250 - help.glow / 2);
              break;
              // Crew Members
              // green
          case 13:
              ct.colour = getRGB(120 - help.glow / 4 - (fRandom() * 20),
                                 220 - help.glow / 4,
                                 120 - help.glow / 4);
              break;
              // Yellow
          case 14:
              ct.colour = getRGB(220 - help.glow / 4 - (fRandom() * 20),
                                 210 - help.glow / 4,
                                 120 - help.glow / 4);
              break;
              // pink
          case 15:
              ct.colour = getRGB(
                  255 - help.glow / 8, 70 - help.glow / 4, 70 - help.glow / 4);
              break;
              // Blue
          case 16:
              ct.colour = getRGB(75, 75, 255 - help.glow / 4 - (fRandom() * 20));
              break;
  
          case 17: // Enemy : Orange
              ct.colour = getRGB(250 - help.glow / 2, 130 - help.glow / 2, 20);
              break;
          case 18: // Enemy : Gray
              ct.colour = getRGB(
                  130 - help.glow / 2, 130 - help.glow / 2, 130 - help.glow / 2);
              break;
          case 19: // Enemy : Dark gray
              ct.colour = getRGB(
                  60 - help.glow / 8, 60 - help.glow / 8, 60 - help.glow / 8);
              break;
              // Purple
          case 20:
              ct.colour = getRGB(220 - help.glow / 4 - (fRandom() * 20),
                                 120 - help.glow / 4,
                                 210 - help.glow / 4);
              break;
  
          case 21: // Enemy : Light Gray
              ct.colour = getRGB(
                  180 - help.glow / 2, 180 - help.glow / 2, 180 - help.glow / 2);
              break;
          case 22: // Enemy : Indicator Gray
              ct.colour = getRGB(
                  230 - help.glow / 2, 230 - help.glow / 2, 230 - help.glow / 2);
              break;
          case 23: // Enemy : Indicator Gray
              ct.colour = getRGB(255 - help.glow / 2 - (fRandom() * 40),
                                 255 - help.glow / 2 - (fRandom() * 40),
                                 255 - help.glow / 2 - (fRandom() * 40));
              break;
  
              // Trophies
              // cyan
          case 30:
              ct.colour = RGBf(160, 200, 220);
              break;
              // Purple
          case 31:
              ct.colour = RGBf(220, 120, 210);
              break;
              // Yellow
          case 32:
              ct.colour = RGBf(220, 210, 120);
              break;
              // red
          case 33:
              ct.colour = RGBf(255, 70, 70);
              break;
              // green
          case 34:
              ct.colour = RGBf(120, 220, 120);
              break;
              // Blue
          case 35:
              ct.colour = RGBf(75, 75, 255);
              break;
              // Gold
          case 36:
              ct.colour = getRGB(180, 120, 20);
              break;
          case 37: // Trinket
              if (!trinketcolset) {
                  trinketr = 200 - (fRandom() * 64);
                  trinketg = 200 - (fRandom() * 128);
                  trinketb = 164 + (fRandom() * 60);
                  trinketcolset = true;
              }
              ct.colour = RGBf(trinketr, trinketg, trinketb);
              break;
              // Silver
          case 38:
              ct.colour = RGBf(196, 196, 196);
              break;
              // Bronze
          case 39:
              ct.colour = RGBf(128, 64, 10);
              break;
              // Awesome
          case 40: // Teleporter in action!
              temp = fRandom() * 150;
              if (temp < 33) {
                  ct.colour = RGBf(255 - (fRandom() * 64),
                                   64 + (fRandom() * 64),
                                   64 + (fRandom() * 64));
              } else if (temp < 66) {
                  ct.colour = RGBf(64 + (fRandom() * 64),
                                   255 - (fRandom() * 64),
                                   64 + (fRandom() * 64));
              } else if (temp < 100) {
                  ct.colour = RGBf(64 + (fRandom() * 64),
                                   64 + (fRandom() * 64),
                                   255 - (fRandom() * 64));
              } else {
                  ct.colour = RGBf(164 + (fRandom() * 64),
                                   164 + (fRandom() * 64),
                                   255 - (fRandom() * 64));
              }
              break;
  
          case 100: // Inactive Teleporter
              temp = (help.glow / 2) + (fRandom() * 8);
              ct.colour = getRGB(42 + temp, 42 + temp, 42 + temp);
              break;
          case 101: // Active Teleporter
              ct.colour = getRGB(164 + (fRandom() * 64),
                                 164 + (fRandom() * 64),
                                 255 - (fRandom() * 64));
              break;
          case 102: // Teleporter in action!
              temp = fRandom() * 150;
              if (temp < 33) {
                  ct.colour = getRGB(255 - (fRandom() * 64),
                                     64 + (fRandom() * 64),
                                     64 + (fRandom() * 64));
              } else if (temp < 66) {
                  ct.colour = getRGB(64 + (fRandom() * 64),
                                     255 - (fRandom() * 64),
                                     64 + (fRandom() * 64));
              } else if (temp < 100) {
                  ct.colour = getRGB(64 + (fRandom() * 64),
                                     64 + (fRandom() * 64),
                                     255 - (fRandom() * 64));
              } else {
                  ct.colour = getRGB(164 + (fRandom() * 64),
                                     164 + (fRandom() * 64),
                                     255 - (fRandom() * 64));
              }
              break;
  
          default:
              ct.colour = getRGB(255, 255, 255);
              break;
      }
  }
  
  void Graphics::menuoffrender()
  {
      SDL_Rect offsetRect1;
      setRect(offsetRect1, 0, 0, backBuffer->w, backBuffer->h);
  
      // put the back buffer in the menubuffer
      BlitSurfaceStandard(backBuffer, NULL, menubuffer, NULL);
  
      int usethisoffset = lerp(oldmenuoffset, menuoffset);
      if (flipmode) {
          //	flipmatrix.translate(0, menuoffset);
          //	screenbuffer.draw(backbuffer, flipmatrix);
          //	flipmatrix.translate(0, -menuoffset);
          SDL_Surface* tempbufferFlipped = FlipSurfaceVerticle(tempBuffer);
          // put the stored backbuffer in the backbuffer.
          SDL_FillRect(backBuffer, NULL, 0x00000000);
          BlitSurfaceStandard(tempbufferFlipped, NULL, backBuffer, NULL);
          SDL_FreeSurface(tempbufferFlipped);
          SDL_Rect offsetRect;
          setRect(offsetRect, 0, usethisoffset, backBuffer->w, backBuffer->h);
          SDL_Surface* temp = FlipSurfaceVerticle(menubuffer);
          BlitSurfaceStandard(temp, NULL, backBuffer, &offsetRect);
          SDL_FreeSurface(temp);
      } else {
          // put the stored backbuffer in the backbuffer.
          BlitSurfaceStandard(tempBuffer, NULL, backBuffer, NULL);
  
          SDL_Rect offsetRect;
          setRect(offsetRect, 0, usethisoffset, backBuffer->w, backBuffer->h);
          BlitSurfaceStandard(menubuffer, NULL, backBuffer, &offsetRect);
      }
  
      SDL_Rect rect;
      setRect(rect, 0, 0, backBuffer->w, backBuffer->h);
      screenbuffer->UpdateScreen(backBuffer, &rect);
      FillRect(backBuffer, 0x000000);
  }
  
  void Graphics::drawhuetile(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles)) {
          return;
      }
      point tpoint;
      tpoint.x = x;
      tpoint.y = y;
  
      SDL_Rect rect;
      setRect(rect, tpoint.x, tpoint.y, tiles_rect.w, tiles_rect.h);
      BlitSurfaceColoured(tiles[t], NULL, backBuffer, &rect, ct);
  }
  
  void Graphics::huetilesetcol(int t)
  {
      switch (t) {
          case 0:
              setcolreal(getRGB(
                  250 - int(fRandom() * 32), 250 - int(fRandom() * 32), 10));
              break;
          case 1:
              setcolreal(getRGB(
                  250 - int(fRandom() * 32), 250 - int(fRandom() * 32), 10));
              break;
          default:
              setcolreal(getRGB(
                  250 - int(fRandom() * 32), 250 - int(fRandom() * 32), 10));
              break;
      }
  }
  
  Uint32 Graphics::bigchunkygetcol(int t)
  {
      // A seperate index of colours, for simplicity
      switch (t) {
          case 1:
              return getRGB((fRandom() * 64), 10, 10);
          case 2:
              return getRGB(int(160 - help.glow / 2 - (fRandom() * 20)),
                            200 - help.glow / 2,
                            220 - help.glow);
      }
      return 0x00000000;
  }
  
  void Graphics::setwarprect(int a, int b, int c, int d)
  {
      warprect.x = a;
      warprect.y = b;
      warprect.w = c;
      warprect.h = d;
  }
  
  void Graphics::textboxcenter()
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxcenter() out-of-bounds!");
          return;
      }
  
      textbox[m].centerx();
      textbox[m].centery();
  }
  
  void Graphics::textboxcenterx()
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxcenterx() out-of-bounds!");
          return;
      }
  
      textbox[m].centerx();
  }
  
  int Graphics::textboxwidth()
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxwidth() out-of-bounds!");
          return 0;
      }
  
      return textbox[m].w;
  }
  
  void Graphics::textboxmove(int xo, int yo)
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxmove() out-of-bounds!");
          return;
      }
  
      textbox[m].xp += xo;
      textbox[m].yp += yo;
  }
  
  void Graphics::textboxmoveto(int xo)
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxmoveto() out-of-bounds!");
          return;
      }
  
      textbox[m].xp = xo;
  }
  
  void Graphics::textboxcentery()
  {
      if (!INBOUNDS(m, textbox)) {
          puts("textboxcentery() out-of-bounds!");
          return;
      }
  
      textbox[m].centery();
  }
  
  int Graphics::crewcolour(const int t)
  {
      // given crewmate t, return colour in setcol
      if (t == 0)
          return 0;
      if (t == 1)
          return 20;
      if (t == 2)
          return 14;
      if (t == 3)
          return 15;
      if (t == 4)
          return 13;
      if (t == 5)
          return 16;
      return 0;
  }
  
  void Graphics::flashlight()
  {
      FillRect(backBuffer, 0xBBBBBBBB);
  }
  
  void Graphics::screenshake()
  {
      if (flipmode) {
          //	tpoint.x = int((Math.random() * 7) - 4); tpoint.y =
          //int((Math.random() * 7) - 4); 	flipmatrix.translate(tpoint.x,
          //tpoint.y); 	screenbuffer.draw(backbuffer, flipmatrix);
          //	flipmatrix.translate(-tpoint.x, -tpoint.y);
  
          SDL_Rect shakeRect;
          setRect(shakeRect,
                  screenshake_x,
                  screenshake_y,
                  backBuffer->w,
                  backBuffer->h);
          SDL_Surface* flipBackBuffer = FlipSurfaceVerticle(backBuffer);
          screenbuffer->UpdateScreen(flipBackBuffer, &shakeRect);
          SDL_FreeSurface(flipBackBuffer);
      } else {
          // FillRect(screenbuffer, 0x000);
          // SDL_Rect rect;
          // setRect(rect, blackBars/2, 0, screenbuffer->w, screenbuffer->h);
          // SDL_BlitSurface(backBuffer, NULL, screenbuffer, &rect);
          SDL_Rect shakeRect;
          setRect(shakeRect,
                  screenshake_x,
                  screenshake_y,
                  backBuffer->w,
                  backBuffer->h);
          screenbuffer->UpdateScreen(backBuffer, &shakeRect);
      }
  
      FillRect(backBuffer, 0x000000);
  }
  
  void Graphics::updatescreenshake()
  {
      screenshake_x = static_cast<Sint32>((fRandom() * 7) - 4);
      screenshake_y = static_cast<Sint32>((fRandom() * 7) - 4);
  }
  
  void Graphics::render()
  {
      if (screenbuffer == NULL) {
          return;
      }
      if (flipmode) {
          SDL_Rect rect;
          setRect(rect, 0, 0, backBuffer->w, backBuffer->h);
          // setRect(rect, 0, 0, backBuffer->w, backBuffer->h);
          // SDL_BlitSurface(backBuffer, NULL, screenbuffer, &rect);
          SDL_Surface* tempsurface = FlipSurfaceVerticle(backBuffer);
          if (tempsurface != NULL) {
              screenbuffer->UpdateScreen(tempsurface, &rect);
              SDL_FreeSurface(tempsurface);
          }
      } else {
          SDL_Rect rect;
          setRect(rect, 0, 0, backBuffer->w, backBuffer->h);
          // setRect(rect, 0, 0, backBuffer->w, backBuffer->h);
          // SDL_BlitSurface(backBuffer, NULL, screenbuffer, &rect);
          screenbuffer->UpdateScreen(backBuffer, &rect);
      }
  }
  
  void Graphics::renderwithscreeneffects()
  {
      if (game.flashlight > 0 && !game.noflashingmode) {
          graphics.flashlight();
      }
  
      if (game.screenshake > 0 && !game.noflashingmode) {
          graphics.screenshake();
      } else {
          graphics.render();
      }
  }
  
  void Graphics::bigrprint(int x,
                           int y,
                           std::string& t,
                           int r,
                           int g,
                           int b,
                           bool cen,
                           float sc)
  {
      std::vector<SDL_Surface*>& font = flipmode ? flipbfont : bfont;
  
      r = clamp(r, 0, 255);
      g = clamp(g, 0, 255);
      b = clamp(b, 0, 255);
      ct.colour = getRGB(r, g, b);
  
      x = x / (sc);
  
      x -= (len(t));
  
      if (cen) {
          x = std::max(160 - (int((len(t) / 2.0) * sc)), 0);
      } else {
          x *= (sc);
      }
  
      int bfontpos = 0;
      int cur;
      int idx;
      std::string::iterator iter = t.begin();
      while (iter != t.end()) {
          cur = utf8::unchecked::next(iter);
          idx = font_idx(cur);
          if (INBOUNDS(idx, font)) {
              SDL_Surface* tempPrint =
                  ScaleSurface(font[idx], font[idx]->w * sc, font[idx]->h * sc);
              SDL_Rect printrect = { Sint16((x) + bfontpos),
                                     Sint16(y),
                                     Sint16(bfont_rect.w * sc),
                                     Sint16(bfont_rect.h * sc) };
              BlitSurfaceColoured(tempPrint, NULL, backBuffer, &printrect, ct);
              SDL_FreeSurface(tempPrint);
          }
          bfontpos += bfontlen(cur) * sc;
      }
  }
  
  void Graphics::drawtele(int x, int y, int t, Uint32 c)
  {
      setcolreal(getRGB(16, 16, 16));
  
      SDL_Rect telerect;
      setRect(telerect, x, y, tele_rect.w, tele_rect.h);
      if (INBOUNDS(0, tele)) {
          BlitSurfaceColoured(tele[0], NULL, backBuffer, &telerect, ct);
      }
  
      setcolreal(c);
      if (t > 9)
          t = 8;
      if (t < 0)
          t = 0;
  
      setRect(telerect, x, y, tele_rect.w, tele_rect.h);
      if (INBOUNDS(t, tele)) {
          BlitSurfaceColoured(tele[t], NULL, backBuffer, &telerect, ct);
      }
  }
  
  Uint32 Graphics::getRGBA(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
  {
      return SDL_MapRGBA(backBuffer->format, b, g, r, a);
  }
  
  Uint32 Graphics::getRGB(Uint8 r, Uint8 g, Uint8 b)
  {
      return SDL_MapRGB(backBuffer->format, b, g, r);
  }
  
  Uint32 Graphics::getBGR(Uint8 r, Uint8 g, Uint8 b)
  {
      return SDL_MapRGB(backBuffer->format, r, g, b);
  }
  
  Uint32 Graphics::getRGB(Uint32 _col)
  {
      return (_col);
  }
  
  Uint32 Graphics::RGBflip(Uint8 r, Uint8 g, Uint8 b)
  {
      return SDL_MapRGB(backBuffer->format, r, g, b);
  }
  
  Uint32 Graphics::RGBf(int r, int g, int b)
  {
      r = (r + 128) / 3;
      g = (g + 128) / 3;
      b = (b + 128) / 3;
      return SDL_MapRGB(backBuffer->format, r, g, b);
  }
  
  void Graphics::setcolreal(Uint32 t)
  {
      ct.colour = t;
  }
  
  void Graphics::drawforetile(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles)) {
          WHINE_ONCE("drawforetile() out-of-bounds!")
          return;
      }
  
      SDL_Rect rect;
      setRect(rect, x, y, tiles_rect.w, tiles_rect.h);
  
  #if !defined(NO_CUSTOM_LEVELS)
      if (t >= 14 && t <= 17 &&
          (!FILESYSTEM_assetsmounted || ed.onewaycol_override)) {
          colourTransform thect = { ed.getonewaycol() };
          BlitSurfaceTinted(tiles[t], NULL, foregroundBuffer, &rect, thect);
      } else
  #endif
      {
          BlitSurfaceStandard(tiles[t], NULL, foregroundBuffer, &rect);
      }
  }
  
  void Graphics::drawforetile2(int x, int y, int t)
  {
      if (!INBOUNDS(t, tiles2)) {
          WHINE_ONCE("drawforetile2() out-of-bounds!")
          return;
      }
  
      SDL_Rect rect;
      setRect(rect, x, y, tiles_rect.w, tiles_rect.h);
  
  #if !defined(NO_CUSTOM_LEVELS)
      if (t >= 14 && t <= 17 &&
          (!FILESYSTEM_assetsmounted || ed.onewaycol_override)) {
          colourTransform thect = { ed.getonewaycol() };
          BlitSurfaceTinted(tiles2[t], NULL, foregroundBuffer, &rect, thect);
      } else
  #endif
      {
          BlitSurfaceStandard(tiles2[t], NULL, foregroundBuffer, &rect);
      }
  }
  
  void Graphics::drawforetile3(int x, int y, int t, int off)
  {
      t += off * 30;
      if (!INBOUNDS(t, tiles3)) {
          WHINE_ONCE("drawforetile3() out-of-bounds!")
          return;
      }
      SDL_Rect rect;
      setRect(rect, x, y, tiles_rect.w, tiles_rect.h);
      BlitSurfaceStandard(tiles3[t], NULL, foregroundBuffer, &rect);
  }
  
  void Graphics::drawrect(int x, int y, int w, int h, int r, int g, int b)
  {
      SDL_Rect madrect;
      // Draw the retangle indicated by that object
      madrect.x = x;
      madrect.y = y;
      madrect.w = w;
      madrect.h = 1;
      FillRect(backBuffer, madrect, getRGB(b, g, r));
  
      madrect.w = 1;
      madrect.h = h;
      FillRect(backBuffer, madrect, getRGB(b, g, r));
  
      madrect.x = x + w - 1;
      madrect.w = 1;
      madrect.h = h;
      FillRect(backBuffer, madrect, getRGB(b, g, r));
      madrect.x = x;
      madrect.y = y + h - 1;
      madrect.w = w;
      madrect.h = 1;
      FillRect(backBuffer, madrect, getRGB(b, g, r));
  }
  
  bool Graphics::onscreen(int t)
  {
      return (t >= -40 && t <= 280);
  }
  
  void Graphics::reloadresources()
  {
      grphx.destroy();
      grphx = GraphicsResources();
      grphx.init();
  
  #define CLEAR_ARRAY(name)                                                      \
      for (size_t i = 0; i < name.size(); i += 1) {                              \
          SDL_FreeSurface(name[i]);                                              \
      }                                                                          \
      name.clear();
  
      CLEAR_ARRAY(tiles)
      CLEAR_ARRAY(tiles2)
      CLEAR_ARRAY(tiles3)
      CLEAR_ARRAY(entcolours)
      CLEAR_ARRAY(sprites)
      CLEAR_ARRAY(flipsprites)
      CLEAR_ARRAY(tele)
      CLEAR_ARRAY(bfont)
      CLEAR_ARRAY(flipbfont)
  
  #undef CLEAR_ARRAY
  
      MakeTileArray();
      MakeSpriteArray();
      maketelearray();
      Makebfont();
  
      images.clear();
  
      images.push_back(grphx.im_image0);
      images.push_back(grphx.im_image1);
      images.push_back(grphx.im_image2);
      images.push_back(grphx.im_image3);
      images.push_back(grphx.im_image4);
      images.push_back(grphx.im_image5);
      images.push_back(grphx.im_image6);
  
      images.push_back(grphx.im_image7);
      images.push_back(grphx.im_image8);
      images.push_back(grphx.im_image9);
      images.push_back(grphx.im_image10);
      images.push_back(grphx.im_image11);
      images.push_back(grphx.im_image12);
  
      music.init();
  }
  
  Uint32 Graphics::crewcolourreal(int t)
  {
      switch (t) {
          case 0:
              return col_crewcyan;
          case 1:
              return col_crewpurple;
          case 2:
              return col_crewyellow;
          case 3:
              return col_crewred;
          case 4:
              return col_crewgreen;
          case 5:
              return col_crewblue;
      }
      return col_crewcyan;
  }
  