Author Topic: MODULE: SpeechBubble v0.8.0  (Read 15983 times)

Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #80 on: 24 Mar 2020, 14:29 »
Yup, merged.

What I want to happen:
1. Buddhist speaks, speech bubble appears
2. The sound sample 1 is over; delete this speech bubble, and create the second one; start the second bit of speech
3. When that's over, delete speechbubble, speech sample 2 has obviously stopped because it's non-blocking
4. Fade out screen

I'm not quite sure I understand this description. What do you mean that "speech sample 2 has obviously stopped because it's non-blocking"? Is the behavior you're after different in any way from normal speech behavior? (Show speech text and play speech clip, then show next speech text and next speech clip.)

but what is blocking/non-blocking?
Should I just write 'Wait(the length of time for the 2 speech samples)', and included it at the beginning/end of the block of code?

No, definitely not. But again, what do you mean "what is blocking/non-blocking"?

The speech bubbles are obviously ending early because they estimate a shorter amount of time to read the bubble than the speech sample has to end;

Well, if you're providing a voice clip it should be using the length of the clip automatically. In fact, it's not calculating anything, just displaying the speech bubble, then calling the AGS Character.Say() function with the speech clip ID as the line and waiting until it's finished.

I think I recall from testing your game before that these lines are extremely long, though. Like, around a minute or so? And you're using the LipSync module as well, right? I seem to remember that there is some limitation somewhere that they overflow (either somewhere in the modules or in AGS). I would first try cutting them down to a more reasonable length and see if that solves things.

Another thing you could test is to replace these SayBubble() calls with Say(). If you still have the problem then it's not an issue with the module.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #81 on: 25 Mar 2020, 00:53 »
Solved: made sound files shorter.
A bigger, betterer game.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #82 on: 06 Apr 2020, 11:11 »
I am having a compile error on this line:

Code: [Select]
void SB_sayImpl(this Character*, String message)
{
//BLR ADDED
  this.SaySync(message);
}

I'm getting the error:
Code: [Select]
SpeechBubble_0.8.0.asc(19): Error (line 19): '.SaySync' is not a public member of 'Character'. Are you sure you spelt it correctly (remember, capital letters are important)?
...but it is. SaySync is most definitely a member of characters.

However I think I've made a typo around this code, but I can't find it.
It's just shot this error now, after months of not updating it.

here's the scripts:
https://redrom.ltd/filez/speechbubble.zip
« Last Edit: 06 Apr 2020, 11:25 by bx83 »
A bigger, betterer game.

Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #83 on: 06 Apr 2020, 11:24 »
Have you reordered the modules so that TotalLipSync is no longer above SpeechBubble? That's the only explanation I can think of.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #84 on: 06 Apr 2020, 11:59 »
SOLVED!

I didn't even know the order in the right-column for Scripts had a meaning - you learn something every day :D
A bigger, betterer game.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #85 on: 21 Apr 2020, 12:12 »
I'm using SayAtBubble to position the bubble as I want. I've put two defines into the global.ash to  set x and y coordinates:

in global.ash:
Code: [Select]
#define hare_x 590
#define hare_y 12

in a conversation with the Hare character:
Code: [Select]
@6
  cJulius.SayAtBubble(hare_x,hare_y,"&615 How did you get invited to this animal party village green thing?");
  cHare.SayBubble("&16 How'd *you* get invited? You an animal?");
...

...BUT the X coordinate does nothing. It always positions the bubble in the centre of Julius head:



Here's the function code:

Code: [Select]
void realSayAtBubble(this Character*, int x, int y, String message, GUI* bubbleGui, DynamicSprite* bubbleSprite)
{
  // Render and display the speech bubble
  if(bubbleSprite == null)
    bubbleSprite = this.renderBubble32(message, true);
  Overlay* bubbleOverlay;
  if(bubbleGui == null && _defaultGui == null)
    bubbleOverlay = Overlay.CreateGraphical(x, y, bubbleSprite.Graphic, true);
  else
  {
    if(bubbleGui == null)
      bubbleGui = _defaultGui;
     
    bubbleGui.Clickable = false;
    bubbleGui.X = _clampInt(x, 0, System.ViewportWidth - bubbleSprite.Width);
    bubbleGui.Y = _clampInt(y, 0, System.ViewportHeight - bubbleSprite.Height);
    bubbleGui.Width = bubbleSprite.Width;
    bubbleGui.Height = bubbleSprite.Height;
    bubbleGui.BackgroundGraphic = bubbleSprite.Graphic;
    bubbleGui.Transparency = 0;
    bubbleGui.Visible = true;
  }
  SpeechBubble* bubble = SpeechBubble.Create(this, message, bubbleSprite, bubbleGui, bubbleOverlay);
 
  bubble.setX(x);
  bubble.setY(y);
  bubble.setBackgroundSpeech(false);
  bubble.setThinking(false);
  _addBubbleChar(this);
 
  // Play speech (this chunk blocks until speech is complete)

  String lineNumber = getLineNumber(message);
  // If we have set an invisible font, just call Say() - or whatever custom Say() implementation we have
  if(SpeechBubble.get_InvisibleFont() != -1)
  {
    FontType speechFont = Game.SpeechFont;
    Game.SpeechFont = _invisibleFont;
    this.SB_sayImpl(message);
    Game.SpeechFont = speechFont;
  }
  // Else if we're going to play a voice clip, call Say() with the clip number and a blank line of text
  // (takes care of animation and doesn't display any text). This doesn't work with text-based lip-sync,
  // so if you're using text-based lip-sync, you MUST set an invisible font to get lip-sync to work
  else if(lineNumber != null && Speech.VoiceMode != eSpeechTextOnly) // && !GetGameOption(OPT_LIPSYNCTEXT))
  {
    String s = lineNumber;
    while(s.Length < message.Length)
      s = s.AppendChar(' ');
    this.SB_sayImpl(s);
  }
  // Otherwise we have to do it manually...
  else
  {
    bubble.setAnimating(true);
    this.animateSpeech(message);
  }
 
 
  // Remove the bubble
  bubble.Remove();
}

...

void SayAtBubble(this Character*, int x, int y, String message, GUI* bubbleGui)
{
  if(message == null) return;
  if(!game.bgspeech_stay_on_display)
    _stopAllBackgroundBubbles();
  if((Speech.VoiceMode == eSpeechVoiceOnly && hasVoiceClip(message)) || message == "...") {
    this.SB_sayImpl(message);
  } else {
    DynamicSprite* bubbleSprite = this.renderBubble32(message, true);
    x = this.x - GetViewportX() - bubbleSprite.Width/2;
    x = _clampInt(x, 0, System.ViewportWidth - bubbleSprite.Width);
   
    this.realSayAtBubble(x, y, message, bubbleGui, null);
  }
}

...


// 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;
 
  SpeechBubbleHeight_blr=totalHeight;//BLR
 
  // 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;
  if(_backgroundTransparency == 0)
  {
    bgSprite = bubbleSprite;
    bgSurface = bubbleSurface;
  }
  else
  {
    bgSprite = DynamicSprite.Create(totalWidth, totalHeight, true);
    bgSurface = bgSprite.GetDrawingSurface();
  }
  if(_borderTransparency == 0)
  {
    borderSprite = bubbleSprite;
    borderSurface = bubbleSurface;
  }
  else
  {
    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;
  bgSurface.DrawRectangle(1, 1, totalWidth-2, bubbleHeight-1);
  drawRoundedCorners32(bgSurface, borderSurface, borderColor, 0, bubbleHeight);
  String tail[]; int tailWidth; int tailHeight;
  if(talkTail)
  {
    tail = _talkTail; tailWidth = _talkTailWidth; tailHeight = _talkTailHeight;
  }
  else
  {
    tail = _thinkTail; tailWidth = _thinkTailWidth; tailHeight = _thinkTailHeight;
  }
  bgSurface.DrawingColor = bgColor;
  bgSurface.drawPixelArray(tail, totalWidth/2-tailWidth, bubbleHeight, tailWidth, tailHeight, 'O', false, false);
  borderSurface.DrawingColor = borderColor;
  borderSurface.drawPixelArray(tail, totalWidth/2-tailWidth, bubbleHeight, tailWidth, tailHeight, 'X', false, false);
  borderSurface.DrawLine(_cornerRoundingRadius, 0, totalWidth - _cornerRoundingRadius, 0);
  // Left Line
  borderSurface.DrawLine(0, _cornerRoundingRadius, 0, bubbleHeight - _cornerRoundingRadius);
  // Right Line
  borderSurface.DrawLine(totalWidth-1, _cornerRoundingRadius, totalWidth-1, bubbleHeight - _cornerRoundingRadius);
  // Bottom Lines
  borderSurface.DrawLine(_cornerRoundingRadius, bubbleHeight, totalWidth/2 - tailWidth, bubbleHeight);
  borderSurface.DrawLine(totalWidth/2, bubbleHeight, totalWidth - _cornerRoundingRadius, bubbleHeight);
 
  if(_backgroundTransparency != 0)
  {
    bgSurface.Release();
    bubbleSurface.DrawImage(0, 0, bgSprite.Graphic, _backgroundTransparency);
    bgSprite.Delete();
  }
  if(_borderTransparency != 0)
  {
    borderSurface.Release();
    bubbleSurface.DrawImage(0, 0, borderSprite.Graphic, _borderTransparency);
    borderSprite.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;
}

I can include more functions; I've altered like 10 lines of code in this module, but the above *looks* okay...???
A bigger, betterer game.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #86 on: 21 Apr 2020, 12:34 »
A bigger, betterer game.

Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #87 on: 21 Apr 2020, 13:08 »
It's because of one of the lines you've added to SayAtBubble():

Code: Adventure Game Studio
  1.     x = this.x - GetViewportX() - bubbleSprite.Width/2;

Why have you changed the module code?

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #88 on: 22 Apr 2020, 02:24 »
I don't know... :/
I'll test it.
A bigger, betterer game.

bx83

  • Get ‘er doooooone
Re: MODULE: SpeechBubble v0.8.0
« Reply #89 on: 22 Apr 2020, 03:26 »
Solved.
A bigger, betterer game.

Dave Gilbert

  • Mittens Vassal
  • AGS Baker
  • Eye see you.
    • Lifetime Achievement Award Winner
    • Dave Gilbert worked on one or more games that won an AGS Award!
    •  
    • Dave Gilbert worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #90 on: 10 Jun 2020, 16:41 »
Hello! Experimenting with this module and really digging it so far!

I naturally have a question about something. In my (1080 x 1920 resolution) game, the player character is communicating with someone by radio, and I wanted to give the radio guy his own bubble. I created a function that sets the radio guy's bubble position 300 pixels behind where the player is standing. Here are the results.

at player.x-300:


at player.x+300:


As you can see, the second speech bubble is significantly further away than the first. Any idea what is causing that?

Also, while I'm here, is there any way to temporarily get rid of the bubble's tail?

Thanks much!

-Dave
« Last Edit: 10 Jun 2020, 16:45 by Dave Gilbert »

Dave Gilbert

  • Mittens Vassal
  • AGS Baker
  • Eye see you.
    • Lifetime Achievement Award Winner
    • Dave Gilbert worked on one or more games that won an AGS Award!
    •  
    • Dave Gilbert worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #91 on: 10 Jun 2020, 17:07 »
Woo. I managed to find a work around. I created an invisible character for the radio guy that changes it's position just before it speaks, and used the regular "SayBubble" command instead of "SayAtBubble." The positioning is much more consistent now.

Also I discovered the Screen2Gif software:



That said, I still would like to remove the bubble tail whenever the radio guy speaks!  Any clues on how to do that?
« Last Edit: 10 Jun 2020, 17:09 by Dave Gilbert »

Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #92 on: 10 Jun 2020, 17:23 »
Hmm. The way it's intended to work is that you supply a new TalkTail array:

Code: Adventure Game Studio
  1.   String tail[] = new String[5];
  2.   tail[0] = " OOOO";
  3.   tail[1] = "  OOO";
  4.   tail[2] = "   OO";
  5.   tail[3] = "    O";
  6.   tail[4] = null;
  7.  
  8.   SpeechBubble.TalkTail = tail;

If you set it to an array with just one entry, null, that would disable the tail completely. However, this feature is broken, because AGS doesn't actually allow you to set arrays via attributes. :~(

To address this and other limitations, I did a complete rewrite of the module a couple of years ago, to make it easier to customize the appearance (it would allow you to set the tail as an array or as a sprite), but never published it because I didn't get around to reimplementing 100% of the old functionality. There's also a longstanding issue with the module causing momentary freezes unless you supply an invisible font.

There are currently various branches and hacked versions floating around the forums. I really should clean it all up and post a new release. Which AGS engine version are you targeting?

Dave Gilbert

  • Mittens Vassal
  • AGS Baker
  • Eye see you.
    • Lifetime Achievement Award Winner
    • Dave Gilbert worked on one or more games that won an AGS Award!
    •  
    • Dave Gilbert worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #93 on: 10 Jun 2020, 17:34 »
I'm using the latest version of AGS. I'm still at the early stages of my project so I still update it when ever a new version comes out.

edit: Wait, so you can change the shape of the tail using those arrays? I was wondering what those were for. I'm curious how that works. Is there any documentation on how that is done?
« Last Edit: 10 Jun 2020, 17:36 by Dave Gilbert »

Dave Gilbert

  • Mittens Vassal
  • AGS Baker
  • Eye see you.
    • Lifetime Achievement Award Winner
    • Dave Gilbert worked on one or more games that won an AGS Award!
    •  
    • Dave Gilbert worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #94 on: 10 Jun 2020, 17:53 »
Aha! Figured it out and found a workaround! I just set the tail to this shape:

Code: Adventure Game Studio
  1. function setNozzoTail()
  2. {
  3.   _nozzoTalkTail = new String[10];
  4.   _nozzoTalkTail[0] = "XXXXXXXX";
  5.   _nozzoTalkTail[1] = "        ";
  6.   _nozzoTalkTail[2] = "        ";
  7.   _nozzoTalkTail[3] = "        ";
  8.   _nozzoTalkTail[4] = "        ";
  9.   _nozzoTalkTail[5] = "        ";
  10.   _nozzoTalkTail[6] = "        ";
  11.   _nozzoTalkTail[7] = "        ";
  12.   _nozzoTalkTail[8] = "        ";
  13.   _nozzoTalkTail[9] = null;
  14.   SpeechBubble.set_TalkTail(_nozzoTalkTail);  
  15. }
  16.  

And that did the job!

Dave Gilbert

  • Mittens Vassal
  • AGS Baker
  • Eye see you.
    • Lifetime Achievement Award Winner
    • Dave Gilbert worked on one or more games that won an AGS Award!
    •  
    • Dave Gilbert worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #95 on: 16 Jun 2020, 21:14 »
Hello! I have encountered another issue, although I am not sure what causes it. I notice that when I click through dialog, occasionally the game registers an extra click and either clicks through the next piece of dialog or (if it's the end of the dialog) activates a click where-ever the mouse cursor happens to be. This happens only very sporadically, but consistently (at least once every five minutes or so) and there's no common cause that I can see.

When I de-activate the bubbles and go back to regular speech, the problem goes away. So I am pretty sure it involves the bubbles in some way. Do you have any idea what might cause this to happen?

Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #96 on: 16 Jun 2020, 23:32 »
 :-\

My guess is that it's to do with the Wait() logic that emulates speech blocking. There have been a number of reported bugs linked back to this part of the code (I think I've even heard something like this reported before). Crimson Wizard and the other AGS devs are currently working on extending the Wait() API so it will support the same behavior as speech blocking (e.g. block indefinitely until there's a mouse or keyboard press) without complicated workarounds.

Meanwhile, if you haven't already done so, I would strongly suggest setting and activating an invisible font, which will bypass all this logic and instead just use the built-in AGS speech blocking. There's a link to download one on the GitHub page for the module: http://www.angelfire.com/pr/pgpf/if.html

eri0o

Re: MODULE: SpeechBubble v0.8.0
« Reply #97 on: 16 Jun 2020, 23:44 »
AGS has a time for which things are ignored after the end of a speech, the unforgettable Game.IgnoreUserInputAfterTextTimeoutMs. This prevents spurious clicks after ending the speech, by blocking input for some milliseconds every time text is skipped or ends by timeout. Maybe there's a way to implement it in SpeechBubble?


Snarky

  • Global Moderator
  • Global Moderator
  • Mittens Lord
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
Re: MODULE: SpeechBubble v0.8.0
« Reply #98 on: 16 Jun 2020, 23:59 »
Yup, I'm aware ;) (I believe I was actually the one who suggested that feature originally — Edit: indeed), but it would not have any effect in this case, since it's not happening after a timeout, but when the user actively clicks away the dialog.

It's something that would probably be worth adding (though it couldn't be 100% the same, since I can't control what happens once the dialog is over), but only if and when this and related bugs are fixed.
« Last Edit: 17 Jun 2020, 00:10 by Snarky »

eri0o

Re: MODULE: SpeechBubble v0.8.0
« Reply #99 on: 18 Jun 2020, 17:16 »
Here's how the thing you asked got implemented: https://github.com/adventuregamestudio/ags/blob/760635024c547face5ee423d62ccbf9abc7976f8/Engine/ac/display.cpp#L311

Maybe it helps to figure out how to add this in the module. :/