I think short summarized it best. You are doing too much in Lua.
You're instantiating windows, entities, and players from within your script? What exactly is your engine doing? Just rendering and checking collision? Are those constructors even registering the objects to C/++, so that they aren't existing solely in Lua?
There is one sentence that should always drive your design decisions with Lua:
Keep as much logic as possible in statically compiled C/++, and use Lua simply to "extend" the engine.
The act of "polling" through Lua is pretty expensive. A single function call is easily 10x slower (probably more) than invoking the same function from C/++. With that being said, "triggers" are the optimal way to handle scripts. You are keeping the frame-by-frame checks away from Lua, and are simply using the script to extend the engine by reacting to preset events. Your Lua scripts should be extending a gigantic, rich codebase that is your engine. Your engine should be handling all generic game logic, not serving as a glorified wrapper between the API and your scripts (as you're doing with the keypress trigger).
Your main.lua should honestly not exist. That is all things that should be kept inside your engine. You're instantiating things and even initializing the windows themselves? It's honestly serving no purpose, other than slowing things down that should be happening within your engine. A function that is called whenever a keypress occurs is not being abstract enough for Lua. Lua isn't supposed to handle every little event like that. If I push a button to move the character, pop the menu, pause the game, advance a dialog, or do almost ANYTHING like that, it really has nothing to do with Lua and is only relevant to the engine itself.
Your NPC script is actually much, much better. Instead of performing things that your engine should handle, it is actually "extending" the engine by performing game-specific logic (custom dialog actions).
But your question still reveals that Lua is doing far, far too much in your engine:
LeonBlade wrote:What I want to do is have a single function for displaying messages, like window:showMessage("Greetings, Hero!") and then have a way to wait until ENTER or something is pressed before continuing on with the rest of the function.
For example:
Waiting for the user to push a key before advancing to the next screen is something that every conversation dialog box for every NPC in every scenario is going to be doing. That is functionality that should be provided to your script by your engine. Making each individual Lua script now essentially implement the core functionality of a dialog box is basically revealing that your engine is not much of an engine at all.
The Dialog box or Dialog "System" handling all generic dialog logic (such as proceeding to the next screen), should be handled fully by your engine, not Lua. The only parts of a conversation that are not completely generic are 1) the text to be displayed and 2) any special reactions to the text ("Here, take this item!").
Your structure is completely wrong for this kind of system. The type of script you are going to want is something more along the lines of:
npc.lua
Code: Select all
function InitTrigger
npc = getNPCFromEngine();
screen1 = npc.AddDialogScreen();
screen1.AddLine("Hello, welcome to my house.");
screen2.AddLine("I notice you didn't bother knocking before entering...");
screen2 = npc.AddDialogScreen();
screen2.AddLine("I really outta kick your ass for that...");
screen2.AddLine("But you know what, you have balls. I like that.");
screen3 = npc.AddDialogScreen();
screen3.AddLine("Take this, audacious one.");
screen3.AddLine("It will help you on your journey");
end function
function AdvanceScreenTrigger(screenNumber)
if screenNumber == 3 then
player.GiveItem("whateverTheFuck");
endif
end
Take note of a few things:
1) I am not
instantiating anything in Lua. I am not creating a new NPC upon initialization. Rather, I am accessing and manipulating entities that
already exist within my engine.
2) I am not dynamically creating a conversation as I go. Rather, the conversation is constructed ONCE upon initialization of the entity. Since the engine has access to my NPC (it exists in the engine), its ConversationSystem is able to check for initial collision with the NPC, check if I pushed A to talk, load the conversation if I did, begin displaying the text, advance to the next screen when the user pushes a button--all generic conversation logic.
The only time anything special happens in a conversation is when you reach a particular screen. That's when the engine's ConversationSystem invokes the lua script's "AdvanceScreenTrigger" and passes the screen number.
With this approach we've effectively abstracted away all generic conversation logic from Lua which ultimately makes our Lua scripts smaller, more abstract, and more lightweight and our engine more powerful and faster, because Lua is doing less shit.
LeonBlade wrote:The actual talk function is in Lua, I just meant how you would call the actual function.
While my above response resolved this issue, I'm concerned with the fact that you even have to ask this to begin with (for the sake of your Lua/Engine structure). A Lua function should be invoked just like any other engine function. It's a nonblocking call that should execute then immediately return control to your engine.
LeonBlade wrote:For example, would Lua check each loop if you're talking to an NPC or would I do that in C++?
God no! Except for very, very specific occasions where the custom logic is essentially completely unrelated to the engine, a Lua script should NEVER be doing any frame-to-frame polling. Most engines shouldn't even allow that kind of behavior. The better alternative is to ALWAYS abstract some sort of event away from Lua, have the engine check for it, then invoke the Lua script once it has occurred.
LeonBlade wrote:I know that the Elysian Shadows engine has a good message system in it; the kind I'm talking about that all RPGs have. I guess I'll just look up some Engines later and look at their source and see how to do it.
Another thing I'd like to mention relating to this matter... There are two balances that you have to strike:
1) The balance between Lua and the Engine
2) the balance between Lua and the Level Editor
While it is completely possible to dynamically pull entities out of your ass, construct their collision geometry, create dialogs at runtime, and manipulate them as I did in the above script, what's the point? You're essentially creating creative content for your game through a series of script text files... That's both inefficient and ugly. In ES, our non-dynamic NPCs and their conversations are loaded with the level as part of the data created by the level editor.
This gives us the ability to create a "conversation editor" in our Toolkit (where we can create our own custom branches and stuff) and leaves even LESS tedious bullshit for a Lua script to be responsible for. A conversation is custom CONTENT. CONTENT is created with a Level Editor. The act of giving the player a special item during a conversation is custom LOGIC--and that, my friend, is the ONLY thing Lua should ever be doing. So in my previous example, you should essentially have only that one function trigger with that single if statement as Lua's contribution to the solution to your entire question...
We plan to go into quite a bit of detail on both our Lua implementation and at least parts of our dialog system in the AiGD18. </shamelessSelfPromotion>