Numenera Playground Update

4 minutes

Hey there, it’s been a while! However, it’s been even longer since I last made any updates to my Numenera Playground, my collection of random generators and tools for helping run and play Numenera TTRPG games.

I think it was about 5 years ago I made any changes, and the app has rotted away due to lack of attention, slowly accruing github security scan warnings about it’s increasingly out of date dependencies.

Considering I’m about to get to play a Numenera game today after the longest time, now seemed appropriate to try and drag the codebase into the present. As a Perservering Wright who Travels through Time, reaching into the past to modernise the code was something I expected to be difficult, but I was pleasantly surprised.

At first, I generated a brand new app with the latest vue3 CLI tools, and then effectively copied all the existing code into it. Some things needed tweaking a little - the router had changed slightly, registering vue extensions like Vuex worked differently, but otherwise I got to a state where it was complaining all my other dependencies were missing.

I soon discovered that vue-mdl didn’t work with vue3, so the entire UI framework I’d used before wasn’t an option. Luckily though, I switched to vuetify3 beta, and basically just did a find and replace s/mdl-/v-/g which got me about 90% of the way to the app Just Working™.

Once it was working again, I could actually get to the work I wanted to do: adding new sourcebooks to the character generator, one of the most requested features, and then I started playing with some other nice to have features that interested me.

Thanks to my friend Mafi, who is a Relentless Jack who Speaks to the Datasphere and absolutely smashed his way through a lot of tedious data entry, the character generator now has support for the following sourcebooks:

  • Numenera 1 Corebook
  • Numenera Destiny
  • Character Options 1
  • Character Options 2
  • Priests of the Aeon
  • Ninth World Guidebook
  • Torment
  • Rusthaven

I’ve also managed to some new features, such as:

  • The ability to permalink to the result of any generators - this was a fun challenge I’ll go into more detail on later.
  • Dark mode support
  • Code-splitting to facilitate faster page loads, since now you don’t need to download everything in the whole app to just use a specific generator.
  • Various bug fixes

I have to say, I’m really impressed with the Vue.js ecosystem. The tooling has been excellent and very low-friction to use and the porting of a half-decade old vue2 app to vue3 was largely painless and required little to no rewriting.

The most fun part of this rewrite was the permalinking support. I iterated on it a few times, and the solution is slightly different for the chargen (which has a lot of state to capture) to the other generators.

For the simpler generators, they are all random number driven which made it obvious that if I used a deterministic random number generator, I could generate the same results if the PRNG was seeded with the same values and had gone through the same number of generations. To do that, I used a specific PRNG library that offered determinism and the ability to set those initial states, and that allowed me to effectively initialise any generator with 2 32bit integers (a seed and a generation number). I then made use of the Uint8Array type and base64 encoding to encode those 8 bytes as text, and that is the permalink slug. Decoding is simple enough, going from base64 to uint8 to int32 and then initialising the PRNG and getting a new result.

For chargen with it’s more complicated state, I had to do some thinking. At first I tried just using a compressed JSON dump of the full state, but that resulted in permalinks over 1000 characters long! Much of the state contains strings for simplicity, but all those string come from lookup tables, so added a step to convert strings to indexes into the table, turning long multi-byte strings into single uint8 numbers. I also track stats and the points you have available to spend on them separately, but I can derive points from the changes you made to the stats from the baselines made available by your type/descriptor/focus selection, so managed to eliminate storing that entirely. After culling down the data, I reached I point where I needed only 11 uint8s! Encoding these in the same way as for the other generators got my chargen permalinks down to approx 16 characters! Nice!

Anyway, it was a fun few days hacking this code back into shape and I hope you find the new playground useful. As always, feedback and suggestions are welcome - just raise an issue on the projects github!

comments powered by Disqus

Previous Post