DisplaySpeechBackground queued talking

Started by Scorpiorus, Sat 21/02/2004 16:43:26

Previous topic - Next topic

Scorpiorus

Having seen a couple of topics regarding background speech reminded me about an old script I had. I made some modifications so it could play talking animations - here it is:

Note: Only one speech queue can be run at a time, i.e. you can have only one conversation being run at background.

LAST UPDATE (23.05.04):
//
// Revision history:
//===================
// v1.02Ã,  [23.05.04]:
// - Added qSkipCurrentMessage() function to skip the message being currently displayed;
// - Play sound was added: qDisplaySpeech(EGO, "@3 hey"); will play "sound3.xxx" file;
//
//==============================================
// v1.01+ [06.04.04]:
// - Fixed import function Delay (should be import string Delay)
//
//==============================================
// v1.01Ã,  [25.02.04]:
// - Delay(int time) directive was added;
// - Fixed qIsTalking() returning 0 when the *last* phrase is being said;
// - Changed the repeatedly execute part of the script;
//



Script header:
import function qDisplaySpeech(int CharID, string message);
import function qStopSpeech();
import function qIsTalking();
import stringÃ,  Ã, Delay(int time);
import function qSkipCurrentMessage();



Main global script file (at the very top of it):
///////////////////////////////////////
// Background queued talking v1.02
///////////////////////////////////////


#define BUFFER_SIZE 300
#define AGS_STRING_LENGTH 200

struct MESSAGESTRING {
   intÃ,  CharID;
   intÃ,  anim_disabled;
   char byte[AGS_STRING_LENGTH];
};

MESSAGESTRING Buffer[BUFFER_SIZE];

function mod(int a, int b) { return a - (b*(a/b)); }

function BufferSetString(int sID, string text) {
   if (sID<0 || sID>=BUFFER_SIZE) { Display("error: SetString buffer error!"); QuitGame(0); }
   StrCopy(Buffer[sID].byte, text);
   return StrLen(text);
}

function BufferGetString(int sID, string buf) {
   if (sID<0 || sID>=BUFFER_SIZE) { Display("error: GetString buffer error!"); QuitGame(0); }
   StrCopy(buf, Buffer[sID].byte);
   return StrLen(buf);
}


int cur_str=0;
int cur_say=0;

int cur_overlay = -1;
int stop_talkÃ,  Ã, =Ã,  0;
int prev_charÃ,  Ã, = -1;
int cur_channel = -1;

function qDisplaySpeech(int CharID, string message) {
Ã, 
   int i = mod(cur_str, BUFFER_SIZE);

   BufferSetString(i, message);
   Buffer[ i ].CharID = CharID;
   cur_str++;
   //if (cur_str >= BUFFER_SIZE) cur_str = 0;
}

function qIsTalking() {
   return IsOverlayValid(cur_overlay);
}

function GetSoundNumber(string text) { //return 0 if unsuccessful

   int pos = StrContains(text, "@");
   if (pos<0) return 0;
   int i = pos + 1;
   string str_rez; StrCopy(str_rez, "");
   int Char = StrGetCharAt(text, i);
   while (Char>='0' && Char<='9' && i<StrLen(text)) {
      StrFormat(str_rez, "%s%c", str_rez, Char);
      i++;
      Char = StrGetCharAt(text, i);
   }

   while (i<=StrLen(text)) {
      StrSetCharAt(text, pos, StrGetCharAt(text, i));
      i++;
      pos++;
   }

   return StringToInt(str_rez);
}

function GetFreeChannel() { // returns a channel number
   ifÃ,  Ã,  Ã,  (IsChannelPlaying(5)==0) return 5;
   else if (IsChannelPlaying(4)==0) return 4;
   else return 3;
}

function qStopSpeechChannel() {
   if (cur_channel>2)
      if (IsChannelPlaying(cur_channel)) {
         StopChannel(cur_channel);
         cur_channel = -1;
      }
}

function qStopSpeech() {
   if (qIsTalking()) {
      qStopSpeechChannel();
      RemoveOverlay(cur_overlay);
      int i = mod(cur_say, BUFFER_SIZE);
      int CharID = Buffer[ i ].CharID;
      ReleaseCharacterView(CharID);
      if (prev_char > -1) { ReleaseCharacterView(prev_char); prev_char = -1; }
      stop_talk = 0;
      cur_say = cur_str;
   }
}

function DisplaySpeechQ_RE() {

   if (qIsTalking()==0) { // wait for character to finish talking
      if (cur_say < cur_str) { // if something left to be said

         int i = mod(cur_say, BUFFER_SIZE);
         int CharID = Buffer[ i ].CharID;

         string buf;
         BufferGetString(i, buf);

         qStopSpeechChannel();
         int cur_sound = GetSoundNumber(buf);
         cur_channelÃ,  Ã, = GetFreeChannel();
         if (cur_sound>0) PlaySoundEx(cur_sound, cur_channel);

         cur_overlay = DisplaySpeechBackground(CharID, buf);
         stop_talk = 1;

         if (prev_char > -1) ReleaseCharacterView(prev_char);
         prev_char = CharID;

         if (character[CharID].animating==0 && Buffer[ i ].anim_disabled==0) {
            int view = character[CharID].talkview+1;
            if (view < 1) { Display("error: Talk view isn't assigned!"); QuitGame(0); }
            int loop = character[CharID].loop;
            int delay = character[CharID].animspeed;
            SetCharacterView(CharID, view);
            AnimateCharacterEx (CharID, loop, delay, 1, 0, 0);
         }

         Buffer[ i ].anim_disabled = 0;
         cur_say++;
         //if (cur_say >= BUFFER_SIZE) cur_say = 0;
      } else if (stop_talk) { //finish talk animation
         if (prev_char > -1) { ReleaseCharacterView(prev_char); prev_char = -1; }
         qStopSpeechChannel();
         stop_talk = 0;
      }
   } // end of if (qIsTalking()==0)
}

string str_delay;
string Delay(int time) {

   int n=time;
   if (n<1) n=1; else if (n>=AGS_STRING_LENGTH) n=AGS_STRING_LENGTH-1;

   string format;
   StrFormat(format, "%%%dc", n);
   StrFormat(str_delay, format, ' ');
   Buffer[mod(cur_str, BUFFER_SIZE)].anim_disabled = 1;
   return str_delay;
}

function qSkipCurrentMessage() {
   if (qIsTalking()) RemoveOverlay(cur_overlay);
}



Main global script file (Repeatedly execute):

function repeatedly_execute() {
Ã, // put anything you want to happen every game cycle here

Ã, 
   DisplaySpeechQ_RE(); // place it before any other script code in rep. exec.
Ã, 
}



Main global script file (on_event function):
function on_event(int event, int data) {

   if (event == LEAVE_ROOM) qStopSpeech();

}




Functions:

----------------------------------------------------------
qDisplaySpeech(int CharID, string message)

Displays a CharID's character speech message at the background and plays an appropriate talk animation. You can queue up multiple messages by typing in several functions one after another.

Example:

qDisplaySpeech(EGO, "hi there MAN!");
qDisplaySpeech(MAN, "hey Roger");
qDisplaySpeech(EGO, "blah");
qDisplaySpeech(MAN, "blah blah");

Note: You can have a pause in the conversation by specifying a special Delay(int time) directive. The time parameter tells how long to wait before proceeding further. The valid range is 1 through 199. Where time=1 is the shortest delay.

qDisplaySpeech(EGO, "hi there MAN!");
qDisplaySpeech(MAN, "hey Roger");
qDisplaySpeech(EGO, Delay(50));
qDisplaySpeech(MAN, "blah blah");

Note: You can also have a sound to be played. Put the character '@' followed by a sound number, for example:

qDisplaySpeech(EGO, "@3 what is that?");

would display "what is that?" text message and play sound3.xxx file. Note, however, it plays a normal sound, not a voice speech from the speech.vox file!


----------------------------------------------------------
qStopSpeech()

Just stops background conversation.


----------------------------------------------------------
qIsTalking()

Returns 1 if conversation is going on. Otherwise returns 0.


----------------------------------------------------------
qSkipCurrentMessage()

Calling this function will skip currently displayed message and force the next one to proceed with.
This is useful if you want, for example, to let the player skip messages by pressing a certain key:

on_key_press:

if ((keycode!=0) && (keycode==game.skip_speech_specific_key)) qSkipCurrentMessage();

Skips the message if the game.skip_speech_specific_key variable is set and the player pressed that key.


~Cheers

Kweepa

Fabulous!
I can see that coming in handy!
Still waiting for Purity of the Surf II

SimSaw

Can I also use file speech with that script? Like:
qDisplaySpeech(EGO, "&11 Hand me your money!");

Scorpiorus

#3
QuoteFabulous!
I can see that coming in handy!
Glad you find it useful, Steve. :)

QuoteCan I also use file speech with that script? Like:
qDisplaySpeech(EGO, "&11 Hand me your money!");

I thought of it. But no it's not possible because the sound speech system is handled internally by AGS and there is no function currently to play a sound from the speech.vox file.

~Cheers

a-v-o

To have also background speech sound the script can be modified to use PlaySound for this task.

something like: qDisplaySpeech (int charid, int sound, string message)

for no sound, sound is -1

Just an idea  ;D

Scorpiorus

Thanks for the suggestion, a-v-o. But, yeah then it will play a sound*.wav file not one from the speech.vox and that I suspect SimSaw is asking of. I'd do it in a way how AGS does - i.e. qDisplaySpeech(EGO, "&5 blah blah") will play sound5.* (not ego5.wav)

~Cheers

Kinoko

This works -great-, thanks! This really helps me out.

I only have two queries.

When I use the delay like in your example, I get an error message saying "Type mismatch; string with non-string".

Also, is there a way I can get my script of background speech to start again from the beginning when it finishes?

Scorpiorus

#7
Quote from: Kinoko on Sat 20/03/2004 15:36:17When I use the delay like in your example, I get an error message saying "Type mismatch; string with non-string".

Ah, I know what's wrong. I tested it from the main global script but you probably run a speech from a room script. But import declaration I made is wrong for Delay :) Thanks for spotting it. I've changed it now. To fix your problem open the script header and replace:
import function Delay(int time);
with
import string Delay(int time);

:)

QuoteAlso, is there a way I can get my script of background speech to start again from the beginning when it finishes?
Yeah, sure. Just make a use of qIsTalking() function by putting it within the repeatedly_execute:
function repeatedly_execute() {

    if (qIsTalking()==0) {
        qDisplaySpeech(EGO, "blah blah");
        qDisplaySpeech(EGO, Delay(20));
        qDisplaySpeech(EGO, "more blah");
        qDisplaySpeech(EGO, Delay(70));
    }

}


that will start talking once again after it was finished

[edit: qIsTalking corrected :)]

~Cheers

Kinoko

ThankYOU Scorpiorus! ^_^ You just solved the major problem I had before releasing my game. Everything works beautifully now.

You might want to change that script there to "qIsTalking" too :)

Fekix

thank you very much, very useful script!!

I think it's ok using this script in my game if i metion ur name in the credits?

And another question:
In the newest version of AGS it is possible to set "game.skip_speech_specific_key" command but this key won't work in ur script :( where do i have to set the proper command?
Currently working on: My Little Real Life Adventure

Scorpiorus

Quote from: Fekix on Sat 22/05/2004 16:27:01I think it's ok using this script in my game if i metion your name in the credits?
Can you think of another reason what I posted it for? ;) ...Sure, and you don't necessarily have to mention me.

QuoteIn the newest version of AGS it is possible to set "game.skip_speech_specific_key" command but this key won't work in your script :( where do i have to set the proper command?
Well, the script is to play background speeching when you have a total control over the player character. How do you want the "skip_speech_specific_key" variable to affect the script?

Fekix

#11
my specific speech key is "." (keycode 46) like in Lucas Arts Games. I want that if i press "." the actual displayed speech removes and if there'sÃ,  next speech the next speech should be displayed.

I hope you understand what i mean.

In my room code:

qDisplaySpeech(JAN, "Now look at that. Blue sky and stuff");
Ã,  qDisplaySpeech(JAN, "Man, I'd give everything to be out of here and free, even the stuff I hide in my mattress.");
Ã,  FaceDirection(JAN, "down");
Ã,  qDisplaySpeech(JAN, "What? Didn't you know?");

and i want that if i press "." the next text will be displayed.

Edit: forgot to say that i use this script because i want that the player has nearly everytime total control over the PlayerCharacter, that's the reason why i use DisplaySpeechBackground instead of DisplaySpeech
Currently working on: My Little Real Life Adventure

Scorpiorus

Thanks for clarifying - I've updated the script, see the qSkipCurrentMessage() example. :)

Fekix

Thank you very much that is exactly what i need :)

Thank you again ;)
Currently working on: My Little Real Life Adventure

G

This looks nice. But I have a problem while using it in my game.

I used the script to set a background conversation in a room, as soon as the player character enters in the room, the game crashes and gives me an error saying: 'AnimateCharacter: invalid loop number specified'

Why?

Thanks for the script anyway.

The Spanish AGSer: G

Scorpiorus

That means the talking view of the character who is currently speaking doesn't have all the loops required. Remember that each talking view must have the exactly same number of loops as the normal (walking) view - i.e. each loop for each direction.
I'll add a check for the next version so it displays a more meaning error message.

ps. I'm glad the script came in handy btw :)

G

Thanks Scorpiorus.

This script is a very nice work.

Lazarus

I'm having a problem with background speech in that when I leave the screen and go into a new room the background speech seems to continue in the new room.

The only way I can stop it is by using "qStopSpeech()"
in the reatedly_execute in the new room which is not ideal.
The qStopSpeech(); doesn't seem to work properly I have placed the Leave room as below.

function on_event (int event, int data)
if (event == LEAVE_ROOM) qStopSpeech();

Any help would be grateful
Thanks
*Currently using AGS Editor 2.70 (Build 2.70.601)*

Scorpiorus

I see two possible causes:

1.
You have to put the on_event(int event, int data) function into the main global script. That's where AGS expects it to be (not in the room script).
You can check if it's actually invoked by adding a Display(...) command, as follows:

function on_event (int event, int data) {
   if (event == LEAVE_ROOM)
   {
      Display("calling on_event for LEAVE_ROOM");
      qStopSpeech();
   }
}


2.
Another possibility is that your background conversation is run from withing the repeatedly_execute function. In that case there is a chance it's continuously restarted each game loop -- hence it can cause some sort of problem you're telling about. Make sure you run it only once (e.g. from within the players enters room interaction).

See if you can solve the problem now, but let me know if it still doesn't work.

Kweepa

Hmm, that's kind of confusing.
Do they have the same functionality?
Still waiting for Purity of the Surf II

SMF spam blocked by CleanTalk