4 min read
Interlude: Wizard x Wizard

Occasionally on a long haul project, you need to remember you’re good at programming rapidly prototype another project.

My wife, a published author, played Dream Daddy and had a spark of creativity. She wanted to make a dating game about wizards dating other wizards in a magical town.

And I thought, wow, that sounds way easier to program than Skymagi.

In less than a few hours, I cludged together TypeScript, React ⚛️, vite, and Electron. Using Pixi.js for rendering on a webgl canvas and React to build a UI on top.

Wizard x Wizard

Modern web development tools are eons ahead of Game UI libraries. Unfortunately, the DOM overhead is a huge performance price, but that doesn’t matter for a wizard dating game!

So, it’s basically just dialogue, right?

Primarily, a dating game is about picking dialogue options. WxW also features D&D-style dice rolls and a map to navigate. I could’ve used Ren’py, but my wife knows TypeScript, and browser compatibility is a nice-to-have.

So, a dating game has…

  • Dialogue choices
  • Dialogue branches
  • Scenes with different backgrounds, characters
  • Some animations on specific cues
  • Variables or simple functions like dice rolls
  • Markdown support

While researching methods to write markdown with special markup for dialogue, I stumbled upon this gem, Ink 🐙

Ink is a domain specific language for narrative games. It has everything on my list and more:

  • Choices, branches
  • Full control flow with knots and diverts
  • Variables, functions, listeners
  • Includes, randomization
  • Choice elimination
  • VScode plugin
  • Vite plugin for autocompiling/bundling .ink files

The ink runtime is usable in javascript (inkjs) and other languages (like… Java!?)

It’s everything I dreamed of.

Ink integration

Within just a few hours, I was able to parse an ink script that sets a scene’s background, has labels for individual characters, and has choices.

// story.ink
#background library
ravenna: Good morning.
dragon: I said hey, what's going on???
ravenna: Let's go to the classroom
#background classroom
Great hanging out with you. // default narrator
+ [Say hi] Hi there!
+ [Say bye] Bye now!
-> END

Ink’s tags worked perfectly for basic directives. Most of the code just called story.ContinueStory() and parsed tags and displayed outputs (e.g. reading labels).

This demo shows off a few other tags. I created a #show tag to add characters to a scene, set their positions.

#background library
#show ravenna left faceRight
#show dragon right
r: Good morning.

Similarly, there’s a set of variables created for skills that unlock special dialogue options. Ink made this incredibly simple.

d: Do you know how to brew a healing potion?
+ {knows_alchemy} [Yes, I've studied alchemy before.]
+ [No, I don't. Can you teach me?]

In TypeScript, I simply had to set parameters on the story when loading.

story = new Story(inkJson);
story.variablesState['knows_alchemy'] = true;

Character Creator

Of course, the best part of playing a dating game is picking your protagonist.

Using React to build out a few forms and menu structure, communicating to the game (and back) with a simple EventBus, and using Ink’s variables for main character artwork, a basic character creator was ready!

And with that, my wife can start writing (and updating features as-needed), and I can return to Skymagi development.

(And Wizard x Wizard gave me a really good idea…)