{"id":16,"date":"2026-05-31T00:54:20","date_gmt":"2026-05-31T00:54:20","guid":{"rendered":"https:\/\/blogs.scummvm.org\/ramyak\/?p=16"},"modified":"2026-05-31T01:01:56","modified_gmt":"2026-05-31T01:01:56","slug":"catching-momentum","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/ramyak\/2026\/05\/31\/catching-momentum\/","title":{"rendered":"Catching momentum"},"content":{"rendered":"<p class=\"ds-markdown-paragraph\"><span class=\"\">Coming back to a large codebase after a break is disorienting.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">I returned this week after a 3-week gap and barely recognized the code I&#8217;d been working on. So I planned the week around small, finishable tasks. Nothing ambitious. Just things I could complete and feel good about, to rebuild momentum.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">Start small<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">Sev assigned me my Planka board (the task tracker we use) and pointed me toward a set of Gus games: Gus Goes to Kooky Carnival, Cybertown, and a few others. I went through the tickets, broke each issue into its own card, and we had a Zoom call where he helped me set up the environment properly.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\">This call is worth describing because it&#8217;s a good example of what mentorship looks like here. We talked about Dumper Companion (a tool for inspecting Director movie internals), useful ScummVM debug flags, debugging workflows. The non-technical conversation ended up being just as helpful for reorientation as the technical one. If you&#8217;re new and a maintainer makes space for this kind of call, take it.<\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">cast viewer in the visual debugger<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">It wasn&#8217;t loading shared cast members. I fixed that and removed some duplicate code while I was there. Small, satisfying, done. That&#8217;s the point of starting small.<\/span><\/p>\n<hr \/>\n<h3><span class=\"\">Bug 1: Crashing on empty cast slots<\/span><\/h3>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">The hard part of a fix is often defining the boundary correctly, not writing the code<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">In original Director, if you set a property on a cast member slot that exists but is empty, it fails silently. ScummVM was throwing an error, which crashed games that relied on that behavior.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-25\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM.png\" alt=\"\" width=\"1634\" height=\"478\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM.png 1634w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM-300x88.png 300w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM-1024x300.png 1024w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM-768x225.png 768w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM-1536x449.png 1536w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/05\/Screenshot-2026-05-31-at-4.29.17-AM-1200x351.png 1200w\" sizes=\"auto, (max-width: 1634px) 100vw, 1634px\" \/><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">Imagine cast slots are numbered 1 through &#8220;b&#8221;<\/span><span class=\"\">, where &#8220;b&#8221;<\/span><span class=\"\"> is the last populated member. Accessing slot &#8220;a&#8221; <\/span><span class=\"\">(within bounds but empty) should fail silently. Accessing slot &#8220;c&#8221; <\/span><span class=\"\">should throw an error. The bounds are defined by the last populated cast member, not the total allocated size.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">The fix seemed obvious: skip the error if the cast member isn&#8217;t found. But that would also swallow errors for genuinely out-of-bounds IDs, real bugs you&#8217;d want to catch. The problem wasn&#8217;t the error. It was defining &#8220;in-bounds&#8221; correctly.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">test movies<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">This is also where Sev introduced me to test movies, minimal Director movies that reproduce a single buggy behavior in isolation. Instead of loading an entire game to verify a fix, you create the smallest possible movie that triggers the issue. Much faster, much cleaner.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">The solution: teach ScummVM to distinguish between an empty cast slot within a valid range and a genuinely invalid cast member ID (error).<\/span><\/p>\n<hr \/>\n<h3><span class=\"\">Bug 2: Sprite dragging not working<\/span><\/h3>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">The symptom and the cause can live in completely different systems<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">In one of the Gus games, clicking a puzzle piece should let you drag it around the screen. In ScummVM, clicking did nothing. The piece stayed frozen.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">I assumed the drag logic was broken. It wasn&#8217;t. The drag code was fine. The real issue was buried in the event pipeline, and finding it meant tracing the entire event flow from click to handler.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">ground truth testing<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">I needed to confirm how Director 4 actually behaves, so I ran the original Director inside Basilisk II (a classic Mac emulator) to observe the real\u00a0<\/span><code>immediate<\/code><span class=\"\"> sprite property. You can&#8217;t rely on documentation alone (shocker for me), some features are marked obsolete but still functional, and behavior varies between versions.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">Here&#8217;s what\u00a0<\/span><code>immediate<\/code><span class=\"\"> does in Director 4 (5 &amp; 6 too):<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\"> normally, <\/span><code>mouseDown<\/code><span class=\"\">\u00a0fires on press and\u00a0<\/span><code>mouseUp<\/code><span class=\"\">\u00a0fires on release. When\u00a0<\/span><code>immediate<\/code><span class=\"\"> is set on a sprite, both <\/span><code>mouseDown <\/code><span class=\"\">\u00a0<\/span><em><span class=\"\">and<\/span><\/em><span class=\"\">\u00a0 <\/span><code>mouseUp<\/code><span class=\"\">\u00a0 fire on the press. <\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">The drag handler works by running a <\/span><code>repeat while the stillDown<\/code><span class=\"\">\u00a0loop inside\u00a0<\/span><code>on mouseUp<\/code><span class=\"\">. If\u00a0<\/span><code>mouseUp<\/code><span class=\"\">\u00a0only fires after the physical release,\u00a0<\/span><code>stillDown<\/code><span class=\"\">\u00a0is already false and the loop exits instantly. Nothing moves.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">The fix was in\u00a0<\/span><code>queueEvent()<\/code><span class=\"\">: when a press arrives on an immediate sprite in D4, queue both <\/span><code>mouseDown<\/code><span class=\"\">\u00a0and\u00a0 <\/span><code>mouseUp<\/code><span class=\"\">\u00a0 events immediately, then suppress the physical <\/span><code>mouseUp<\/code><span class=\"\">\u00a0later to prevent double-firing.<\/span><\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">version boundaries<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">sev pointed out something: does <\/span><code>immediate<\/code><span class=\"\"> behave the same in D3? Does it stop working in D5?\u00a0<\/span><\/p>\n<p>Turns out immediate works in D4, D5, D6. Not tested D3 yet. Will update this after.<\/p>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">the director-tests repo<\/span><\/strong><\/p>\n<p class=\"ds-markdown-paragraph\"><span class=\"\">Instead of loading full games to test a behavior, we use the\u00a0<\/span><a href=\"https:\/\/github.com\/scummvm\/director-tests\" target=\"_blank\" rel=\"noopener noreferrer\"><span class=\"\">director-tests repo<\/span><\/a>, <span class=\"\">a collection of minimal test movies that verify specific Director engine behaviors. You create the smallest possible movie that reproduces just the thing you care about, and it lives in the repo as a permanent regression test.<\/span><\/p>\n<hr \/>\n<h3><span class=\"\">What I&#8217;m aiming for next week<\/span><\/h3>\n<ul>\n<li>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">Work through more Gus bugs.<\/span><\/strong><span class=\"\">\u00a0<\/span><\/p>\n<\/li>\n<li>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">Look at build-bot issues.<\/span><\/strong><span class=\"\">\u00a0<\/span><\/p>\n<\/li>\n<li>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">Go deep on one subsystem end-to-end.<\/span><\/strong><\/p>\n<\/li>\n<li>\n<p class=\"ds-markdown-paragraph\"><strong><span class=\"\">Improve tests and stress the visual debugger.<\/span><\/strong><\/p>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Coming back to a large codebase after a break is disorienting. I returned this week after a 3-week gap and barely recognized the code I&#8217;d been working on. So I planned the week around small, finishable tasks. Nothing ambitious. Just things I could complete and feel good about, to rebuild momentum. Start small Sev assigned [&hellip;]<\/p>\n","protected":false},"author":32,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-16","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/16","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/users\/32"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/comments?post=16"}],"version-history":[{"count":4,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/16\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/16\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/media?parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/categories?post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/tags?post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}