Non-blocking Sequences

Started by Samwise, Thu 22/03/2007 09:34:32

Previous topic - Next topic

Samwise

Hello,

In my game I have several sequences which in the player can do something only if a certain NPC is doing another (non-blocking) thing:

- During a conversation with a merchant ("talk to character" script), the merchant is talking, laughs for several seconds and then continues talking.Ã,  ONLY while he's laughing, you can steal something from his stand.

- When the player says something to a clerk, she leaves the screen for a minute, and then returns.Ã,  The player can mess with her stuff only in the minute she's outside.

- When the player says something to a bank manager, the manager calls a teller and they have a background conversation (animation only, no voice or text) for several seconds.Ã,  Only in these seconds, the player can use the teller's computer.Ã,  Afterwards she returns to her seat.

- When the player gives some guy a dress, the guy is going to a closet to put the dress back there.Ã,  Only while the guy is standing in front of the closet (and his "putting the dress back" animation is running), the player can push him and lock him inside.

- When the player shows an inventory item to a guard, the guard walks away for several second, and only while he's walking or out of the screen, the character can ran away.

I figured out that most of these situations can be handled with timers and repeatly_execute scripts, but I didn't find any clear explanation for my problems or the repeatly_execute thread in the manual or in the forums.Ã,  I simply don't understand how is it possible to start one script ("talk to character" for instance), make this script continue in a non-blocking way (the NPC go away and only then the player can do something he refused to do before), and then returning to the original, blocking script (the NPC returns to his place and says something).

Help, please?

Jonathan



Ashen

Like it says on the front page of the BFAQ:
Quote
Please keep the following in mind. Most of our problems can be easily solved using variables. Need to change a hotspot's state in another room? Use variables! Need an object to be used only once? Use variables!

So to apply it to some of your examples:

Quote
- During a conversation with a merchant ("talk to character" script), the merchant is talking, laughs for several seconds and then continues talking.  ONLY while he's laughing, you can steal something from his stand.

The first part (up to the laughing) would go in the 'Talk to Character' script. Then it'd start an animation (the laughing) with eNoBlock and eRepeat parameters, and a Timer for however long you want them to laugh. In the Room's rep_ex function, you'd check if the Timer is expired, and stop the animation and run the rest of the code.
Meanwhile, the thing that you can steal would need to check in the Merchant is laughing. In this case, you can do that without having to add any more variables?:
Code: ags

if (cMerch.Animating == true && cMerch.View == LAUGHVIEW) {
  //Steal thing
}
else { // Merchant isn't laughing
  cMerch.Say("Hey! Keep your hands to yourself.");
}


The same basic thing(check Character.Animating and Character.View should also work for:
Quote
- When the player says something to a bank manager, the manager calls a teller and they have a background conversation (animation only, no voice or text) for several seconds.  Only in these seconds, the player can use the teller's computer.  Afterwards she returns to her seat.

- When the player gives some guy a dress, the guy is going to a closet to put the dress back there.  Only while the guy is standing in front of the closet (and his "putting the dress back" animation is running), the player can push him and lock him inside.

Quote
- When the player says something to a clerk, she leaves the screen for a minute, and then returns.  The player can mess with her stuff only in the minute she's outside.

...

- When the player shows an inventory item to a guard, the guard walks away for several second, and only while he's walking or out of the screen, the character can ran away.

IsTimerExpired to bring them back again, and you can check if (cNpc.Room == player.Room) to see if they're around. For the guard, you can also check his Loop and/or Moving, to see if he's walking away.

Other situations might mean you have to make your own variables to check, but hat's not that hard (and it's in the BFAQ).
I know what you're thinking ... Don't think that.

Samwise

Hey Ashen, many thanks.

Yes, I'm cool with all the whole variables thing, I'm actually kinda loving it, but I'm still pretty mixed-up:

First and foremost, I don't see how to do what you suggested in the Bank or the Dress examples, in which I think timer shouldn't be used.  The NPC needs to do a whole set of actions that aren't blocking the player from doing other things in the same time, not based on a specific timer but on the length of the list of actions defined.

I take the Bank example to make myself clear...  During the player's conversation with the manager, the manager calls the teller.  From this time, the player should be free to roam around and do stuff, while the following actions are taking place in the background: the teller is walking to the manager and facing him, the manager runs a talking animation (without voice or text), the teller runs a talking animation, the manager runs a talking animation again, and the teller runs a talking animation again.  Then, the player shouldn't be able to move or do further things, while the teller walks back to her place.  When she's back in her place, the player is free again to go around and do stuff.

Could you please show me how to do this using variables, and where should I put every part of the script?

Regarding to the example you've given me:

1. Where do I place the timer?  In the original "talk to character" script's end?

2. Since the merchant is a character, do I really need to place the laughing part in the ROOM rep_ex script (since the characters appear in the GLOBAL script)?  Or is it not the case?

3. Where do I place the last part of the script (when the merchant's talking after he already laughed)? 

Thanks a lot, I deeply appreciate you helping my game to come true  :)

Jonathan


Ashen

Quote
I don't see how to do what you suggested in the Bank or the Dress examples, in which I think timer shouldn't be used.  The NPC needs to do a whole set of actions that aren't blocking the player from doing other things in the same time, not based on a specific timer but on the length of the list of actions defined.

...

During the player's conversation with the manager, the manager calls the teller.  From this time, the player should be free to roam around and do stuff, while the following actions are taking place in the background: the teller is walking to the manager and facing him, the manager runs a talking animation (without voice or text), the teller runs a talking animation, the manager runs a talking animation again, and the teller runs a talking animation again.

OK, I was thinking of the conversation animation as being 'Start them both off and leave them to it', not animating back and forth. (Actually, you could probably make it one animation each, with a bit of work syncing them up and use of frame delays, but that's another issue entirely...) In this case, you probably WILL need to make a new int variable, and make it Global (export/import as explained in the BFAQ):

Character script for Teller:
Code: ags

// Whatever you currently have
// Teller walks to Manager
SetTimer(1, 120); // or whatever timer and delay you're using
BankConvo = 1; // This is your new variable


Room rep_ex:
Code: ags

if (BankConvo != 0 && cTeller.Moving == false) { // Convo has started, and Teller has reached manager
  if (BankConvo == 1 && cManager.Animating == false && cTeller.Animating == false) {
    cTeller.Animate(Loop, Delay, eOnce, eNoBlock);
    BankConvo = 2;
  }
  if (BankConvo == 2 && cTeller.Animating == false && cManager.Animating == false) {
    cManager.Animate(Loop, Delay, eOnce, eNoBlock);
    BankConvo = 1;
  }
}

If (IsTimerExpired(1)) {
  BankConvo = 0;
   // cTeller.Walk back to original location, with Blocking parameter
}


For the Dress example, again, I was assuming the NPCs actions were much simpler than they apparently are - go to the closet, animate for a certain interval, then come back. A Timer seemed the simplest way (no need to make new variables), but you can use variables to track the various stages if that make more sense to you:
Code: ags

//Character script
  // Start character moving towards closet, non-blocking
  ClosetGuy = 1; // New, Global int

// Room rep_ex
  if (ClosetGuy == 1 && cGuy.Moving == false && cGuy.Animating == false) {
    // Guy has arrived at the closet, but hasn't put the dress back.
    // Run the 'putting the dress back' anim, eOnce, eNoBlock
    ClosetGuy = 2;
  }
  if(ClosetGuy == 2 && cGuy.Moving == false && cGuy.Animating == false) {
    // Dress in back in closet, Guy can go back to whatever he was doing
    ClosetGuy = 0;
  }


For the 'pushing into the closet' interaction, you can either check the ClosetGuy variable (if (ClosetGuy == 2) {...), or directly check the character's View/Loop. And don't forget to reset ClosetGuy when he's in, so it doesn't try to animate him or have him walking around in there.


Quote
Regarding to the example you've given me:

1. Where do I place the timer?  In the original "talk to character" script's end?
Yes.

Quote
2. Since the merchant is a character, do I really need to place the laughing part in the ROOM rep_ex script (since the characters appear in the GLOBAL script)?  Or is it not the case?

3. Where do I place the last part of the script (when the merchant's talking after he already laughed)? 
There is no 'laughing part' as such, since it's a repeatedly run animation called from the Character interaction, just everything pre-laugh and post-laugh. The pre-laugh goes in the Character script, in Global.
You can put the post-laughing part in the Global rep_ex , but snce it should only happen in one room, the Room rep_ex makes more sense. For one thing, you can reuse the Timer in other rooms, without worrying about weird things happening when you try to manipulate a character from another room. (This is also the answer to Q3 - 'Room rep_ex'.)

I was trying to avoid introducing new variables to keep it simple, but here's another suggestion you might actually find easier. Make a new int variable, in the Global script (doesn't have to be exported/imported).

Then make the 'Talk to Character' interaction like this:
Code: ags

if (Laughed == 0) { // New variable
  // pre-laugh stuff
  cMerch.LockView(LAUGHVIEW);
  cMerch.Animate(0,3,eRepeat,eNoBlock);
    //Set View, Loop and Delay parameters as needed, but keep eRepeat and eNoBlock
  SetTimer (2, 80); // Or whatever timer and delay
  Laughed = 1;
}
else if (Laughed == 1) {
  cMerch.UnlockView();
  // post-laugh stuff
  Laughed = 0; // reset
}


Then, in the Room rep_ex:
Code: ags

if (IsTimerExpired(2)) cMerch.RunInteraction(eModeTalkto);
// Will trigger post-laugh part


Again, this could go in the Global rep_ex, but Room makes more sense IMO.
I know what you're thinking ... Don't think that.

Samwise

Ok, I think I got it...

Pretty heavy stuff (at least for a newbie like me).   I'll try to work it out in the near future.

Thanks again for your patience   :)

SMF spam blocked by CleanTalk