Development Progress Report, week 66

In which a valuable lesson about Lua is learned.

This week, I modified the expression-evaluator to accept expressions in Lua, and throw those to the Lua engine. It’s pretty cool. I messed up a few times with some of the functions I wrote to allow Lua scripts to query engine/game data, but it went pretty smoothly.

Buoyed by this success, I then decided to revise scene-preconditions.

At any given point during the game, there’s one or more scenes in the pending queue that might be able to run. All things being equal, we just take the first scene off that FIFO queue, and run it.

But things are rarely equal.

Some scenes have chronological requirements, where scenes with an earlier timestamp have to be run before scenes with a later timestamp. Some scenes have an explicit list of one or more scenes that have to be completed before they can be run.

Some scenes cannot have their casting-requirements fulfilled right away and are deferred (or if flagged in a certain way, discarded instead).

And then we get to the odd conditions.

Sometimes a scene needs to take place after certain points in the narrative, but we can’t explicitly say which scenes it has to follow. For example, scene 330 needs to take place before scene 291 and after scene 120, but either scene 280 or 290 have to have happened first, because another area of the narrative could have branched to either one.

It’s not stupidly complicated, but you have to be able to express it.

So (after rewriting preconditions with Lua) it looks like this:

Scene 330
 Summary:[spoilers omitted]
 Arc:The Red Lighthouse
 Precondition:completed(290,0) or completed(280,0)

And I must say, this failed spectactularly.

The functions I provided did what they were supposed to. They checked the completion state of the indicated scene, and returned a zero or non-zero value. The code that triggered execution of the Lua expression went off without a hitch, and Lua did its thing.

So, what went wrong?

Lua’s boolean operators (andornot) don’t behave like you expect them to, except under very specific circumstances.

Boolean logic is one of the first things you learn when you start programming.

You expect boolean or to work like this:

0 or 0 = 0
0 or 1 = 1
1 or 0 = 1
1 or 1 = 1

That is, if any operands are true, the result is true. Only if all of the operands are false is the result false.

Here’s the corresponding result in Lua:

0 or 0 = 0
0 or 1 = 0
1 or 0 = 1
1 or 1 = 1

… and that odd little logic was killing the sense of my expressions, and causing the Narrator to deadlock when trying to figure out which scenes were runnable.

So, here’s the large, red warning-label:

Lua didn’t have proper boolean types until quite recently, so and and or, particularly, are not normally boolean operators. Instead they act like evaluation-short-circuit/chaining operators (just as && and || can be used in C/C++) when applied to most value-types.

But that’s not what I wanted.

Lua now has an explicit boolean type, and and, or, and not only work as expected when the operands are of that type and no other!

I was returning integer values from my functions, and expecting them to be treated as boolean values. Lua doesn’t do that (in any way you’d expect), so I had to be careful and explicit about making sure the type was correct throughout.

And then it worked, but golly did I scorch a couple brain-cells finding out why it didn’t in the first place.

Perhaps I’ll have saved you the same problem in future.

I also found an annoying bug in a common story-path where a character overheard a conversation between Tracy and Ella after the character had technically left the scene. Somewhere along the way, I’d deferred moving the character to their next location, so the engine treated them as still being present (because they were).

That was easily sorted by wrapping the rest of the scene up in a conversation-scope (to exclude the character who shouldn’t be hearing it), but I don’t really know just how long that bug’s been sitting there. It’s a common branch, but one I normally skip through when I’m testing.

There have been a raft of cosmetic and optimisation improvements at various points as well, including replacing the oldest image-asset in the project (the loading/busy screen, with the yellow tape). If that doesn’t ring a bell, it’s probably because it generally only appears for a maximum of a couple of seconds, ever. Normally it flashes by so quickly, it barely even registers.

There’s also work on the story happening. It’s a bit slow, but picking up the pace slowly.

And that’s all for this week! Thanks for taking a look!