Background Speech (tried modules)

Started by Nixxon, Mon 02/11/2015 23:10:02

Previous topic - Next topic

Nixxon

Hey all,

Just a quickie. I'm trying to get a character to talk to himself in the background (with audio), I've been using the forum search function over the last week or so and ultimately downloaded a couple of modules that didn't work (they may have been out of date). I only need this to occur during one room of my game, so the easiest I can get away with coding this the better.

It doesn't need to be randomized though it would be nice if it looped.

The code would be roughly as follows (I tried this in the room repeat execute, with little luck).

Code: ags
cChar.SayBackground("&1 I am randomly talking to myself");
Wait(600, NoEblock);
cChar.SayBackground("&2 Again I speak to myself");
Wait(600, NoEblock);
cChar.SayBackground("&3 Talkie Talkie Poo Poo");
Wait(600, NoEblock);
cChar.SayBackground("&4 Yawn... Running out of things to say");
Wait(600, NoEblock);
cChar.SayBackground("&5 Weenis!");
Wait(600, NoEblock);

Kumpel

I think a combination of the BgSpeech module in this particular room and some if-prompt in the rep_exec_always of the global script could work. Just let him say the stuff you want with the module and let the game check, if he says one particular phrase number via BgSpeech.GetCurrentPlayingIndex();, to play a particular audiofile.

This is the module:
http://www.adventuregamestudio.co.uk/forums/index.php?topic=48086.msg636452160#msg636452160

Cheers
Kumpel

Nixxon

Thanks Kumpel,

That's one of the Modules' I used. While the the functions explain in that thread are very easy to understand. To actually "make it go!" aren't... at least for me.

Do you write the actual dialog in the module script? or in the room? After installing the module, the BgSpeech.Add worked. but Wasn't sure how to "call it"? If that makes sense.

Khris

It won't work like that; you can do this:

Code: ags
int cChar_speechTimer = 0;

function repeatedly_execute_always() {
  cChar_speechTimer++;
  if (cChar_speechTimer % 600 == 0) {
    int which = cChar_speechTimer / 600;
    String s;
    if (which == 1) s = "&1 I am randomly talking to myself";
    if (which == 2) s = "&2 Again I speak to myself";
    // etc
    cChar.SayBackground(s);
  }
  if (cChar_speechTimer == 3000) cChar_speechTimer = 0;
}

Nixxon

Thank Khris,

So this just goes into the room repeat exec?

Khris

No, this goes in the room script as-is, outside any functions.
It's a room variable and the repeatedly_execute_always() function, which, if existent, is called by AGS without having to link it.

(I always use the correct indentation; even if a function could go inside another function, lines 1, 3 and 14 aren't indented. When I post stuff that goes inside a function, I make sure to a) mention the functon and b) indent the code so it can be used as-is, too.)

Nixxon

#6
Seems to work great!

At one point the character dies and is removed from screen. I'm thinking I should use a global variable in conjunction with this? So that it doesn't run if the character is absent.

Also. the audio "&1" etc. doesn't seem to be working?

Khris

Yeah, Character.SayBackground doesn't support voice speech. You could play the samples manually:
Code: ags
    String s;
    AudioClip *vs;
    if (which == 1) { s = "&1 I am randomly talking to myself"; vs = aNpcBg1; }
    if (which == 2) { s = "&2 Again I speak to myself";         vs = aNpcBg2; }
    // etc
    vs.Play();
    cChar.SayBackground(s);


To fix the other problem, just check if the character is still present (between line 3 and 4 of the previous snippet):
Code: ags
  if (cChar.Room != player.Room) return; // do nothing

Nixxon

Quoteif (cChar.Room != player.Room) return; // do nothing

Worked brilliantly.

Bit of trouble with the manual clips. It seems to play the last one for each line (aPickuppebble) Is there a way to have a unique clip for each line of dialog?

Code: ags
// room script file
int cPedoMan_speechTimer = 0;
 
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600;
    String s;
    AudioClip *vs;
      if (cPedoMan.Room != player.Room) return; // do nothing
  if (which == 1) s = "&1 I am randomly talking to myself"; vs = aPEDO7; 
    if (which == 2) s = "&2 Again I speak to myself"; vs = aStonethrow;
    if (which == 3) s = "&3 I am randomly talking to myself"; vs = aMonstereat;
    if (which == 4) s = "&4 Again I speak to myself"; vs = aAopentrunk;
    if (which == 5) s = "&5 I am randomly talking to myself"; vs = aPickuppebble; // plays this every time.
   vs.Play();
   cPedoMan.SayBackground(s);
  }
  if (cPedoMan_speechTimer == 3000) cPedoMan_speechTimer = 0;
}

Snarky

#9
The { } brackets that Khris put in weren't just for show. They create a block, so that the if-condition covers both "lines" (separated by semi-colons). What you have is structurally identical to this â€" I've just reformatted for clarity:

Code: ags
// room script file
int cPedoMan_speechTimer = 0;
 
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600;
    String s;
    AudioClip *vs;

    if (cPedoMan.Room != player.Room)
      return; // do nothing

    if (which == 1)
      s = "&1 I am randomly talking to myself";

    vs = aPEDO7; 
    if (which == 2)
      s = "&2 Again I speak to myself";

    vs = aStonethrow;
    if (which == 3)
      s = "&3 I am randomly talking to myself";

    vs = aMonstereat;
    if (which == 4)
      s = "&4 Again I speak to myself";

    vs = aAopentrunk;
    if (which == 5)
      s = "&5 I am randomly talking to myself";

    vs = aPickuppebble; // plays this every time.

    vs.Play();
    cPedoMan.SayBackground(s);
  }
  if (cPedoMan_speechTimer == 3000)
    cPedoMan_speechTimer = 0;
}


It should now be obvious why it's always aPickuppebble that plays: that's what you set vs to right before you play it!

This is another good example of how important it is to format and indent your code consistently. Khris is an experienced coder and uses these one-line "abbreviations" (multiple statements on one line) that can be handy in some cases, but if you're not already familiar with code formatting, I think you should just stick to the simple form; that way you won't make mistakes like this.

Khris

I was debating whether to take this shortcut, and I shouldn't have... :-D

Code: ags
    String s;
    AudioClip *vs;
    if (which == 1) {
      s = "I am randomly talking to myself";
      vs = aPEDO7;
    }
    if (which == 2) {
      s = "Again I speak to myself";
      vs = aStonethrow;
    }
    // etc
    vs.Play();
    cChar.SayBackground(s);

Nixxon

#11
Poop, I've tried both suggestions, and it's still playing the sound file from the last if with all dialogs. (aAopentrunk from line 32).

See code below, what am I missing?? Really sorry about this.

Code: ags
// room script file
int cPedoMan_speechTimer = 0;
 
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600;
    String s;
    AudioClip *vs;
 
    if (cPedoMan.Room != player.Room)
      return; // do nothing
 
    if (which == 1) {
      s = "&1 DIALOG 1";
      vs = aPEDO7; 
    }
    if (which == 2) {
      s = "&2 DIALOG 2";
      vs = aStonethrow;
    }
    if (which == 3) {
      s = "&3 DIALOG 3";
      vs = aMonstereat;
    }
    if (which == 4) {
      s = "&4 DIALOG 4";
      vs = aPickuppebble;
    }
    if (which == 5)
      s = "&5 DIALOG 5";
      vs = aAopentrunk;
  
    vs.Play();
    cPedoMan.SayBackground(s);
  }
  if (cPedoMan_speechTimer == 3000)
    cPedoMan_speechTimer = 0;
}


Scavenger

This:

Code: ags
    if (which == 5)
      s = "&5 DIALOG 5";
      vs = aAopentrunk;


needs to be:

Code: ags
if (which == 5)
    {
        s = "&5 DIALOG 5";
        vs = aAopentrunk;
    }


You're still missing the brackets on the code - make sure to double check them, otherwise the if statement will only apply to the first function run after it, and always run the one after that.

Nixxon

Absolutely amazing. You're all noble Jedi. Works a treat now.

Snarky

Quote from: Scavenger on Wed 04/11/2015 08:31:07
This:

Code: ags
    if (which == 5)
      s = "&5 DIALOG 5";
      vs = aAopentrunk;


needs to be:

Code: ags
if (which == 5)
    {
        s = "&5 DIALOG 5";
        vs = aAopentrunk;
    }


You're still missing the brackets on the code - make sure to double check them, otherwise the if statement will only apply to the first function run after it, and always run the one after that.

Yes, except you've got the indentation wrong. It should be:

Code: ags
    if (which == 5)
    {
      s = "&5 DIALOG 5";
      vs = aAopentrunk;
    }


See? The brackets are in line with the if-condition they belong to, and the block inside the brackets are indented one level (two spaces, by AGS default).

Glad it's working, Nixxon! I would also refactor this code a bit, to make it simpler and more future-proof:

Code: ags
// room script file
int cPedoMan_speechTimer = 0;

// A helper function to simplify the job of background speech with voice
void SayBackgroundClip(this Character*, String message, AudioClip* clip)
{
  this.SayBackground(message);
  if(clip != null)
    clip.Play();
}

function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600;
    if (cPedoMan.Room != player.Room)
      return; // do nothing
 
    if (which == 1)
      cPedoMan.SayBackgroundClip("&1 DIALOG 1", aPEDO7);
    else if (which == 2)
      cPedoMan.SayBackgroundClip("&2 DIALOG 2", aStonethrow);
    else if (which == 3)
      cPedoMan.SayBackgroundClip("&3 DIALOG 3", aMonstereat);
    else if (which == 4)
      cPedoMan.SayBackgroundClip("&4 DIALOG 4", aPickuppebble);
    else if (which == 5)
      cPedoMan.SayBackgroundClip("&5 DIALOG 5", aOpentrunk);
    else
      cPedoMan_speechTimer = 0; // Loop around
  }
}


This way you can add more lines very easily, without worrying about keeping track of the relationship between which and cPedoMan_speechTimer (a likely source of bugs, if you add a line but forget to update the 3000 value). And you can even use it to do background speech with voice for any other characters.

Nixxon

#15
Hey Guys,

I'm back on this one.  :sealed:

I decided to try Snarky's code instead as I wanted to add additional code to each line (such as set character animations etc.).

Initially i received the error -
Failed to save room room3.crm; details below
room3.asc(11): Error (line 11): '.SayBackgroundClip' is not a public member of 'Character'. Are you sure you spelt it correctly (remember, capital letters are important)?


I then tried to remove the 'BackgroundClip' and simply have 'cPedoMan.Say'

The game luunches this way, however when in that room the game crashes with the following error -
A blocking function was call from within a non-blocking event such as repeatedly_execute_always

Any ideas? Below is the code -

Code: ags
// room script file
int cPedoMan_speechTimer = 0;
 
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600; 
    if (cPedoMan.Room != player.Room)
      return; // do nothing
 
    if (which == 1)
      cPedoMan.Say("&1 DIALOG 1", aPEDO7);
    else if (which == 2)
      cPedoMan.Say("&2 DIALOG 2", aStonethrow);
    else if (which == 3)
      cPedoMan.Say("&3 DIALOG 3", aMonstereat);
    else if (which == 4)
      cPedoMan.Say("&4 DIALOG 4", aPickuppebble);
    else if (which == 5)
      cPedoMan.Say("&5 DIALOG 5", aPEDO7);
    else
      cPedoMan_speechTimer = 0; // Loop around
  }
}



EDIT - COMPLETELY MISSED THIS BIT. Duh

Code: ags
// A helper function to simplify the job of background speech with voice
void SayBackgroundClip(this Character*, String message, AudioClip* clip)
{
  this.SayBackground(message);
  if(clip != null)
    clip.Play();
}
 

Nixxon

BAH, never mind, I just missed a huge chunk of your code.

Clinically stupid. Sorry.

Nixxon

Spoke to soon.

How would I go about adding additional scripts to each (which ==)

For instance -

if (which == 1)
      cPedoMan.SayBackgroundClip("&1 DIALOG 1", aPEDO7);
      set a character view etc.
    else if (which == 2)
      cPedoMan.SayBackgroundClip("&2 DIALOG 2", aStonethrow);
      play another line of dialog
    else if (which == 3)
      cPedoMan.SayBackgroundClip("&3 DIALOG 3", aMonstereat);
      something something etc.
    else if (which == 4)
      cPedoMan.SayBackgroundClip("&4 DIALOG 4", aPickuppebble);
something something etc.
    else if (which == 5)
      cPedoMan.SayBackgroundClip("&5 DIALOG 5", aPEDO7);
something something etc.
    else
      cPedoMan_speechTimer = 0; // Loop around
  }

Snarky

Just wrap it in more curly brackets, like so:

Code: ags
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600;
    if (cPedoMan.Room != player.Room)
      return; // do nothing
 
    if (which == 1) {
      cPedoMan.SayBackgroundClip("&1 DIALOG 1", aPEDO7);
      cPedoMan.NormalView = vPEDO9; // Or whatever
    }
    else if (which == 2) {
      cPedoMan.SayBackgroundClip("&2 DIALOG 2", aStonethrow);
      cPedoMan.SayBackgroundClip("&0 DIALOG X", aWhatever);  // Or whatever else
    }
    // ... other cases ...
    else
      cPedoMan_speechTimer = 0; // Loop around
  }
}

Nixxon

Thanks Snarky,

A bit of an issue. It appears to be jumping directly to the last line of code between the brackets -

Code: ags
function repeatedly_execute_always() {
  cPedoMan_speechTimer++;
  if (cPedoMan_speechTimer % 600 == 0) {
    int which = cPedoMan_speechTimer / 600; 
    if (cPedoMan.Room != player.Room)
      return; // do nothing
 
    if (which == 1) {
      cPedoMan.SayBackgroundClip("&1 Blah Blah 1", aPEDO7); //SKIPS THIS LINE
      cPedoMan.SayBackgroundClip("&1 Blah Blah 2", aPEDO7); //GOES STRAIGHT TO THIS LINE FIRST
    }
    else if (which == 2) {
      cPedoMan.SayBackgroundClip("&2 DIALOG 2", aStonethrow);
    }
    else if (which == 3) {
      cPedoMan.SayBackgroundClip("&3 DIALOG 3", aMonstereat);
    }
    else if (which == 4) {
      cPedoMan.SayBackgroundClip("&4 DIALOG 4", aPickuppebble);
    }
    else if (which == 5) {
      cPedoMan.SayBackgroundClip("&5 DIALOG 5", aPEDO7);
    }
    else
      cPedoMan_speechTimer = 0; // Loop around
  }
}

SMF spam blocked by CleanTalk