Bubbles

Those connected to speaking!

And I subtly showed you an entirely new (and working) room of the game! Hah!
I am writing this post because I am leaving for three days tomorrow and I’ll only come back Thursday night at best (but Friday morning is more possible). So let me sum up for you the progress of the past few days now!
First and foremost let me talk about the thing which needed the most work: the speech bubbles.
In truth, the actual drawing of them wasn’t really hard. ::Graphics::Surface::fillRect() and Graphics::drawdrawPieSlice() did the most of the work. The thing I had to implement was the “tails” of the bubbles. For that, I introduced a new function, Graphics::drawTriangle():

void Graphics::drawTriangle(::Graphics::Surface &surface, Common::Point *p, byte color) {
 // Draw the borders with a marking color.
 _scrolls.drawLine(p[0].x, p[0].y, p[1].x, p[1].y, 255);
 _scrolls.drawLine(p[1].x, p[1].y, p[2].x, p[2].y, 255);
 _scrolls.drawLine(p[2].x, p[2].y, p[0].x, p[0].y, 255);

 // Get the top and the bottom of the triangle.
 uint16 maxY = p[0].y, minY = p[0].y;
 for (byte i = 1; i < 3; i++) {
  if (p[i].y < minY)
   minY = p[i].y;
  if (p[i].y > maxY)
   maxY = p[i].y;
 }

 // Fill the triangle.
 for (uint16 y = minY; y <= maxY; y++) {
  uint16 x = 0;
  while (*(byte *)_scrolls.getBasePtr(x, y) != 255)
   x++;
  uint16 minX = x;
  uint16 maxX = x;
  x++;
  while ((*(byte *)_scrolls.getBasePtr(x, y) != 255) && (x != 639))
   x++;
  if (x != 639)
   maxX = x;
  if (minX != maxX)
   _scrolls.drawLine(minX, y, maxX, y, color);
 }

 // Redraw the borders with the actual color.
 _scrolls.drawLine(p[0].x, p[0].y, p[1].x, p[1].y, color);
 _scrolls.drawLine(p[1].x, p[1].y, p[2].x, p[2].y, color);
 _scrolls.drawLine(p[2].x, p[2].y, p[0].x, p[0].y, color);
}

It’s clearly not the most optimized, nor the smartest code, but for now, it’ll do. The operation of it is quite simple: First, we draw the borders of the triangle with a marking color. (255 is as good as any number bigger than 15, since our EGA palette supports only 16 colors.) After that we search for the highest and the lowest point of the triangle, and moving from the top to the bottom, we go trough every relevant line of the screen and fill the selected parts of it with the color of the shape. In the end, we redraw the borders – now with the desired color.

Finished with the implementation of it, the next thing I had to pay attention was the printing of the characters to the bubbles. Since the original code used Pascal’s outtextxy(), I had to come up with something to replace it as accurately as I could. As a solution, I introduced Graphics::drawText():

void Graphics::drawText(::Graphics::Surface &surface, const Common::String &text, fontType font, byte fontHeight, int16 x, int16 y, byte color) {
 for (byte i = 0; i < text.size(); i++)
  for (byte j = 0; j < fontHeight; j++) {
   byte pixel = font[(byte)text[i]][j]; 
   for (byte bit = 0; bit < 8; bit++) {
    byte pixelBit = (pixel >> bit) & 1;
    if (pixelBit)
     *(byte *)surface.getBasePtr(x + i * 8 + 7 - bit, y + j) = color;
   }
  }
}

It’s a very simple function, which’s only purpose is to draw the given text with the given font to the given place on the given surface with the given color. (GIVENGIVENGIVEN!!!) It works like that: in font (which has the type of fontType), we store a mask for every character. The function simply search for every character’s mask in this array, and then using that, the coordinates and the color, draws the picture of the character to the surface. Passing the height of the font is needed, because Avalanche uses 3 types of font and 2 of them has the height of 12 (used on the scrolls), but the third one (used on speech bubbles, the drop-down menu and the text input field) is only 8 pixel high.
Using these two functions it was super easy to implement the speech bubbles, I only had to center the texts a little bit in Scrolls::bubble().

Besides these improvements, I managed to implement a lot of the game logics, so now the first 4 rooms of the game are complete and playable, all the NPC-s on these screens are fully functioning, and everything is basically very fancy. I could even make Spludwick (the mage on the screenshot above) follow Avvy around the room as in the original game.

That’s all for now! I hope next time I will be able to tell you about how I managed to implement the handling of the mouse in the game and I am planning to get done with the drop-down menu right after that. See you soon!