Original Post
Hey folks! I'm just delving into working on my RPG in earnest, but I find myself paralyzed by a design issue. I'm using Lua coroutines for my NPCs, as well as the introductory cutscene in each chapter of my game's plot. Any of those coroutines can do things like play sounds and music, make an NPC move, or direct text to the dialogue controller. For the purpose of this problem, I'll focus on the latter case. The dialogue controller gets updated in each frame with the amount of time that's passed, and adds text to the dialogue box accordingly. This gives the game that old school type of dialogue box, where text appears letter by letter at a consistent pace instead of all at once. Let's say right now that I have an NPC script that's just a series of these text instructions, like so: ----- say('Hello!') say('How are you doing?') playSound('fanfare.wav') ----- "say" merely enqueues a string in the dialogue controller and then returns. This script will run to completion with no yields. It enqueues both lines of text in the controller. Immediately afterwards, a sound will play. The sound does not necessarily play after the text is on screen, but will most likely play while the text is slowly appearing on the screen. This kind of concurrent action might be desired in some cases. But now let's say I'd like an NPC to *wait* for certain time-consuming operations to complete. For example, if I wanted the NPC to wait until some speech is on screen, I would want to implement a say-then-yield(string) function that yields the coroutine until the string is entirely on screen (which will likely take multiple iterations through the game loop). It's the engine-side implementation of "say-then-yield" that stumps me. I can't think of an implementation that wouldn't result in an awkward design on the engine side. Do I pass the dialogue controller the information necessary to resume a coroutine when it is finished (i.e. an ID or structure pointing to the particular lua_State), and rely on it to resume the script when the text appears, like a signal/block construct? If so, then won't I have to implement that kind of functionality in *every possible component* that the NPC script may ever possibly wait for? Playing sounds, fading music, moving from point A to point B... it seems like a lot of repeated code to put into every component that the script would interact with, and I see no simple way to abstract it out across components for easy extension or maintenance. I currently see no other way to implement this, unfortunately. Ultimately, I might be stressing out over nothing; sometimes there are situations where no design decision can be perfect. But before I go on with a design decision that I already have a bad feeling about, I'd like to know if there are smarter ways to implement this kind of functionality. :) If anyone has any suggestions on how to nicely implement coroutine waiting and resuming in an engine, I would really appreciate any insights.