Custom border problem with SpeechBubble module

Started by Héctor Bometón, Wed 10/01/2018 10:28:16

Previous topic - Next topic

Héctor Bometón


Hi!

I'm using the wonderful SpeechBubble Module but I'm having some trouble getting the border as I want it (since my progamming skills are quite lame).

I'd like the bubble to look like the one in the bottom, but the top one is the closest I can get:



I got the tail of the bubble figured out, but I don't know how to fix the rest of it.

The shadow would be basically like a duplicate of the bubble (but in x + 1, and y + 1). I'm not even sure that you can do this with the SpeechBubble module, but I'd appreciate any kind of help.

Thanks!

Héctor.

Snarky

#1
Yeah, that's not possible in the module as of now. I suppose I could extend it to support it, but it's not a high priority.

For now, it's easier to hack the render function to do what you want. Try replacing the drawRoundedCorners32() and renderBubble32() functions in the module with these versions:

Spoiler
Code: ags
// Round off the corners of the bubble by erasing to transparent
void drawRoundedCorners32(DrawingSurface* background, int left, int top, int right, int bottom)
{
  // Uses Bresenham's circle formula, found online
  int r = _cornerRoundingRadius;
  
  int x = 0; 
  int y = r; 
  int p = 3 - 2 * r;
  int dc = background.DrawingColor;
  background.DrawingColor = COLOR_TRANSPARENT;
  while (y >= x) // only formulate 1/8 of circle
  {
    // Erase background corners
    // Top Left
    background.DrawLine(left + r - x, top+r - y, left + r - x, top);
    background.DrawLine(left + r - y, top+r - x, left + r - y, top);
    
    // Top Right
    background.DrawLine(right - r + y, top+r - x,  right - r + y, top);
    background.DrawLine(right - r + x, top+r - y,  right - r + x, top);
    
    // Bottom Left
    background.DrawLine(left + r - x, bottom-r + y, left + r - x,  bottom);
    background.DrawLine(left + r - y, bottom-r + x, left + r - y,  bottom);
    
    // Bottom Right
    background.DrawLine(right - r + y, bottom-r + x, right - r + y, bottom);
    background.DrawLine(right - r + x, bottom-r + y, right - r + x, bottom);

    if (p < 0)
    {
      p += 4*x + 6;
      x++;
    }
    else
    {
      p += 4*(x - y) + 10;
      x++;
      y--;
    }
   }
   background.DrawingColor = dc;
}


Code: ags
// Draw a speech bubble in 32-bit (using transparency)
DynamicSprite* renderBubble32(this Character*, String message, bool talkTail)
{
  // Calculate text dimensions
  int textWidth = _maxTextWidth;
  if(textWidth <= 0)
    textWidth = calculateDefaultTextWidth(this);
  textWidth = _minInt(textWidth, System.ViewportWidth - _paddingLeft - _paddingRight);
  int textHeight = GetTextHeight(message, Game.SpeechFont, textWidth);
  textWidth = calculateExactTextWidth(message, Game.SpeechFont, textWidth, textHeight);
  
  // Calculate bubble dimensions
  int totalWidth = textWidth + _paddingLeft + _paddingRight;
  int bubbleHeight = textHeight + _paddingTop + _paddingBottom;
  int totalHeight;
  if(talkTail)
    totalHeight = bubbleHeight + _talkTailHeight;
  else
    totalHeight = bubbleHeight + _thinkTailHeight;
  
  // Set up the canvases
  DynamicSprite* bubbleSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  DrawingSurface* bubbleSurface = bubbleSprite.GetDrawingSurface();
  //bubbleSurface.Clear();
  
  DynamicSprite* bgSprite; DrawingSurface* bgSurface;
  DynamicSprite* borderSprite; DrawingSurface* borderSurface;
  bgSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  bgSurface = bgSprite.GetDrawingSurface();
  borderSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  borderSurface = borderSprite.GetDrawingSurface();
  
  int bgColor = mixColors(this.SpeechColor, _backgroundColor, _backgroundSpeechTint);
  int borderColor = mixColors(this.SpeechColor, _borderColor, _borderSpeechTint);
  
  // Draw!
  bgSurface.DrawingColor = bgColor;
  borderSurface.DrawingColor = borderColor;
  bgSurface.DrawRectangle(0, 0, totalWidth-2, bubbleHeight-2);
  borderSurface.DrawRectangle(1,  1, totalWidth-1, bubbleHeight-1);
  drawRoundedCorners32(bgSurface, 0, 0, totalWidth-1, bubbleHeight);
  drawRoundedCorners32(borderSurface, 1, 1, totalWidth, bubbleHeight+1);
  String tail[]; int tailWidth; int tailHeight;
  if(talkTail)
  {
    tail = _talkTail; tailWidth = _talkTailWidth; tailHeight = _talkTailHeight;
  }
  else
  {
    tail = _thinkTail; tailWidth = _thinkTailWidth; tailHeight = _thinkTailHeight;
  }
  bgSurface.drawPixelArray(tail, totalWidth/2-tailWidth, bubbleHeight-1, tailWidth, tailHeight, 'O', false, false);
  borderSurface.drawPixelArray(tail, totalWidth/2-tailWidth+1, bubbleHeight, tailWidth, tailHeight, 'O', false, false);
  
  borderSurface.Release();
  bubbleSurface.DrawImage(0, 0, borderSprite.Graphic, _borderTransparency);
  borderSprite.Delete();
  
  bgSurface.Release();
  bubbleSurface.DrawImage(0, 0, bgSprite.Graphic, _backgroundTransparency);
  bgSprite.Delete();
  
  bubbleSurface.DrawingColor = this.SpeechColor;
  int outlineColor = mixColors(this.SpeechColor, _textOutlineColor, _textOutlineSpeechTint);
  if(_textOutlineWidth > 0)
    bubbleSurface.drawStringWrappedOutline(_paddingLeft, _paddingTop, textWidth, _textOutlineStyle, Game.SpeechFont, _textAlign, message, _textTransparency, outlineColor, _textOutlineWidth);
  else
    bubbleSurface.drawStringWrappedAA(_paddingLeft, _paddingTop, textWidth, Game.SpeechFont, _textAlign, message, _textTransparency);
  
  bubbleSurface.Release();
  return bubbleSprite;
}
[close]
(This won't work correctly if you set the speech bubble background to be semi-transparent. You can make the border/shadow transparent, though.)

Héctor Bometón


Oh! Great!

I just tested it and it's definitely closer to what I wanted.

Thank you! :)

Snarky

That's good, but 'closer'? Doesn't it give exactly the result in your example?

Héctor Bometón


Not exactly...

These are the results with different corner radius settings:



The rest of the settings:

Code: ags


  // Set default values
  SpeechBubble.set_InvisibleFont(-1);
  SpeechBubble.set_TextAlign(eAlignCentre);
  
  SpeechBubble.set_BackgroundColor(15);
  SpeechBubble.set_BorderColor(0);
  
  SpeechBubble.set_BackgroundTransparency(0);
  SpeechBubble.set_BorderTransparency(10);

  SpeechBubble.set_MaxTextWidth(-1);
  SpeechBubble.set_CornerRoundingRadius(3);
  SpeechBubble.set_PaddingTop(5);
  SpeechBubble.set_PaddingBottom(4);
  SpeechBubble.set_PaddingLeft(9);
  SpeechBubble.set_PaddingRight(9);
  
  _talkTail = new String[6];
  _talkTail[0] = " OOOOX";
  _talkTail[1] = "  OOOX";
  _talkTail[2] = "   OOX";
  _talkTail[3] = "    OX";
  _talkTail[4] = "     X";
  _talkTail[5] = null;
  //_talkTail[9] = null;
  SpeechBubble.set_TalkTail(_talkTail); // Just to set height/width


¿Maybe I did something wrong?

Thanks again!

Snarky

No, the mistake was mine. (I was a little drunk when I wrote it.) Try this for the two functions:

Spoiler
Code: ags
// Round off the corners of the bubble by erasing to transparent
void drawRoundedCorners32(DrawingSurface* background, int left, int top, int right, int bottom)
{
  // Uses Bresenham's circle formula, found online
  int r = _cornerRoundingRadius - 1;
  
  int x = 0; 
  int y = r; 
  int p = 3 - 2 * r;
  int dc = background.DrawingColor;
  background.DrawingColor = COLOR_TRANSPARENT;
  while (y >= x) // only formulate 1/8 of circle
  {
    // Erase background corners
    // Top Left
    background.DrawLine(left + r - x, top+r - y, left + r - x, top);
    background.DrawLine(left + r - y, top+r - x, left + r - y, top);
    
    // Top Right
    background.DrawLine(right - r + y, top+r - x,  right - r + y, top);
    background.DrawLine(right - r + x, top+r - y,  right - r + x, top);
    
    // Bottom Left
    background.DrawLine(left + r - x, bottom-r + y, left + r - x,  bottom);
    background.DrawLine(left + r - y, bottom-r + x, left + r - y,  bottom);
    
    // Bottom Right
    background.DrawLine(right - r + y, bottom-r + x, right - r + y, bottom);
    background.DrawLine(right - r + x, bottom-r + y, right - r + x, bottom);

    if (p < 0)
    {
      p += 4*x + 6;
      x++;
    }
    else
    {
      p += 4*(x - y) + 10;
      x++;
      y--;
    }
   }
   background.DrawingColor = dc;
}


Code: ags
// Draw a speech bubble in 32-bit (using transparency)
DynamicSprite* renderBubble32(this Character*, String message, bool talkTail)
{
  // Calculate text dimensions
  int textWidth = _maxTextWidth;
  if(textWidth <= 0)
    textWidth = calculateDefaultTextWidth(this);
  textWidth = _minInt(textWidth, System.ViewportWidth - _paddingLeft - _paddingRight);
  int textHeight = GetTextHeight(message, Game.SpeechFont, textWidth);
  textWidth = calculateExactTextWidth(message, Game.SpeechFont, textWidth, textHeight);
  
  // Calculate bubble dimensions
  int totalWidth = textWidth + _paddingLeft + _paddingRight;
  int bubbleHeight = textHeight + _paddingTop + _paddingBottom;
  int totalHeight;
  if(talkTail)
    totalHeight = bubbleHeight + _talkTailHeight;
  else
    totalHeight = bubbleHeight + _thinkTailHeight;
  
  // Set up the canvases
  DynamicSprite* bubbleSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  DrawingSurface* bubbleSurface = bubbleSprite.GetDrawingSurface();
  //bubbleSurface.Clear();
  
  DynamicSprite* bgSprite; DrawingSurface* bgSurface;
  DynamicSprite* borderSprite; DrawingSurface* borderSurface;
  bgSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  bgSurface = bgSprite.GetDrawingSurface();
  borderSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
  borderSurface = borderSprite.GetDrawingSurface();
  
  int bgColor = mixColors(this.SpeechColor, _backgroundColor, _backgroundSpeechTint);
  int borderColor = mixColors(this.SpeechColor, _borderColor, _borderSpeechTint);
  
  // Draw!
  bgSurface.DrawingColor = bgColor;
  borderSurface.DrawingColor = borderColor;
  bgSurface.DrawRectangle(0, 0, totalWidth-2, bubbleHeight-2);
  borderSurface.DrawRectangle(1,  1, totalWidth-1, bubbleHeight-1);
  if(_cornerRoundingRadius > 0)
  {
    drawRoundedCorners32(bgSurface, 0, 0, totalWidth-2, bubbleHeight-2);
    drawRoundedCorners32(borderSurface, 1, 1, totalWidth-1, bubbleHeight-1);
  }
  String tail[]; int tailWidth; int tailHeight;
  if(talkTail)
  {
    tail = _talkTail; tailWidth = _talkTailWidth; tailHeight = _talkTailHeight;
  }
  else
  {
    tail = _thinkTail; tailWidth = _thinkTailWidth; tailHeight = _thinkTailHeight;
  }
  bgSurface.drawPixelArray(tail, totalWidth/2-tailWidth, bubbleHeight-1, tailWidth, tailHeight, 'O', false, false);
  borderSurface.drawPixelArray(tail, totalWidth/2-tailWidth+1, bubbleHeight, tailWidth, tailHeight, 'O', false, false);
  
  borderSurface.Release();
  bubbleSurface.DrawImage(0, 0, borderSprite.Graphic, _borderTransparency);
  borderSprite.Delete();
  
  bgSurface.Release();
  bubbleSurface.DrawImage(0, 0, bgSprite.Graphic, _backgroundTransparency);
  bgSprite.Delete();
  
  bubbleSurface.DrawingColor = this.SpeechColor;
  int outlineColor = mixColors(this.SpeechColor, _textOutlineColor, _textOutlineSpeechTint);
  if(_textOutlineWidth > 0)
    bubbleSurface.drawStringWrappedOutline(_paddingLeft, _paddingTop, textWidth, _textOutlineStyle, Game.SpeechFont, _textAlign, message, _textTransparency, outlineColor, _textOutlineWidth);
  else
    bubbleSurface.drawStringWrappedAA(_paddingLeft, _paddingTop, textWidth, Game.SpeechFont, _textAlign, message, _textTransparency);
  
  bubbleSurface.Release();
  return bubbleSprite;
}
[close]
Note that I've also adjusted how the corner rounding parameter is used: you might want to increase it by 1 to get the same results as with the original version.

One thing: it looks like you're editing the initSpeechBubble() function in order to configure the module. This is not how it's meant to be used: You're not supposed to edit the module code at all (except as otherwise instructed). Rather, as the header documentation explains, you're supposed to configure it from outside, e.g. in your game's GlobalScript. For your settings, that would be something like:

Spoiler
Code: ags
// in GlobalScript.asc

function game_start()
{
  // Setup SpeechBubble module
  SpeechBubble.CornerRoundingRadius = 3;
  SpeechBubble.PaddingTop = 5;
  SpeechBubble.PaddingBottom = 4;
  SpeechBubble.PaddingLeft = 9;
  SpeechBubble.PaddingRight =9;

  // (You don't need the shadow any more, it's now drawn automatically)
  String tail[] = new String[5];
  tail[0] = " OOOO";
  tail[1] = "  OOO";
  tail[2] = "   OO";
  tail[3] = "    O";
  tail[4] = null;

  SpeechBubble.TalkTail = tail;
}
[close]
However, when I test, I see that that last line doesn't work â€" an AGS limitation I wasn't aware of. I will fix it so you can write:

Code: ags
  SpeechBubble.SetTalkTail(tail);


In the mean time you can leave the setup as you have it.

Héctor Bometón


Dude... I just logged in after some time and I realized I dindn't thank you for this. I'm so sorry!

It's working wonderfully now. Thank you very much!

Héctor.

SMF spam blocked by CleanTalk