Home

  • Dialog puzzles in KIAVC

    Dialog puzzles in KIAVC

    One of the things that makes adventure games so fun are the conversations you can make with the other characters. While it wasn’t always like that, it’s pretty much a standard in adventure games today (and not only those, nowadays) to have branching conversations where you can choose the next thing to say and see where that leads you: sometimes it will allow you to unravel some plot points, other times they’ll be used for comedic effects, and sometimes they’re even used as puzzles to progress the game themselves.

    These branching conversations are usually called “dialog puzzles”, and if you want to learn more about the history behind them, I definitely encourage you to read this excellent entry in the Thimbleweed Park development blog, where Ron Gilbert talks about how the functionality was born, how it worked in SCUMM, and how he implemented them in his DINKY engine for Thimbleweed Park (which I assume is how they work in Return to Monkey Island too!).

    Obviously, I was very curious about how they could be implemented in KIAVC too, especially considering that they’re supposed to be a very dynamic feature, that can work quite differently in different games (or even different parts of the same game), which meant hardcoding them in the code really didn’t sound like a viable approach. As such, I first of all tried to look around to see how others did it.

    Learning from the best!

    My first stop was, as anticipated, Ron’s post I linked above, which was already quite enlightning. He explains how dialog puzzles were hand coded in SCUMM, which was a painful process, and how we was quite struck by the simpler approach other developers took instead, where they resorted to some custom scripting format for designing them instead. The post then proceeds to describe a simple scripting mechanism Ron came out with, which helped me understand a few key points:

    • a dialog puzzle can be seen as a set of blocks you transition from and to, depending on the choices of the player;
    • each block can contain multiple options, where not all of them may be visible right away, and some may actually be conditioned to some state in the game itself;
    • access to the game state should be possible when dealing with a dialog puzzle, since a selection can either depend on the current state of a variable, or trigger a change in a game state variable instead;
    • a dialog puzzle can involve things happening that go beyond the simple process of choosing a sentence to say (e.g., you choose something, and the NPC does more than just say something back, or something else moves in the room);
    • there should be an entry point for dialog puzzles, and a way to leave.

    This is not an exhaustive list of the implications of dialog puzzles, but they still strenghtened the perception of a very dynamic feature that would be hard to constrain to code alone: this is especially important when you think that dialog puzzles may be often rewritten or moved around, which means there should be relatively easy ways to script them, and possibly update them too without having to rewrite the game itself.

    Ron’s approach seemed quite powerful, but it relied on a custom scripting mechanism he came up with himself, which seemed a bit overkill for my much more modest needs. Searching around, I found more information yet again from the as usual incredibly informative folks of the Groebelshoot blog, who wrote not one, but THREE blog posts on the matter! (you can find the posts here, here and here respectively). I won’t go too much in detail on their content (I encourage you to read the post yourself if you’re curious about their choices), but in a nutshell they describe how they initially experimented with JSON as a format to represent dialog puzzles, and then landed on YAML instead which they found more flexible and suited to their needs. They also explain a clever approach they came up with to map properties in YAML to game state conditions and triggers, and how the syntax they invented could also be used to trigger some actions in response to choices. Again, the examples they presented confirmed that if I wanted to implement something similar, I would need to make sure it was flexible enough to handle dynamic scenarios, and allow for more than just selections to happen.

    A dialog puzzle engine

    After studying how others did it, I tried to investigate how I could implement it instead, and eventually came up with a slightly different approach. In fact, while the YAML approach seemed quite clever, I must admit I’m not that familiar with the format, and so it looked like it could be a bit too much of an effort for me to get it to work properly with my Lua scripting engine; besides, it would probably also have made the process of scripting dialog puzzles in the first place more awkward and complicated for me, again due to my little familiarity with it.

    As such, I chose what looked like a simpler approach, which led me to develop a generic engine for dialog puzzles in Lua (which you can find in the dialog.lua file in the repo), that could then be fed with Lua objects describing the actual dialog puzzle to run. While this may look like I ended up hardcoding them in the engine, that’s not really like it, since in this context I’m only using Lua objects to represent the dialog puzzles to implement. As such, they’re not code at all, but mostly an easier to manage and integrate alternative to JSON or the above mentioned yaml, and can be written completely out of context of the code itself. Since we only use it as a format, you just need to use some conventions when you create these objects, and when you pass them to the dialog puzzle engine, they’ll proceed on “autopilot” for you (meaning the Lua utilities and the KIAVC engine will do all the heavy lifting for you).

    I made a few assumptions for the format of these Lua objects, and namely:

    • a settings property provides information for the KIAVC engine itself, namely the unique ID of the dialog, where the dialog options should appear, the appearance of the text, etc.;
    • all other properties of the object are dialog blocks, which can contain either a list of options to present (to allow the player to pick a sentence to say) or a list of steps to perform (e.g., as a consequence of an option being selected by the user);
    • options and steps can contain additional attributes acting as conditions, triggers to change game state variables, or other actions (e.g., an actor doing or saying something, closing the dialog, etc.);
    • options always contain a pointer to the next block they should move to when selected, and steps can have one too;
    • when dealing with options, the engine always only shows up to 4 at the same time (currently hardcoded, this may be changed in the future), meaning that the dialog puzzle engine will decide looking at the existing conditions which ones should be presented to the player;
    • steps are always performed in sequence instead (unless interrupted by a move to a different block).

    Considering the block-based nature of the object, it was of course important to ensure there would always be an entry point, which is always represented by a main property. This means that, at the very least, such a Lua object should always contain a settings property (with instructions for KIAVC) and a main property (to provide an entry point for the dialog puzzle), while other blocks are optional, depending on the dialog to implement. Notice that this main property can either be a list of options, or a list of steps: the engine doesn’t really care how you decide to start a dialog. What’s important is that transitions are correctly described in the object, so that the engine knows what to do next any time something happens in the dialog, and never ends in a place it can’t get out of because of a missing transition.

    Let’s have a look at how the dialog puzzle in the basic demo we have in the repo is implemented for instance: you can find it in the npc.lua script of the demo, since it implements a conversation you can have with the only NPC there. I’ll just copy the whole code here, and then we’ll go through it block by block to understand how it works:

    local npcDialog = {
    	-- All dialogue objects need settings: this is the object that is
    	-- automatically passed to the startDialog() core engine function
    	settings = { id = 'test', font = 'dialogues',
    		color = lightgrey, selected = yellow,
    		outline = black, selectedOutline = black,
    		background = { r = 0, g = 0, b = 0, a = 96 }, autohide = true,
    		area = { x1 = 0, y1 = 144, x2 = 320, y2 = 180 } },
    	-- All dialogue objects also need a "main" block, which acts as an
    	-- entry point for the dialogue: it could present some options that
    	-- the user can pick (as the example below) or just some steps that
    	-- introduce the conversation before actually making it interactive
    	main = {
    		options = {
    			{ name = "1", text = "npcTalk1", once = true, next = "greeting" },
    			{ name = "2", text = "npcTalk2", notif = { npcAskedWhat = true }, next = "looking" },
    			{ name = "3", text = "npcTalk3", notif = { npcAskedWhere1 = true }, next = "clone" },
    			{ name = "3b", text = "npcTalk3b", onlyif = { npcAskedWhere1 = true }, notif = { npcAskedWhere2 = true }, next = "clone" },
    			{ name = "3c", text = "npcTalk3c", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true }, next = "clone" },
    			{ name = "4", text = "npcTalk4", next = "done" }
    		}
    	},
    	-- All the blocks below are possible iterations in the dialog: any
    	-- of them contain either a series of steps (actors doing or saying
    	-- something), or options that can be presented to the user. Adding
    	-- a "next" property to one of the items moves to the selected block
    	greeting = {
    		steps = {
    			{ actor = "npc", say = "npcResp1_1" },
    			{ actor = "npc", say = "npcResp1_2" },
    			{ actor = "npc", say = "npcResp1_3", state = { npcSaidHi = true }, next = "main" }
    		}
    	},
    	looking = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_1" },
    			{ actor = "npc", say = "npcResp2_2" },
    			{ say = "npcTalk2_mh", next = "list" }
    		}
    	},
    	list = {
    		options = {
    			{ name = "1", text = "npcTalk2_list1", once = true, next = "no" },
    			{ name = "2", text = "npcTalk2_list2", once = true, next = "no" },
    			{ name = "3", text = "npcTalk2_list3", once = true, next = "no" },
    			{ name = "4", text = "npcTalk2_list4", once = true, next = "no" },
    			{ name = "5", text = "npcTalk2_list5", once = true, next = "nohat" },
    			{ name = "6", text = "npcTalk2_list6", once = true, next = "noglasses" },
    			{ name = "7", text = "npcTalk2_list7", once = true, next = "no" },
    			{ name = "8", text = "npcTalk2_list8", once = true, next = "no" },
    			{ name = "9", text = "npcTalk2_list9", once = true, next = "no" },
    			{ name = "10", text = "npcTalk2_list10", once = true, next = "no" },
    			{ name = "11", text = "npcTalk2_list11", once = true, next = "no" },
    			{ name = "12", text = "npcTalk2_list12", once = true, next = "no" },
    			{ name = "13", text = "npcTalk2_list13", once = true, next = "no" },
    			{ name = "14", text = "npcTalk2_list14", once = true, next = "no" },
    			{ name = "15", text = "npcTalk2_list15", state = { npcAskedWhat = true }, next = "main" },
    		}
    	},
    	no = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_no", next = "list" },
    		}
    	},
    	nohat = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_nohat1" },
    			{ actor = "npc", look = "down", sleep = 500 },
    			{ actor = "npc", look = "left", say = "npcResp2_nohat2" },
    			{ actor = "npc", sleep = 1000 },
    			{ actor = "npc", say = "npcResp2_nohat3", next = "list" },
    		}
    	},
    	noglasses = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_noglasses", next = "list" },
    		}
    	},
    	clone = {
    		-- This is an example of conditional responses: we have three separate
    		-- possible paths we can go through, depending on some state variables.
    		-- It's probably easier to just use different blocks, but in some cases
    		-- such an approach may be useful to prevent "block overload".
    		steps = {
    			-- Path #1
    			{ actor = "npc", say = "npcResp3_1", notif = { npcAskedWhere1 = true }, state = { npcAskedWhere1 = true }, next = "main" },
    			-- Path #2
    			{ actor = "npc", say = "npcResp3b_1", onlyif = { npcAskedWhere1 = true }, notif = { npcAskedWhere2 = true } },
    			{ actor = "npc", say = "npcResp3b_2", onlyif = { npcAskedWhere1 = true },
    				notif = { npcAskedWhere2 = true }, state = { npcAskedWhere2 = true }, next = "main" },
    			-- Path #3
    			{ actor = "npc", say = "npcResp3c_1", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ actor = "npc", say = "npcResp3c_2", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ say = "npcResp3c_3", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ actor = "npc", say = "npcSearch3", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true },
    				state = { npcAskedWhere3 = true }, next = "main" }
    		}
    	},
    	-- This is our way out, since there's an "exitDialog = true" item
    	done = {
    		steps = {
    			{ actor = "npc", say = "npcResp4_1" },
    			{ load = "rooms['street']:startScript('searching', npcSearchingScript)", exitDialog = true }
    		}
    	}
    }
    

    This is an object we only need to define once for this conversation with the NPC, and then we can use it as many times as we want. More precisely, using the dialog engine tool in the Lua utilities, we just need to pass it as an argument to the enterDialog() function and it will be executed. In the demo, this is triggered when we left-click the NPC, which is mapped to the talkTo verb:

    	verbs = {
    		talkTo = function(self)
    			if state.npcSaidHi and state.npcAskedWhat and state.npcAskedWhere then
    				activeActor:look('down')
    				activeActor:say(text('npcTalk'))
    			else
    				rooms['street']:stopScript('searching')
    				startScript(startNpcDialog)
    				self:look('left')
    			end
    		end,
    

    where startNpcDialog is simply a function that wraps the enterDialog() call to invoke it asynchronously as a coroutine:

    function startNpcDialog()
    	enterDialog(npcDialog)
    end
    

    As you can see looking at the npcDialog object itself, we have the two mandatory blocks (settings and main), plus a few other blocks that define different contexts we may find ourselves in (greeting, looking, list, no, nohat, noglasses, clone, done). The names of those blocks are entirely up to you: in the demo I just chose some that matched the context, in order to make it easy for me to know where to transition to when needed. A quite ugly (and hopefully not too confusing) diagram of the boxes and their transitions is presented below:

    Let’s start from the settings property:

    	settings = { id = 'test', font = 'dialogues',
    		color = lightgrey, selected = yellow,
    		outline = black, selectedOutline = black,
    		background = { r = 0, g = 0, b = 0, a = 96 }, autohide = true,
    		area = { x1 = 0, y1 = 144, x2 = 320, y2 = 180 } },
    

    As anticipated, this is the property we fill to instruct the KIAVC engine itself how we want the dialog puzzle block (that is, the place where dialog options will appear) should look like. That property will be automatically passed as is to the startDialog() function, which is implemented by the KIAVC engine itself (please refer to the previous blog post on Lua scripting in KIAVC to understand what that means and how that works). In this case, we’re saying the id of this dialog is test (which is what we’ll also use to be notified about which option has been selected), that we want the dialog to appear at the bottom of the screen with a semitransparent background, that we’ll use a specific font and want the color of options to be a light grey, but selected text to appear yellow (and both with a black outline), and finally that we want the dialog box to automatically disappear after an option has been selected even when the dialog is still running (meaning it hasn’t been declared concluded). The end result is that when we actually display options for the player to choose, they’ll appear like this:

    So far, so good, but how do we actually choose what to display, and what to do when things are selected? This is where the other blocks come into play, starting from our main entry point:

    	main = {
    		options = {
    			{ name = "1", text = "npcTalk1", once = true, next = "greeting" },
    			{ name = "2", text = "npcTalk2", notif = { npcAskedWhat = true }, next = "looking" },
    			{ name = "3", text = "npcTalk3", notif = { npcAskedWhere1 = true }, next = "clone" },
    			{ name = "3b", text = "npcTalk3b", onlyif = { npcAskedWhere1 = true }, notif = { npcAskedWhere2 = true }, next = "clone" },
    			{ name = "3c", text = "npcTalk3c", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true }, next = "clone" },
    			{ name = "4", text = "npcTalk4", next = "done" }
    		}
    	},
    

    As you can see, our entry point is a collection of options, meaning that, as soon as the dialog is started, the first thing we do is show the players some options they can choose from. If you look at the available options, you’ll notice that each of them has a name, a text, and a next property: name is a unique ID (unique to the block) that can be used to reference that specific option, most importantly when the engine tells us which one has been selected; text instead dictates what text should be rendered for the option itself (which here you see as a named reference to a text line, rather than the text itself; more on this in a future post on localization!); next tells us which block we should move to if that option has been selected by the player. About the text property, notice you can also add a say property, to specify what the actor should say after an option has been selected: in my example it’s omitted, which means that the value of text will be used instead (say is helpful when the two should actually be different for any reason).

    Besides these fundamental properties, though, some option also have a few others, that actually dictate whether or not the option should be prompted to the player. In fact, as we anticipated, only 4 options will be displayed at a time, which means the dialog engine will go through the available options and pick the first 4 that are marked as “enabled”. The once property set to true, for instance, means that if the player selects it once, it shouldn’t be displayed anymore. The notif and onlyif properties, instead, are conditional checks on game state properties: the fact that option “3” has a notif = { npcAskedWhere1 = true } property, for instance, means that if the npcAskedWhere1 is set to true, this option should be ignored; likewise, options “3b” and “3c” have more complex conditions that check two different things at the same time. While there could be improvements in how these conditions are represented (there really isn’t a simple way to implement an OR, for instance), they’re already flexible enough to do cool things with dialog puzzles, as you can see in the demo.

    Once the dialog engine has picked which options should appear, it will transparently invoke the KIAVC Lua function called addDialogLine() for each of them, and then pause on waitDialog(dialogId) (where dialogId is test in our example), which is pretty much equivalent to the waitActor() or waitMs() we saw in the scripting blog post (meaning it will pause waiting for a signal by the engine), with the key difference that it won’t simply wake up the coroutine, but also notify which option was selected by the player, presenting the related option ID. After that, the engine will have the actor explicitly say the selected line (possibly with some positioning tweak, if a look property was provided too), and then move to the block associated with the option next property.

    The animation below, for instance, shows what happens when, after starting the dialog, we pick option “1”.

    Selecting option “1” will cause the actor to say the text identified by “npcTalk1” (which is “Hi, nice to meet you!”), and then move to the block called greeting, which is the following:

    	greeting = {
    		steps = {
    			{ actor = "npc", say = "npcResp1_1" },
    			{ actor = "npc", say = "npcResp1_2" },
    			{ actor = "npc", say = "npcResp1_3", state = { npcSaidHi = true }, next = "main" }
    		}
    	},
    

    That block represents a series of steps for the dialog engine to perform, which in this case is a simple sequence of lines we’ll have the NPC say. As you can see, the last step also includes a game state change (we set the npcSaidHi property to true), and moves the dialog engine back to the main block. This time, though, we don’t display option “1” anymore, since it was marked with once=true and we went though that already. As you can see, those very simple directives already gave us some basic dynamic behaviour.

    Let’s see what happens when we select option “2” instead, which as we’ve seen in the main block above we should only display if npcAskedWhat is not set to true already. When selected, it will move the dialog to the block called looking, and the end result is what we can see below (and kind of an attempt to emulate Herman Toothrot’s “What colour is the tree?” long-winded dialog puzzle in Monkey Island 2).

    The looking block is actually quite short and simple, and simply a stepping point to the actual long list of options that follows:

    	looking = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_1" },
    			{ actor = "npc", say = "npcResp2_2" },
    			{ say = "npcTalk2_mh", next = "list" }
    		}
    	},
    

    Very simply, the NPC says a couple of things, we say “Mh”, and then move to the list block, which is where the long list of options appears:

    list = {
        options = {
            { name = "1", text = "npcTalk2_list1", once = true, next = "no" },
            { name = "2", text = "npcTalk2_list2", once = true, next = "no" },
            { name = "3", text = "npcTalk2_list3", once = true, next = "no" },
            { name = "4", text = "npcTalk2_list4", once = true, next = "no" },
            { name = "5", text = "npcTalk2_list5", once = true, next = "nohat" },
            { name = "6", text = "npcTalk2_list6", once = true, next = "noglasses" },
            { name = "7", text = "npcTalk2_list7", once = true, next = "no" },
            { name = "8", text = "npcTalk2_list8", once = true, next = "no" },
            { name = "9", text = "npcTalk2_list9", once = true, next = "no" },
            { name = "10", text = "npcTalk2_list10", once = true, next = "no" },
            { name = "11", text = "npcTalk2_list11", once = true, next = "no" },
            { name = "12", text = "npcTalk2_list12", once = true, next = "no" },
            { name = "13", text = "npcTalk2_list13", once = true, next = "no" },
            { name = "14", text = "npcTalk2_list14", once = true, next = "no" },
            { name = "15", text = "npcTalk2_list15", state = { npcAskedWhat = true }, next = "main" },
        }
    },
    

    Despite its length, this is a very simple block, which simply shows each option only once, and almost always leads to the same block (no), except a couple of options (we have some different text appear when the player mentions the hat or glasses, specifically). All the next blocks in this context are meant to simply provide a way for the NPC to say something back to us and quickly go back to list. The only one worth mentioning here is the nohat block, as it includes some more complex action management steps: as you can see, for instance, we have a step where we do nothing else than have the NPC look down and wait for a few seconds, before looking left again and saying something. This is the dialog engine equivalent of using sayActor(), waitActor() and/or waitMs() (which are indeed used under the curtains).

    	no = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_no", next = "list" },
    		}
    	},
    	nohat = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_nohat1" },
    			{ actor = "npc", look = "down", sleep = 500 },
    			{ actor = "npc", look = "left", say = "npcResp2_nohat2" },
    			{ actor = "npc", sleep = 1000 },
    			{ actor = "npc", say = "npcResp2_nohat3", next = "list" },
    		}
    	},
    	noglasses = {
    		steps = {
    			{ actor = "npc", say = "npcResp2_noglasses", next = "list" },
    		}
    	},
    

    Coming back to the list block itself, the fact each option is only meant to appear once means that, any time we click an option, it makes room for a next one, until we exhaust the list. When we click the last option, we mark npcAskedWhat to true (which means option “2” will not appear anymore, making this block a one-time kind of dialog rather than something you can go back to) and go back to main.

    Let’s see the last meaningful option in the main block, now, that is option “3”. As you can see, we have one named “3”, one named “3b” and another named “3c”. The names themselves mean nothing (they’re opaque IDs), but if you look at the conditions they have, you’ll see they’re actually meant to be alternative representations of the same options, which is why for the sake of clarity I chose those IDs to address them. This means that you’ll always see only one of them at any time, which also explains why, when we first start the dialog, we see 4 options including the last one (the one to leave the conversation), while there are actually 6 in main with two of them preceding “4”.

    Let’s see how that looks like in the demo (where we assume we’ve already gone through “1” and “2”, which explains why “3” and “4” are the only ones displayed).

    Since npcAskedWhere1 is not true yet, “3” is what we display, that when selected will bring us to the clone block:

    	clone = {
    		-- This is an example of conditional responses: we have three separate
    		-- possible paths we can go through, depending on some state variables.
    		-- It's probably easier to just use different blocks, but in some cases
    		-- such an approach may be useful to prevent "block overload".
    		steps = {
    			-- Path #1
    			{ actor = "npc", say = "npcResp3_1", notif = { npcAskedWhere1 = true }, state = { npcAskedWhere1 = true }, next = "main" },
    			-- Path #2
    			{ actor = "npc", say = "npcResp3b_1", onlyif = { npcAskedWhere1 = true }, notif = { npcAskedWhere2 = true } },
    			{ actor = "npc", say = "npcResp3b_2", onlyif = { npcAskedWhere1 = true },
    				notif = { npcAskedWhere2 = true }, state = { npcAskedWhere2 = true }, next = "main" },
    			-- Path #3
    			{ actor = "npc", say = "npcResp3c_1", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ actor = "npc", say = "npcResp3c_2", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ say = "npcResp3c_3", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true } },
    			{ actor = "npc", say = "npcSearch3", onlyif = { npcAskedWhere2 = true }, notif = { npcAskedWhere3 = true },
    				state = { npcAskedWhere3 = true }, next = "main" }
    		}
    	},
    

    As a matter of fact, you may have noticed “3b” and “3c” lead to clone as well, and in fact this block is implemented as a series of steps that are actually conditional themselves too (as we need to know if we got there from “3”, “3b” or “3c”). The end result is that for each step we check the related condition, and if that passes that step is performed, otherwise it’s skipped.

    Performed steps that have a state property also result in game state changes, which is how we end up in the different lines being rendered in main and clone: when we first select “3”, for instance, we know that appeared because npcAskedWhere1 was not true; this means that the first step is executed (the NPC says a single thing, and then we go back to main), while the others aren’t. That step also has a state = { npcAskedWhere1 = true }, though, which means that now npcAskedWhere1 is indeed set to true, which changes how main will be rendered next: specifically, since npcAskedWhere1 is true but npcAskedWhere2 isn’t, we only show “3b”, which when selected performs the steps identified by the comment “Path #2”; and so on and so forth.

    When a single option remains to be selected, KIAVC is configured not to prompt it to the player, but automatically select it instead (since it’s the only thing they can do). This is why, in the animation above, you see the engine leaving the dialog after we’ve exhausted the different incarnations of option “3”, since at the end only option “4” remains which is where we go to the done block that wraps things up.

    	done = {
    		steps = {
    			{ actor = "npc", say = "npcResp4_1" },
    			{ load = "rooms['street']:startScript('searching', npcSearchingScript)", exitDialog = true }
    		}
    	}
    

    Notice how our exit block also starts a Lua command besides setting exitDialog to true, using the load property. Within the context of this demo, this is because when we walk around the streets there’s a background script that automatically animates the NPC: since we stopped it before entering the dialog (as you can see in the talkTo management a few sections before), it’s up to us to start it again when the dialog is over.

    That’s all!

    I hope this was a lighter and more interesting read than the one for the Lua integration, which I realize was much longer and heavier in content. While I know there’s more work to do to make dialog puzzles even more flexible than they are right now (and I’m sure new things will come to mind when I’ll start thinking about how they do work in other games, a bit like Herman’s puzzle guided my choices in the current version), I think the approach I came up with already allows for cool things to be done (that I tried to take advantage of in the demo to attempt a bit of comedy). In the future, some tooling to make designing dialog puzzles easier may be something worth considering too, but for the time being the Lua object format isn’t too terrible. Of course, feedback is more than welcome!

  • Lua scripting in KIAVC

    Lua scripting in KIAVC

    While an engine is a powerful component that can take care of most of the heavy lifting a videogame requires, in the vast majority of cases it’s not also responsible of the game itself, i.e., its logic or progression: that’s something that most of the times is delegated to some form of scripting instead. This is very common in games on the market today, even AAA ones, especially when there’s “quests” that can expand on the basic functionality of a game.

    This blog post will try to explain how I addressed this requirement in KIAVC. Please notice that this post assumed you read the previous post on the KIAVC internals: while I try to explain everything comprehensively, many higher level concepts are better introduced and explained there, and so allow for an easier understanding of what will be a much more technical description in this post instead.

    Why not just do everything in the engine?

    There are many reasons for this. First of all, an engine is usually written in a low level language (KIAVC is written in C, for instance), which is fine for coding and optimizing the management of input, rendering, textures, etc., but not as much for implementing the dynamic behaviour you may want your game to have. Of course you can do it, but you really shouldn’t: in the context of an adventure game, for instance, it would force you to write new code any time you want to add a room, or a character, or lines to a dialogue, etc., which would be overkill; even slightly changing parts of a game would require rewriting parts of the code, which can quickly turn development into a nightmare.

    Most importantly, though, the main idea behind an engine is that this is exactly what it should be: an engine, not the game itself. An engine is supposed to provide you with the basics to implement a game on top of it, which means the same engine can be used for multiple games, possibly even very different from each other. If you think about it, that’s exactly what SCUMM did: while the engine was enhanced and improved game after game, the bulk of it remained the same across the years, which means that games like “The Secret of Monkey Island”, “Sam & Max Hit the Road” and “Grim Fandango” were all written using pretty much the same “engine”, and so relying on the SCUMM tools and scripting functionality to actually become different games. Sierra games followed the same pattern as well, using the same engine for a multitude of games.

    Since I started working on KIAVC exactly because of my fascination with SCUMM, I decided to follow the same approach: writing a more or less generic engine, that could then be used to design and implement different games without having to touch the engine itself. This means, again, relying on some form of scripting, that is a lighter and higher level language that can be used to code and wire a game, leveraging the functionality made available by the engine itself. As I was saying before, this is very common in games: if you write an adventure game in AGS or Unity, the same thing will happen there, as both are engines that allow you to somehow script (using a custom language) what behaviour you want to implement.

    Why Lua and not something else?

    I shared some details on why I chose Lua for the job in the first post on this blog, but in a nutshell, the main motivation for using Lua was that I had already played with it in the past, and so was more or less comfortable with its syntax and mechanisms. Ron Gilbert apparently isn’t very fond of Lua, and in fact in his blog he explains how and why he started using Squirrel instead for the Thimbleweed Park engine (and I assume for Return to Monkey Island too, since the engine is supposed to be pretty much the same), but while I can understand how he felt limited by some of Lua’s shortcomings, it felt like it could indeed work well enough for me (especially considering that if it’s used in so many AAA games as well, it’s probably good enough for an amateur engine!).

    And as a matter of fact, so far it did! Since the very beginning, it felt very exciting to be able to use simple Lua commands and scripts to tell my engine how to behave independently, and how to react depending on user input. Week after week, I’ve kept on adding functionality, and I’ve come to the point where I’m starting to really like how the engine is coming up. There’s still a lot missing (savegame support, for instance), but many of the features that I think I’d need to write an actual game are already there, which is definitely exciting.

    Embedding Lua scripting in the engine

    To help you understand why scripting can be so helpful, and why it’s supposed to be so powerful in KIAVC itself, consider what the KIAVC engine does when it starts:

    1. initialize the SDL2 backends;
    2. initialize the core engine (e.g., maps);
    3. load the kiavc.lua script (helper classes, more on that later);
    4. load the main.lua script (user provided script).

    That’s it! There’s nothing else the engine does as soon as it starts: after a quick initialization, it already waits for an external script (provided by who’s writing the game) to tell it what to do. This includes a few key operations to complete the initialization (e.g., setting the video resolution and framerate), the resources the game will use (e.g., images, sounds, rooms, actors, etc.), and how the game itself should proceed (which room do you start from? who’s the main actor? what do they do as soon as the game starts? where does the user input start from? etc.).

    For all this to work, of course, the engine itself must somehow embed the Lua engine, so that the KIAVC engine (written in C) can interact with Lua scripts in a bidirectional way. The bidirectional nature of this interaction is indeed of paramount importance: while it’s of course important for Lua scripts to tell the engine what to do (most of the interactions will work that way), it’s just as important for the engine itself to be able to ask things of the script, notifying it about events it should be aware of, or trigger some behaviour in the script itself.

    The way this works in practice is that, thanks to the Lua library, it’s the C part of the engine that actually “runs” the Lua engine as well. Basically, as a developer you create a Lua state object, and that object is what you use for all interactions with it: you can access properties or call functions, but more importantly register functions that will be visible to Lua scripts and actually implemented in C in the core engine itself. Let’s take a look at some of the first lines in scripts.c, which is where the Lua state is initialized and some functions are registered as well:

    /* Initialize Lua */
    lua_state = luaL_newstate();
    luaL_openlibs(lua_state);
    /* Register our functions */
    lua_register(lua_state, "kiavcRequire", kiavc_lua_method_kiavcrequire);
    lua_register(lua_state, "getVersion", kiavc_lua_method_getversion);
    lua_register(lua_state, "getVersionString", kiavc_lua_method_getversionstring);
    [..]
    

    The luaL_newstate() command is what creates the Lua state object, so what the KIAVC engine will use for whatever interaction is required with the scripts. After that, a series of lua_register() calls register functions the engine wants to expose to Lua scripts, telling the Lua stack which C function will actually be responsible of handling the function when called from Lua. In the snippet above, we see a few examples: kiavcRequire() is a helper function I added to include other Lua scripts from a script (pretty much an equivalent of the stock require() that accounts for the fact scripts may not be in the file system but in an archive file), while getVersion() and getVersionString() are functions that allow Lua scripts to quickly detect which version of the KIAVC engine is running (which may be important if a script is know to work on one version but not another, for instance).

    If we check how getVersion() works, we see it’s implemented by a C function called kiavc_lua_method_getversion():

    /* Return version as major/minor/patch */
    static int kiavc_lua_method_getversion(lua_State *s) {
    	/* This method allows the Lua script to retrieve info on the engine version */
    	int n = lua_gettop(s), exp = 0;
    	if(n != exp) {
    		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[Lua] Wrong number of arguments: %d (expected %d)\n", n, exp);
    		return 0;
    	}
    	lua_pushnumber(s, KIAVC_VERSION_MAJOR);
    	lua_pushnumber(s, KIAVC_VERSION_MINOR);
    	lua_pushnumber(s, KIAVC_VERSION_PATCH);
    	return 3;
    }
    

    This method expects no arguments (so from Lua you do indeed call getVersion()), and returns three different numbers. The This method expects no arguments (so from Lua you do indeed call getVersion()), and returns three different numbers. The main.lua script shipped with the demo does make use of it to showcase how it works:

    -- We may want to make sure we're using a specific version of the engine
    major, minor, patch = getVersion()
    if major ~= 0 or minor ~= 1 or patch ~= 0 then
    	kiavcLog('Unsupported KIAVC engine version')
    	return
    end
    

    Basically, that’s how the “magic” happens: the Lua script thinks it’s calling a Lua function, but in practice it’s the engine that handles it. And while we’ve seen very basic examples, here (querying the version of the game), there’s plenty more that the engine exposes to Lua scripts: at the time of writing, there’s 109 (!) functions exposed by the core, all implementing different features a Lua script may need. The setResolution() function, for instance, allows a Lua script to tell the engine what resolution the game should have, which framerate, and if scaling should be performed:

    setResolution({ width = 320, height = 180, fps = 60, scale = 4 })
    

    In the example above (which again comes from the main.lua of the demo), we’re creating a game that will have a 320×180 resolution and will run at 60fps: we’re also asking the engine to scale it 4x, though, which means the engine will acually create a 1280×720 window and scale the game screen automatically. Of course, while I’ve chosen a low-res approach (the demo uses pixel art), the engine is not supposed to only support that: you could tell the engine to use a 1920×1080 resolution instead, for instance, if you’re using higher resolution graphics for your game. Notice how scaling at the moment involves an integer, which means you really cannot scale a game down at the moment: this is one of the things I plan to address in future versions of the engine.

    Registering resources

    Some of the most important functions the engine exposes are those related to the registration of resources. In fact, whatever the game, you’ll definitely want it to contain images, animations, music or sound effects. All of them are media resources that only you know about, and that could vary from game to game, which means that if you want the engine to actually use them, you’ll have to tell it about them. In KIAVC, no matter how many time the same image is used (possibly even by multiple entities at the same time), you only need to register each image once: since you provide an ID to associate with the image, it’s that ID that is used from that point forward in the scripts whenever that image needs to be used for something. Depending on what you tell the engine to do with the image, the engine will automatically take care of decoding, opening and rendering it, and when possible also clean it up when it looks like it’s not used for the time being. The same can be said for basically all other resources as well, with a few differences here and there depending on the nature of the medium.

    The way you register resources, in fact, varies depending on what you’re passing to the engine. Animations and images are registered via the same function (since the engine assumes that a static image is basically an animation with a single frame), and is registered via the registerAnimation() function, e.g.:

    registerAnimation({ id = 'fire-loop', path = './assets/images/fire.png', frames = 8 })
    

    The call above tells the engine we want to register a fire animation, made of 8 different frames, and stored in the provided file, and that the ID for that image will be “fire-loop”. There’s more properties you can pass when register an image (e.g., which color should be interpreted as transparency, or how long each frame should be displayed), but the important part to understand here is that, once this function has been called, the engine is aware of that image/animation, and that all you need for it to be used (e.g., to animate an object or an actor, or to use as background/layer in a room) you just need to pass the “fire-loop” reference and the engine will know what to do.

    Media resources are not the only ones you can register in the engine: when designing a game, it’s important that the engine is also aware of game-related concepts like rooms, actors, costumes, objects and so on. In order to make it simpler to work with all these resources, I created some Lua classes that abstract the usage of most of the Lua functions that the engine actually exposes.

    Implementing classes in Lua

    To make a simple example, when you want to add a room to your game (where a room is more in general a specific scenery the action takes place in, whether it’s an actual room, a forest, or somewhere else), there are different methods you’d use to tell the engine about it: first of all, you’d use registerRoom() to make the engine aware of the room and its ID, but then you’d start adding properties to tell the engine how to handle it when active; setRoomBackground(), for instance, tells the engine which image/animation should be used as the room background; addRoomLayer() can be used to add layers to this background, e.g., for parallax effects or to show things in the foreground; addRoomWalkbox() can be used to define a list of walkboxes actors can walk on in the room, as explained in a previous post; and so on and so forth.

    While you’re free to use and orchestrate these individual functions yourself, it would be much easier to handle them as a class instead, and then use some setters/getters on the class to actually have the engine react to what you’re trying to accomplish. This is why I created different classes for most of the resources you can use in KIAVC. As such, this is how you can create a room instead:

    Room:new({
    	id = 'outskirts',
    	background = 'outskirts-bg',
    	walkboxes = {
    		{ x1 = 28, y1 = 122, x2 = 66, y2 = 134, speed = 1.5 },
    		{ x1 = 40, y1 = 134, x2 = 194, y2 = 176, speed = 1.5 },
    		{ x1 = 140, y1 = 124, x2 = 186, y2 = 134, speed = 1.5 },
    		{ x1 = 194, y1 = 155, x2 = 226, y2 = 180, speed = 1.5 },
    	},
    	onenter = function(self)
    		-- When we enter the room, we fade in the local music track
    		luke1:play(1000)
    		if previousRoom ~= nil and previousRoom.id == 'street' then
    			-- If we're coming from the 'street' room, we place the main
    			-- actor next on the top left
    			activeActor:moveTo('outskirts', 30, 126)
    			activeActor:look('down')
    		end
    	end,
    	onleave = function(self)
    		-- When we leave the room, we fade out the local music track
    		-- and then fade in/out for a little while
    		luke1:stop(1000)
    		fadeOut(250)
    		waitFor('fade')
    		fadeIn(250)
    	end
    })
    

    The code above is supposed to be much more readable, as it immediately shows what we’re trying to do:

    • we’re creating a new room, with id “outskirts”: as a result, the class will call registerRoom("outskirts") automatically for us;
    • we’re defining the background image to use, passing the “outskirts-bg” reference, which will result in a setRoomBackground() call; as we’ve seen before, this is a reference to an image previously registered in the engine;
    • we’re defining a set of four different walkboxes, so where on the background our character can walk; for each of those walkboxes, the class will automatically invoke the addRoomWalkbox() function;
    • we’re defining a couple of functions that define what to do when this room becomes active, and when we’re leaving it for another room; both are callback functions where we can specify a custom behaviour, like starting a music track when we get in, and stopping it when we get out (but if you look at the code, there’s more happening).

    These are all things we could have done manually as well, but I thought that working with a class would make the initialization easier. Once a room has been created, you can use the class functions to change it dynamically if needed. This is especially useful for those resources that are inherently more dynamic, like music, actors or objects. If you check the onenter() callback, for instance, you can see there’s line that reads:

    luke1:play(1000)
    

    The luke1 variable, in this context, refers to an audio track we previously registered in another part of the script:

    local luke1 = Music:new({ id = 'luke1', path = './assets/music/luke1.ogg' })
    

    Since we created a class for music tracks as well, we could register a music track in the engine, with the path to the music track itself (an ogg file) and ID “luke1”: this registration process automatically invoked registerAudio(), which is basically the audio equivalent of the registerAnimation() function we introduced before (just as a heads up, there’s an Image and Animation class as well!). And since it’s a track, we can interact with it using its methods. In this case, we’re invoking the play() method on the class instance, with a 1000 parameter. If we check the Music class:

    Music = {
    	play =
    		function(self, fadeInMs)
    			if fadeInMs == nil or fadeInMs < 0 then
    				fadeInMs = 0
    			end
    			-- Tell the engine to start playing the track
    			playAudio(self.id, fadeInMs, true)
    		end,
    

    This means that the class will automatically translate the luke1:play(1000) call to:

    playAudio('luke1', 1000, true)
    

    which will fade in that track in about a second while we enter the room. If the room we’re leaving has a track:stop(1000) call, it means that while they’re fading out their track, we’re fading in ours, thus creating a basic imitation of an iMuse-like transition. Pretty cool!

    Of course working with tracks is more interesting when you’re working with actors or objects. In the onenter() function of the room, for instance, you can see:

    activeActor:moveTo('outskirts', 30, 126)
    activeActor:look('down')
    

    The activeActor variable is a static reference to the actor class object that’s currently considered to be the main actor, that is the one the user is controlling. As such, working with that variable means we’re telling the engine to do something with that specific actor that was registered before. In this specific instance, the script is telling the engine that, whenever the user enters that room, the actor should be placed at those coordinates, and should be faced to look down. The result in the demo is what you can see in the image below:

    The engine obviously doesn’t know where the actor should be when the room is loaded, or whether the actor should be visible at all. These are all details that only who’s writing the game can know, and so it’s up to them to script the related behaviour: the engine will then take care of the heavy lifting and physically implement what was asked of it (in this case updating the internal coordinates of the actor and their direction).

    As such, it’s up to the user script to register all resources it needs, and script how to interact with them. If you’re curious how the demo in the repo does that, check the resources.lua file: in fact, the main.lua script automatically includes that one:

    -- Let's load all the resources first
    kiavcRequire('game/resources')
    

    which is where all resources are registered. You’ll see that some resources are registered directly in that file, while others are delegated to other files that are included, but all of them make use of classes to register resources and their related properties.

    When does the engine talk to the script?

    So far we’ve seen all instances of functions that the engine exposes, and Lua scripts then invoke, to allow user scripts to tell the engine to do something. As anticipated, though, there are many instances where the other way around should happen, i.e., the engine calls a Lua function to notify the script about something. A few examples are the following:

    • the user left/right clicked somewhere;
    • the user is passing with the mouse on one of the registered resources;
    • the user pressed a key on the keyboard (generic input)
    • an event the script was interested in happened (e.g., an actor reached their destination, or has finished saying a line);
    • a specific line in a dialog puzzle was selected;

    and so on and so forth. Another very important example, though, is more in general the “update the world” scenario: in fact, the Lua script doesn’t have its own thread, but as we discussed initially is very much embedded in the engine itself, which means it runs only when the engine tells it to do something and progress the state. This means that it’s up to the engine to keep the Lua stack running, and keeping it updated on, e.g., timing progression, especially in cases where Lua scripts may be interested in such an information. It’s also important to allow paused and pending coroutines (that we’ll talk about in a second) to move forward, in order to allow multiple Lua scripts to move on at the same time within the context of the same process.

    Coroutines are particularly important in this context. In fact, KIAVC is a single thread application, which means that everything (user input, rendering, and processing of Lua scripts) happens in a single thread. Nevertheless, it’s also fundamental that multiple scripts can run at the same time: you may want to have NPCs moving around, for instance, or other things happening in the background, all while (and independently) you click around the screen to interact with the game. In order to allow this to happen, you can make use of Lua’s coroutines: a coroutine is usually, and generically, introduced as a “thread”, which it really isn’t; a thread has a life of its own and will run concurrently with other processes, which is not the case of coroutines. A coroutine is more simply a function you can pause and resume at any time, and that only ends when you return. This means that, any time you want more things to happen at the same time, all you need to do is launch coroutines and ensure they don’t monopolize the process, but rather pause when they need to (e.g., when they’re waiting for something), so that another coroutine can be executed while other things happen. The classes I wrote for Lua script take care of automatically running all pending coroutines when the engine wakes the Lua state up, and to resume coroutines waiting for some specific piece of information as well.

    In order to make this clearer, let’s make a simple example, using the intro scene from the demo:

    In this intro, the main character moves to some coordinates, looks around, and then says something. These are all things that should happen in sequence, but all Lua functions implemented by the engine are asynchronous: this means that, as a script, you tell the engine what to do, and the engine will acknowledge it and then return right away. As a consequence, as a script you know the engine will do it, but don’t know when it will be completed. This is where the bidirectional nature of the engine-Lua interaction helps, and where coroutines play an important role as well. In fact, let’s have a look at the code tha implements the scene above:

    function intro()
    	startCutscene()
    	fadeIn(1000)
    	activeActor:walkTo(148, 170)
    	waitFor(activeActor.id)
    	waitMs(800)
    	activeActor:look('right')
    	waitMs(800)
    	activeActor:look('left')
    	waitMs(800)
    	activeActor:look('right')
    	waitMs(800)
    	activeActor:look('down')
    	activeActor:say(text('intro'))
    	waitFor(activeActor.id)
    	stopCutscene()
    end
    

    Let’s ignore the first two lines, right now (what they do should be obvious), and let’s focus on what follows instead. The first command the script sends the engine is:

    activeActor:walkTo(148, 170)
    

    We’ve met the activeActor variable before, so we know it’s the actor class instance associated with the main character (the detective, in our demo). We’re telling the engine to have to actor walk to those coordinates, which as we’ve seen in a previous post, will have the engine run the pathfinding process to find a path from the current coordinates of the actor to the destination. All this will happen deep within the engine, though: as far as the Lua script is concerned, the function returns immediately, thus allowing the Lua script to do more if it wants. We definitely can’t have the script do the “look around” routine now, though: we’ve just told the engine to have the actor walk around, and that hasn’t even started. What we do, then, is wait for the actor to reach their destination, which we can do with the waitFor() function:

    waitFor(activeActor.id)
    

    This function, quite simply, tells the Lua helper code that we’re interested in knowing when the engine says the actor is done with something, and puts the coroutine to sleep until it happens:

    -- Helper function to wait for a specific event
    function waitFor(event)
    	local co = coroutine.running()
    	if co == nil then return end
    	if waiting[event] == nil then
    		waiting[event] = { co }
    	else
    		table.insert(waiting[event], co)
    	end
    	return coroutine.yield(co)
    end
    

    Quite simply, we add the coroutine that called the function to the list of coroutines interested in that specific event (the ID of the actor in our case), and then we put the coroutine to sleep using yield().

    As soon as the engine has completed walking the actor to the provided coordinates, it will invoke the signal() function with the ID of the actor as the only argument: the signal() function of course needs to exist in the Lua “world”, and just as waitFor() in fact it’s implemented in kiavc.lua, so as part of the Lua helper functionality that the engine loads at startup. It’s implemented like this:

    -- Helper function to signal a specific event
    function signal(event)
    	kiavcLog("Got '" .. event .. "' event")
    	local toWake = waiting[event]
    	if toWake == nil then
    		return
    	end
    	waiting[event] = nil
    	for _, co in ipairs(toWake) do
    		if coroutine.status(co) == "dead" then return end
    		local res = { coroutine.resume(co) }
    		if coroutine.status(co) == "dead" and res[1] ~= true then
    			kiavcError(res[2])
    		end
    	end
    end
    

    What the signal function does is quite simple: it goes through all the coroutines that were waiting for that event, and wakes them up calling resume(). In our example, this results in our function exiting the waitFor() slumber, and moving to the next function. In our case, the next step is one more wait, but this time it’s:

    waitMs(800)
    

    This function works pretty much like waitFor(), with the difference that we’re not waiting for a signal from the engine to happen, but we’re asking the Lua engine to wake us up in about 800ms. When that happens, the coroutine wakes up once more, and can proceed. There are other wait functions implemented in kiavc.lua: one is waitDialog(), for instance, which helps managing dialog puzzles (and that we’ll cover in a dedicated post soon).

    Once you’re familiar with this wait/sleep/wakeup/resume routine, it’s quite easy to understand the rest of the function: after we’ve reached the destination and waited 800ms for suspence, we turn the character right, wait other 800ms, turn the characer left, wait 800ms, turn the character right once more, wait 800ms, and then turn the character down and have them say something. As you can see, there’s no waiting function in between when we turn the character down and when we have the character speak:

    activeActor:look('down')
    activeActor:say(text('intro'))
    

    This is because looking in a certain direction is something that happens right away, so we don’t really need to wait until we need to (as was the case when we were looking around, and so wanted to linger on that direction for some more time). We do need one more waitFor() after the say() directive, though, again because saying a line does take some time, and we don’t know in advance how long that will take: the engine uses the same signal() with the actor ID after a line has been completed.

    This wait/signal paradigm is quite powerful, and yet another thing I’ve learned reading the excellent Groebelshoot blog, which drew inspiration from this presentation (showing how Lua is used in the Uncharted game series as well!) and in turn refers to this very useful guidelines by Jonathan Fischer.

    What about random scripts?

    The asynchronous nature of coroutines, and the fact we can launch more than one at the same time and take advantage of the pattern we just introduced, means we can also use the same approach for any random script. The Lua classes I wrote for rooms, classes, objects and more, for instance, all have ways to launch custom scripts to be associated with a specific resources.

    Let’s make one more practical example to explain how this works using the demo from the repo. In the main room (“streets”), you’ll see things happening in the background while you click and interact with things, namely some noise text appearing around a door, and an NPC (who looks exactly like our main character because I didn’t have other sprites to work with :mrgreen: ) complaining about the fact they can’t find something:

    Both of those things are implemented as standalone scripts that the room class starts when the main actor enters the room, and stops when the main actor leaves. If we check code used to register the “streets” room in the Lua script:

    Room:new({
    	id = 'street',
    	background = 'street-bg',
    	layers = {
    		{ id = 'street-back', image = 'street-bg-back', plane = -100 },
    		{ id = 'street-far', image = 'street-bg-far', plane = -200 }
    	},
    	walkboxes = {
    		{ x1 = 127, y1 = 148, x2 = 172, y2 = 150, scale = 0.38, speed = 0.5 },
    		{ x1 = 118, y1 = 150, x2 = 180, y2 = 154, scale = 0.45, speed = 0.6 },
    		{ x1 = 106, y1 = 154, x2 = 190, y2 = 158, scale = 0.5, speed = 0.7 },
    		{ x1 = 80, y1 = 158, x2 = 200, y2 = 162, scale = 0.6, speed = 0.8 },
    		{ x1 = 60, y1 = 162, x2 = 216, y2 = 168, scale = 0.7, speed = 0.9 },
    		{ x1 = 0, y1 = 166, x2 = 14, y2 = 180, scale = 0.76, name = 'barrier1' },
    		{ x1 = 14, y1 = 166, x2 = 580, y2 = 180, scale = 0.76 },
    		{ x1 = 580, y1 = 166, x2 = 608, y2 = 180, scale = 0.76 },
    	},
    	triggers = {
    		barrier1 = function(actor)
    			-- When we trigger the walkbox called 'barrier', we start
    			-- a script that prevents us from going further: triggers
    			-- on walkboxes can be helpful to do different things, and
    			-- since we know which actor caused the trigger, we can
    			-- choose to react differently depending on the scenario
    			startCutscene()
    			activeActor:look('down')
    			activeActor:say(text('streetBarrier1'))
    			waitFor(activeActor.id)
    			waitMs(500)
    			activeActor:say(text('streetBarrier2'))
    			waitFor(activeActor.id)
    			activeActor:walkTo(58, 170)
    			waitFor(activeActor.id)
    			stopCutscene()
    		end,
    		barrier2 = function()
    			rooms['outskirts']:enter()
    		end
    	},
    	onenter = function(self)
    		-- When we enter the room, we fade in the local music track
    		luke9:play(1000)
    		if previousRoom ~= nil and previousRoom.id == 'outskirts' then
    			-- If we're coming from the 'outskirts' room, we place the main
    			-- actor on the right side, not on the left as in the intro
    			activeActor:moveTo('street', 570, 174)
    			activeActor:look('left')
    		end
    		-- We automatically start a script to show noises out of the girls bar
    		self:startScript('noises', noisesScript)
    		-- We also start a script to make the NPC do something
    		self:startScript('searching', npcSearchingScript)
    	end,
    	onleave = function(self)
    		-- When we leave the room, we fade out the local music track
    		-- and then fade in/out for a little while
    		luke9:stop(1000)
    		fadeOut(250)
    		waitFor('fade')
    		fadeIn(250)
    	end
    })
    

    it’s very similar to the setup of the “outskirts” room we’ve shown before (apart for the room layers, and the walkbox trigger that is better explained here and uses the same concept of coroutines), but also has the room start a couple of scripts when the user enters the room:

    -- We automatically start a script to show noises out of the girls bar
    self:startScript('noises', noisesScript)
    -- We also start a script to make the NPC do something
    self:startScript('searching', npcSearchingScript)
    

    Both scripts are automatically interrupted when leaving the room, which is why you don’t see any reference to them in the onleave() callback. As you can see, getting those things to happen is quite easy, since we’re just asking the room class to spawn a new script (which will have a unique ID, in case we want to interrupt it sooner) pointing to the function that will implement it. Namely, we have a script called “noises” that’s implemented like this:

    -- This is the script we use for showing noises on the bar
    function noisesScript()
    	local color = green
    	local outline = black
    	while(true) do
    		showText({ text = '*Crash*', font = 'dialogues', color = color, outline = outline, x = 320, y = 127, duration = 500 })
    		waitMs(600)
    		showText({ text = '*Bang*', font = 'dialogues', color = color, outline = outline, x = 340, y = 120, duration = 500 })
    		waitMs(600)
    		showText({ text = '*Pow*', font = 'dialogues', color = color, outline = outline, x = 320, y = 124, duration = 500 })
    		waitMs(600)
    		showText({ text = '*Boom*', font = 'dialogues', color = color, outline = outline, x = 340, y = 130, duration = 500 })
    		waitMs(600)
    	end
    end
    

    In a nutshell, this function creates an endless loop that shows the same four lines of text in sequence at different coordinates, each of them appearing for 500ms and then disappearing for about 600ms. Very simple, very basic, and yet quite effective thanks to the waitMs() function that prevents the coroutine from “monopolizing” the running thread.

    The other script, instead, is identified by the “searching” label, and is more similar to the intro (NPC looks around, says something), but again performed in a loop since we’re using this to add some colour to the room:

    -- This is the script we use for the automated npc actions
    function npcSearchingScript()
    	local npc = actors['npc']
    	if npc == nil then return end
    	local current = 1
    	local messages = { "npcSearch1", "npcSearch2", "npcSearch3", "npcSearch4" }
    	while(true) do
    		npc:look('up')
    		waitMs(1000)
    		npc:look('left')
    		waitMs(1000)
    		npc:look('right')
    		waitMs(1000)
    		if current == 4 then
    			npc:look('down')
    		else
    			npc:look('up')
    		end
    		npc:say(text(messages[current]))
    		waitFor('npc')
    		current = current+1
    		if current > #messages then
    			current = 1
    		end
    	end
    end
    

    Just as in the intro, we’re using a combination of waitFor() and waitMs() to orchestrate the NPCs behaviour. As you can see in the demo, this script is interrupted when you try to talk to the NPC, and automatically restarted when you’re done talking to them.

    User interactions

    As anticipated, an important role of the engine-to-Lua callbacks is of course played by user interactions. Whether the user hovers over something, clicks somewhere or presses a button, it’s not up to the engine to decide what should happen: you may want the F5 button to act as a trigger for something, for instance, while others may want to use it for something else; at the same time, mouse clicks could be used to trigger different behaviours depending on the nature of the game itself (are you using verbs? a medallion? a simple left/right click UI? etc.). As such, any time the user interacts with the engine, the engine notifies the Lua script so that something can be done with it.

    Without bothering you too much with the details (also considering this blog post turned out WAY longer than I expected!), let’s just focus on keyboard input as an example, especially since they’re simpler to explain. Specifically, any time the user presses a key on the keyboard, the engine will invoke the userInput() function. In kiavc.lua, this function is implemented like this:

    function userInput(key)
    	if key ~= nil and inputTrigger[key] ~= nil then
    		inputTrigger[key]()
    	end
    end
    

    In a nutshell, the script checks if the user asked to be notified about such a keypress, and in case it invokes the related function. The user can express such an interest via the onUserInput() function, of which you can see a few examples in the main.lua demo itself. Let’s just look at a couple of them, for the sake of simplicity (you can study the others in the demo code itself, as they should be easy enough to figure out):

    -- When we press F, we get in or out of fullscreen
    onUserInput('F', function()
    	setFullscreen(not getFullscreen())
    end)
    ...
    -- Pressing Esc exits a cutscene (if one is playing) or leaves the game
    onUserInput('Escape', function ()
    	if cutscene ~= nil then
    		endCutscene()
    	else
    		quit()
    	end
    end)
    

    In this example, we’re asking the script to intercept two different keypresses: when the user presses ‘F’, we’ll use that as a trigger to get in or out of fullscreen (the not getFullScreen() you see there means we first ask the engine if we’re currently fullscreen or not, and then we trigger the reverse), while the ‘Esc’ key we use to either skip a cutscene (e.g., the intro) or quit the game, depending on the current state.

    That’s all!

    I realize this was a much heavier read than previous posts, just as I realize I barely scratched the surface: there’s so much more that could be said about what you can do with Lua scripts, and so little time/space to do that! Nevertheless, I hope this post gave you a good enough overview of how it was conceived to work, and that it will spark your interest in learning more. In case you are indeed curious, the best way to study what can be done is looking at the demo code, where I’ve tried to use most of the features currently available: the plan is to use the demo to showcase them all, sooner or later, so that could act as a reasonably good source of information on the available functionality, until proper documentation is written.

    The next posts are supposed to be “simpler” and more interesting, as there are a few things I’d like to talk about: how I implemented dialog puzzles, for instance, but also how to play with layers, parallax effects, dynamic transparencies or text localization.

    And of course, the plan is to also use all this to actually write a game sooner or later, but I’m afraid that will take a little longer :mrgreen:

  • Walkboxes and pathfinding in KIAVC

    In any adventure game (in any game, really), one of the most challenging functionality to implement is often pathfinding, that is the ability for the engine to figure out which path a character should follow to go from point A to point B. In fact, in a land full of pixels you can walk on, you may want some of them to be avoided, e.g., because they’re a pool of lava, or a wall, meaning the character should go around them rather than go in a straight line. Needless to say, for the novices (as I am) pathfinding can be quite complex, as there are different ways to solve the problem, and all of them require you to change your perspective of how you’d go around finding a path yourself.

    In this blog post, I’ll explain how I did it in this first iteration of KIAVC, by sharing some info I found across the way as well. As you’ll see, I did make a few assumptions to simplify the domain (which comes with some disadvantages), but as a first step I was quite happy with the result anyway: in the future, I plan to revisit the implementation so that it’s made a bit more generic and flexible.

    What is pathfinding?

    As we anticipated, it’s the ability to find a walkable path from point A to point B. While it’s overwhelmingly used in videogames of any kind, we actually see similar applications of the same concept in other domains as well, e.g., in network routing or whenever you use the navigator in your car. In point and click adventures it’s particulatly important, as it’s what dictates what path a character follows any time you click somewhere on the screen: it’s even more important when the destination you want to get to can’t be reached by just walking in a straight line, but you have to go around something, or follow a more complex path. All these considerations have to be taken into account when working on a pathfinding algorithm.

    Of course, the first thing I went to check was how they did it in SCUMM, which is how I encountered “walkboxes” for the first time. In a nutshell, every room in SCUMM could be configured with a set of walkboxes, that is, boxes you could walk on, which would overlap the room screen background: points outside of those boxes were areas you could not walk on (e.g., the water in the Melee docks). Considering that each walkbox was a polygon determined by four points (so a rectangle or something more complex than that), multiple walkboxes were needed to provide some flexibility in drawing the walking areas, since a single walkbox would not be able to do that. A simple example is the set of walkboxes you can see in the screenshot below, which represents the walkbox of the Melee Island jail from the first game (extracted and rendered using the popular SCUMM Revisited tool):

    You can already picture the big walkbox on the left following the line of the jail cells. SCUMM was configured to figure out where different walkboxes would overlap, as they’d provide a connection from one walkbox to another: if two walkboxes were not directly connected, you’d have to check if there were one or more intermediate walkboxes that provided a connection between the two, thus allowing for a walking path; again, not that different from how routers work in networking, for instance. Considering how CPU intensive a pathfinding algorithm can be, though, these paths were not computed in real-time back then: when a set of walkboxes was assigned to a room, SCUMM would compute a map of connections and store that in memory, so that it could be easily referred to when needing a path in the game.

    This was a clever and effective approach, but this is not really needed anymore. CPUs are powerful enough today that pretty much every game out there finds paths in real-time, using algorithms that have been refined over time.

    Study time!

    Once I had a better understanding of the root problem, I started looking into the possible ways of addressing them in KIAVC. A very good resource to start from was, once more, the excellent Groebelshoot blog, which had not one but even TWO posts dedicated to pathfinding.

    In the first posts, it generalized the concept of the 4-point walkboxes used in SCUMM to generic polygons, highlighting the main challenge that comes with that: the fact that, while in a polygon with all convex angles pathfinding is easy (all points in a polygon are visible to each other), the same can’t be said when concavity is involved as well. The same concept is explained in the as usual very informative Thimbleweed Park blog as well, which is where this clarifying picture comes from:

    The main lesson I got from that was that (long story short), rather than considering the whole pixel space in a map as a walkable vs. non-walkable area (which could result in a huge dataset the pathfinding algorithm would have to go through each time), all you really need are the vertices of those polygons, and how they’re connected to each other. This concept is explained in much better detail in this very interesting Stanford lesson on map representations, which introduces concepts like polygonal maps and navigational meshes (which, if you’re familiar with Unity, is what that engine uses for pathfinding), and the importance of visibility graphs.

    While this helped me greatly understanding how to reduce the problem space to something manageable, I still had to figure out what algorithm would be best suited to then handle the data and give me a result.

    The Groebelshoot blog posts were once again my gateway to this, as they’re what first introduced me to the so called A* (A-star) algorithm, which is pretty much the standard de-facto (modulo different optimizations that have been made over the years) when it comes to pathfinding. If you’ve had to deal with network routing in the past (whether as part of your work, or because you’ve studied them), you may remember depth-first, breadth-first and Dijkstra’s algorithm as some that would very often be used to find paths between two nodes. Dijkstra’s algorithm in particular is quite suited to find the shortest path between two nodes, and the above mentioned A* is an improvement on that. Without going too much into the details of how the algorithms differ from each other, I’ll just point to this excellent article that explains it quite clearly, and in a visual way as well. There’s another very interesting article that focuses more on A* itself and how it works, using visual aids (shown below, and borrowed from the article) to highlight the improvements it provides on Dijkstra’s algorithm for pathfinding purposes:

    Digging more for a better understanding, I kept on finding other resources that provided some additional information, like this interesting intro on A*, this other informative overview, and more, often with pseudo-code that helped figure out the steps needed to replicate the algorithm. All those results further confiirmed that A* was indeed the algorithm I needed to use in KIAVC as well, so I just needed to understand how to represent the domain space and work on it.

    Implementing pathfinding in KIAVC

    Figuring out how to represent walking areas was the first big challenge. In fact, while I was intrigued by the large polygon approach presented in the Groebelshoot blog, I felt it was a bit too compex; besides, I was intrigued by how simply Ron Gilbert had implemented a walkbox “trigger” in his Thimbleweed Park blog post, something that in my understanding could only be done using a set of smaller, and connected, polygons instead (so something closer to the way SCUMM handled walkboxes). Reading the amazing overview on how Flight of the Amazon Queen was made further pushed me in that direction, especially when they explained how they also used walkboxes as ways to automate scaling (e.g., to reduce the size of your main character as they walk towards the sunset).

    As such, I eventually decided to represent walkable areas as a set of rectangles that can overlap the background. As in the SCUMM times, a connection between rectangles meant a way to go from one to the other. You may be wondering why I chose rectangles as a walkbox unit, rather than a generic 4-point polygon as SCUMM did, and the reason was, to be blunt, to make it as simple as possible, as:

    1. it’s very easy to detect whether a point is in a specific walkbox, when the walkbox is a rectangle;
    2. I’m lazy;
    3. it’s also very easy to detect when and where rectangles overlap, to figure out connections among walkboxes;
    4. I’m lazy :mrgreen:

    This gross simplification means it’s harder to properly represent walkboxes sometimes (e.g., you can’t have the smooth diagonal line we saw before in the Melee Island jail example, or even have a single diagonal line BE the walkbox), but again, my main purpose at this initial stage was getting something done quickly and that worked. There will be time in the future to make those polygons more generic.

    As an algorithm, as anticipated I decided to implement A*, referring to existing pseudo-code examples as a reference. Using the information I studied from the resources linked above, I decided to use some specific points as nodes to be used in the A* algorithm, determined by the overlap area of walkboxes. Specifically, I decided to use the vertices of the rectangles that come out of overlapping walkboxes, as shown in the diagram below:

    In fact, if we need to find a way to go from a point in Walkbox #1 to a point in Walkbox #2, we don’t really need to consider ALL the points in their overlap area (that could add way too many nodes to pass to the A* algorithm, and take a long time to resolve), but just some key ones, and the vertices of their interaction is a good enough simplification. This is described as “Polygon vertex movement” in the Stanford lesson we introduced before. Just to make the process a bit smoother, I decided to also add the central point of each side of the overlap rectangle as well (which that lesson calls “Hybrid movement”), as shown below:

    Of course, walkboxes don’t necessarily create a rectangle when they overlap, and will sometimes only overlap on a single line. In that case, the process remains largely the same, except we only take into account the overlap line, rather than all four sides of a rectangle that isn’t there:

    KIAVC is configured to create a list of all this points (and the connections they make) just once, that is when you configure the walkboxes for a room. Then, whenever we need a path found, the engine does the following:

    1. a new list of nodes, that we’ll pass to the algorithm is created;
    2. we add the starting point (where the actor is now) to the list;
    3. we add all the computed walkbox nodes to the list;
    4. we add the target point (where the actor needs to go to) to the list;
    5. we run the algorithm.

    As you can see, we’re having A* run on a list of nodes that represent our walkable space, where start and target points are nodes as well. Of course, this first of all means smoothing the start and end point so that they wall in one of the walkboxes (if you click on the sea, we’ll change the target point to the closest one you can walk to). Besides, it also means figuring out which walkbox a specific point is in, so that the proper connections can be created for both start and end nodes (which A* needs to do its work). As a heuristic for A* (which I didn’t explain for the sake of simplicity, you can learn more in the links I shared before), I kept it simple and just chose the euclidean distance between.

    The end result of the process is a sequence of points that connect the start to the target, where each point is connected to the next in a straight line (e.g., because they are in the same walkbox). The KIAVC engine then consumes this list of point connections in sequence, so that we can see the actor following a path to their destination. Eureka!

    Let’s see this in action

    While the wall of text I’ve thrown at you may have been interesting to the most curious, I understand it can feel boring and too “academic”, so let’s see how it actually works in practice in KIAVC. This will help highlight where it’s working fine, the tweaks we added that could be very helpful when scripting a game, and most importantly the areas where the current implementation unfortunately falls short (we’ve anticipated the limitation of using rectangles, but there’s more!).

    First of all, let’s see how we can configure a set of walkboxes in KIAVC. We haven’t introduced how Lua scripting works (that will come in a later post), but, spoiler alert, a Lua script is indeed how you do that: when you create a room you want the engine to manage, you also pass the coordinates of all walkboxes for that room. If you check the street.lua file in the repository, which is where we initialize the main street in the demo, you’ll see walkboxes are defined like that:

    	walkboxes = {
    		{ x1 = 127, y1 = 146, x2 = 172, y2 = 150, scale = 0.5 },
    		{ x1 = 118, y1 = 150, x2 = 180, y2 = 154, scale = 0.6 },
    		{ x1 = 106, y1 = 154, x2 = 190, y2 = 158, scale = 0.7 },
    		{ x1 = 80, y1 = 158, x2 = 200, y2 = 162, scale = 0.8 },
    		{ x1 = 60, y1 = 162, x2 = 216, y2 = 168, scale = 0.9 },
    		{ x1 = 0, y1 = 166, x2 = 14, y2 = 180, name = 'barrier1' },
    		{ x1 = 14, y1 = 166, x2 = 580, y2 = 180 },
    		{ x1 = 580, y1 = 166, x2 = 608, y2 = 180 },
    	},
    

    Let’s skip the scale and name properties for now (I’ll address them in a minute), and let’s focus on the coordinates instead. As you can guess, those are the coordinates that define each walkbox as a rectangle: since we’re only dealing with rectangles, we only need two points, namely the top-left corner (x1, y1) and the bottom right corner (x2, y2). That’s all the KIAVC engine needs to import those walkboxes and automatically figure out if/how they’re connected.

    The KIAVC engine comes with a pathfinding debug mode, that allows you to visualize all those concepts as an overlay on top of the screen. In the demo that’s shipped out of the box, you can enable that mode pressing F11, which will display something like this:

    Quite simply, we’re actually drawing the walkbox rectangle on top of the screen, so that it’s immediately visible where you can actually walk to. This already highlights on of the limitations of our use of rectangles: the sides of the street would be better served by diagonal lines, but we have to “approximate” that by using rectangles that are, step after step, larger than the other.

    The debugging mode also shows us the path that is calculated any time we click somewhere on the screen. An example coming from the demo is provided below:

    If you remember the discussion we made on how we compute the nodes out of walkboxes overlap areas, it’s easy to see this in the path the algorithm resulted with: the actor was in one specific walkbox, and the first step was towards the closest walkbox to get to the destination, which they traversed in the central point of the overlap between those walkboxes; the same happened with the walkbox below, and for the last one, where we went for the vertical side of the overlap instead; finally, it was a straight line to the destination, since we eventually ended up in the walkbox containing the target point.

    This does show the algorithm doing its job properly, but highlights another problem: why the changes in direction, when we could have gone in a straight line instead? The short answer is that my pathfinding algorithm doesn’t really take into account the so-called “line of sight” yet: it’s true that those are different walkboxes, and so the decisions the algorithm makes definitely make sense had we needed to go around something, but when there is line of sight between some points (even when they’re in different walkboxes) it would save some steps (and be smoother) to just walk a straight line. That’s indeed something I plan to address in the future, but it’s not there yet, meaning you can get some even weired calculations like this:

    Again, definitely room for improvement! But it does its job, and works nicely when you’re actually going around things too.

    What’s particularly interesting, though, is the additional things you can do with walkboxes. Looking at the animations above, you may have noticed how the size of the actor changes automatically while going up and down the road: this is a consequence of some additional properties we can add to walkboxes, and that the engine understands. If we check the walkboxes definition again:

    	walkboxes = {
    		{ x1 = 127, y1 = 146, x2 = 172, y2 = 150, scale = 0.5 },
    		{ x1 = 118, y1 = 150, x2 = 180, y2 = 154, scale = 0.6 },
    		{ x1 = 106, y1 = 154, x2 = 190, y2 = 158, scale = 0.7 },
    		{ x1 = 80, y1 = 158, x2 = 200, y2 = 162, scale = 0.8 },
    		{ x1 = 60, y1 = 162, x2 = 216, y2 = 168, scale = 0.9 },
    		{ x1 = 0, y1 = 166, x2 = 14, y2 = 180, name = 'barrier1' },
    		{ x1 = 14, y1 = 166, x2 = 580, y2 = 180 },
    		{ x1 = 580, y1 = 166, x2 = 608, y2 = 180 },
    	},
    

    you can see that some walkboxes have a scale property: as the name suggests, that indeed tells the engine that, for any actor walking on that specific walkbox, that scale factor must be applied to the (possibly already scaled) actor. In this example, we start from telling to scale the actor to half their size, and then slowly increase them as they go down. Where a scale property is not provided, the default is 1.0. Notice that this does not replace a scaling we may have done on the actor already, but acts on that: in the demo, for instance, the main actor is scaled at 0.76 in the streets, which means that a 0.5 in the walkbox means “half of the scale the actor is currently in”. I stole this “use walkboxes to scale” concept from the Flight of the Amazon Queen blog post, which really opened my eyes when I read it, since it once again showed how you can actually use a thing that may seem to only have a single purpose for other objectives too.

    Another interesting property walkboxes have is the ability to be named. This doesn’t seem to be much (walkbox has a name? yeah cool, who cares!), but is actually quite important in KIAVC, because if a walkbox has a name, the engine will tell your script any time an actor enters that specific walkbox: this opens the door to interesting opportunities, because it allows you to script a specific behaviour for when a walkbox is triggered by a specific actor. In the demo, we have a single walkbox that has a name, which is called “barrier1”, and is the walkbox on the far left of the screen. Let’s see what happens when we walk on it, with debugging enabled so that we can see when the actor hits the box:

    In the demo, we handle the walkbox trigger by starting a script: the script stops the actor, makes them say something, and then automatically walks them back to a point in the previous walkbox. This is an interesting way to add triggers to a room (e.g., a bouncer that stops you on your feet until you solve the puzzle to get in), and since the actor ID is provided as well it can be configured to react differently (e.g., the bouncer letting every NPC actor in except you). Nothing fancy or crazy complex, as you see, but still quite effective in its simplicity!

    That’s all!

    I hope you enjoyed this little insight on how pathfinding works in KIAVC! As you’ve seen, it’s quite basic right now (complexity of A* aside), and definitely needs work (which I hope I’ll be able to put in next, in the future), but still it does its job despite its limitations, and the scaling/trigger tweaks make walkboxes quite interesting from a scripting perspective. At the very least, I hope I provided some context and resources on pathfinding for those that wish to learn more.

    Next step now is working on a blog post to introduce how scripting works: that’s going to be interesting, but also quite challenging, as there’s so much to say! See you in a few weeks for some info on that πŸ˜€

  • A look at the KIAVC internals

    A look at the KIAVC internals

    As a first “deep dive” post, I thought I’d share some details about how KIAVC works internally, in terms of its main cycle, how it loads and keep track of resources, how it deals with scripting, rendering, etc. Again, I won’t focus too much on any specific feature (the scripting part will definitely have its own post, for instance), but it should give a good understanding on how I structured the code and some of the choices I made.

    I’ll start by giving a quick intro on how the engine is structured internally, mostly in how it deals with resources, to then describe the main loop that actually implements the engine behaviour.

    Dependencies

    Before starting, though, let’s have a quick look at dependencies.

    The main one is SDL2, which to me was a no-brainer for a few reasons: first of all, it’s used by a ton of projects out there; then, I had played with the 1.0 version of the library already, many years ago, so I had a bit of familiarity wth it; and finally, as a library conceived for cross-platform development, it made a lot of sense to use it to abstract as many differences between Linux, Windows and MacOS away.

    Another library I chose to rely on was GLib. It’s a library I rely on a lot in other projects, since it provides amazing helper functionality like hashtables, queues, and stuff like that, but for KIAVC I initially tried to avoid it, as I wanted to first check if SDL2 provided some replacements and shims already (as it does for memory allocation and strings management already, for instance), and I wasn’t sure whether it could be an issue when targeting platforms like Windows (due to the additional DLL dependency). Eventually, I figured out GLib was actually already implicitly part of the set of dependencies due to SDL_ttf (for HarfBuzz), so I decided to simply start using it and making my life easier. In KIAVC, I currently only use it to implement hashtables (which I use a lot for addressing registered resources) and linked lists (because I’m lazy πŸ˜€ ).

    Finally, another important core dependency is of course the Lua embedded engine, which is required for scripting the game. The way I implemented scripting management in the core, it’s supposed to try and shim most of the Lua engine functionality through functions: I did it this way to keep the doors open to other scripting languages in the future, but in practice it’s currently quite heavily tailored on how the Lua engine works.

    Generic structure

    As anticipated in the introduction post, I wrote KIAVC in C, while using Lua for the scripting part. This means there’s an engine core written in C that implements the “hard” stuff, like getting user input, rendering, audio management, handling resources, and stuff like that, which then interacts with a set of Lua scripts that implement logic on top of that. Borrowing an image from the excellent Groebelshoot blog, it looks quite similar to this:

    As I’ll explain in a future post on scripting, there’s a few things I do a bit differently (the game state is partly shared between the engine itself and scripts, for instance), but apart from that, this is a good representation of how responsibilities are shared.

    The whole engine is single threaded, which is a choice I made on purpose to ensure we’d keep things simple, and to minimize the risk on nasty race conditions. This allowed me to work on an event-based pattern as part of a main loop, as I’ll explain better further below. Of course, using a single thread also means using a single core on the machine, which may be suboptimal: I’m not sure if this will be a problem further along the road, e.g., if/when I’ll start tinkering with higher resolutions or smoother animations, but for now this seems to be working nicely and with a very low use of resources. I’ll worry about performance when I need to worry about it :mrgreen: .

    Focusing on the engine itself (the “game framework” in the picture above), each resource type has its own struct, which helped me organized the code in pseudo-classes: there’s one for images/animations, for instance, one for rooms, one for actors, one for fonts and so on, each with their own helper code for things I needed to be able to do from the engine loop.

    More specifically, the engine revolves around the idea of “registering” resources. This simply means that, whenever you want to for instance use an image in your game, you first need to “register” it with a unique ID: this results in the engine creating a dedicated instance of the related image struct, and saving it into a map, so that we can quickly reference it any time that we need it. This also allows us to re-use the same resource in different contexts: if you want the same background to be used in two different rooms, you just configure both rooms to refer to the same image ID. The simple sequence diagram below, for instance, shows how we can register a “tree” image, and then use it for multiple objects we’ll put in a room:

    Registering a resource doesn’t mean it will be loaded right away: the engine loads resources automatically when it needs them, which means that in the context of the background or object image we were using as an example, we’ll only load the image when we know we’re about to render it. KIAVC is not as “efficient” at unloading resources at the moment, though: once a resource has been loaded, it stays in memory until it’s needed again, wh. In the future I’ll probably add some kind of reference counting mechanism, where we can unload resources that aren’t needed anymore (until they are), but so far this hasn’t been much of an issue, and with the small demo I wrote memory never was an issue.

    Of course, not all struct instances are related to actual resources: some of them are associated with functionality that the engine needs to provide, like BAG archiving support (the ability to package asset files in an archive, and loading from it rather than from disk), interaction with Lua scripts and pathfinding. I’ll focus on those in future posts.

    Main loop

    As anticipated, the engine is single threaded, and basically works in a loop that starts when you fire up the engine, and is only interrupted when you quit. To design this loop, I basically did what every game seems to be doing out there, so it’s nothing new:

    1. you get user input (mouse, keyboard)
    2. you update your “world”, making it progress using specific units of time (e.g., ticks)
    3. you actually render something on screen on a regular basis

    All of those three steps are repeated over and over again, even if of course you don’t always do something when dealing with any of those steps. You only capture input if you got events about something happening, for instance, otherwise you move on: if you always wait for a mouse click before you leave step 1., for instance, this means that in a single threaded application the image will freeze until you click, since step 3. (rendering) only comes later. In a nutshell, the flow is depicted in the ugliest diagram you’ll see today:

    At the same time, “updating the world” means changing the state of the resources as time goes by: deciding which is the current frame in a walking animation for an actor, for instance, depends on how much time passed; if you move to the next frame any time you get to step 2., you’ll get a super fast animation depending on how fast your CPU is. As such, this is the step where you decide how resources are updated in general, which includes if/how animations should proceed, updating the current position of actors, rooms or objects depending on user input (e.g., using previously calculated pathfinding to move the actor a step further), and so on. However this is handled, it’s important that this is kept completely separated from rendering, as otherwise you risk having a game that moves at different speeds depending on how fast the machine can draw things. KIAVC does take this into account, by updating everything in step 2. (which includes poking the Lua engine, so that pending coroutines can be resumed and the “world” can be updated there too), and only taking care of that to draw in step 3.

    In fact, the rendering step actually needs to only worry about a single thing: rendering stuff. In KIAVC, this means iterating on all things we need to draw, in order of spatial placement (the engine keeps an ordered list where the ordering is dictated by the z-plane of each resource to draw), and just draw according to the last updates that were performed in step 2. Again, the rendering part will NOT be performed any time we iterate in the loop, quire the opposite: in fact, we should only draw as often as dictated by the framerate we want the game to have. This means that, if we configured the engine to draw at 60fps (frames-per-second), then we’ll only do something if 1/60 of a second has passed since the last time we drew something on screen. Considering updating the state of the animations is performed independently of rendering, the end result is that the “speed” of the game will be exactly the same when you play at 60fps as when you play at 5fps: the only difference will be that at lower framerates you may see things a bit more choppy, and lose some animation frames, but the game will always work at the intended speed. Again, this is something KIAVC does automatically.

    File resources

    Let’s have a look, now, at some of the resources that KIAVC can register, with a few words on how they were implemented and why, and the current limitations I plan to take care of next. These are all resources that the loop introduced above uses automatically, when they’re configured to be part of a current scene.

    Images and animations

    As you can see, I’ve put images and animations together, and for a simple reason: they share a lot in common. Initially I had two separate structs for them, but since they differed only in the number of frames, I eventually confluted them together, meaning the engine now consider an image an animation with a single frame, and treats it that way. Notice that, in Lua scripts, you actually refer to images and animations using different classes, which then transparently interact with the code to register the same kind of resource: I chose to do that for semantic reasons, and try to avoid some confusion for developers. Implementation-wise, I used the SDL2_image library to deal with both images and animations, obviously, since I’m using SDL2 as a foundation.

    Most of the times you just have to provide a path to the image to register it, but considering not all images may have a transparent background, I took advantage of the SDL2 SDL_SetColorKey function to also expose a way to provide a color to treat as transparency. In the demo, this is used for instance for the blinking cursor, which has a yellowish background instead of being transparent.

    Animations are handled a bit differently. At the moment, the engine assumes the animation will be provided as a single image, presenting a row of the frames that make the animation itself, rather than separate images with the different frames. Besides, to keep things simple in this first iteration, I also made a few assumption that I’ll definitely need to address sooner or later, namely:

    1. the code assumes all frames of the animation will have the same size, that is computed automatically from the size of the full image and the number of frames (which needs to be provided by who’s registering the image); this makes sense in most cases, but there are times where this might be considered limiting;
    2. the code at the time of writing also doesn’t provide any way to specify how long each animation frame should last, and simply uses a hardcoded timer (100ms) to advance frames; this is of course quite silly and stupid, but again, it’s how I started writing the code to get something working sooner, and I haven’t fixed that yet.

    An example of an animation used in the demo (which was adapted from this excellent free asset) can be seen below, where we have a single image with 8 different frames. Since the whole image has a 320×69 resolution, and we tell the engine there are 8 frames, the engine will automatically assume it can clip it in 8 chunks of 40×69: when rendering, this clipping is performed at runtime, passing an SDL_Rect for the source to SDL_RenderCopy when drawing the current frame.

    That said, once you register an image/animation, and configure a game resource to use it, there’s nothing else you need to do: the engine will take care of the rendering for you automatically.

    Music tracks and sound effects

    Just as images and animations are implemented in the same struct, I did the same for music tracks and sound effects, since they share the same implementation; once again, they’re exposed as different classes in Lua scripts, instead, to make a distinction between the two clearer (e.g., music tracks loop, while sound effects don’t).

    The main reason why I chose to keep them together is related to how SDL2_mixer, the library I use for audio management, works internally. SDL_mixer does have different functions for dealing with music tracks and generic sounds (which they call chunks), each with their own optimizations: as explained in the documentation, chunks are decoded right away and stored in memory, while music is decoded on demand (as they may be larger). The main problem, though, is that the music related functions, while neat, don’t allow you to play multiple tracks at the same time: I considered that quite limiting, especially considering I wanted to be able to emulate iMuse kind of functionality (e.g., by smoothly fade out one track while I fade another one in), and that didn’t seem to be possible with music channels. As such, I decided to simply use chunks for both, and then expose different functionality on top of that, e.g., in terms of fading channels, looping vs. not looping, and so on. So far this seems to be working nicely!

    Whether it’s music or sounds, our chunks don’t have a pre-assigned channel, but we let the library pick one when starting to play a chunk, and we then keep track of it. Considering we can fade out channels too, this also means we need to be able to know when a channel has finished playing: for this, I chose to rely on Mix_ChannelFinished so that a callback would be invoked where I could update the mapping between chunks and channels. That said, this is one of the places where the single threaded nature of the engine, and the assumptions I had made, caused a problem: in fact, SDL_mixer uses separate threads for audio playback, and the callback was invoked from one of those threads. This forced me to add some limited locking functionality just to the audio “class” in KIAVC, so that I could use them in a thread-safe way transparently to the core.

    One important feature that is still missing, though, is volume management. I’d like to be able to expose a way to set a global volume, but that isn’t as simple as it sounds. In fact, while SDL2_mixer does provide a function called Mix_MasterVolume that would be perfect for that, it’s unfortunately only available from version 2.6.0, which is very recent and not available to many (it definitely isn’t to me on my Fedora 35!) . This means that any attempt to set a master volume on older versions would typically need setting the volume for the different channels separately and independently. That’s all good and doable, but there’s a different issue with this: since we support fading audio channels in an out, setting the volume channel actually interferes with that 😦 As such, for now I’ve kept this feature on hold, until I can figure out a better way to deal with this.

    Fonts and text rendering

    In an adventure game, text is quite important of course, so I did spend some time trying to figure out how to render text properly. I once more relied on the help of an SDL2 library for that, specifically SDL2_ttf. In fact, while I briefly considered using image based fonts (which are apparently quite common in the gaming world), they seemed to require more effort than I wanted to spend in this initial stage, and so I went for the “easy way out” of TrueType fonts, at least for now.

    In the KIAVC core, fonts are one of the resources you can register, specifically by providing a path to the TTF file you want to use, and the size of the text: this does mean that if you want to support the same font in different sizes you need to register multiple instances, but that’s probably not a big deal (as I don’t expect that to happen very often anyway). Once such a font is registered, it can then be referenced to create as much rendered text as you want, e.g., for dialogs, cursor text, random text that needs to appear in the game and so on.

    Text rendering requires a reference to the font you want to use, and then has a few settings you can specify, like the color of the text and, optionally, an outline to draw around the text itself. This allows you, for instance, to create white text with a black border for the lines your main character is speaking. Outlines did provide an interesting challenge: initially I thought I simply had to draw the same text more than once in slightly different positions (e.g., multiple instances of black text, and then white text on top of that), but while that is indeed how you do it with image-based fonts, it turned out there was an easier way of doing that with SDL2_ttf, by using some of its Blended functions. I tinkered with that until I got something I was happy with, and it seems to be working fine now. The two screenshots below, for instance (which come from the engine demo), show the outline at work for an actor speaking and for some cursor text when hovering on object, using different fonts:

    Of course, as anticipated an outline is entirely optional, and you can choose to render text without it as well if that’s what you want. The screenshot below, for instance, show how I chose to use no outline when displaying dialog puzzles instead:

    That said, there’s one key feature that, at the time of writing, is still incomplete, and is related to automatic wrapping of the code when you don’t want to exceed a set width for the rendered text. SDL2_ttf does have some _Wrapped versions of its rendering functions for text, but once more not all are available if not in recent versions, and they seem to have some quirks too: first of all, it seemed to cause some issues when using outlined text (sometimes the text and outline wouldn’t wrap at the same point), and besides there were issues with text justification as well, since text would not be centered (the ability to control that is provided by TTF_SetFontWrappedAlign, but only in too recent versions of the library). To keep things simple, for now, I added some code to manually split text to render in multiple lines when detecting a “new line” character, and that works quite nicely: it also requires the game developer to manually worry about multiple lines, though, which is definitely suboptimal, so as a next step I plan to figure out a way to arbitrarily split the text in multiple lines in an automated way depending on the maximum target width (e.g., using TTF_SizeUTF8 to estimate the resulting width of the lines before actually performing any text rendering).

    Game resources

    Actual files are obviously not the only resources you can register in the engine. There are actually other resources that play a much more important role, and it’s the ones related to key concepts in the engine from a gaming perspective. There are resources, for instance, to represent the concept of rooms, actors, costumes, objects and cursors, besides resources that are more closely related to some engine functionality like dialog puzzles and pathfinding (both of which I’ll talk about in different posts to come) .

    Rooms

    Borrowing this concept from SCUMM, a room in KIAVC is where a scene is taking place, and may not be a room at all: it may be a forest, or a close-up of a character, or end credits rolling. Historically, they were called rooms since SCUMM was created for Maniac Mansion, which being a mansion did have a lot of them πŸ˜€

    Within the engine, you typically first register a room with a unique ID (that you can use to reference it any time you need it later), and then set some of its key properties, which includes the background image you want to use, plus optionally some foreground and background layers (depending on their z-plane). The engine also keeps track of the room “position” (what’s currently displayed on the screen), which is particularly important in large rooms where you may need to follow an actor around and perform some scrolling. Besides, a room in KIAVC also needs information on the walkboxes to use for pathfinding, that is which parts of the room an actor can actually walk on and how to go from where you are to where you click (which is too long to explain here, and I’ll address in a dedicated post), and a list of actors and objects that it contains.

    We mentioned before how we can register images and sounds in the engine, so the fact that a room needs to have a specific background or a specific background music is something that’s decided when scripting a room, so from Lua in this case. We’ll focus more on this aspect when we’ll talk about the relation between Lua scripts and the KIAVC engine, as there’s a lot to say there. What’s important to point out here is that, once you set some properties from the script and let the engine know what you want a room to have, the engine, will take care of rendering things automatically for you, which includes scrolling when it needs to, rendering things in the right order (e.g., if you add a layer with a silhouette of a tree that needs to be in front, an actor will always be behind it), and things like that.

    As many resources in the engine, there are some things that are incomplete for rooms as well. A key thing that’s currently missing is vertical scrolling: I started tinkering with the engine using assets from existing games from the past (like Monkey Island and Flight of the Amazon Queen), and many of them only had horizontal scrolling (when I think about it, the first time I saw vertical scrolling in a LucasArts game was probably in a scene from The Dig, although I may be wrong!). As such, it was easy to start that way. That said, this is definitely a limitation I’ll have to address: if you think about Thimbleweed Park, for instance, vertical scrolling was very much an option there (remember the shop library with all those books? πŸ˜€ ), and more in general modern games do need that option at times.

    Another partial limitation is in scrolling itself, which can be a bit choppy at times. At the moment, I have some hardcoded values on how much you should scroll when following actors around, which is most definitely not the best way to handle that: at the very least, for instance, we should take into account the actor speed. Besides, there could be nicer effects to apply when scrolling as well: I mean, look at how glorious this tech demo from TP was!

    Actors and costumes

    An actor in the engine is basially anything that can potentially talk, walk around, be talked to, etc. As such, actors are used not only for the main character (or characters, if you want to do something Γ -la Maniac Mansion, Thimbleweed Park or Resonance with multiple characters you can control), but also NPCs you’ll engage with. Notice that an actor doesn’t need to represent a person: it could be an object, or an animal, or whatever else, as long as you need it to do more things than a more static object can do.

    Anything related to how an actor should look like (still or animated) is not a property of the actor itself, but a so-called “costume”. In a nutshell, in the engine registering a costume means associating a set of previously registered images and/or animations to different activities: in the demo, for instance, we register four different static images for the detective standing still (one for each direction), four animations for talking, and four animations for walking. This set constitutes a “costume”, that can then be assigned to an actor, so that when an actor, for instance, walks around, the engine knows it needs to use the related animations from the associated costume. Separating actors from costumes is important since it allows us to re-use the same costume for different actors, without having to duplicate registrations: this is again something I did in the demo as a necessity, since I only had a single animated character to play around with, and so I ended up assigning the same detective costume to both the player actor and the NPC you talk to. In actual games, this may be useful in different scenarios: in SCUMM, for instance, they used this trick for the Nazi soldiers in the Indiana Jones 3 game (with some more advanced tricks that are not part of KIAVC yet). Besides, it allows for more flexibility for a single actor as well: if you need different animations for an actor depending on whether he’s wearing jeans or a tuxedo, you simply define different costumes, and then set one or another for the actor depending on the state.

    The engine supports a few additional properties that can be dynamically applied to actors in a scene, like their position (relative to the room), their walking speed, their z-plane index, the scale factor (e.g., to make them smaller when going towards a mountain), and so on. Actors are also the only resources that the engine can apply pathfinding to, since they’re the only resources that can be talked to walk to a specific point in the room. Rendered text for an actor is also automatically placed on top of them by the engine, and their duration roughly timed to how long the string is.

    That said, there are a few things that we still need to implement as far as actors are concerned, which are mainly limitations in the set of images and animations you can provide in a costume (and so for rendering an actor). At the moment, you can only specify three different things: images to use for standing still, animations to use when talking, and animations to use when walking, and all of them only for 4 directions (up, down, left, right). When you look at how SCUMM works, it’s less static than that, as you can also define “custom animations for the so-called “special animations”, that is those animations you’ll only use for specific cases (e.g., Guybrush grinning, or raising the bone to the sky and his pants falling down). As such, in the future there will need to be more flexibility in KIAVC as well, so that game developers are given more freedom should they need it.

    Objects

    Objects are a special kind of resource in the engine, as they have many different purposes. On one end, they can indeed represent actual objects, meaning objects you can possibly pick up and interact with, all with their own images or animations to be represented in the game world. On the other end, they could be nothing more than props, or even abstract UI elements you may want to be displayed on the screen (e.g., an inventory, a settings button, a knob of some kind, etc.). At such, the engine is configured to act differently depending on how scripts register and configure/update objects.

    As far as the engine is concerned, an object is basically a structure identified by a unique ID plus a few optional properties, like the room the object is in, if it’s owned by an actor, and so on; you can also specify whether the object is an interactable object, a prop (something you only put on the background to give some color, like an animated lantern), or an UI item, which also dictates whether the object position will be relative to the screen or the room; in case the object is configured to be interactable, coordinates for how to detect when the mouse is hovering on the object can be provided too, so that the engine can work on that and notify the script when hovering on an object starts/stops.

    It’s important to point out that an object may or may not have an image/animation associated with it: to make a simple example, in the demo there are several objects you can interact with, that are actually part of the background, though. When you look at the restaurant, hovering over it does display its information, even though there’s no actual restaurant object: simply speaking, we registered an object and told the engine those coordinates had to be associated with it, specifying what needed to happen when we interacted with it.

    Objects as part of the UI rather than the room are particularly interesting for different reasons, as they’re what allows you to provide a custom user interface depending on how you want your game to work and look like. In the demo, for instance, I wanted to add a simplified inventory permanently on top: to do that, I first of all created a non-interactable object called “inventory”, marked it as a UI element, and then told the engine to display it on top of the screen (x=0, y=0); then, for objects that can be picked up, I added some more code to the script so that when an object (i.e., the skull, in my demo) was picked up, it would also be changed to have a UI status (while before it was part of the room), and its position changed so that it would fit one of the slots on the inventory, as shown in the screenshot below.

    Future enhancements to the demo may include additional UI objects for, e.g,, arrows to use when the inventory starts to fill up, so that when you click one the index of the inventory changes. You may even want to implement the inventory in a completely different way, e.g., with a button that makes a bag appear with the objects in it: that could be implemented with a UI object acting as the inventory on/off trigger, and a different UI object that only appears when you click the inventory button to show the inventory content. The idea is that this could be used for implementing a ton of different things (possibly even a SCUMM-like verb menu!), but of course whether the code is indeed that flexible or not already is very much to be verified.

    One thing I’d like to implement soon, for objects, is the ability to group them, so that we can enforce group actions: to make a simple example, if you have multiple UI objects all part of an “inventory” group (the group background, the objects themselves, other buttons), a showObjectGroup(“inventory”) or hideObjectGroup(“inventory”) could tell the engine to start or stop rendering multiple objects at the same time, rather than independently trigger the visibility of each object we want to impact (that would force the script to keep possibly more state than needed).

    Cursors

    It may seem weird to include cursors as part of the resources you can register in KIAVC, but there’s a reason for that. In fact, while in most cases the pointer may always remain the same, there may be cases where you may want it to change in the game: e.g., a blinking cursor when you move the mouse around, a different cursor when you’re hovering over something, or an object icon as the cursor when you want to try and use the object with something. Considering that each cursor resource you create can be associated with a specific image/animation, it’s quite easy to then script cursor changes to tell the engine which cursor to switch to dynamically. The animation below, which again comes from the demo, shows a practical example of this, where we start with the “regular” blinking mouse, which then changes when we’re hovering on the skull, and finally “becomes” the skull when we click it to select it, just to become the “main” cursor again when we right click to deselect the object:

    As far as the engine is concerned, once a specific cursor resource is dynamically configured to be the active one, it will instruct the engine to render the right thing as you move the mouse around. At the time of writing, you can configure two different types of cursor: the “main” one (the default cursor that will be displayed), and a “hotspot” one (the cursor that should be automatically used when hovering over something). The reason for having two sets of cursors configurable in the engine is mostly avoiding too much back and forth: in fact, while scripts are notified when hovering over something happens, and so tey could automate a cursor change accordingly, since that may happen a lot it felt like too much guidance needed from scripts, and so an additional burden on developers. That said, engine can also be configured to add some text to a pointer, which gave me the opportunity to still use those hovering events in the scripts to react somehow: specifically, in the demo I chose to use those events to tell the engine to use the related object name as the cursor text, which is one more thine the image above displays.

    In theory, these dynamically configured cursors could also be used for using contextual cursors: e.g., you may want right clicking to change the action you want to perform, so that the cursor icon can change accordingly (e.g., a mouth to talk, or a fist to pick up or use things). In practice, I haven’t really checked, but that could be some nice homework for interested developers :mrgreen:

    That’s all!

    I hope this wasn’t too boring, but I felt I needed to cover some of the foundation first, before addressing more interesting and “appealing” topics like scripting, pathfinding, dialog puzzles and so on. As explained in this post, a lot of the “meat” is there already, but there’s also quite some work to do in order to finalize the implementation of what’s available (e.g., more control on animations, automated splitting of text on multiple lines, vertical scrolling, etc.).

    In case you noticed anything that smelled weird to you when reading the post, please do let me know! I implemented things in ways that made sense to me, but that may be quite differently from how games are usually developed. As such, feedback from people actually working on things like this for more than fun would be great to have.

    See you soon again for another post!

  • Introducing KIAVC!

    Introducing KIAVC!

    This is an amazing time to be alive, if you’re a fan of point and click adventure games. Ron Gilbert and Dave Grossman shocked us all when they announced they were working on a new Monkey Island game, but as if that wasn’t enough, a sequel to Flight of the Amazon Queen (one of my favourite games of all times) was announced by John Passfield too! Not to forget other nostalgic comebacks, like the upcoming Simon the Sorcerer: Origins prequel and the Colossal Cave 3D Adventure remake, or soon to be published games like Old Skies by fantastic developers like Wadjet Eye.

    All these exciting news eventually revived an old dream of mine, that is writing my own engine for an adventure game. I actually tried to start one a couple of decades ago, which ended up nowhere, but this time, taking advantage of the summer break from my job, I decided to focus on that more seriously. Eventually, I got to a point where most of it is there, and even if there’s still a lot of work to do, I decided to share this first shape of my new engine, called KIAVC! And to make this even juicier, the engine is completely open source, and available on GitHub.

    This post will provide a brief introduction on the motivation and the choices I ended up making, without going in much detail: in fact, time permitting I plan to then write more detailed blog posts on different aspects of the engine (e.g., structure, rendering, scripting, pathfinding, etc.), in hope that other developers may look at it and point at things I’m doing exceptionally wrong!

    Another engine? Why?!

    I think I should clarify something right away: I have absolutely no ambitions for this project. I’m not interested in making it profitable, making it grow, or even see it “succeed”. I do have a job, working on things I love that are quite fun too, and I’m not even remotely considering a change in “career” (which is why I’ve waited for my summer break to work on this). The only thing I wanted to do with this project was trying to figure out how to write a point and click adventure game, studying the giants from the past and trying to understand how to replicate the same set of features. As such, it’s been a hugely educational and fun experience for me so far, as I’ve learned a lot in the process (and I’m sure I’ll learn even more going further).

    As such, if you’re looking for a professional engine to create your commercial game, by all means this is NOT the one for you (or at the very least, not yet). There’s a ton of excellent options out there, like AGS, Escoria (Godot), Adventure Creator (Unity) and many more. These are all excellent solutions when you just want to start creating your game, which again was NOT my objective: while I may end up creating a game to validate my efforts on KIAVC, sooner or later, what I really wanted to do was check how an engine works, what parts you need, how to make it configurable and flexible enough for different use cases, and so on. If it turns out people like it and start using it, all the better, but that’s not what I’m aiming for (even though I would appreciate contributions and feedback!).

    Now that this is out of the way….

    What does KIAVC stand for?

    As for many projects, I created the name first, and only worried about how to make an acronym out of it later… πŸ˜‰ Technically speaking, it stands for “KIAVC Is an Adventure Videogame Creator”, but the truth is a bit different!

    I’ve mentioned Monkey Island before, and that’s not a coincidence: that was indeed the game that made me discover adventure games, and made me fall in love with the genre, way too many years ago. That game, as many others published by LucasFilm at the time, was written using an engine created by Ron Gilbert called SCUMM (Script Creation Utility for Maniac Mansion), which included a set of tools for different purposes. I always loved “SCUMM” as a name, which as most of the tools they wrote was named after a bodily fluid. Since I wanted to write an engine that tried to work a bit like SCUMM itself, I decided to call it “KIAVC”, which is a Neapolitan word that has pretty much the same meaning :mrgreen: (NB: it’s actually “chiavica”, but in Neapolitan the last “i” and “a” are basically muted!)

    Learning from the best!

    Before starting, I of course decided to first delve in some studying, starting from videos that would address SCUMM in a bit more detail, like this very interesting post-mortem on Maniac Mansion (that provided interesting insights on some engine choices and/or limitations), this really look deep dive in the Monkey Island code (where some scripting examples where provided too), and this overview of what led to Thimbleweed Park (which again provided interesting bits here and there on some technical aspects). Of course, there’s only so much you can learn for videos apart from a generic overview, so I soon started looking at some other material as well.

    This blog post, for instance, was really enlightening and helped me better grasp some fundamental concepts like “rooms”, “actors” and, most importantly, the role of scripting in all this, especially for automated actions. That blog post also contains a link to a complete tutorial on SCUMM itself, in the form of a technical document that I assume was used internally at LucasArts at the time for new hires: that document was also very helpful for understanding some of the mechanisms a tool like SCUMM was based upon, and how people could interact with it or implement the scenarios they wanted to realize.

    That said, there’s not just SCUMM out there, and one of the most interesting source of information I could find was the Thimbleweed Park blog. If you’re not familiar with Thimbleweed Park, it’s another amazing point and click adventure game authored by Ron Gilbert and Gary Winnick a few years ago, and partly crowdfunded via Kickstarter: as part of the crowdfunding interactions, a good chunk of the development process was partly documented on the blog, which provided interested developers with a ton of precious information. You could read about Ron and Gary talking about their choices for rendering, or scripting, how they dealt with pathfinding or dialog puzzles, and much more. Of course, there’s only so much they could write in a blog post, but all the information they decided to share was always quite precious to my hungry mind! Most of the choices I made for my engine were blatantly inspired by what Ron and Gary talked about here.

    Looking at how other games from the past were written was also quite refreshing and interesting, especially to understand how people not working at LucasArts approached their development instead. I anticipated before how I adore Flight of the Amazon Queen, and I was exctatic when I managed to find a very long and detailed blog post that John Passfield (one of the authors of the original game, and of the upcoming sequel) wrote on the FOTAQ development process. This article is particularly interesting, since John shares what his process was in trying to write a new engine from scratch to try and emulate what SCUMM could do, and it’s often a very interesting read. Just to make a simple example, I loved the insight on how you could use walkboxes as triggers to scale characters (e.g., when you’re going further away in a location), which helped me better understand how you can use different resources for different purposes.

    Last but not the least, I also had a look at how other fellow developers handled problems similar to mine, and so I must give a special kudos to Mic Urloon and Jaap who shared a LOT of information on an adventure engine they were imlementing a few years ago in a dedicated blog: not sure if they eventually finished it, because there’s no other post after December of 2015, but they seemed to be at a very good point. In particular, the information they shared on how they approached the challenges of scripting, pathfinding and dialog puzzles was really fantastic, and while I ended up making different choices in some cases, their articles were incredibly educational, detailed and fun to read.

    So, how does KIAVC work?

    I’m a C developer (it’s what I code in my job every single day) so I decided to use C for my engine as well. I realize many will be frowning now (“Why not C++? Why not Rust? Why not Go? Why not <insert-my-favourite-language-here>?”) but again, this is a pet project, and so you use what you’re already familiar with. For rendering I relied on SDL2 and its companion libraries (SDL_image, SDL_ttf, SDL_mixer), which made sense since: (i) I had already tinkered with SDL a bit, in my first attempts at coding a game ages ago, and (ii) it does make it incredibly easy to get something working on different platforms (I work on Linux but managed to target Windows as well already; who knows, maybe MacOS in the future too!). So far I’ve been very happy with it, and reading the above mentioned Thimbleweed Part blog, it looks like Ron does rely on SDL2 for some aspects too, which means it definitely cannot suck!

    I chose a single thread and event based pattern for the engine. While this may come back biting me in the a$$ in the future, it looked like the simplest way to get started, and not worry too much about race conditions, locking or other threading problems (that I’m all too familiar with, since my daily job project is heavily multithreaded). So far it seems to be working nicely, and is quite lightweight too, but of course things may differ once I get the engine working on more CPU intensive activities (e.g., high resolution graphics). This also allowed me to follow what seems to be a common pattern in videogame development, though, that is:

    1. dealing with user input first (e.g., mouse, keyboard);
    2. updating the game “world”, using a specific unit (e.g., ticks)
    3. actually rendering the game using a predefined framerate (independently of ticks).

    There’s a single exception to the single thread approach, which is related to audio management: in fact, as anticipated I’m using SDL_mixer to deal with audio streams (whether it’s background music or sound effects). Since I added features like support for fade in/out (e.g., to emulate iMuse, *wink* *wink*!), this means I rely on functions like Mix_ChannelFinished to know when some channels have finished playing: since SDL_mixer uses a separate thread for audio than the main thread in my application, though, this forced me to add some limited locking functionality to the audio related code, so that I’d avoid incurring in ugly race conditions.

    For scripting, instead, I decided to use Lua. While in the TP blog Ron explains he chose Squirrel for his new engine, and that he’s not very fond of Lua, I once more chose the “devil I knew”. In fact, a few years ago I had already worked on integrating Lua with C code (for the WebRTC server I authored), and so I already had some familiarity with it. I do like its syntax and how easy it is to write code in it, and also getting it to interact with the C side of things (and viceversa) was easy enough for my needs. This is an example (using well known assets of well known games just for testing purposes, I swear!) of a cutscene I wrote in Lua, for instance:

    startCutscene()
    fadeIn(1000)
    activeActor:walkTo(306, 133)
    waitFor(activeActor.id)
    waitMs(800)
    activeActor:look('right')
    waitMs(800)
    activeActor:look('left')
    waitMs(800)
    activeActor:look('right')
    waitMs(800)
    activeActor:look('down')
    activeActor:say('This place looks eerily familiar...')
    waitFor(activeActor.id)
    stopCutscene()
    

    As you can see, it’s relatively trivial to write a sequence of steps, and functions like waitFor or waitMs ensure that we only proceed at the next step when the engine itself has finished performing what we asked it to do, or after enough time has passed. The same wait/signal kind of approach is also used for dialog puzzles, for instance, where we may need to wait for things to happen in the engine before the script can go on. Thanks to the use of Lua coroutines, we can launch multiple scripts at the same time to do different things without keeping the engine waiting.

    Eventually I may have relied a bit too much on Lua, as Lua code is sometimes responsible of things that may actually belong more to the engine itself (more on this in a dedicated post later on), but overall I’m quite happy with how flexible it is, and my first attempts a writing a demo mini-game (which the snippet above is from) have been quite successful in taking advantage of the features I implemented in the engine so far. I even ended up implementing a simple in-game console I can open, from where I can write Lua code to be run: this allowed me to quickly test and prototype functionality in a quick way, without a need to restart the game and reload the scripts. A simple and dumb example is presented below:

    What’s in there already?

    Without bothering you too much with details (which again may come in future posts), these are the functionality that I have implemented so far (in some cases partially, or as a WIP) as part of the C/Lua integration:

    • support for configurable (via scripting) resolution, scaling and framerate, plus window title and icon;
    • support for fullscreen and dynamically rendered scanlines (to be improved, though);
    • support for mouse (left/right click) and keyboard interactions (script is notified, in order to perform the required action);
    • support for registering static images and animations (even though animations currently lack a dynamic configuration of timing), to be used in different contexts;
    • basic support for timed fade in/out;
    • support for playing multiple music tracks and sound effects at the same time, with fade in/out support;
    • support for dynamically changing cursors;
    • support for concepts like rooms, actors and objects, and how they can interact with each other;
    • support for room backgrounds and multiple “layers” (currently used for z-plane purposes, but in the future they may be used for parallax effects as well);
    • support for changeable “costumes” for actors (which was definitely “stolen” from SCUMM!);
    • support for following actors with the camera (room scrolling);
    • support for text rendering for different purposes (e.g., actors saying things, dialog puzzles, generic text, etc.) and using different fonts (text placement, e.g., splitting on multiple lines, needs work though);
    • support for room-, actor- and object-scripts that can be launched for automation (e.g., an NPC that must do something in the background, or activity in a room that is independent of user actions);
    • ability to start and stop cutscene-mode (which disables the cursor and any interaction);
    • basic support for dialog puzzles (which at the moment are hardcoded in the game, which I definitely want to change);
    • A-star pathfinding on rectangle-based walkboxes (I’ll probably change this to more generic polygons in the future, but using rectangles made it MUCH easier to get something working);
    • walkboxes that can also be used as triggers to change the scale factor of actors, or launch generic scripts (e.g., to have a bouncer stop you when you hit a walkbox, until you solve a puzzle);
    • basic support for translated text (where you provide different translations for the same sentence, and in the code you just reference their ID so that the right translation is used/displayed);
    • very basic support for objects to be used for UI purposes (which I plan to use for stuff like inventories, settings, etc.);
    • basic support of storing game assets (scripts, fonts, images, audio, etc.) in an archive format called BAG (which I’d like to obfuscate in the future, since at the moment it’s just a structured collection of all the files as they are).

    As you can see, the engine ships a lot already, which is quite exciting (to me) to see working in the demo mini-games I’m working on to test the functionality as I implement it. That said, most of the features are far from complete and do need some work before they can be considered really done.

    What’s missing?

    Apart from improving the existing set of features (which are often either incomplete or a bit too complex/byzantine to use at the moment), there are some features that, at the time of writing, are sadly still missing. The two main ones I can think of are savegames (the ability to save a game state, and restore it later) and proper tooling.

    Support for savegames is, to be honest, not that important at this stage: while it’s definitely a fundamental feature to support in a shipped game, for a quick demo where I’m prototyping stuff it’s way less needed, which is why I’ve neglected it so far. That said, I do have it in the back of my mind, and I do have a few ideas on how it could be implemented. Once more, the TP blog provides interesting info on how Ron solved this for his own needs: I think I’ll probably choose a slightly different approach, though, mostly because of the different ways we’ve implemented our own engine. In fact, from what can be seen in the blog post, Ron is actually saving the game state in the C++ code of the engine itself, accessing the Squrrel resources from there. As I anticipated above, I’m delegating quite a bit to Lua scripting for keeping state, at the moment, in particular using pre-loaded utility scripts (e.g., for creating rooms, actors, etc.) that can be seen as a Lua “extension” to the C engine itself: as such, while it’s indeed possible to read the Lua objecs from the C code of KIAVC as well, it sounds much easier to lazy ol’ me to implement a Lua utility (that game developers can use) to automatically create a Lua table with the info we have to save, encode it to a string (e.g., a JSON string), and then just pass that to the engine, so that it can be saved somewhere; restoring might work in a similar way, where the engine feeds the Lua script with a string blob, and the Lua utility that deserializes it and enforces it on the objects it has access to. Assuming that only some specific properties of each object needs to be saved, this looks like a potentially viable approach.

    Tooling is another problem that will become substantial in the future. In most cases, wiring rooms, actors, etc. in a Lua script is not that hard (you just set some properties for what you want to be used and done), but there are cases where help from some tool would be quite desirable. A simple example are room walkboxes: in the current version of the engine, you’re supposed to provide a list of rectangles that constitute the different walkboxes of a room, as a set of “from” and “to” coordinates (top/left corner, bottom/right corner). It’s clear that, while you can manually write them on your own, this can quickly become a nightmare when you have to match them to the room background you’re using: sure, you can use an image editor to check the coordinates to use (it’s what I did for my demos), but it’s still a very slow and cumbersome process. The SCUMM suite itself made an extensive use of tooling for different purposes, allowing artists and editors to work on aspects of the game independently of the scripting itself (e.g., to place objects on top of a background, specify interactions in a visual way, and so on). This is one area where I expect to be quite slow, not only because I probably lack the proper skills, but also because I may not know exactly what people are used to: engines like AGS, for instance, come with their own tools as well, so there may be best common practices I’m completely unaware of since I’ve never used engines like those myself. Hopefully, should enough interest grow around KIAVC, generous developers may contribute some tools in the future πŸ˜‰

    Finally, as anticipated I can already create executables for Linux (I’m a Fedora user) and Windows (using cross-compilation on Linux via MingW), but not MacOS yet. I’m admittedly completely ignorant on that matter, and don’t know if getting a MacOS executable might be as easy as I did for Windows, but at least in theory it should be possible (my Janus WebRTC Server can be compiled on MacOS too, for instance, and I did nothing to make that work!). Again, this is an area where I hope I’ll get some help from interested people, e.g., with fixes to my ugly Makefile.

    That’s all!

    I hope this first intro picked your interest enough to give the engine a look. There’s not much in the existing repo in terms of scripts to use (just a few mini-games aimed at showcasing features) but hopefully, despite the current sad lack of proper documentation, they’ll be enough to get you to play a bit with the engine and get a quick demo of your own running! And, who knows, maybe someday someone will write a complete game in KIAVC too: I know I want to, but that may take a while, since while I’m an average developer, I’m most definitely a crappy artist πŸ˜›

    Stay tuned for upcoming posts that will go a bit deeper in the engine features!