{"id":16,"date":"2025-06-16T12:58:55","date_gmt":"2025-06-16T12:58:55","guid":{"rendered":"https:\/\/blogs.scummvm.org\/prime\/?p=16"},"modified":"2025-06-16T12:59:15","modified_gmt":"2025-06-16T12:59:15","slug":"week-2-supernova-voyeur","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/prime\/2025\/06\/16\/week-2-supernova-voyeur\/","title":{"rendered":"Week 2: Supernova &amp; Voyeur"},"content":{"rendered":"<p data-start=\"152\" data-end=\"427\">This week, I continued working on implementing keymapper for more game engines. The highlight was getting my <a href=\"https:\/\/github.com\/scummvm\/scummvm\/pull\/6693\">SLUDGE keymapper PR<\/a> merged, and diving into two new engines: <strong data-start=\"398\" data-end=\"411\">Supernova<\/strong> and <strong data-start=\"416\" data-end=\"426\">Voyeur<\/strong>.<\/p>\n<hr data-start=\"429\" data-end=\"432\" \/>\n<h4 data-start=\"434\" data-end=\"495\"><strong data-start=\"442\" data-end=\"495\">Supernova Engine: The Challenge of Custom Actions<\/strong><\/h4>\n<p data-start=\"497\" data-end=\"898\">The <strong data-start=\"501\" data-end=\"521\">Supernova engine<\/strong> powers the <em data-start=\"533\" data-end=\"552\">Mission Supernova<\/em> series\u2014old-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\u2019ve spent more time solving puzzles than coding the keymapper.<\/p>\n<p data-start=\"900\" data-end=\"1092\">Unlike SLUDGE, this engine uses <strong data-start=\"932\" data-end=\"964\">engine-driven input handling<\/strong>, which gave me the flexibility to define <strong data-start=\"1006\" data-end=\"1024\">custom actions<\/strong> instead of just simulating keypresses. Why is this better? Because:<\/p>\n<blockquote data-start=\"1094\" data-end=\"1340\">\n<p data-start=\"1096\" data-end=\"1340\">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.<\/p>\n<\/blockquote>\n<p data-start=\"161\" data-end=\"447\">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 <code data-start=\"320\" data-end=\"326\">_key<\/code>, which was then used for both gameplay actions and full-text input.<\/p>\n<p data-start=\"449\" data-end=\"798\">This became a problem when introducing <strong data-start=\"488\" data-end=\"506\">custom actions<\/strong>. Unlike raw keycodes, custom actions are more abstract\u2014they represent &#8220;what to do&#8221; rather than &#8220;what key was pressed.&#8221; 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.<\/p>\n<h5 data-start=\"800\" data-end=\"820\">The Solution<\/h5>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-18\" src=\"https:\/\/blogs.scummvm.org\/prime\/wp-content\/uploads\/sites\/80\/2025\/06\/screenshot_16062025_174150.png\" alt=\"\" width=\"533\" height=\"240\" srcset=\"https:\/\/blogs.scummvm.org\/prime\/wp-content\/uploads\/sites\/80\/2025\/06\/screenshot_16062025_174150.png 533w, https:\/\/blogs.scummvm.org\/prime\/wp-content\/uploads\/sites\/80\/2025\/06\/screenshot_16062025_174150-300x135.png 300w\" sizes=\"auto, (max-width: 533px) 100vw, 533px\" \/><\/p>\n<p data-start=\"822\" data-end=\"891\">I separated the handling of keycodes and actions using two variables:<\/p>\n<ul data-start=\"892\" data-end=\"1032\">\n<li data-start=\"892\" data-end=\"963\">\n<p data-start=\"894\" data-end=\"963\"><code data-start=\"894\" data-end=\"900\">_key<\/code> \u2192 stores the raw keycode (used only when text input is needed)<\/p>\n<\/li>\n<li data-start=\"964\" data-end=\"1032\">\n<p data-start=\"966\" data-end=\"1032\"><code data-start=\"966\" data-end=\"975\">_action<\/code> \u2192 stores the custom action (used during normal gameplay)<\/p>\n<\/li>\n<\/ul>\n<p data-start=\"1034\" data-end=\"1069\">Then, I made sure the engine would:<\/p>\n<ul data-start=\"1070\" data-end=\"1380\">\n<li data-start=\"1070\" data-end=\"1240\">\n<p data-start=\"1072\" data-end=\"1240\"><strong data-start=\"1072\" data-end=\"1097\">Disable the keymapper<\/strong> when full keyboard input was required (e.g., entering text). This way, real keypresses would go through, and <code data-start=\"1207\" data-end=\"1213\">_key<\/code> would be filled correctly.<\/p>\n<\/li>\n<li data-start=\"1241\" data-end=\"1380\">\n<p data-start=\"1243\" data-end=\"1380\"><strong data-start=\"1243\" data-end=\"1267\">Enable the keymapper<\/strong> during gameplay. In this mode, keypresses would be intercepted, converted into actions, and stored in <code data-start=\"1370\" data-end=\"1379\">_action<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p data-start=\"1382\" data-end=\"1631\">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.<\/p>\n<hr data-start=\"2107\" data-end=\"2110\" \/>\n<h4 data-start=\"2112\" data-end=\"2163\"><strong data-start=\"2124\" data-end=\"2163\">Voyeur Engine: Simple, Then Smarter<\/strong><\/h4>\n<p data-start=\"2165\" data-end=\"2401\">Compared to Supernova, <strong data-start=\"2188\" data-end=\"2198\">Voyeur<\/strong> 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\u2014left and right click\u2014and be done in a few hours.<\/p>\n<p data-start=\"2403\" data-end=\"2423\">But\u2026 why stop there?<\/p>\n<p data-start=\"2425\" data-end=\"2676\">I decided to improve it by breaking the keymap into <strong data-start=\"2477\" data-end=\"2514\">multiple context-specific keymaps<\/strong>, 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.<\/p>\n<p data-start=\"2678\" data-end=\"2886\">The tricky part was figuring out <em data-start=\"2711\" data-end=\"2718\">where<\/em> 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.<\/p>\n<hr data-start=\"2888\" data-end=\"2891\" \/>\n<h3 data-start=\"2893\" data-end=\"2910\"><strong data-start=\"2899\" data-end=\"2910\">Wrap-Up<\/strong><\/h3>\n<p data-start=\"2912\" data-end=\"2925\">This week, I:<\/p>\n<ul data-start=\"2927\" data-end=\"3099\">\n<li data-start=\"2927\" data-end=\"2967\">\n<p data-start=\"2929\" data-end=\"2967\">Got my SLUDGE keymapper PR merged \ud83c\udf89<\/p>\n<\/li>\n<li data-start=\"2968\" data-end=\"3047\">\n<p data-start=\"2970\" data-end=\"3047\">Implemented full keymapper support for Supernova (with custom actions)<\/p>\n<\/li>\n<li data-start=\"3048\" data-end=\"3099\">\n<p data-start=\"3050\" data-end=\"3099\">Added smarter, context-aware keymapping to Voyeur<\/p>\n<\/li>\n<\/ul>\n<p data-start=\"3101\" data-end=\"3320\">Next week, I plan to continue this momentum and bring keymapper support to more engines. The work is getting smoother now that I\u2019m more familiar with ScummVM\u2019s input systems\u2014and every engine brings a new twist to solve.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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\u2014old-school point-and-click adventures that are surprisingly engaging (and tough!). To test [&hellip;]<\/p>\n","protected":false},"author":28,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-16","post","type-post","status-publish","format-standard","hentry","category-week-2"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/posts\/16","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/users\/28"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/comments?post=16"}],"version-history":[{"count":13,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/posts\/16\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/posts\/16\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/media?parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/categories?post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/prime\/wp-json\/wp\/v2\/tags?post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}