Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Secret Fawful on Fri 14/12/2012 04:56:32

Title: Implementing a lengthy background dialog - redux
Post by: Secret Fawful on Fri 14/12/2012 04:56:32
All right, so I followed a similar topic from November on this subject, but that led me nowhere. I came to the conclusion I'm using this code all wrong, but I've used it the only two ways I can think of and it's still not working correctly. I'm wanting two characters or more to hold a continuous, non-blocking conversation in the background that starts once a dialog ends. Basically, I want to be able to start up such a dialogue from any point. Then if I leave and come back to the room, I want the dialogue to be running again. To accomplish this, I, of course, put the dialogue under repeatedly execute. Currently I was giving one of the characters a dummy inventory item, and then later I would take it away when I no longer wanted this conversation to happen in that room anymore.

Here is my code in the end of the dialog:

Code (AGS) Select
cCharJack.Walk(83, 125, eBlock, eWalkableAreas);
SetTimer(1,0);
cCharJack.HasInventory(iInvDummy);


And then under Rep_Exec in the room itself-

Code (AGS) Select
function room_RepExec()
{
  if (cCharJack.HasInventory(iInvDummy)){
  if(IsTimerExpired(1))
  {
    if(turn == 0)
    {
      cCharJack.SayBackground("You pompous @#%$!, just who do you think you are?");
      cRangSlim.SayBackground("I'm not going to sit here and watch you abuse your power.");
      cCharJack.SayBackground("Abuse my POWER! Huff! Huff!");
      cCharJack.SayBackground("You're lucky I don't split your skull!");
      cRangSlim.SayBackground("I've fought better men than you!");
      cCharJack.SayBackground("Who? The elderly?");
      cRangSlim.SayBackground("You are elderly! I can hear your dentures clattering!");
      cCharJack.SayBackground("Just who do you think you are, you %$@!?");
      cRangSlim.SayBackground("Don't you know who I am? I'm Steve Dedman!");
      cCharJack.SayBackground("Oh! Some puffed-up TV stooge, eh!?");
      cRangSlim.SayBackground("What did you call me, you big windbag!?");
      cCharJack.SayBackground("Windbag!?");
      SetTimer(1, 280);
    }
    else if(turn == 1)
    {    //etc etc.
      SetTimer(1, 150);
    }
    turn ++; //this is universal to all instances of dialog, so put here
  }
}
}


However, currently, the background dialogue will not play at all unless I remove the Inventory requirements, and then it will only play the final two pieces of dialogue, and at the same time. I want each piece of dialogue to play one after the other, and I want the characters to say them while animating. They do none of this. I know I'm horribly screwing this code up, so....what do I need to do to make this work? Because I can't find anything else on this. I'm honestly embarrassed to even ask because I know I'm doing this very, very wrong.
Title: Re: Implementing a lengthy background dialog - redux
Post by: Slasher on Fri 14/12/2012 07:45:16
At the moment, Without looking any further than this part:

Code (AGS) Select
cCharJack.Walk(83, 125, eBlock, eWalkableAreas);
SetTimer(1,0);
cCharJack.HasInventory(iInvDummy);


Should be:

Code (AGS) Select
cCharJack.Walk(83, 125, eBlock, eWalkableAreas);
SetTimer(1,0);
cCharJack.AddInventory(iInvDummy); // Gives player iInvDummy which sets off Rep Exec events
// OR
cCharJack.LoseInventory(iInvDummy); //Takes away player iInvDummy
Title: Re: Implementing a lengthy background dialog - redux
Post by: Snarky on Fri 14/12/2012 08:57:32
Beyond that, you can't just write a sequence of SayBackground statements, because they're (obviously) not blocking, and so they'll all be executed instantly, with the effect that only the last line will actually be displayed. Instead, use SSH's QueueBGSpeech (http://ssh.me.uk/moddoc/QBGSpeechST.zip) module to do the conversation.
Title: Re: Implementing a lengthy background dialog - redux
Post by: Secret Fawful on Fri 14/12/2012 10:39:24
Damn it, I can't believe I made that mistake on the inventory code.

As far as SSH's module, I've spent an hour trying to update the code on it, because it gives all sorts of errors now, and I'm still getting errors on it.

Code (AGS) Select
// Main script for module 'QueueSpeech'
queuespeech_t squeue;

function game_start () {
  squeue.insert=0;
  squeue.say=0;
  squeue.ol=null;
  squeue.stop_talk=0;
  squeue.prev_char=null;
  squeue.channel=-1;
}

function qDisplaySpeechSubtitled(Character *CharID, const string message, const string subtitle) {
   int i = squeue.insert % SPEECH_QUEUE_SIZE;

   squeue.s[i]=message;
   squeue.st[i]=subtitle;
   squeue.c[i]=CharID;
   squeue.insert++;
}

function qDisplaySpeech(Character *CharID, const string message) {
   qDisplaySpeechSubtitled(CharID, message, "");
}

function qSubtitleSetup(int y, int font, int colour) {
  squeue.subtitle_col=colour;
  squeue.subtitle_font=font;
  squeue.subtitle_y=y;
}

function qIsTalking() {
  if (squeue.ol!=null) return squeue.ol.Valid;
  else return 0;
}

function GetSoundNumber(String text) { //return 0 if unsuccessful
   String txt=text;

   int pos = txt.Contains("@");
   if (pos<0) return 0;
   int i = pos + 1;
   String str_rez="";
   while (i<txt.Length && txt.Chars[i]>='0' && txt.Chars[i]<='9') {
str_rez=str_rez.AppendChar(txt.Chars[i]);
      i++;
   }

   while (i<=txt.Length) {
     String new_Str = "@";
      new_Str = new_Str.ReplaceCharAt(pos, i);
      i++;
      pos++;
   }

   return str_rez.AsInt;
}

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 (squeue.channel>2)
      if (IsChannelPlaying(squeue.channel)) {
         StopChannel(squeue.channel);
         squeue.channel = -1;
      }
}

function qStopSpeech() {
   if (qIsTalking()) {
      qStopSpeechChannel();
if ((squeue.ol!=null) && squeue.ol.Valid) squeue.ol.Remove();
if ((squeue.stol!=null) && squeue.stol.Valid) squeue.stol.Remove();
      int i = squeue.say % SPEECH_QUEUE_SIZE;
      if (squeue.c[i]!=null) squeue.c[i].UnlockView();
      if (squeue.prev_char != null) { squeue.prev_char.UnlockView(); squeue.prev_char = null; }
      squeue.stop_talk = 0;
      squeue.say = squeue.insert;
   }
}

function isnumchar (char c) {
  if (c=='-') return 1;
  else return (c>='0' && c<='9');
}

function SpecialHandler(String s) {
  int i=s.Contains("set-globalint ");
if (i>=0) {
i+=14;
    String str_rez="";
    while (i<s.Length && isnumchar(s.Chars[i])) {
str_rez=str_rez.AppendChar(s.Chars[i]);
      i++;
    }
int gi=str_rez.AsInt;
i++;
str_rez="";
    while (i<s.Length && isnumchar(s.Chars[i])) {
str_rez=str_rez.AppendChar(s.Chars[i]);
      i++;
    }
SetGlobalInt(gi, str_rez.AsInt);
return 1;
} else {
i=s.Contains("add-score ");
if (i>=0) {
i+=10;
String str_rez="";
while (i<s.Length && isnumchar(s.Chars[i])) {
str_rez=str_rez.AppendChar(s.Chars[i]);
i++;
}
    GiveScore(str_rez.AsInt);
    return 1;
  } else return 0;
}
}

function DisplaySpeechQ_RE() {

   if (qIsTalking()==0) { // wait for character to finish talking
      if (squeue.say < squeue.insert) { // if something left to be said

        int i = squeue.say % SPEECH_QUEUE_SIZE;

if (!SpecialHandler(squeue.s[i])) {
String buf;
String new_Copy = buf;

qStopSpeechChannel();
int cur_sound = GetSoundNumber(buf);
squeue.channel   = GetFreeChannel();
if (cur_sound>0) PlaySoundEx(cur_sound, squeue.channel);

squeue.ol = squeue.c[i].SayBackground(buf);
if ((squeue.stol!=null) && squeue.stol.Valid) squeue.stol.Remove();
if (squeue.st[i]!=null && squeue.st[i]!="") {
squeue.stol = Overlay.CreateTextual((system.viewport_width-GetTextWidth(squeue.st[i], squeue.subtitle_font))/2, squeue.subtitle_y, 320, squeue.subtitle_font, squeue.subtitle_col, squeue.st[i]);
}

squeue.stop_talk = 1;

if (squeue.prev_char != null) squeue.prev_char.UnlockView();
squeue.prev_char = squeue.c[i];

if (squeue.c[i].Animating==0 && squeue.noanim[i]==0) {
            int view = squeue.c[i].SpeechView;
            if (view < 1) { Display("error: Talk view isn't assigned!"); QuitGame(0); }
            int loop =squeue.c[i].Loop;
            int delay=squeue.c[i].AnimationSpeed;
            squeue.c[i].LockView(view);
            squeue.c[i].Animate(loop, delay, eRepeat, eNoBlock, eForwards);
}

}
        squeue.noanim[i] = 0;
        squeue.say++;
      } else if (squeue.stop_talk) { //finish talk animation
if ((squeue.stol!=null) && squeue.stol.Valid) squeue.stol.Remove();
         if (squeue.prev_char != null) { squeue.prev_char.UnlockView(); squeue.prev_char = null; }
         qStopSpeechChannel();
         squeue.stop_talk = 0;
      }
   } // end of if (qIsTalking()==0)
}

String str_delay;
String qDelay(int time){

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

   String format;
   String.Format(format, "%%%dc", n);
   String.Format(str_delay, format, ' ');
   squeue.noanim[squeue.insert % SPEECH_QUEUE_SIZE] = 1;
   return str_delay;
}

String   qSetGlobalInt(int globalint, int value) {
  String.Format(str_delay, "set-globalint %d %d", globalint, value);
  squeue.noanim[squeue.insert % SPEECH_QUEUE_SIZE] = 1;
  return str_delay;
}
 
String   qAddScore(int points) {
  String.Format(str_delay, "add-score %d", points);
  squeue.noanim[squeue.insert % SPEECH_QUEUE_SIZE] = 1;
  return str_delay;
}

function qSkipCurrentMessage() {
   if (qIsTalking()) {
squeue.ol.Remove(); // IsTalking already checks if null/valid
if ((squeue.stol!=null) && squeue.stol.Valid) squeue.stol.Remove();
}
}



//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==eEventLeaveRoom) qStopSpeech();

}


function qSetSkippable(int skippable) {
  squeue.skippable=skippable;
}

// optional
// on_key_press:
function on_key_press (int keycode) {
  if (squeue.skippable && (game.skip_speech_specific_key>0) && (keycode==game.skip_speech_specific_key)) qSkipCurrentMessage();
}


I had to update all the String code, but it crashes on a RepExec function error on line 40 under the GetSoundNumber function on SSH's code. The problem is due to a null pointer referenced.

EDIT: I fixed the null errors by changing repeatedly_execute to RepExec but now background dialog never happens.
Title: Re: Implementing a lengthy background dialog - redux
Post by: Khris on Fri 14/12/2012 12:50:38
If you change "repeatedly_execute" to "RepExec" in a module script, the function will simply not be called anymore.
AGS is specifically looking for "repeatedly_execute", how would it know that now some other function is supposed to be called every loop?

The error is in line 132, where String buf isn't initialized to the next line of text in queue but null (which produces the null pointer error).
If should probably be
Code (ags) Select
  String buf = squeue.s[i];
Title: Re: Implementing a lengthy background dialog - redux
Post by: Secret Fawful on Fri 14/12/2012 13:26:26
It works now. I had a couple other errors, but I managed to fix them. I should have asked SSH directly about these, but I'm tired and impatient. Anyway, thanks for the help. I'm doing things that I'm out of my league on here, but I don't know any other way to learn.
Title: Re: Implementing a lengthy background dialog - redux
Post by: Snarky on Fri 14/12/2012 14:50:17
No shame in being in over your head, it's the best way to learn. I'm surprised there'd be bugs in SSH's module, though, since it's been used in many games; I'm using it myself and don't remember running into this issue. Sorry about that.