{"id":114,"date":"2026-07-28T15:32:05","date_gmt":"2026-07-28T15:32:05","guid":{"rendered":"https:\/\/blogs.scummvm.org\/ramyak\/?p=114"},"modified":"2026-07-28T15:32:05","modified_gmt":"2026-07-28T15:32:05","slug":"you-are-here","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/ramyak\/2026\/07\/28\/you-are-here\/","title":{"rendered":"You are here"},"content":{"rendered":"<p>Most of a Director game is one movie playing at a time. You leave one, you enter the next, and the engine only ever has a single score ticking over. But Director also lets a cast member <em>be<\/em> another whole movie (a \u201cmovie cast member\u201d, <code>#movie<\/code> in Lingo) that runs in parallel, inside a sprite, while the host movie keeps going around it. This week I made ScummVM actually do that.<\/p>\n<p>Director&#8217;s vocabulary, quickly: a <strong>movie<\/strong> is a timeline (its <strong>score<\/strong>) of <strong>frames<\/strong>. Each frame has numbered <strong>channels<\/strong>, and a <strong>sprite<\/strong> sitting in a channel is one on-screen instance of a <strong>cast member<\/strong>, an asset from the movie&#8217;s library. The <strong>stage<\/strong> is the window everything draws into, and <strong>Lingo<\/strong> is Director&#8217;s scripting language.<\/p>\n<p>The thing that forced the issue is the mini-map in <em>Star Trek: TNG Interactive Technical Manual<\/em>. You walk the decks of the Enterprise-D in a QuickTime VR panorama, and in the corner there\u2019s a little schematic of the deck with a marker showing where you are. That mini-map isn\u2019t a picture the host draws: it\u2019s a <strong>separate Director movie<\/strong>, linked in as a cast member, running its own scripts, watching a shared global to know where the marker goes, and setting another global to send you somewhere when you click it. Get movie cast members working and the mini-map comes alive. That was the goal.<\/p>\n<p>By the time the mini-map worked I had three problems on my list that looked entirely unrelated, to each other and to it: the host&#8217;s caption text had stopped drawing, the input queue was growing without bound, and DT, the debugger, was flickering between two movies\u2019 scores several times a second. I assumed three bugs. Underneath, they were one.<\/p>\n<h5>A cast member that drew nothing<\/h5>\n<p>Movie cast members and film loops share the exact same layout on disk. The only real difference is a single flag: a film loop is a canned animation, a movie cast member runs its own Lingo (<code>scriptsEnabled<\/code>). Because of that shared format, ScummVM already modelled <code>MovieCastMember<\/code> as a subclass of <code>FilmLoopCastMember<\/code>, and inherited the film loop\u2019s loader, which looks for an embedded <code>SCVW<\/code> resource. A movie cast member doesn\u2019t have one. Its content lives in a <em>separate movie file<\/em>, named in the cast info. So the loader found nothing, and the cast member <strong>drew nothing at all<\/strong>.<\/p>\n<p>The first job, then, was a real <code>load()<\/code>: take the linked path out of the cast info, resolve it with <code>findMoviePath<\/code>, open the archive, and build a proper <code>Movie<\/code> out of it that the cast member owns. Point the inherited <code>_score<\/code> at that movie\u2019s score and suddenly there are frames to draw.<\/p>\n<p>There was also a bug. The moment the linked movie loaded, the whole stage resized itself to the mini-map\u2019s dimensions and the panorama vanished. A movie, when it loads, assumes it owns the stage: it resizes the window, recentres it, repaints the background colour. That\u2019s correct for a movie you open normally, and completely wrong for one living inside a sprite on someone else\u2019s stage.<\/p>\n<p>The fix is a flag, <code>Movie::_isEmbedded<\/code>, set on the linked movie. Anywhere <code>loadArchive()<\/code> reaches for the stage, it now checks that flag first and leaves it alone. An embedded movie borrows the host\u2019s window; <strong>it never gets to own it<\/strong>. That flag turns out to be load-bearing: it comes back to guard rendering twice more before this is over.<\/p>\n<figure id=\"attachment_115\" aria-describedby=\"caption-attachment-115\" style=\"width: 1412px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-115 size-full\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM.png\" alt=\"Class diagram: MovieCastMember subclasses FilmLoopCastMember and owns a linked Movie whose _parentMovie points back at the host.\" width=\"1412\" height=\"1104\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM.png 1412w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM-300x235.png 300w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM-1024x801.png 1024w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM-768x600.png 768w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.17.20-PM-1200x938.png 1200w\" sizes=\"auto, (max-width: 1412px) 100vw, 1412px\" \/><figcaption id=\"caption-attachment-115\" class=\"wp-caption-text\">Where a movie cast member sits: it subclasses the film-loop cast member and owns a linked Movie, which points back at the host through _parentMovie.<\/figcaption><\/figure>\n<h5>Running in parallel<\/h5>\n<p>Here is the idea that organizes everything below, and it isn&#8217;t mine, sev handed it to me at the start: <strong>a movie cast member is a <code>Window<\/code> that doesn&#8217;t own a window.<\/strong> A real ScummVM <code>Window<\/code> has its own current movie, its own execution state, and its own step. A movie cast member needs all three, but it has to borrow them from the host, use them for exactly one step, and give them back. Every fix in this post is a consequence of getting that borrow-and-return right.<\/p>\n<p>Start with the step. Loading a movie is not the same as running one. A film loop is a flipbook: you just ask it for the sprites at frame <em>N<\/em>. A movie cast member has to <em>step<\/em>: run its frame scripts, honour <code>go<\/code>, fire its events, advance. So <code>update()<\/code> now steps the linked movie\u2019s score exactly like a real movie does.<\/p>\n<p>The word \u201cexactly\u201d hides a decision: how fast does it step? A movie has its own tempo channel, so should the mini-map run at its own tempo, independent of the host? Rather than guess, I built a test movie in Director 4: a host at one tempo with an embedded movie authored at a different one, each incrementing a visible counter, and watched what the original engine did. The counters stayed locked together. Under D4, a movie cast member <strong>ignores its own tempo channel entirely and advances once per host frame<\/strong>, no independent clock, no drift. I&#8217;ve only verified this for D4; if D5 or D6 differ, that&#8217;s the first thing I&#8217;d re-check.<\/p>\n<p>Then there\u2019s the question of <em>whose<\/em> movie is \u201ccurrent\u201d. Almost everything in Lingo resolves against the current movie: <code>go<\/code>, handler lookup, cast lookup, <code>the clickOn<\/code>. If the mini-map\u2019s <code>go<\/code> is going to move the mini-map and not the panorama, the linked movie has to be the current movie while it steps. So <code>update()<\/code> swaps the window\u2019s current movie to the linked one, steps, and swaps it back. For the duration of one step, the embedded movie is the current movie, and everything in Lingo resolves against it.<\/p>\n<h5>Reaching out, and getting on screen<\/h5>\n<p>Two things fell out of the borrow that I hadn\u2019t planned for. First, the mini-map\u2019s scripts call handlers that don\u2019t exist in the mini-map: <code>levelblinker<\/code> lives in the <em>host<\/em>. And they read cast members that also live in the host. The linked movie isn\u2019t self-contained; it\u2019s written assuming it can reach up into whatever movie loaded it. That gave <code>Movie<\/code> a second new member, <code>_parentMovie<\/code>, and handler and cast lookups now fall back to it when the embedded movie comes up empty. The mini-map simply doesn\u2019t run without it.<\/p>\n<p>This fallback makes <em>this<\/em> title work, and it might be the wrong general answer: Director also has shared and external cast libraries, and a message hierarchy with a specified order, and it&#8217;s possible the &#8220;authentic&#8221; fix for another game is one of those rather than &#8220;fall back to whoever loaded me.&#8221; I verified that the mini-map needs the parent for <code>levelblinker<\/code> and its cast lookups; I have <em>not<\/em> verified that &#8220;fall back to the parent&#8221; is what Director itself does in general.<\/p>\n<figure id=\"attachment_116\" aria-describedby=\"caption-attachment-116\" style=\"width: 1106px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-116 size-full\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.18.28-PM.png\" alt=\"Per-frame flow: the host steps the linked movie, which refreshes its own channels, then the host composites them through getSubChannels().\" width=\"1106\" height=\"1260\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.18.28-PM.png 1106w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.18.28-PM-263x300.png 263w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.18.28-PM-899x1024.png 899w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-1.18.28-PM-768x875.png 768w\" sizes=\"auto, (max-width: 1106px) 100vw, 1106px\" \/><figcaption id=\"caption-attachment-116\" class=\"wp-caption-text\">One host frame: the host steps the linked movie, which refreshes its own channels; the host then pulls those channels into the sprite through getSubChannels().<\/figcaption><\/figure>\n<p>Second, getting the embedded movie\u2019s pixels onto the screen. Rendering in ScummVM&#8217;s Director engine is <em>pull-based<\/em>: a window renders the channels of its current movie and nothing else. The embedded movie is never the window\u2019s real current movie (only for that flicker of a step), so it can\u2019t push itself onto the stage. Instead the host <em>pulls<\/em>: the embedded movie\u2019s own <code>renderFrame()<\/code> refreshes its channels but, seeing <code>_isEmbedded<\/code>, stops short of drawing them anywhere, and then <code>getSubChannels()<\/code> takes those live channels, scales them into the sprite\u2019s bounding box, and composites them. <strong>\u201cLive\u201d is the important word<\/strong>: it\u2019s the actual running channels, so anything the mini-map\u2019s scripts change shows up immediately.<\/p>\n<h5>Ghost in the corner<\/h5>\n<p>With all that in place the mini-map appeared, animated, and tracked my position. And it also drew a second copy of itself, small and wrong, jammed into the top-left corner of the stage.<\/p>\n<p>My first guess was a highlight artifact, something about the sprite\u2019s hilite flag drawing where it shouldn\u2019t, so I tried guarding that. It didn\u2019t help, and I dropped it.<\/p>\n<p>The real cause was the pull-based rule again, from the other side. When the embedded movie stepped and called something that triggered <code>updateStage<\/code>, it went all the way into <code>Window::render()<\/code> and rendered <em>its own<\/em> channels onto the shared window, at the embedded movie\u2019s native origin, which is the top-left of the stage. The composite path was doing its job in the sprite; this was a second, illegitimate path drawing the same content in the wrong place.<\/p>\n<p>The guard I added is at the top of <code>Window::render()<\/code>: if the current movie is embedded, return immediately. It&#8217;s a guard; I&#8217;m discarding a render request that should never have travelled this far, rather than modelling what <code>updateStage<\/code> &#8220;should&#8221; do for an embedded movie in real Director. For this engine the invariant I want holds either way, <strong>an embedded movie reaches the stage through exactly one door, <code>getSubChannels()<\/code>, and no other<\/strong>. Ghost gone.<\/p>\n<p>Two other things were wrong by this point, and I was ignoring both. Mouse events were piling up in the queue instead of draining. DT was flickering between two scores. Neither seemed connected to anything I&#8217;d just done, so I wrote them down and kept going.<\/p>\n<h5>Text that wouldn&#8217;t draw<\/h5>\n<p>The mini-map worked, but a piece of the <em>host<\/em> broke: the panorama\u2019s description text (the caption that tells you what you\u2019re looking at) stopped drawing. It rendered fine on <code>master<\/code>. Something in my branch had killed it.<\/p>\n<p>I couldn\u2019t see it by reading, so I bisected (kind of). First across the commit series: build each commit, run it, look for the text. One commit was fine, the next was broken, which pinned it to &#8220;run the movie cast member as a parallel movie&#8221;. That&#8217;s a big commit, so I bisected <em>within<\/em> it, as a designed ladder of four builds, each enabling one more thing than the last:<\/p>\n<ul>\n<li>don&#8217;t step the embedded movie at all, text draws;<\/li>\n<li>start the embedded movie but don&#8217;t step it, text draws;<\/li>\n<li>step it, but skip the sprite update, text draws;<\/li>\n<li>step it <em>with<\/em> frame-script execution, text gone.<\/li>\n<\/ul>\n<p>The last rung was the one that broke, so the culprit was specific: <strong>the embedded movie running its own frame scripts.<\/strong><\/p>\n<p>The invariant that was violated is small and exact. A <code>Window<\/code> owns one <code>LingoState<\/code>: a call stack, plus a stack of <em>frozen<\/em> states, which is how Director suspends a script mid-run to go do something else. That&#8217;s one thread of execution. The mini-map issues a <code>go()<\/code> on <strong>every single frame<\/strong> (that\u2019s how it repositions the marker), and each <code>go()<\/code> freezes the current state and pushes it onto the window\u2019s frozen stack. Because that stack was <em>shared<\/em> between the host and the embedded movie, the mini-map\u2019s per-frame freeze kept pushing the host\u2019s own scripts aside, and the script that draws the caption never got to run.<\/p>\n<p>The fix is to stop sharing the one thing that must not be shared. The movie cast member now carries its <em>own<\/em> <code>LingoState<\/code>, and a new <code>Window::swapLingoState()<\/code> exchanges it onto the window only for the duration of a step, then swaps it back. The mini-map\u2019s <code>go()<\/code> now freezes the mini-map\u2019s own state and leaves the host\u2019s completely alone.<\/p>\n<figure id=\"attachment_130\" aria-describedby=\"caption-attachment-130\" style=\"width: 1254px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-130 size-full\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM.png\" alt=\"Before: one shared LingoState, host scripts blocked. After: one state per movie, the host&apos;s untouched.\" width=\"1254\" height=\"572\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM.png 1254w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM-300x137.png 300w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM-1024x467.png 1024w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM-768x350.png 768w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.23.43-PM-1200x547.png 1200w\" sizes=\"auto, (max-width: 1254px) 100vw, 1254px\" \/><figcaption id=\"caption-attachment-130\" class=\"wp-caption-text\">Before, one shared execution state, so the mini-map&#8217;s per-frame freeze blocked the host&#8217;s scripts. After, one state per movie, with the host&#8217;s left untouched.<\/figcaption><\/figure>\n<p>Then the other two symptoms went quiet, and it took me a moment to see why, because they weren&#8217;t fixed by identical means.<\/p>\n<p>The input queue drains only when the window isn&#8217;t mid-jump: in <code>Score::step()<\/code> the drain is gated on <code>!hasJump<\/code> and an empty frozen stack. With one shared frozen stack, the mini-map&#8217;s per-frame <code>go()<\/code> left the window permanently mid-jump, so the host&#8217;s routed mouse events had no frame in which they were allowed to drain. Isolating the state means the host is never mid-jump on the mini-map&#8217;s behalf; and because the mini-map is <em>always<\/em> mid-jump on its own behalf, <code>update()<\/code> drains its routed clicks explicitly, once per step. Same root cause, two coordinated fixes.<\/p>\n<p>DT was flickering because it samples the window&#8217;s live Lingo state once per frame, and with a single shared state it had a real chance of sampling while the embedded movie owned it. The current-movie swap is still there, so DT can still land inside a step, but it now reads a state that belongs unambiguously to one movie instead of a half-updated shared one, and in practice the flicker is gone. I&#8217;d call this one strongly suspected rather than proven.<\/p>\n<p><strong>One shared <code>LingoState<\/code>; three symptoms.<\/strong><\/p>\n<h5>Shared globals, private minds<\/h5>\n<p>It would be easy to conclude from all that the two movies should be walled off completely. They shouldn\u2019t, and the mini-map is exactly why. Its whole job is a conversation with the host through shared <em>globals<\/em>: it reads <code>gNodeNow<\/code> to know where to put the marker, and writes <code>gNewNode<\/code> when you click to ask the host to travel. Wall the globals off and that conversation becomes impossible.<\/p>\n<p>So the line to draw is between two things that get lumped together as \u201cstate\u201d. Global <strong>variables<\/strong> stay shared: they live on <code>Lingo<\/code>, one table, host and embedded both reading and writing it; that\u2019s the communication channel. Execution <strong>state<\/strong> (the call stack, the frozen stack, where each movie is in its own scripts) is private, one per movie.<\/p>\n<figure id=\"attachment_128\" aria-describedby=\"caption-attachment-128\" style=\"width: 1120px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-128 size-full\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.21.07-PM.png\" alt=\"Runtime objects: the Window owns one LingoState and swaps the embedded one in per step; Lingo&apos;s globalvars are shared.\" width=\"1120\" height=\"1048\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.21.07-PM.png 1120w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.21.07-PM-300x281.png 300w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.21.07-PM-1024x958.png 1024w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.21.07-PM-768x719.png 768w\" sizes=\"auto, (max-width: 1120px) 100vw, 1120px\" \/><figcaption id=\"caption-attachment-128\" class=\"wp-caption-text\">Globals stay shared on Lingo; execution state is swapped per movie, one LingoState at a time on the window.<\/figcaption><\/figure>\n<h5>A click, end to end<\/h5>\n<p>The click is the whole design in one path, because it crosses the host\/embedded boundary twice and the only things that cross with it are two globals. The diagram below traces all seven steps; the part worth saying in prose is the shape. Your click is hit-tested by the host, routed <em>into<\/em> the mini-map (with the bounding-box scaling inverted so the coordinates land in its own space), and handled by its <code>on mouseUp<\/code> as <code>set gNewNode to the clickOn - 10<\/code> (<code>the clickOn<\/code> is the clicked sprite&#8217;s channel number; the <code>- 10<\/code> is the mini-map&#8217;s own offset from channel to node id). From there it&#8217;s globals only: the host reads <code>gNewNode<\/code>, navigates, and writes <code>gNodeNow<\/code>; on its next <code>exitFrame<\/code> the mini-map reads <code>gNodeNow<\/code> and moves the marker. <strong>Two globals cross the boundary; everything else stays on its own side.<\/strong><\/p>\n<figure id=\"attachment_129\" aria-describedby=\"caption-attachment-129\" style=\"width: 1008px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-129 size-full\" src=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.22.18-PM.png\" alt=\"Seven-step click sequence, from the host&apos;s processInputEvent to the mini-map moving its marker.\" width=\"1008\" height=\"658\" srcset=\"https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.22.18-PM.png 1008w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.22.18-PM-300x196.png 300w, https:\/\/blogs.scummvm.org\/ramyak\/wp-content\/uploads\/sites\/84\/2026\/07\/Screenshot-2026-07-28-at-7.22.18-PM-768x501.png 768w\" sizes=\"auto, (max-width: 1008px) 100vw, 1008px\" \/><figcaption id=\"caption-attachment-129\" class=\"wp-caption-text\">A click crosses the boundary twice; only gNewNode and gNodeNow cross with it.<\/figcaption><\/figure>\n<h5>Loose ends<\/h5>\n<p>A handful of things I closed, verified, or explicitly left open:<\/p>\n<ul>\n<li><strong><code>scriptsEnabled<\/code>.<\/strong> The flag this all started with was decoded but never enforced, so a movie cast member always ran its Lingo. It&#8217;s now honoured: with scripts off, the linked movie is a passive flipbook. The lever already existed as <code>Score::_haveInteractivity<\/code>, which gates every event, frame scripts included, while a frame still advances underneath it, so the movie&#8217;s frames and channels update but no Lingo runs and it doesn&#8217;t respond to the mouse.<\/li>\n<li><strong>Blast radius of the hit-test change.<\/strong> <code>Sprite::respondsToMouse()<\/code> now returns true for a movie cast member (when its scripts are enabled). That&#8217;s an engine-wide function, but the change only adds a branch for <code>kCastMovie<\/code>; every other cast type takes the same path as before, so no other game&#8217;s click behaviour moves.<\/li>\n<li><strong>Regressions.<\/strong> These changes touch shared render and hit-test paths, so the check that matters is the engine&#8217;s D4 unit-test suite: it still passes at its baseline (196 pass, 11 pre-existing failures), unchanged from master. But there are places tests don&#8217;t cover.<\/li>\n<li><strong>Untested corners.<\/strong> One movie cast member on stage, single instance, is what I ran. Two at once, and a movie cast member nested inside a linked movie, are both things Director allows and I haven&#8217;t tested; the state swap is a two-slot exchange, and a robust version is probably a push\/pop. I also haven&#8217;t profiled the per-frame cost of stepping a second score, though with one embedded movie it isn&#8217;t perceptible.<\/li>\n<li><strong>Moving in the panorama doesn\u2019t move the map.<\/strong> Clicking the map navigates the panorama, but walking through the panorama doesn\u2019t move the marker. The map is a pure consumer of <code>gNodeNow<\/code>, and the host only updates <code>gNodeNow<\/code> from its QTVR node-change path, so this is a QTVR-side gap, not a moviecast member one.<\/li>\n<\/ul>\n<h5>Where this is headed<\/h5>\n<p>Next up is the DT side, so the next person to open a movie cast member can watch both scores at once. None of the debugging above was done with a nice tool: it was rebuild-and-observe and reading the call graph, and DT itself was one of the broken things. Which is exactly the gap I want to close, and why this line from Zig&#8217;s creator Andrew Kelley <a href=\"https:\/\/youtu.be\/Qncdi-Fg0-I?t=1345\">(link to the quote)<\/a> is the note I want to end on:<\/p>\n<blockquote><p>I needed to code up a simulation, I needed some visualization, I needed more introspection, I needed a way to understand what is happening, so that debugging wasn&#8217;t an all-day affair where I was using command line tools, but debugging could look like just looking at an animated graphic of what&#8217;s happening and just spotting the obvious problem and fixing it immediately.<\/p>\n<p>When you have the right simulation, that&#8217;s what you can do. When your system is accessible, bugs are trivial.<\/p>\n<p>&#8211; Andrew Kelley<\/p><\/blockquote>\n<h5>The code<\/h5>\n<p>Everything above is in the movie cast member PR: <a href=\"https:\/\/github.com\/scummvm\/scummvm\/pull\/7752\">https:\/\/github.com\/scummvm\/scummvm\/pull\/7752<\/a><\/p>\n<p>It\u2019s seven commits, building from reading the flag word correctly, through loading and playing the linked movie, to running it in parallel and finally isolating the Lingo state.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most of a Director game is one movie playing at a time. You leave one, you enter the next, and the engine only ever has a single score ticking over. But Director also lets a cast member be another whole movie (a \u201cmovie cast member\u201d, #movie in Lingo) that runs in parallel, inside a sprite, [&hellip;]<\/p>\n","protected":false},"author":32,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-114","post","type-post","status-publish","format-standard","hentry","category-week-9"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/114","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=114"}],"version-history":[{"count":17,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/114\/revisions"}],"predecessor-version":[{"id":139,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/posts\/114\/revisions\/139"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/media?parent=114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/categories?post=114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ramyak\/wp-json\/wp\/v2\/tags?post=114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}