Here is my implementation of StartDialogue() (from the GK3 level viewer
source, which happens to be C#, but the same idea should work for C/C++):
private static void sheep_StartDialogue(IntPtr vm){ int numLines = SheepMachine.PopIntOffStack(vm); string licensePlate = SheepMachine.PopStringOffStack(vm); bool waiting = SheepMachine.IsInWaitSection(vm); WaitHandle handle = Game.DialogManager.PlayDialogue(licensePlate, numLines, waiting); if (waiting && handle != null) SheepMachine.AddWaitHandle(vm, SheepMachine.GetCurrentContext(vm), handle);}
The call to DialogManager.PlayDialogue() begins playing the dialog. It returns a "WaitHandle" which is just a class that has a Finished boolean property that returns whether whatever the WaitHandle is waiting on has finished (in this case the playing dialog). Then if the script is inside a wait section it associates the wait handle and the current context together and keeps track of it.
Just so the code doesn't confused anyone, SheepMachine.AddWaitHandle() is just a method in my Sheep VM wrapper class that doesn't actually interact with the Sheep VM library. All the other SheepMachine methods here, like PopStringOffStack(), GetCurrentContext(), etc are just wrapped versions of SHP_*() functions.
Anyway, the next bit of code is my "end wait callback" that gets called whenever the script tries to leave a wait section:
private static void endWaitCallback(IntPtr vm, IntPtr context){ if (_waitHandles.ContainsKey(context)) { foreach (var wait in _waitHandles[context]) { if (wait.Finished == false) { // still waiting on stuff to finish, so suspend the VM SHP_Suspend(vm); return; } } // everything is done! _waitHandles[context].Clear(); }}
At this point all that's left is to periodically check all the WaitHandles to see if any are done (I do it once per frame), and if any suspended context has no more unfinished WaitHandles then resume that context with SHP_Resume().
The Sheep library just provides the low-level waiting stuff, like SHP_Suspend(), SHP_Resume(), etc. The code you see here is my high-level implementation of waiting on top of the Sheep library inside the GK3 level viewer. It doesn't necessarily have to work this way. But there are no threads or spinlocks or anything like that.