Categories
Week 2

Week 2: Supernova & Voyeur

This week, I continued working on implementing keymapper for more game engines. The highlight was getting my SLUDGE keymapper PR merged, and diving into two new engines: Supernova and Voyeur.


Supernova Engine: The Challenge of Custom Actions

The Supernova engine powers the Mission Supernova series—old-school point-and-click adventures that are surprisingly engaging (and tough!). To test my changes, I had to play through parts of the game, and I genuinely enjoyed the puzzles and storytelling, despite the lack of modern visuals. Honestly, without the walkthrough, I would’ve spent more time solving puzzles than coding the keymapper.

Unlike SLUDGE, this engine uses engine-driven input handling, which gave me the flexibility to define custom actions instead of just simulating keypresses. Why is this better? Because:

Custom actions give the engine full control over what happens when a key is pressed, while mimicking keypresses can cause issues like duplicate handling or unexpected default behavior still triggering.

The biggest challenge I faced in Supernova was how the engine handled keyboard input. It stored the raw keycode of every key press in a single variable called _key, which was then used for both gameplay actions and full-text input.

This became a problem when introducing custom actions. Unlike raw keycodes, custom actions are more abstract—they represent “what to do” rather than “what key was pressed.” But if I allowed both to be handled the same way, the engine might mistakenly store a custom action as if it were a real keypress, which would break things like text input.

The Solution

I separated the handling of keycodes and actions using two variables:

  • _key → stores the raw keycode (used only when text input is needed)

  • _action → stores the custom action (used during normal gameplay)

Then, I made sure the engine would:

  • Disable the keymapper when full keyboard input was required (e.g., entering text). This way, real keypresses would go through, and _key would be filled correctly.

  • Enable the keymapper during gameplay. In this mode, keypresses would be intercepted, converted into actions, and stored in _action.

This setup allowed me to cleanly separate when to use keycodes and when to use actions, without breaking any existing input logic. Once this system was in place, replacing the original key handling with action-based logic was pretty straightforward. All I had to do was replace hardcoded keycodes with custom actions.


Voyeur Engine: Simple, Then Smarter

Compared to Supernova, Voyeur was a breeze. The game is almost entirely mouse-driven, with very little keyboard interaction. I initially thought I could get away with two simple binds—left and right click—and be done in a few hours.

But… why stop there?

I decided to improve it by breaking the keymap into multiple context-specific keymaps, depending on where the player is in the game. For example, different menus or in-game sequences would have their own set of keybinds with clearer action labels.

The tricky part was figuring out where in the code to enable/disable each keymap. That took a whole day of reading through the engine, but once I nailed it, the final implementation was clean and intuitive.


Wrap-Up

This week, I:

  • Got my SLUDGE keymapper PR merged 🎉

  • Implemented full keymapper support for Supernova (with custom actions)

  • Added smarter, context-aware keymapping to Voyeur

Next week, I plan to continue this momentum and bring keymapper support to more engines. The work is getting smoother now that I’m more familiar with ScummVM’s input systems—and every engine brings a new twist to solve.

Leave a Reply

Your email address will not be published. Required fields are marked *