Character Background Speech Issues (using Queued Speech Module)

Started by Tenacious Stu, Sat 07/09/2013 15:25:48

Previous topic - Next topic

Tenacious Stu

Hello

The sequence I want to create involves the player character unable to walk around the screen and another character (the Warlock) is ranting a long speech at them. But, the player is able to interact/look at nearby hotspots while the other character is talking. If the player clicks on something, then the function will occur and afterwards the Warlock character will continue their speech where they left off (sometimes the function will include the Warlock deviating from his speech, as below). The Warlock will continue ranting until the player completes the interaction that will stop him.

I am using the Queued Speech Module and here is what I have so far:

Code: ags
function room_RepExec()
{
  if (Complete==true){ //Complete will become true when the player completes the correct interaction to stop the Warlock ranting
    The Warlock will stop ranting //The Warlock will stop ranting and the game will carry on.
}
else {
    cWarlock.SayQueued (" Sentance One.");
    cWarlock.SayQueued ("Sentance Two.");
    cWarlock.SayQueued ("Sentance Three."); //There will be more than this, but you get the idea.
}
}

function oObject1_Interact()
{
  QueuedSpeech.PauseQueue();
cWarlock.Say ("Stop doing things while I'm ranting at you.");
cWarlock.Say ("Now where was I?");
QueuedSpeech.UnPauseQueue(); //this should continue the Warlock's rant where he left off
}


I'm currently having a few issues:

1. When using QueuedSpeech.PauseQueue(); it pauses, but the text stays on screen. While it is paused, other characters will be talking and the dialog text overlaps, even when it is the Warlock speaking, so in the example above, when the player interacts with object1, the Warlock's own speech overlaps the sentence currently displayed from his rant.

2. The dialog text using cWarlock.SayQueued ("whatever."); will appear up and right to where the text should disaply. It does not display above the characters head as normal?

3. After a few minutes,  the Warlock will repeat sentence one over and over and will not say the rest of his rant? This happens whether the player interacts with stuff or not.

Thank you in advance for any help you can offer.

Tenacious Stu

Does anyone care to offer a solution? Or perhaps an alternate way around it?

Thanks

Snarky

There are multiple queued speech modules. Could you be more specific about which one you're using?

One thing that looks wrong about your code is putting those calls to SayQueued() in room_RepExec(). That basically means that you're adding all those lines (three in the example) to the queue forty times per second, and after just a couple of seconds the queue will have hundreds of lines. Since it has a limited size, it will immediately fill up, and at that point it could start behaving in unexpected ways. That's probably the reason for your third problem, at least.

Does the queued speech module have a way to check the length of the current queue? If so, I'd add a test to only enqueue those lines if the queue is under some minimum length. It might also have a way to remove currently displayed speech to solve your first problem.

Sephiroth

Hello,

I just wanted to offer an alternative, it is not the best way but could help if nothing else works.
You could store the dialog lines into an array and use an index to make it easier to manipulate.
game_start(),rep_exec() are just examples, I hope you get the idea.

Code: ags

//Copy to: top of the script outside functions

String Lines[10];
int current_line;
bool warlock_event;   //true: the event is active
bool interrupted;      
Overlay *text1;

//Copy to: game_start()

  Lines[0] = "Hello young man";
  Lines[1] = "how are you";
  Lines[2] = "can I help you in any way?";
  warlock_event = true;

//Copy to: repeatedly_execute()

if(warlock_event)
{
  if(current_line > 2)   //event stops when warlock has said his last line (Lines[2])
    warlock_event = false;
   
  if(!interrupted)    //if the player isn't interrupting
  {
    if(text1 == null || !text1.Valid)    //if previous background text is gone
    {
      text1 = warlock.SayBackground(Lines[current_line]);    //say the line
      current_line++;
    }
  }
  else    //player has interrupted, skip current line, say interrupt text, and go back to previous line
  {
    text1 = warlock.SayBackground("You're interrupting me! What was I saying... Ah...");
    current_line--;
    interrupted = false;
  }
}


You will need to use this in relevant places:

When the event starts.
Code: ags
warlock_event = true;


When the player interrupts him.
Code: ags
interrupted = true;

Edit: The last dialog line cannot be interrupted but it can be easily modified. I've also added comments if you're wondering what the code is about.
That's it, I hope you can get it to work with your scripts.

Tenacious Stu

Thanks for replying

@Snarky
I'm not sure which module I am using, but it sounds like in trying to use it, I'm causing myself more harm than good?

@Sephiroth
I like the look of your alternative, but the different Lines (where you would place the whole speech), you say to place in game start? I'm not sure where you mean to place this code for the game to call upon the speech text? Where in the room text is the best place to put the speech lines?

Sephiroth

If you only need this for one room, you could use the code like this:

Copy this at the top of room script outside functions:
Code: ags

String Lines[10];
int current_line;
bool warlock_event;
bool interrupted;      
Overlay *text1;


Copy this inside  room_load(), make sure you use the room property panel to create this function inside room script (before_fadein), do not write it by yourself:
It is just the definition of the dialog lines so you can later refer to them as Lines[index], it only needs to be called once.
Code: ags

  Lines[0] = "Hello young man";
  Lines[1] = "how are you";
  Lines[2] = "can I help you in any way?";


Copy this inside room_rep_exec(), also make sure to use the room property panel:
This is the part that displays the text and handles interruptions.
Code: ags

if(warlock_event)
{
  if(current_line > 2)   //event stops when warlock has said his last line (Lines[2])
    warlock_event = false;
   
  if(!interrupted)    //if the player isn't interrupting
  {
    if(text1 == null || !text1.Valid)    //if previous background text is gone
    {
      text1 = warlock.SayBackground(Lines[current_line]);    //say the line
      current_line++;
    }
  }
  else    //player has interrupted, skip current line, say interrupt text, and go back to previous line
  {
    text1 = warlock.SayBackground("You're interrupting me! What was I saying... Ah...");
    current_line--;
    interrupted = false;
  }
}


Now all you need to do is to write this when you want the ranting dialog to start:
Code: ags

warlock_event = true;


And this when the player examines an object or does something which is supposed to interrupt the ranting:
Code: ags

interrupted = true;


It will automatically do the job and stop once the ranting is over, if you want to start over later then just use "warlock_event = true;" again.   
I also used 'warlock.saybackground' as my test character which will need to be changed to your actual warlock character name.
If you add lines inside room_load, then you have to change the number at line 3 in the room_rep_exec block I wrote, as described in the comments.

Tenacious Stu

Thank you for the swift reply Sephiroth. Before your latest reply, I was looking at the code myself, trying to figure it out and now I have this, which seems to work exactly how I want it:

Code: AGS

// room script file

String Lines[100];
int current_line;
bool interrupted;
Overlay *text1;

function room_AfterFadeIn()
{
//This contains a cutscene that plays before the Warlock begins ranting. Then:

  Lines[0] = "Sentance One";
  Lines[1] = "Sentance Two";
  Lines[2] = "Sentance Three";
  Lines[3] = "Sentance Four";
  Lines[4] = "Sentance Five";

warlock_event=true; //This is declared in Global Variables as I need to use it within a dialog
}

function room_RepExec()
{
if(warlock_event)
{
 
  if(current_line > 4) { //change to the max number of lines
    current_line=0; // When the warlock finishes his rant, he will begin again from the start
  }
   
  if(!interrupted)
  {
    if(text1 == null || !text1.Valid)
    {
      text1 = cWarlock.SayBackground(Lines[current_line]);
      current_line++;
    }
  }
  else
  {
    text1 = cWarlock.SayBackground("Now where was I? Ah yes...");
    current_line--;
    interrupted = false;
  }
}


else {
// This will play a cutscene that will start after you return from a dialog which contains warlock_event=true; at the end
// And carry on with the rest of the game...
}
}


function hHotspot1_UseInv()
{
if (player.ActiveInventory==iItem){
  cWarlock.Say ("What do you think you're doing?");
  dialog01.Start(); //This will begin a dialog where you will need to answer correctly to activate warlock_event=true;
  
}
}


function oObject0_Interact()
{
  cGirl.Say ("That stupid Object can't help me now."); //when the player clicks on something with an event, then the dialog takes priority over the SayInBackground and interrupts the Warlock
interrupted=true; // The warlock then continues his rant from where he left off.
}


The ONLY issue is that the Warlocks talking animation DOES NOT play when he is saying things in the background. I tried to download Electroshokker's Background Speech Module, but there isn't a working link? Is there another solution around this?

Thanks for the great help!

Sephiroth

About the animation, you could add this inside rep_exec block as a workaround, and I also changed warlock_event to an Integer variable otherwise your next cutscene would be triggered anytime you enter the room:
Code: ags


function room_RepExec()
{

if(warlock_event == 1)
{  
  if(text1 != null && text1.Valid && !cWarlock.Animating)
  {
    cWarlock.Animate(talk_view, talk_loop, eOnce, eNoBlock);  //if warlock is saying text in bg and is not animating, animate him
  }

  if(current_line > 4) { //change to the max number of lines
    current_line=0; // When the warlock finishes his rant, he will begin again from the start
  }
   
  if(!interrupted)
  {
    if(text1 == null || !text1.Valid)
    {
      text1 = cWarlock.SayBackground(Lines[current_line]);
      current_line++;
    }
  }
  else
  {
    text1 = cWarlock.SayBackground("Now where was I? Ah yes...");
    current_line--;
    interrupted = false;
  }

}else if(warlock_event == 2)
{
// This will play a cutscene that will start after you return from a dialog which contains warlock_event=true; at the end
// And carry on with the rest of the game...
}
}
 


Do nothing:
warlock_event = 0;

Start event:
warlock_event = 1;

Event completed, launch cutscene:
warlock_event = 2;

When ranting and cutscene is over and game must go on:
warlock_event = 0;

I hope this fixes your problem and works as intended.
Edit again: Before I forget, you can also add a dummy last line, like: Line[5] = ""; and change the other reference to 5, so that you can actually interrupt the last sentence.

Snarky

Quote from: Tenacious Stu on Fri 13/09/2013 20:16:58
@Snarky
I'm not sure which module I am using, but it sounds like in trying to use it, I'm causing myself more harm than good?

No, I wouldn't say that. The modules simplify the task of implementing functionality like this, and I think it would be easier to make one work than to reimplement it yourself, like Sephiroth is suggesting. I'm a little puzzled by your attitude to just give up when you come across some minor problem with a solution: solving problems, figuring out what's causing a bug and then fixing it is what programming is all about.

The module will almost certainly have credits in the header file, so just look there to see which one it is.

Sephiroth

Snarky: You'd be right if the module allowed you to actually interrupt its background queue, remember what sentence it was saying and resume the queue. Which is not possible, AFAIK. Implementing it yourself is therefore the only solution, besides I think 30 lines of code is rather good compared to using a whole module, especially when he only needs it for a precise scene and not as a general speech mechanic.
QuoteI'm a little puzzled by your attitude to just give up when you come across some minor problem with a solution
How cold, the thing he's trying to achieve can't be done with the module he's using, and I'm sure you can tell (from his code) he doesn't know enough to find out a workaround by himself.

Tenacious Stu: If you want to make it easier by using a module, then you could choose the BGSpeech module, and replace the calls to saybackground by the module SayInBackground, this would make speech/talking animation available with saybackground.

Monkey_05_06's QueuedSpeech Module is the closest to what you're looking for, it is pretty hard to make it work exactly like you want, but it may be possible, as Snarky mentionned.

static void QueuedSpeech.StartLooping()
static int QueuedSpeech.GetCurrentIndex()
static void QueuedSpeech.PauseQueue()
static void QueuedSpeech.UnPauseQueue()

But again the link is broken and I couldn't try it.

Tenacious Stu

Hi Guys

Thank you for all of your help. The scene is working great now, thanks. Here is how I have it now:
Code: AGS

// room script file

String Lines[100];
int current_line;
bool interrupted;
Overlay *text1;

function room_AfterFadeIn()
{
//Cut Scene when player enters the room

  Lines[0] = "Sentence One";
  Lines[1] = "Sentence Two";
  Lines[2] = "Sentence Three";
  Lines[3] = "Sentence Four";
  Lines[4] = "Sentence Five";

warlock_event=1;
}

function room_RepExec()
{

if(warlock_event == 1)
{  
  if(text1 != null && text1.Valid && !cWarlock.Animating)
  {
    cWarlock.LockView(19); //Warlock Talking view
    cWarlock.Animate(0, 3, eRepeat, eNoBlock, eForwards);  //if warlock is saying text in bg and is not animating, animate him
  }
 
  if(current_line > 24) { //change to the max number of lines
    current_line=0; // When the warlock finishes his rant, he will begin again from the start
  }
   
  if(!interrupted)
  {
    if(text1 == null || !text1.Valid)
    {
      text1 = cWarlock.SayBackground(Lines[current_line]);
      current_line++;
    }
  }
  else
  {
    text1 = cWarlock.SayBackground("Now where was I? Ah yes...");
    current_line--;
    interrupted = false;
  }
 
}else if(warlock_event == 2)
{
// This will play a cutscene that will start after you return from a dialog which contains warlock_event=2; at the end
// And carry on with the rest of the game...
}
}


function hHotspot1_UseInv()
{
if (player.ActiveInventory==iItem){
  cWarlock.Say ("What do you think you're doing?");
  dialog01.Start(); //This will begin a dialog where you will need to answer correctly to activate warlock_event=2;
  
}
}


function oObject0_Interact()
{
  cWarlock.UnlockView(); //So that the Warlock Stops Animating while the event is triggered
  cGirl.Say ("That stupid Bookcase can't help me now.");
interrupted=true;
}



Quote from: Snarky on Sat 14/09/2013 08:42:38
I'm a little puzzled by your attitude to just give up when you come across some minor problem with a solution: solving problems, figuring out what's causing a bug and then fixing it is what programming is all about.

I see what you're saying, but I don't have a lot of experience with programming, let alone implementing modules. I decided to use Sephiroth's suggestion as it seemed to offer fewer problems to contend with over the original 3 problems I was having with using the module. Plus, Surely if I'm interested in expanding my knowledge of programming, then looking at the basics of how to implement something like this rather than using a module would be better anyway? I know that I was trying to bypass the whole 'learning how to do it' phase by using a module in the first place, but perhaps it worked out better in the end?

Quote from: Snarky on Sat 14/09/2013 08:42:38
The module will almost certainly have credits in the header file, so just look there to see which one it is.

It was monkey_05_06's QUEUEDSPEECH Module.

Quote from: Sephiroth on Sat 14/09/2013 14:53:01
I'm sure you can tell (from his code) he doesn't know enough to find out a workaround by himself.

Ha Ha, is it that obvious? Unfortunately I haven't spent as much time with programming as I'd of liked to have. If I could go back in time I think I would have studied Programming at University, rather than Game Design, I probably would have come away with a much more useful skill set. But I suppose it's never to late :-D

Thanks again guys

SMF spam blocked by CleanTalk