There’s been a big dip in the development schedule. Family urgencies and other commitments sapped a lot of my time and energy, but I’m back to developing again.
Time to look at progress for the previous week, and maybe a few other treats, besides.
So, you might have wondered – since you’ve likely seen enough fragments of the process so far – what the story source actually looks like. A number of you have expressed interest in seeing what it looks like.
Thus, I have a simplified example. This is a minimally-functional story file:
:Actors 1,Prince Hamlet,,Playable 2,Lord Polonius,,Playable 999,Test Character,,Nolog|Playable|NODEMO :Locations // id,name,flags 1,Somewhere,,Place :Data #Background:gradient1.png #ActorBackground:title.png #ActorBGM:Sinfonietta5.ogg #Font-Narrative:font1.ttf #Font-Narrative/italic:font1i.ttf #Font-Speaker:font2.ttf #DFont-Narrative:fonta1.otf #DFont-Narrative/italic:fonta1i.otf #DFont-Speaker:fonta1.otf #Version: _DATE_ #Name:EXAMPLE // Kerning tables |Narrative/italic:an:-1 |Narrative/italic:dd:-1 |Narrative/italic:od:-1 |Narrative/italic:fi:4 |Narrative/italic:fr:2 |Narrative/italic:fu:4 |Narrative/italic:f :4 |Narrative/italic:ha:-1 |Narrative/italic:hi:1 |Narrative/italic:is:1 |Narrative/italic:jo:1 |Narrative/italic:oe:-1 |Narrative/italic:on:-1 |Narrative/italic:pe:-1 |Narrative/italic:th:-1 |Narrative/italic:uc:-1 |Narrative/italic:ve:-1 |Narrative/italic:wh:-1 |Narrative/italic:Ca:-1 |Narrative/italic:ta:-1 |Narrative/italic:Th:-1 |Narrative/italic:ra:-1 |Narrative/italic:xp:1 |Narrative/italic:yG:2 |Narrative/italic:y :6 |Narrative/italic:yo:1 |alt-Narrative/italic:fi:-4 |alt-Narrative/italic:st:-1 /Actor/1 .gender=male .age=30 .agegroup=adult .description=Hamlet (30) is a Prince of Denmark /Actor/2 .gender=male .age=75 .agegroup=elderly .description=Polonius (75) is chief counselor of the king, and the father of Laertes and Ophelia. :Script Chapter 1 An example Scene 1 Meta Summary:Preliminary setup. Arc:Startup Introduction:Yes Branches:2 Roles None Requirements None Performance { // Global setup bgm:*:BGM-generic.ogg // Play some background music } Scene 2 Meta Summary:Some sample performance Arc:Startup Branches:0 Roles role:1:name=Lord Polonius [polonius] role:2:name=Prince Hamlet [hamlet] Requirements None Performance { location:*:Somewhere // Move actors to the same place polonius>"What do you read, my lord?" hamlet>"Words, words, words." polonius>"What is the matter, my lord?" hamlet>"Between who?" polonius>"I mean, the matter that you read, my lord." hamlet>"Slanders, sir: for the satirical rogue says here that old men have grey beards...." polonius:Though this be madness, yet there is method in't. polonius>"Will you walk out of the air, my lord?" hamlet>"Into my grave." polonius>"Indeed, that is out o' the air." polonius:How pregnant sometimes his replies are! :A happiness that often madness hits on, which reason and sanity could not so prosperously be delivered of. :I will leave him, and suddenly contrive the means of meeting between him and my daughter. polonius>"My honourable lord, I will most humbly take my leave of you." hamlet>"You cannot, sir, take from me any thing that I will more willingly part withal." hamlet>"Except my life, except my life, except my life." polonius>"Fare you well, my lord." offstage:[polonius] hamlet:These tedious old fools! // There are no more scenes, so the story ends here. }
Like I said, it is minimalist, setting up a single short scene, which is playable from either the perspective of Polonius or Hamlet.
I don’t have any of the more complex mechanics here, like conditional text, special effects, partially-specialised scene-templates, etc, but at least that gives you the gist of it.
Now, on to business.
KnStore
KnStore is the most fundamental data-store for Argus stories (other than the storage of the compiled story-file itself). It’s an Exclusion Logic structure which stores character data, character flags, events (kind of a global flag), knowledge, opinions, inferences, and inference rules.
We dive in and out of this structure constantly (by which, I mean up to several thousand times per second), storing and fetching data. If you have to save or restore the game, this is the primary data-structure you need to worry about.
It’s also some of the oldest code sitting in the engine, having been originally produced around week two or week three.
I revisited it, because I figured that I could probably do better.
Yes. Yes, I could.
Because it uses exclusion-logic, inserting new statements into the store can cause sections of the tree to be suddenly invalidated during the insert. That might be a single node (often) or an entire subtree of data (rarer, but not exactly uncommon). The new statement goes in, but the invalidated data had to be pruned out first, as a part of the process.
It turns out that this was an excellent place to add a thread. The invalidated node/subtree is now disconnected, and handed to a separate thread for deletion. It doesn’t actually matter when that happens, because from the moment it is disconnected, the insert can complete, and the entire structure is whole. The deletion thread carries on cleaning up, and it doesn’t matter how much or how little time it takes to do that.
This produced a significant saving in cycles on the main processing (read: most important) thread.
There were also a number of places that the code could benefit from some optimisation improvements (I was in a bit of a rush to get a working prototype when I wrote that code), so I dived in and improved the overall flow (bonus: Also more readable now). The extra optimisation pass has more than halved the time we spend in the KnStore code.
I am well-pleased with all of that.
Title Screen / Main Menu
Up until now, things jumped from the obligatory introductory logos straight into the character-selection screen. I felt a little odd about it, because most games have a main-menu/title-screen that comes up first. I didn’t have one, but now I do. I should be able to use the same mechanisms to skin it up that work on the rest of the interface widgets, though I haven’t had a chance to properly try it out. Nevertheless, I’m happy that it’s there.
Also, putting it in exposed a few poor assumptions I’d made about when and precisely where certain onscreen elements were initialised, which necessitated going back and properly sorting them out. There’s a little duplication of labour between the main-menu and character-selection screens, but that isn’t crucial, and I can do a fixup pass on that later on.
Texture Memory
I’ve been operating, to-date, as if texture-memory (VRAM) was essentially unlimited. Mostly because I’ve never actually hit that limit, though eventually I was going to, I’m sure.
Not managing your texture-memory leads to bad things. It’s why Fallout 4 crashes silently to the desktop at times, for example. When you’re out of texture-memory, you’re out of it, and there’s nothing to be done.
I’ve got three levels of image management. There’s mmap()ed data on disk, there’s SDL_Surfaces (image data in memory), and SDL_Textures (image data in the graphics card memory).
I’ve essentially ripped out all of my image-management code and written entirely new encapsulations for each of these, enabling them to be separately and jointly managed in a far more sensible manner.
Additionally, all of the key operations are now methods on those encapsulations, rather than having to hand them off to other code that must handle them as largely opaque blobs.
I can now expire or discard textures and surfaces when I need to, to reduce the overall usage of either VRAM or system-RAM, and needed textures will be automatically regenerated on-demand, if required.
Happy with that. There’s still some reliance on the story author not to go to overboard with textures within a scene, lest they exceed the limits of the player’s hardware, but that’s not the worst thing in the world.
And that’s enough of a progress report for one week.
With a little good fortune, I’ll be able to move forward some more this week and have another progress report for next week.