<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>JakMods.dev Blog</title>
        <link>https://jakmods.dev/blog</link>
        <description>JakMods.dev Blog</description>
        <lastBuildDate>Wed, 08 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Pause menu hardlock?]]></title>
            <link>https://jakmods.dev/blog/2026-07-08-autosave-progress-lock</link>
            <guid>https://jakmods.dev/blog/2026-07-08-autosave-progress-lock</guid>
            <pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Today I looked into an uncommon bug where Jak 1 can completely freeze up on an empty pause menu screen. Zed mentioned to me that he had looked into this before, and determined it was related to conflicting autosave and pause menu popups.]]></description>
            <content:encoded><![CDATA[<p>Today I looked into an uncommon bug where Jak 1 can completely freeze up on an empty pause menu screen. Zed mentioned to me that he had looked into this before, and determined it was related to conflicting autosave and pause menu popups.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reproducing-the-issue">Reproducing the issue<a href="https://jakmods.dev/blog/2026-07-08-autosave-progress-lock#reproducing-the-issue" class="hash-link" aria-label="Direct link to Reproducing the issue" title="Direct link to Reproducing the issue" translate="no">​</a></h3>
<p>I was actually asked about this occurring in the Speedrun Practice Mod when pause buffering the top of Citadel power cell. Under the right circumstances, in the pause menu when you press Circle to switch to the Options page, it will transition out of the power cell page, but never actually transition into the Options page. You can't unpause or go back in the menu - the game appears completely frozen!</p>
<p>So what's the exact setup that we need to reproduce? First we need a fresh save file with no previous autosaves, so that the next autosave triggers the popup explaining autosave and the memory card icon.</p>
<p>Then we need a way to trigger both the pause menu and the autosave popup simultaneously. Normally if you try to pause when the autosave popup is about to come up, it will force a transition from the power cell page to the autosave one.</p>
<p>On certain power cells, we can use a pause buffer (pressing Start and L2/R2 on the same frame) before collecting the cell - this allows us to get credit for collecting the cell, but opens the pause menu before the power cell animation can start.</p>
<p>Importantly in this scenario, despite not playing the power cell animation, the autosave process is still kicked off. And if we do this on a power cell and trigger the first autosave on this file, we'll be met with the frozen screen.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hardlock-or-softlock">Hardlock or softlock?<a href="https://jakmods.dev/blog/2026-07-08-autosave-progress-lock#hardlock-or-softlock" class="hash-link" aria-label="Direct link to Hardlock or softlock?" title="Direct link to Hardlock or softlock?" translate="no">​</a></h3>
<p>For most crashes that happen on PS2, when reproduced in OpenGOAL, the program will fully crash, and if we attach either the GOAL or a C++ debugger we can catch an exception being thrown.</p>
<p>In the case of this autosave/pause menu freeze however, OpenGOAL stays open, and no exceptions are thrown - so is the game actually frozen?</p>
<p>We can actually attach the REPL to the game running in debug mode, and even during the freeze the game will be responsive to code that we run via the REPL. So the game IS still running, it's just stuck in some sort of loop where it can't move on and doesn't respond to any inputs.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="debugging-the-issue">Debugging the issue<a href="https://jakmods.dev/blog/2026-07-08-autosave-progress-lock#debugging-the-issue" class="hash-link" aria-label="Direct link to Debugging the issue" title="Direct link to Debugging the issue" translate="no">​</a></h3>
<p>I started by looking at the state of the <code>progress</code> (pause menu) process while the game was frozen:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ppointer-&gt;process</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*progress-process*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2124612</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x206b44</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;progress</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">suspended</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">state</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress-normal</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">flags</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">r</span><span class="token plain">   :</span><span class="token symbol" style="color:#36acaa">stack</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">112</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">256</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">heap</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6608</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">6608</span><span class="token plain"> @ </span><span class="token number" style="color:#36acaa">#x206b44</span><span class="token symbol" style="color:#36acaa">&gt;</span><br></div></code></pre></div></div>
<p>We can see it's in the <code>progress-normal</code> state, so I poked around there, trying to see how it transitions between pages while in that state. I found this bit of code:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-proc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">force-transition</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">in-transition</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&gt;=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">transition-offset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">zero?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">level-transition</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nonzero?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token number" style="color:#36acaa">512</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&gt;=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">enter!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">pop!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>And looking at the <code>enter!</code> method we can see this is where the <code>display-state</code> (page) of the menu is updated:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">enter!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">screen</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress-screen</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">option</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">screen</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*progress-state*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">yes-no-choice</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">selected-option</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">option-index</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">option</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">last-option-index-change</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">real-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">screen</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>We can use the REPL again to see the value of <code>display-state</code> and <code>next-display-state</code> in the <code>progress</code> process during the freeze:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ppointer-&gt;process</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*progress-process*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ppointer-&gt;process</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*progress-process*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">next-display-state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x3</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;invalid</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">object</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">#x3</span><span class="token symbol" style="color:#36acaa">&gt;</span><br></div></code></pre></div></div>
<p>And checking this against the <code>progress-screen</code> enum, these values makes sense given where the freeze occurs:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defenum</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress-screen</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">type</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int64</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">invalid</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fuel-cell</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">money</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">buzzer</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">settings</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>So for some reason, we're never making that <code>enter!</code> call to switch over to the <code>settings</code> page. I used the REPL to check the conditions in the <code>when</code> block required to make it to the <code>enter!</code> call, and found only one was failing:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-proc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1375524</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x14fd24</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-proc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2142564</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x20b164</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;auto-save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">suspended</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">state</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">done</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">flags</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">r</span><span class="token plain">   :</span><span class="token symbol" style="color:#36acaa">stack</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">96</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">512</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">heap</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1088</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">1088</span><span class="token plain"> @ </span><span class="token number" style="color:#36acaa">#x20b164</span><span class="token symbol" style="color:#36acaa">&gt;</span><br></div></code></pre></div></div>
<p>So as expected, this is definitely autosave related. Now we need to figure out why the <code>auto-save</code> process is getting stuck in the <code>done</code> state.</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defstate</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">done</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">auto-save</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">virtual</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#t</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">code</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">behavior</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">event-hook</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">method-of-object</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">state-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">real-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">real-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">state-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">seconds</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">progress-allowed?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">state-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">real-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">suspend</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic">;; Don't display the auto-save warning prompt if speedrunning</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">speedrunner-mode?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">activate-progress</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*dproc*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">progress-screen</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>So normally here the <code>auto-save</code> process (if it's our first time and we're not in speedrun mode) tries to activate <code>progress</code> and show the autosave/memory card warning page. It has a <code>while</code> loop which only runs if it's been less than 0.2s since the last time <code>state-time</code> was updated (when it reached <code>done</code> state).</p>
<p>This loop exists primarly to check <code>progress-allowed?</code> - if we're not allowed to open the progress menu right now (e.g. maybe we're in a cutscene), the <code>auto-save</code>'s <code>state-time</code> is updated to the current time, and we <code>suspend</code> until the next frame, where we run these checks again.</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defun</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">progress-allowed?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">talking</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">movie</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">movie?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pov-camera-handle</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">other-camera-handle</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">base-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">letterbox-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">base-frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">blackout-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">bg-a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">bg-a-force</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">allow-progress</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-proc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">send-event</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">auto-save-proc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">progress-allowed?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cheat-mode*</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">camera</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Again, we can go back to the REPL during the freeze and figure out what's failing here:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">current</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">movie</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1921416</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x1d5188</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">240177</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">movie?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1375532</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x14fd2c</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token boolean" style="color:#36acaa">#t</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">other-camera-handle</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2507652</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x264384</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;othercam</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">othercam</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">suspended</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">state</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">othercam-running</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">flags</span><span class="token plain">     :</span><span class="token symbol" style="color:#36acaa">stack</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">112</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">256</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">heap</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">480</span><span class="token symbol" style="color:#36acaa">/</span><span class="token number" style="color:#36acaa">480</span><span class="token plain"> @ </span><span class="token number" style="color:#36acaa">#x264384</span><span class="token symbol" style="color:#36acaa">&gt;</span><br></div></code></pre></div></div>
<p>Because the power cell animation was queued up to be played after touching the cell, the game actually considers us in a cutscene, causing <code>progress-allowed?</code> to return <code>#f</code>.</p>
<p>Now we can see the full picture - because we were able to trigger the autosave and bring up the pause menu simultaneously, each process is stuck waiting on conditions that will never happen! The <code>auto-save</code> can't proceed because <code>progress-allowed?</code> is false due to the pending cutscene. But the pending cutscene can't play out (unblocking <code>progress-allowed?</code>) because we're stuck in the pause menu - and that's because <code>progress</code> won't transition while there's an active <code>auto-save</code> process.</p>
<p>Funny enough, using the GOAL debugger we can <code>(:break)</code> during the freeze to force a breakpoint in the code, wait a bit, and then <code>(:cont)</code> to resume from the breakpoint, and we'll artificially satisfy the <code>state-time</code> check, and un-freeze the game!</p>
<p><!--$?--><template id="B:0"></template><!--/$-->&nbsp;</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fix">The fix<a href="https://jakmods.dev/blog/2026-07-08-autosave-progress-lock#the-fix" class="hash-link" aria-label="Direct link to The fix" title="Direct link to The fix" translate="no">​</a></h3>
<p>The "proper" fix for this would probably be to prevent cells from being picked up in the pause buffer scenario, but changing that would be a bit invasive and too much divergence from original behavior (it would also break some speedrun strategies).</p>
<p>Since the issue is not something casual players are likely to run into, we can just focus on fixing this for speedrunners. As we saw earlier, there's already some code added in OpenGOAL that prevents the autosave popup from being activated if we're in speedrun mode. We can simply move the check up a bit earlier, and avoid reaching the code checking <code>state-time</code> and <code>progress-allowed?</code>:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    (when (= (-&gt; *game-info* auto-save-count) 1)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    ;; og:preserve-this Don't display the first auto-save warning prompt if speedrunning</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    (when (and (= (-&gt; *game-info* auto-save-count) 1) (not (-&gt; *pc-settings* speedrunner-mode?)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     (set! (-&gt; self event-hook) (-&gt; (method-of-object self error) event))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     (set! (-&gt; self state-time) (-&gt; *display* real-frame-counter))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     (while (&lt; (- (-&gt; *display* real-frame-counter) (-&gt; self state-time)) (seconds 0.2))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       (if (not (progress-allowed?)) (set! (-&gt; self state-time) (-&gt; *display* real-frame-counter)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       (suspend))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">      ;; Don't display the auto-save warning prompt if speedrunning</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">      (when (= (-&gt; *pc-settings* speedrunner-mode?) #f)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">        (activate-progress *dproc* (progress-screen auto-save))))))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">      (activate-progress *dproc* (progress-screen auto-save)))))))</span><br></div></code></pre></div></div>
<p>Now the <code>auto-save</code> process will finish executing and be deactivated successfully - unblocking the <code>progress</code> code and allowing us to call <code>enter!</code> and access the Options page!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-07-08-autosave-progress-lock#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/4340" target="_blank" rel="noopener noreferrer" class="">PR #4340 (the fix for speedrun mode)</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging a SFHBG crash]]></title>
            <link>https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash</link>
            <guid>https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash</guid>
            <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In Jak 3 speedrunning there is a trick sometimes used called the "Super Fast Hover Bike Glitch", or SFHBG, where you use Light Jak Flash Freeze and the Wave Concussor to launch zoomers at crazy high speeds. However on console, if you overdo the launch (e.g. charge the Wave Concussor too long) the game will actually crash upon launching the zoomer. Let's look into what causes this crash!]]></description>
            <content:encoded><![CDATA[<p>In Jak 3 speedrunning there is a trick sometimes used called the "Super Fast Hover Bike Glitch", or SFHBG, where you use Light Jak Flash Freeze and the Wave Concussor to launch zoomers at crazy high speeds. However on console, if you overdo the launch (e.g. charge the Wave Concussor too long) the game will actually crash upon launching the zoomer. Let's look into what causes this crash!</p>
<!-- -->
<!--$?--><template id="B:0"></template><!--/$-->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pinpointing-the-crash">Pinpointing the crash<a href="https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash#pinpointing-the-crash" class="hash-link" aria-label="Direct link to Pinpointing the crash" title="Direct link to Pinpointing the crash" translate="no">​</a></h3>
<p>First let's see if we can catch exactly where the crash happens by attaching the debugger. In the REPL, after we've compiled (ran <code>(mi)</code>) and launched the game in debug mode, we can run <code>(lt)</code> to connect the REPL, and then <code>(dbgc)</code> to connect the GOAL debugger.</p>
<p>Now we just need to reproduce the crash, and in the REPL we'll see it catches the exception:</p>
<p><code>Target raised an exception (EXCEPTION_ACCESS_VIOLATION [0xC0000005]). Run (:di) to get more information.</code></p>
<p>Running <code>(:di)</code> will show us the stack trace:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">In function (method get-height-at-point xz-height-map) in segment 0 of obj height-map, offset_obj 0x817, offset_func 0xf3</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:99</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (+ (+ (min a2-0 (+ v1-0 -2)) (* (min a1-1 (+ a3-0 -2)) v1-0) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                                                ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA24] mov rdi, r12                               mov igpr-48, igpr-24</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA27] mov rsi, r10                               mov igpr-49, igpr-45</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA2A] add r9, r15                                call igpr-50 (ret igpr-47) (args igpr-48 igpr-49)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA2D] call r9</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:99</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (+ (+ (min a2-0 (+ v1-0 -2)) (* (min a1-1 (+ a3-0 -2)) v1-0) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                                             ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA30] imul eax, ebp                              imul igpr-52, igpr-32</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA33] movsxd rax, eax</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:99</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (+ (+ (min a2-0 (+ v1-0 -2)) (* (min a1-1 (+ a3-0 -2)) v1-0) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                   ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA36] add r11, rax                               addi igpr-43, igpr-52</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA39] xor r9, r9                                 mov-ic igpr-53, 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA3C] add r11, r9                                addi igpr-43, igpr-53</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:99</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (+ (+ (min a2-0 (+ v1-0 -2)) (* (min a1-1 (+ a3-0 -2)) v1-0) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA3F] mov r9d, [r15+rbx*1+0x1C]                  mov igpr-55, [igpr-3 + 28]</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA44] add r11, r9                                addi igpr-54, igpr-57</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:102</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         (f3-3 (the float (-&gt; a1-7 0)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                  ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49"> [0x18D4BE6AA47] movsx r9, byte ptr [r15+r11*1] mov igpr-61, [igpr-59 + 0]</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA4C] cvtsi2ss xmm7, r9d                         i2f ifpr-60, igpr-61</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:103</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         (f4-1 (the float (-&gt; a1-7 1)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                  ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA51] movsx r9, byte ptr [r15+r11*1+0x01]        mov igpr-64, [igpr-59 + 1]</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA57] cvtsi2ss xmm6, r9d                         i2f ifpr-63, igpr-64</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:104</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         (f2-8 (the float (-&gt; a1-7 v1-0)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                             ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA5C] mov r9, rbp                                mov igpr-66, igpr-32</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:104</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         (f2-8 (the float (-&gt; a1-7 v1-0)))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                  ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA5F] add r9, r11                                addi igpr-69, igpr-59</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA62] movsx r9, byte ptr [r15+r9*1]              mov igpr-68, [igpr-69 + 0]</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> [0x18D4BE6AA67] cvtsi2ss xmm5, r9d                         i2f ifpr-67, igpr-68</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">rax: 0x0000000000000000 rcx: 0x0000000000147d21 rdx: 0x000000003c888889 rbx: 0x00000000022dd8f0 </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">rsp: 0x0000018d49d0ef50 rbp: 0x0000000000000050 rsi: 0x0000000000000000 rdi: 0x0000000000000000 </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">r8: 0xfffffffffffffffe  r9: 0x00000000022db5f0 r10: 0x000000000000006e r11: 0xffffffff822db5f0 </span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain">r12: 0x0000000000000000 r13: 0x000000000028f884 r14: 0x0000000000147d21 r15: 0x0000018d49b90000 </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">rip: 0x0000018d4be6aa47</span><br></div></code></pre></div></div>
<p>These stack traces can seem intimidating at first, but we really just want to focus on the code one line above where the debugger shows the breakpoint (the line in red):</p>
<div class="language-lisp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lisp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">height-map.gc:99</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">min</span><span class="token plain"> a2-0 </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">+</span><span class="token plain"> v1-0 </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">min</span><span class="token plain"> a1-1 </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">+</span><span class="token plain"> a3-0 </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> v1-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">the-as</span><span class="token plain"> int </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">the-as</span><span class="token plain"> pointer </span><span class="token punctuation" style="color:#393A34">(</span><span class="token car">-&gt;</span><span class="token plain"> this data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 ^</span><br></div></code></pre></div></div>
<p>Let's look at this in the source code for a bit more context:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">get-height-at-point</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">xz-height-map</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">vector</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmax</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-inv-spacing</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-offset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">2</span><span class="token number" style="color:#36acaa">-4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmax</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-inv-spacing</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-offset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">2</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f2-4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-7</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a2-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f2-4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a1-1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-dim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">3</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-dim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-7</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">pointer</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">min</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a2-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">min</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a1-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a3-0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>It's a bit hard to read with the generated variable names, but this is computing <code>a1-7</code> as a pointer to some address relative to <code>(-&gt; this data)</code> as a starting point. Presumably the crash is caused by this pointing to an invalid memory address under some scenarios.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="making-sense-of-xz-height-map">Making sense of <code>xz-height-map</code><a href="https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash#making-sense-of-xz-height-map" class="hash-link" aria-label="Direct link to making-sense-of-xz-height-map" title="Direct link to making-sense-of-xz-height-map" translate="no">​</a></h3>
<p>What exactly is <code>this</code> in this context, and what's in its <code>data</code> field?</p>
<p>Well, we're in the <code>get-height-at-point</code> method for an <code>xz-height-map</code>, so we can look at that type's definition:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">deftype</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">xz-height-map</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">structure</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">offset</span><span class="token plain">         </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain">  </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-offset</span><span class="token plain">       </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">y-offset</span><span class="token plain">       </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-offset</span><span class="token plain">       </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-inv-spacing</span><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">float</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-inv-spacing</span><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">float</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">y-scale</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">float</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dim</span><span class="token plain">            </span><span class="token symbol" style="color:#36acaa">int16</span><span class="token plain">  </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-dim</span><span class="token plain">          </span><span class="token symbol" style="color:#36acaa">int16</span><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">dim</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-dim</span><span class="token plain">          </span><span class="token symbol" style="color:#36acaa">int16</span><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">dim</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">data</span><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">pointer</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Not the most helpful... where is this actually used, and for what purpose? Let's peek at <code>traffic-height-map.gc</code> where I found the type referenced:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">define</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*traffic-height-map*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">xz-height-map</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">array</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-3178496.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">71680.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-3178496.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">x-inv-spacing</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.000010172526</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">z-inv-spacing</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.000010172526</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">y-scale</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4096.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">dim</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">array</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int16</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">#x70</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               :</span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">array</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int8</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8960</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                 </span><span class="token comment" style="color:#999988;font-style:italic">;; ~9000 lines of integers here, redacted</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                 </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                               </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>So <code>data</code> is a giant list of integers, and while it's still not immediately clear what this is, if we think about the names <code>xz-height-map</code> and <code>get-height-at-point</code>, and look at the implementation it begins to make a bit more sense. We can update the variable names and linebreaks to make it a bit easier to read:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">get-height-at-point</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">xz-height-map</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">vector</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-rel</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmax</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-inv-spacing</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-offset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-rel</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmax</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-inv-spacing</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-offset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-rel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-rel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-frac</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-rel</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-frac</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-rel</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x-dim</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-dim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">z-dim</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-dim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-7</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">pointer</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">min</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-int</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x-dim</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">min</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-int</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z-dim</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token symbol" style="color:#36acaa">x-dim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>The <code>get-height-at-point</code> method takes in a vector, and we can see the method only really cares about its X and Z coordinates. It uses the <code>x-offset</code>/<code>z-offset</code>, <code>x-inv-spacing</code>/<code>z-inv-spacing</code>, and <code>x-dim</code>/<code>z-dim</code> values to efffectively convert the coordinates into integer "buckets", and points to some offset from <code>data</code> using these values.</p>
<p>In other words, <code>data</code> is a 2D table of values, and it's looking up the value from this table based on the X and Z coordinates. The values represent the height at which zoomers should float, as this height varies throughout Haven City - you can sort of think of the <code>data</code> table like a pixelated version of a topography map:</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/heightmap-d4230fcb06110d76c15437221cefbafd.png" width="1885" height="1882" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-when-does-it-crash">So when does it crash?<a href="https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash#so-when-does-it-crash" class="hash-link" aria-label="Direct link to So when does it crash?" title="Direct link to So when does it crash?" translate="no">​</a></h3>
<p>Now that we have a basic understanding of what the <code>get-height-at-point</code> method is doing, we can try to break down what happens leading up to a crash here. We can print some debug logs to check the value of these variables - in this example, I'm interested in the value of <code>a2-0</code>/<code>x-int</code> and <code>a1-1</code>/<code>z-int</code> in normal situations vs during the crash.</p>
<p>There are dozens of ways to print this value, but here's one quick and dirty solution to print it just before evaluating the pointer address:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">(defmethod get-height-at-point ((this xz-height-map) (arg0 vector))</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> (let* (...</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">        (a1-7</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">          (begin</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">           (format 0 "x-int: ~D / z-int: ~D~%" x-int z-int)</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">           (the-as</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (pointer int8)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             (+ </span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                 (+ (min x-int </span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                         (+ x-dim -2))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                   (* (min z-int </span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                           (+ z-dim -2))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                       x-dim) </span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                   0)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">                 (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">             )</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">           )</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">          )</span><br></div></code></pre></div></div>
<p>With this, we'll discover that in normal situations these two values are non-negative integers, and leading up to a crash one or both of them is a negative integer!</p>
<p>You might see where this is going - when we index into the <code>data</code> table using these values, the negative values will actually cause us to look <em>outside</em> of the <code>data</code> table area of memory, and thus cause a crash.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-do-we-fix-it">How do we fix it?<a href="https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash#how-do-we-fix-it" class="hash-link" aria-label="Direct link to How do we fix it?" title="Direct link to How do we fix it?" translate="no">​</a></h3>
<p>Array out-of-bounds access is a classic bug when it comes to programming, and Naughty Dog attempted to account for it in this function.</p>
<p>The <code>min</code> calls here ensure that extra large values for <code>x-int</code>/<code>z-int</code> don't lead to array out-of-bounds access, capping them at <code>x-dim</code>/<code>z-dim</code> minus 2. My guess is that <code>dim</code> is short for <code>dimension</code> here, i.e. the length and width of the 2D array.</p>
<p>However it seems ND didn't consider the possibility of extra small X/Z coordinates, resulting in negative indexing. And that's precisely what happens when SFHBG goes overboard and sends Jak and the zoomer to extreme coordinates (in a certain direction).</p>
<p>The fix is straightforward, mirroring what ND did but on the other end of the spectrum - we just need to ensure we never use an index smaller than 0:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">        (a1-7</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">          (the-as</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">            (pointer int8)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">             (+ (+ (min x-int (+ x-dim -2)) (* (min z-int (+ z-dim -2)) x-dim) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">             ;; og:preserve-this prevent array out-of-bounds access</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">             (+ (+ (min (max 0 x-int) (+ x-dim -2)) (* (min (max 0 z-int) (+ z-dim -2)) x-dim) 0) (the-as int (the-as pointer (-&gt; this data))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">            )</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">          )</span><br></div></code></pre></div></div>
<p>That's it! Now we can let SFHBG rip without worry of crashing (though it can still sometimes lead to other glitches, like ending up stuck in a zoomer with a civilian driving).</p>
<p>This same bug exists in Jak 2 as well, and although we don't have the ability to do SFHBG in Jak 2 and test it out, we can make the same changes to ensure no array out-of-bounds access happens.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-06-20-debugging-sfhbg-crash#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/4106" target="_blank" rel="noopener noreferrer" class="">PR #4106 (the fix)</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Jak Multiplayer]]></title>
            <link>https://jakmods.dev/blog/2026-05-24-multiplayer</link>
            <guid>https://jakmods.dev/blog/2026-05-24-multiplayer</guid>
            <pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In the early days of OpenGOAL development/modding, adding some kind of multiplayer support was of course one of the most common topics to come up. It was surely possible given we had the source code, but there would be a lot of hurdles to get over, and we would see a variety of different approaches over the years.]]></description>
            <content:encoded><![CDATA[<p>In the early days of OpenGOAL development/modding, adding some kind of multiplayer support was of course one of the most common topics to come up. It was surely possible given we had the source code, but there would be a lot of hurdles to get over, and we would see a variety of different approaches over the years.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multiple-controllers">Multiple controllers<a href="https://jakmods.dev/blog/2026-05-24-multiplayer#multiple-controllers" class="hash-link" aria-label="Direct link to Multiple controllers" title="Direct link to Multiple controllers" translate="no">​</a></h3>
<p>Online multiplayer obviously comes with the additional challenge of building out the networking and synchronization systems, so in the beginning we focused on local multiplayer, with everything running in a single game instance.</p>
<p>So the first thing we needed to get working correctly was handling inputs from multiple controllers. GOAL already supported reading inputs from the 2nd controller actually, as there are some cheat codes and debug tools that read from it.</p>
<p>For example, in cheat mode if you press X on the 2nd controller during the rats mini-game <code>(cpad-pressed? 1 x)</code> (index 1 because of zero-indexing), it will automatically end the game:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defstate</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">billy-playing</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">billy</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">code</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">behavior</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">loop</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">billy-game-update</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">zero?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">num-snacks</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">zero?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">num-rats</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">passed-last-stage</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cheat-mode*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">cpad-pressed?</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">go</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">billy-done</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>However despite the PS2 (with Multitap) supporting up to 4 controllers, any inputs on the 3rd or 4th controllers wouldn't be recognized in (Open)GOAL. We were hoping to support at least 4 players locally (I was envisioning a 4-way split-screen view), so we had some work to do here.</p>
<p>On the GOAL side of the code, the controllers are managed via the <code>*cpad-list*</code> which has an array of <code>cpad-info</code>s. We just needed to bump the size of this array from 2 up to 4, and then adjust the asserts to line up with the new memory layout:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">;; List of controllers. It always has 2 controllers.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">(deftype cpad-list (basic)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> ((num-cpads int32       :offset-assert 4)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">   (cpads     cpad-info 2 :offset-assert 8)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">   (cpads     cpad-info 4 :offset-assert 8)  ;; modified from 2-&gt;4 for PC 4-pad support</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">  )</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> (:methods</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">  (new (symbol type) _type_ 0)</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">  )</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> :method-count-assert 9</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  :size-assert         #x10</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  :size-assert         #x18</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  :flag-assert         #x900000010</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  :flag-assert         #x900000018</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> )</span><br></div></code></pre></div></div>
<p>Then in the C++ code, there were some similar bits of code that assumed only 2 controllers, which we extended to support 4:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/* newpad.h */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">static constexpr int CONTROLLER_COUNT = 2;  // support 2 controllers.</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">static constexpr int CONTROLLER_COUNT = 4;  // support 4 controllers.</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/* newpad.cpp */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">struct GamepadState {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  int gamepad_idx[CONTROLLER_COUNT] = {-1, -1};</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  int gamepad_idx[CONTROLLER_COUNT] = {-1, -1, -1, -1};</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  bool glfw_joystick_used[GLFW_JOYSTICK_LAST + 1] = {false};</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token plain">} g_gamepads;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void check_gamepads() {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> auto check_pad = [](int pad) {  // -&gt; bool</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   if (g_gamepads.gamepad_idx[pad] == -1) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     for (int i = GLFW_JOYSTICK_1; i &lt;= GLFW_JOYSTICK_LAST; i++) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">        if (pad == 1 &amp;&amp; i == g_gamepads.gamepad_idx[0])</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">        if (g_gamepads.glfw_joystick_used[i]) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         continue;</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">        }</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       if (glfwJoystickPresent(i) &amp;&amp; glfwJoystickIsGamepad(i)) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         g_gamepads.gamepad_idx[pad] = i;</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">          lg::info("Using joystick {}: {}, {}", i, glfwGetJoystickName(i), glfwGetGamepadName(i));</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">          g_gamepads.glfw_joystick_used[i] = true;</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">          lg::info("Using joystick {} for pad {}: {}, {}", i, pad, glfwGetJoystickName(i),</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">                   glfwGetGamepadName(i));</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">         break;</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       }</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     }</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   } else if (!glfwJoystickPresent(g_gamepads.gamepad_idx[pad])) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">      lg::info("Pad {} has been disconnected", pad);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">      lg::info("Pad {} / joystick {} has been disconnected", pad, g_gamepads.gamepad_idx[pad]);</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">      g_gamepads.glfw_joystick_used[g_gamepads.gamepad_idx[pad]] = false;</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     g_gamepads.gamepad_idx[pad] = -1;</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     return false;</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   }</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   return true;  // pad already exists or was created</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> };</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  if (check_pad(0))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    check_pad(1);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  else</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    g_gamepads.gamepad_idx[1] = -1;</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  for (int i = 0; i &lt; CONTROLLER_COUNT; i++) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    check_pad(i);</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void update_gamepads() {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> check_gamepads();</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  if (g_gamepads.gamepad_idx[0] == -1) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    clear_pad(0);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    clear_pad(1);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  read_pad_state(0);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49"></span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  if (g_gamepads.gamepad_idx[1] != -1)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    read_pad_state(1);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">  else</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    clear_pad(1);</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  for (int i = 0; i &lt; CONTROLLER_COUNT; i++) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    if (g_gamepads.gamepad_idx[i] != -1)</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">      read_pad_state(i);</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    else</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">      clear_pad(i);</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token plain">}</span><br></div></code></pre></div></div>
<p>And voila, now you could successfully connect up to 4 controllers, and read their inputs. For example, to check if the X button was pressed on the 3rd controller: <code>(cpad-pressed? 2 x)</code> (2 because of zero-indexing). Or to check if R2 is being held on the 4th controller: <code>(cpad-hold? 3 r2)</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multiple-jaks">Multiple Jaks<a href="https://jakmods.dev/blog/2026-05-24-multiplayer#multiple-jaks" class="hash-link" aria-label="Direct link to Multiple Jaks" title="Direct link to Multiple Jaks" translate="no">​</a></h3>
<p>One of the next hurdles we needed to solve was simply being able to spawn multiple instances of <code>target</code>, the process used for Jak as a playable character.</p>
<p>water111 actually toyed around with this very early on in OpenGOAL's development (ironically, before water was rendering 😅):</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/water-multi-cc94a45d19baae0a64acfb89c3251550.png" width="630" height="533" class="img_ev3q"></p>
<p>In this case he was just spawning Jak's model as a <code>viewer</code> process, meaning it didn't have any of the <code>target</code> code attached to it, and just rendered the model in a particular animation.</p>
<p>Normally there is only one <code>target</code>, referenced by the global <code>*target*</code> variable, and spawned by the <code>start</code> function which is called when starting a new game, loading a save, respawning after dying, etc:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defun</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">start</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">continue-point</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*level*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">border?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*setting-control*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">default</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">border-mode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">stop</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-3</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">process-spawn</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">init</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">init-target</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg1</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">from</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target-dead-pool*</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">to</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target-pool*</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">stack</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*kernel-dram-stack*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-3</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-3</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target*</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">*target*</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>In theory you could spawn multiple <code>target</code> processes properly by making multiple of these <code>process-spawn</code> calls, but with vanilla code you will immediately crash and see the following in the console logs: <code>WARNING: target pair had to be allocated from the debug pool, because *target-dead-pool* was empty.</code></p>
<p>We need to increase the size of <code>*target-dead-pool*</code>, which was written with the assumption there would only ever be a single <code>target</code> spawned:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">(define *target-dead-pool* (new 'global 'dead-pool 1 (* 48 1024) '*target-dead-pool*))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">(define *target-dead-pool* (new 'global 'dead-pool 4 (* 48 1024) '*target-dead-pool*))</span><br></div></code></pre></div></div>
<p>If we try again to spawn a second <code>target</code>, we'll get a slightly different crash and message: <code>WARNING: sidekick pair had to be allocated from the debug pool, because *16k-dead-pool* was empty.</code></p>
<p><code>sidekick</code> here refers to the process for Daxter - we need to make a similar change to the dead pool that he is spawned from:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">(define *16k-dead-pool* (new 'global 'dead-pool 1 (* 16 1024) '*16k-dead-pool*))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">(define *16k-dead-pool* (new 'global 'dead-pool 4 (* 16 1024) '*16k-dead-pool*))</span><br></div></code></pre></div></div>
<p>(Later on we discovered we would also need to extend the <code>*8k-dead-pool*</code> and <code>*4k-dead-pool*</code> in the same way to be able to use the Zoomer, FlutFlut, etc)</p>
<p>Great, now we can spawn multiple <code>target</code>s, but by default they will all respect controller 1. This is because <code>init-target</code> always assigns <code>(-&gt; *cpad-list* 0)</code> as the controller to be used across the <code>target</code> code:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defbehavior</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">init-target</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">continue-point</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">control</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">unknown-cpad-info00</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-multiplayer">Local Multiplayer<a href="https://jakmods.dev/blog/2026-05-24-multiplayer#local-multiplayer" class="hash-link" aria-label="Direct link to Local Multiplayer" title="Direct link to Local Multiplayer" translate="no">​</a></h3>
<p>As a proof-of-concept, we can just update the controller assignment after spawning additional <code>target</code>s to read from one of the other controllers:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">target</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">process-spawn</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">init</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">init-target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">get-continue-by-name</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"village1-hut"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">from</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target-dead-pool*</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">to</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target-pool*</span><span class="token plain"> :</span><span class="token symbol" style="color:#36acaa">stack</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*kernel-dram-stack*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target2</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">control</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">unknown-cpad-info00</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>The camera goes a little crazy when you spawn additional <code>target</code>s like this, but aside from that, we now had 2 Jaks running around together, controlled by 2 different controllers - multiplayer!</p>
<p>Now there were still a variety of limitations with this initial version. One obvious one is that everybody shares one view and the camera is focused on only one of the <code>target</code>s. Related to this, the game only allows for 2 levels to be loaded at a time. So even if we supported real split-screen, players wouldn't be able to venture off into separate levels unless we refactored the whole level loading system too. For this and various other reasons, we didn't address these issues, and instead focused on getting everything else working in a shared-view local multiplayer.</p>
<p>We quickly found a handful of different behaviors in the game that misbehaved. One common theme was an assumption in the code that there would only ever be one <code>target</code>. For example, if anyone died, it would kill everyone off and reload from the last checkpoint, only respawning the first player, <code>*target*</code>. Or when player 2 reached a FlutFlut transpad and pressed Circle to get on, it would always send the event to player 1, <code>*target*</code>, and they would mount FlutFlut instead.</p>
<p>We started to track of all the <code>target</code>s in a global list, and stored each <code>target</code>'s index in a field within the <code>target</code> object itself. With this, we were able to solve many issues by passing around the index as needed, and looking up the correct <code>target</code> from our list, instead of always using <code>*target*</code> like the original code.</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">define</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">MAX_MULTIPLAYER_COUNT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">define</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*remote-targets*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">global</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">boxed-array</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">handle</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">MAX_MULTIPLAYER_COUNT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defun</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">get-target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">idx</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*remote-targets*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">idx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nonzero?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*remote-targets*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">idx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*remote-targets*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">idx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">handle-&gt;process</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*remote-targets*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">idx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>A couple of problem areas that needed more debugging and code changes were handling edge grabs, and interactions with water volumes. Without getting into too much detail, the edge grab code relies on some collision checks which cache tons of data in global objects like <code>*collide-work*</code> and <code>*collide-cache*</code>. These were designed to only hold enough data for a single <code>target</code>, but since they were being shared by every spawned <code>target</code>, you would get weird behavior like this:</p>
<p><!--$--><video style="display:block;width:640px;height:360px" src="/assets/medias/edgegrabs-fcf743c2fe132f45f1f6ba50774b7ba6.mp4" controls=""></video><!--/$-->&nbsp;</p>
<p>Similar to the list of <code>target</code>s, we extended these systems as needed to hold lists instead of single objects, so each <code>target</code> instance had it's own dedicated version. Then we just need to replace all the references to these objects to pull from the list instead, using the specific <code>target</code>'s index.</p>
<p>We made a few other small accessibility changes I won't go into detail about here:</p>
<ul>
<li class="">adding a button combo to spawn in a <code>target</code> for your controller</li>
<li class="">making each <code>target</code> a different color</li>
<li class="">using a wider camera than the default view</li>
<li class="">respawning far-off players back at player 1</li>
</ul>
<p>And with that, we had a pretty stable shared-screen local multiplayer working!</p>
<!--$?--><template id="B:0"></template><!--/$-->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="online-multiplayer">Online Multiplayer<a href="https://jakmods.dev/blog/2026-05-24-multiplayer#online-multiplayer" class="hash-link" aria-label="Direct link to Online Multiplayer" title="Direct link to Online Multiplayer" translate="no">​</a></h3>
<p>The next step here was of course to get this online so you could play with people across the world.</p>
<p>Steam and Parsec actually have some nice remote play features where the host's screen is shared to other players, and the other player's controller inputs are mapped as additional controllers back on the host's computer. So these tools could immediately be used with the local multiplayer - see this old setup guide Zedb0t put together:</p>
<p><!--$?--><template id="B:1"></template><!--/$-->&nbsp;</p>
<p>While this was cool, in terms of actual playability, the single camera view was still an incredibly limiting factor. Since we wanted to keep exploring online options, rather than going back to split-screen support, we started thinking about ways to have multiple OpenGOAL instances communicating with each other. This way each player would have their own game running "like normal" with the camera focused on their Jak.</p>
<p>One of the earliest iterations of this is a proof-of-concept Zedb0t built where each OpenGOAL instance posted player positions to a Discord server, and read back other player's positions from that server. Then in each game, the remote player's position would be updated, in this case using the Farmer as a stand-in model.</p>
<p><!--$?--><template id="B:2"></template><!--/$-->&nbsp;</p>
<p>As you can hear Zedb0t and Ruh discussing in the video, the latency was pretty bad when using a Discord server as the "host" for these messages between OpenGOAL instances, but the idea was there.</p>
<p>For the next iteration, we built and hosted a small Python web server that would receive HTTP requests from OpenGOAL and track each connected player's position. We extended the local multiplayer code to periodically send the server our current position, and request the position of all the other players so that we could update them in our local game.</p>
<p><!--$?--><template id="B:3"></template><!--/$-->&nbsp;</p>
<p>In this way, everyone had their own OpenGOAL instance running with the camera focused on them, but remote players would still be seen moving around in their game. While we were only syncing position/rotation and not properly animating remote players, they were still able to pick up Orbs or Cells if they were in a level that you had loaded locally.</p>
<p>The level loading issue meant there was still de-sync across games if players went to different levels, but overall it was becoming much more playable. While not perfect, the latency in this version was way better than the Discord-based approach. It was even decent enough that we built out a Hide and Seek / Tag gameplay mode!</p>
<p><!--$--><video style="display:block;width:640px;height:360px" src="/assets/medias/hns-1-d49397450bbf754b76604494dfc1da6f.mp4" controls=""></video><!--/$-->&nbsp;</p>
<p><!--$--><video style="display:block;width:640px;height:360px" src="/assets/medias/hns-2-1ea3bb28005311940ea614408d815ff3.mp4" controls=""></video><!--/$-->&nbsp;</p>
<p>Around the same time Zedb0t and I were playtesting this with the community, Dexz revealed that he had been working on his own multiplayer project for OpenGOAL, called TeamRuns! His project was a bit more focused on syncing collectables and game state (e.g. for co-op speedrunning, lockout races. etc), and in this early version you wouldn't actually see other players in your game, only the result of their actions:</p>
<p><!--$?--><template id="B:4"></template><!--/$-->&nbsp;</p>
<p>Zedb0t and I quickly connected with Dexz to try to work on combining our two projects. TeamRuns used peer-to-peer networking, and had way better latency than our HTTP dedicated server approach since it relied on UDP instead of TCP/IP.</p>
<p>Based on our multiplayer changes, Dexz was able to make changes to his code to allow for spawning multiple <code>target</code>s. So he started including player positions in the payloads sent across the network, updating remote <code>target</code> positions based on these similar to what we were doing in our previous approach.</p>
<p>A few months later, and Dexz had taken this one step further - he was now syncing animations for remote players as well! It was incredible how much more "real" the multiplayer felt when remote players were punching and rolljumping through your world instead of just sliding and floating around 😅</p>
<p><!--$?--><template id="B:5"></template><!--/$-->&nbsp;</p>
<p>And that's more or less how we arrived at the modern version of online Jak multiplayer!</p>
<p>TeamRuns has a bunch of cool game modes and built-in speedrunning leaderboards. It's since been featured at the European Speedrunner Assembly as well:</p>
<p><!--$?--><template id="B:6"></template><!--/$-->&nbsp;</p>
<p>Big shoutout to Dexz for his hard work on TeamRuns, be sure to check it out and play with your friends!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-05-24-multiplayer#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/1751" target="_blank" rel="noopener noreferrer" class="">PR #1751 - 4 controller support</a></li>
<li class=""><a href="https://github.com/OpenGOAL-Unofficial-Mods/jak-project/tree/curl-hide-and-seek" target="_blank" rel="noopener noreferrer" class="">Curl Hide and Seek branch</a></li>
<li class=""><a href="https://teamruns.web.app/" target="_blank" rel="noopener noreferrer" class="">TeamRuns website</a></li>
<li class=""><a href="https://github.com/JoKronk/teamruns-jak-project" target="_blank" rel="noopener noreferrer" class="">TeamRuns repo</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Eco Meter Flashing Bug]]></title>
            <link>https://jakmods.dev/blog/2026-05-23-eco-hud-flashing-bug</link>
            <guid>https://jakmods.dev/blog/2026-05-23-eco-hud-flashing-bug</guid>
            <pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A user in the OpenGOAL Discord today asked about a bug they encountered in Jak 2 and 3 where the Dark Eco HUD would stop flashing after reaching around 70% game progress. They claimed that the issue even persisted after saving, rebooting console, and loading the save.]]></description>
            <content:encoded><![CDATA[<p>A user in the OpenGOAL Discord today asked about a bug they encountered in Jak 2 and 3 where the Dark Eco HUD would stop flashing after reaching around 70% game progress. They claimed that the issue even persisted after saving, rebooting console, and loading the save.</p>
<p>I had never heard of such a bug, so my first guess was that this was another high FPS bug. But the user said no, it happens at 60 FPS and on console as well. Plus the claim that it only happened after progressing far enough into the game, and that it persisted after a reboot was strange...</p>
<!-- -->
<!--$?--><template id="B:0"></template><!--/$-->
<p>You can see in this example that the Dark Jak icon stops flashing at around 2:36:05. The Dark Eco meter stops flashing later on around the 4.5 hour mark, just before the boss fight in Mars Tomb.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="investigation">Investigation<a href="https://jakmods.dev/blog/2026-05-23-eco-hud-flashing-bug#investigation" class="hash-link" aria-label="Direct link to Investigation" title="Direct link to Investigation" translate="no">​</a></h3>
<p>I started poking around the HUD code, looking for the code that added the flashing effect. My guess was that it would be referencing some kind of <code>time-frame</code> in order to flash with a consistent frequency.</p>
<p>In Jak 2, there are two separate HUD elements involved - <code>hud-health</code> which includes the Dark Eco meter, and <code>hud-dark-eco-symbol</code> which handles the Jak / Dark Jak icon. Sure enough in the <code>draw</code> method for both of these, we can see the flashing effect is achieved by scaling the sprite color based on a <code>sine</code> function involving the <code>game-clock</code>:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">draw</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">hud-health</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-12</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">182.04445</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-clock</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">127</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-12</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">y</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-12</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-12</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">draw</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">hud-dark-eco-symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-31</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">182.04445</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-clock</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">160</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-31</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">y</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-31</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">sprites</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">color</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">z</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-31</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>As I expected, there was nothing related to checking game progression in this code, so what's causing the bug? Let's look more closely at the math for the flash effect.</p>
<p>The <code>(-&gt; *display* game-clock frame-counter)</code> tracks how long you've been playing the game on this save file (e.g. for the playtime display on the save/load screens). This just an integer counting the number of ticks (300 ticks / sec) since the game was started (technically it start at <code>(seconds 1000) = 300000</code>). This doesn't include time where the game is paused or in cutscenes.</p>
<p>The bug lies within the <code>(sin (* 182.04445 (the float (* (-&gt; *display* game-clock frame-counter) &lt;multiplier&gt;))))</code> calculation - it turns out GOAL's <code>sin</code> breaks down when you feed it really big float values (~2147483584.0 or bigger):</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483400.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">-1127675244</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#xffffffffbcc90a94</span><span class="token plain">             </span><span class="token number" style="color:#36acaa">-0.0245</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;invalid</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">object</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">#xbcc90a94</span><span class="token symbol" style="color:#36acaa">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483500.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">-1136062860</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#xffffffffbc490e74</span><span class="token plain">             </span><span class="token number" style="color:#36acaa">-0.0122</span><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">#&lt;invalid</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">object</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">#xbc490e74</span><span class="token symbol" style="color:#36acaa">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483600.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483700.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483800.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2147483900.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token symbol" style="color:#36acaa">gc&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">sin</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">9999999999.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">0</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">#x0</span><span class="token plain">              </span><span class="token number" style="color:#36acaa">0.0000</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">0</span><br></div></code></pre></div></div>
<p>Once your <code>(-&gt; *display* game-clock frame-counter)</code> gets large enough, <code>sin</code> starts to return <code>0.0</code> no matter what, effectively pausing the flash effect.</p>
<p>For example, the code for the Dark Jak icon has a <code>&lt;multipler&gt;</code> of 4, so it will stop flashing after approximately <code>(2147483584.0 / (182.04445 * 4) - 300000) / (300 * 60 * 60) = 2.45</code> hours, which roughly matches the example video from earlier.</p>
<p>So the bug isn't related to game progression, but rather game playtime! The <code>(-&gt; *display* game-clock frame-counter)</code> value is stored in your savefile, which explains why the bug persists even after rebooting and loading your savefile. This bug also affects the Light Eco meter in Jak 3.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fix">The Fix<a href="https://jakmods.dev/blog/2026-05-23-eco-hud-flashing-bug#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h3>
<p>Thankfully dass saved me some head scratching and pointed out that the <code>(* 182.04445</code> is effectively the same as the <code>degrees</code> macro (65536 / 360 = 182.0444):</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defglobalconstant</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">DEGREES_PER_ROT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">65536.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">;; this was deg in GOAL</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmacro</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">degrees</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Convert number to degrees unit.</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">   Will keep a constant float/int constant."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">cond</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">float?</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">integer?</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">DEGREES_PER_ROT</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">360.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">#t</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token reader-macro operator" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">360.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token symbol" style="color:#36acaa">DEGREES_PER_ROT</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>So first, we can refactor the code to be a bit more readable by using the <code>degrees</code> macro:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">(defmethod draw ((this hud-dark-eco-symbol))</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">      (let ((v1-31</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">              (+ (the int (* 15.0 (sin (* 182.04445 (the float (* (-&gt; *display* game-clock frame-counter) 4)))))) 160)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">              (+ (the int (* 15.0 (sin (degrees (* (-&gt; *display* game-clock frame-counter) 4))))) 160)</span><br></div></code></pre></div></div>
<p>Now we see the <code>frame-counter</code> (times the <code>&lt;multiplier&gt;</code>) is being converted to degrees before calling <code>sin</code> on it. Since the sine function fluctuates between -1 and 1 and back every 360 degrees, this results in the flash effect cycling every <code>360 / &lt;multiplier&gt;</code> ticks.</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/sine-5e1de3bdb34afecf4a43462780a07935.jpg" width="1967" height="778" class="img_ev3q"></p>
<p>Knowing this, we can modulo the value by 360 and still get the same result from <code>sin</code>:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">(defmethod draw ((this hud-dark-eco-symbol))</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">      (let ((v1-31</span><br></div><div class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">              (+ (the int (* 15.0 (sin (* 182.04445 (the float (* (-&gt; *display* game-clock frame-counter) 4)))))) 160)</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">              (+ (the int (* 15.0 (sin (degrees (mod (* (-&gt; *display* game-clock frame-counter) 4) 360))))) 160)</span><br></div></code></pre></div></div>
<p>By doing this, we ensure the value we pass to <code>sin</code> is between (-360.0, 360.0), and avoid any large float values that would cause it to break. Now no matter how long you play, i.e. how big <code>(-&gt; *display* game-clock frame-counter)</code> gets, the <code>sin</code> call should function correctly, and the HUD elements will keep flashing correctly!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-05-23-eco-hud-flashing-bug#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/4285" target="_blank" rel="noopener noreferrer" class="">PR #4285</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[same-attack-invulnerable-timeout clock drift bug]]></title>
            <link>https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug</link>
            <guid>https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug</guid>
            <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In the 2025 Jak 3 No Out of Bounds speedrun route, players would need to perform a glitch to get invulnerability by getting their health down to 1 hit left, and then spin kicking and shooting on the exact same frame as they touch a source of damage, like a torch. This particular setup "confuses the game" and leaves Jak alive but with certain flags set that grant him invulnerability.]]></description>
            <content:encoded><![CDATA[<p>In the 2025 Jak 3 No Out of Bounds speedrun route, players would need to perform a glitch to get invulnerability by getting their health down to 1 hit left, and then spin kicking and shooting on the exact same frame as they touch a source of damage, like a torch. This particular setup "confuses the game" and leaves Jak alive but with certain flags set that grant him invulnerability.</p>
<p>To get Jak's health down to 1 hit, players would often stand on the torch and take several hits. But they started to notice something weird - the cooldown time in between taking damage from the torch would sometimes be longer than the normal 2 seconds. After encountering the issue several times, theUsual figured out a particular sequence of actions in the speedrun route that would cause the cooldown time to increase (the Temple climb and 2 restart missions to break into Temple), though it wasn't clear exactly what was happening.</p>
<p>Eventally I realized this was something I had also seen happen to Blahpy in Jak 2, on the hot plates in the Palace Cables level. But I still wasn't sure what exactly triggered the bug in either game.</p>
<!-- -->
<!--$?--><template id="B:0"></template><!--/$-->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="investigation">Investigation<a href="https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug#investigation" class="hash-link" aria-label="Direct link to Investigation" title="Direct link to Investigation" translate="no">​</a></h3>
<p>First I wanted to understand how the 2 second cooldown between Jak taking damage works. After some poking around I realized that pretty much all attacks to Jak go through <code>target-attacked</code>, which for many cases makes a call to <code>target-log-attack</code>, where I noticed a suspiciously named value: <code>(-&gt; *TARGET-bank* same-attack-invulnerable-timeout)</code>.</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defbehavior</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target-log-attack</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">3</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">object</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-info-old</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dotimes</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">2</span><span class="token number" style="color:#36acaa">-2</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-9</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-info-old</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a2-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-9</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">time-elapsed?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-9</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*TARGET-bank*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">same-attack-invulnerable-timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">#f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div></code></pre></div></div>
<p>Let's break this down - <code>target</code> (Jak) has an <code>attack-info-old</code> array of length 8, which we loop through and check if the old attack has the same <code>id</code> as the new attack we're processing. If there's a match, then we check if a certain amount of time <code>same-attack-invulnerable-timeout</code> has passed since the old attack's <code>attack-time</code> - if not, we're still in the cooldown window, so we return <code>#f</code> and the attack does nothing to Jak.</p>
<p>The <code>same-attack-invulnerable-timeout</code> is never changed anywhere in the code, it's always <code>(seconds 2)</code>, so how could we end up getting longer cooldown windows? I started with my favorite brute force approach when I'm confused about the code / state of the game - printing a bunch of info to the console to verify my assumptions and hopefully find a lead.</p>
<p>Right before the line in question, I printed the value of <code>(-&gt; v1-9 attack-time)</code>, as well as <code>(current-time)</code> which is what the <code>time-elapsed?</code> macro compares against. I should mention that these fields are <code>time-frame</code>s, which are really just integers counting the number of ticks (300 ticks / second) since some starting time, so they get printed as integers.</p>
<p>Under normal circumstances while standing on a torch, it would print <code>time-frame</code> values within 600 ticks of each other, which makes sense:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 327745 (-&gt; v1-9 attack-time) vs 327750 (current-time)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 327745 (-&gt; v1-9 attack-time) vs 327755 (current-time)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 327745 (-&gt; v1-9 attack-time) vs 327760 (current-time)</span><br></div></code></pre></div></div>
<p>But after following the setup that theUsual pointed out, we would see something very strange - the <code>attack-time</code> was in the future, relative to <code>(current-time)</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 328750 (-&gt; v1-9 attack-time) VS 328527 (current-time)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 328750 (-&gt; v1-9 attack-time) VS 328532 (current-time)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">~~~~&gt; 328750 (-&gt; v1-9 attack-time) VS 328537 (current-time)</span><br></div></code></pre></div></div>
<p>Well, this fits with the extra long cooldowns, but why is it happening?</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="clocks">Clocks<a href="https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug#clocks" class="hash-link" aria-label="Direct link to Clocks" title="Direct link to Clocks" translate="no">​</a></h3>
<p>The game actually keeps track of several different clocks simultaneously, and on a given frame it may or may not tick each clock forward, and by varying rates, depending on the state of the clock and state of the game.</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">deftype</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">display</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">basic</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">clock</span><span class="token plain">                 </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          </span><span class="token number" style="color:#36acaa">22</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">session-clock</span><span class="token plain">         </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">game-clock</span><span class="token plain">            </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">base-clock</span><span class="token plain">            </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">real-clock</span><span class="token plain">            </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">frame-clock</span><span class="token plain">           </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">real-frame-clock</span><span class="token plain">      </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">target-clock</span><span class="token plain">          </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">entity-clock</span><span class="token plain">          </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">part-clock</span><span class="token plain">            </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">bg-clock</span><span class="token plain">              </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">camera-clock</span><span class="token plain">          </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">total-game-clock</span><span class="token plain">      </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain">          :</span><span class="token symbol" style="color:#36acaa">overlay-at</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>For example, the <code>target-clock</code> is not ticked ahead whenever the player pauses the game, whereas <code>real-clock</code> will keep ticking like normal while paused. Or while using Light Jak Flash Freeze, the rates for <code>entity-clock</code> will be reduced so that time moves more slowly for actors other than Jak.</p>
<p>So let's go back to the <code>same-attack-invulnerable-timeout</code> logic. We're checking the <code>attack-time</code> we pulled out of the <code>attack-info-old</code> array - where/how does that <code>attack-time</code> get set? It happens as part of this <code>compute-intersect-info</code> method that we call once we've confirmed an attack should count:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">compute-intersect-info</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">object</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">process-drawable</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">process</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">3</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">touching-shapes-entry</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">logtest?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">attack-mask</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">attack-time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*display*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">base-clock</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">frame-counter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div></code></pre></div></div>
<p>We can see <code>attack-time</code> is set from the <code>base-clock</code>, so let's see if what we're comparing against is also based on the same clock.</p>
<p><code>time-elapsed?</code> is checking <code>(current-time)</code> AKA <code>(-&gt; PP clock frame-counter)</code>. <code>PP</code> here is the current process, at the time when this code is executed - in this case it will be <code>target</code> since we're in the <code>target-log-attack</code> behavior which belongs to <code>target</code>. Since <code>target</code> will be spawned from the <code>*target-pool*</code>, it will have the <code>target-clock</code> as its clock:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defun</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">set-display</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">display</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*target-pool*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">clock</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">target-clock</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>So we're comparing <code>time-frame</code>s from two different clocks, which starts to lose meaning if one of these clocks is/was paused or is/was running at a different rate than the other.</p>
<p>During various "slow down" effects, the <code>target-clock</code> runs at a reduced rate compared to the <code>base-clock</code>. So every restart mission, every Light Jak Freeze, every Dark Jak special attack, etc causes the <code>target-clock</code> to run slower and fall behind the <code>base-clock</code>.</p>
<p>Once this has happened one or more times, any <code>attack-time</code> will be set based on <code>base-clock</code>, with a value seconds ahead of <code>target-clock</code>. Then in the <code>same-attack-invulnerable-timeout</code> logic, we'll compare our process's clock (<code>target-clock</code>) vs the <code>attack-time</code>, and because of the clock drift, the cooldown will end up being seconds longer than intended!</p>
<p>Most annoying of all is that these clocks won't get reset by starting a new game or going back to the main menu, the only option is to reset your console / close and reopen OpenGOAL.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fix">The Fix<a href="https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h3>
<p>This is another fairly straightforward fix - we just need to use the same clock on both sides of the comparison. We decided to use the <code>base-clock</code> in this case, so we just modified the <code>same-attack-invulnerable-timeout</code> logic a bit:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">(if (not (time-elapsed? (-&gt; v1-9 attack-time) (the-as time-frame (-&gt; *TARGET-bank* same-attack-invulnerable-timeout))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">;; og:preserve-this fix clock drift bug which can lead to longer invuln timeouts (use base-clock instead of target-clock)</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">(if (not (&gt;= (- (-&gt; *display* base-clock frame-counter) (-&gt; v1-9 attack-time)) (the-as time-frame (-&gt; *TARGET-bank* same-attack-invulnerable-timeout))))</span><br></div><div class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> (return #f)</span><br></div></code></pre></div></div>
<p>Since this bug was just an annoyance and didn't have any benefits, it was non-controversial to get this merged into the vanilla <code>jak-project</code>. Other bugs however, such as the invulnerability glitch mentioned earlier, or Citadel Skip in Jak 1, won't be a detriment to casual gameplay and do have value in the speedruns, so you probably won't see them getting fixed. Unless you play the Patched mod 😜</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-05-19-invuln-timeout-bug#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/3978" target="_blank" rel="noopener noreferrer" class="">PR #3978</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Patching "NG+ Glitch"]]></title>
            <link>https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch</link>
            <guid>https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch</guid>
            <pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Years ago, the Jak 1 speedrun community came up with an NG+ category where you start the game with all of the Power Cells except for the ones in Citadel. There was a bug players would run into while running this category where under certain conditions, if you picked up some collectables (e.g. Precursor Orbs) and then (without saving) loaded a save file, those collectables would still be missing from the level, though you wouldn't have gotten credit for picking them up. It's actually part of the reason you'll see console speedrunners load a dummy save in Sandover before they start a new run back in Geyser, to ensure levels get fully unloaded and the glitch is avoided.]]></description>
            <content:encoded><![CDATA[<p>Years ago, the Jak 1 speedrun community came up with an NG+ category where you start the game with all of the Power Cells except for the ones in Citadel. There was a bug players would run into while running this category where under certain conditions, if you picked up some collectables (e.g. Precursor Orbs) and then (without saving) loaded a save file, those collectables would still be missing from the level, though you wouldn't have gotten credit for picking them up. It's actually part of the reason you'll see console speedrunners load a dummy save in Sandover before they start a new run back in Geyser, to ensure levels get fully unloaded and the glitch is avoided.</p>
<!-- -->
<p>I was made aware of this glitch in OpenGOAL after we added Individual Level and Hub 2/3 100% speedrun options to the speedrunner menu. For example, players running Hub 2 100% would hit the Blue Sage warp gate button, skip the cutscene, and then run off into Precursor Basin. If they reset the run from there, they would spawn back at the end of Fire Canyon and while entering Rock Village the warp gate cutscene would start playing early, because the button was still pressed from the previous run. Similarly, any orbs or flies collected in Rock Village in the previous run would be missing after resetting with this same setup.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="entitys-and-perm-data"><code>entity</code>s and <code>perm</code> data<a href="https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch#entitys-and-perm-data" class="hash-link" aria-label="Direct link to entitys-and-perm-data" title="Direct link to entitys-and-perm-data" translate="no">​</a></h3>
<p>To understand how this glitch happens, we need to cover some fundamentals about how the game keeps track of all these things.</p>
<p>All of the things in the game world that you interact with (lets call them "actors") are spawned as different types of <code>process-drawable</code>s, and the majority of them are backed by an <code>entity</code>. I like to think of an <code>entity</code> as the "idea" of an actor, and the <code>process-drawable</code> as the physical representation of the actor. Each <code>entity</code> also has a variety of <code>perm</code> data - this data is used for things like remembering a button has been pushed, or that Plant Boss has been killed, or that you've collected 3 out of 4 Orbs from a crate.</p>
<p>The game tracks this <code>perm</code> data in a couple places. The global <code>*game-info*</code> object has a <code>perm-list</code> which functions as a sort of snapshot of all <code>perm</code> data for the current game. Separate from this, the game has up to 2 <code>level</code>s loaded into memory, and each <code>level</code> holds onto a list of <code>entity</code>s along with their <code>perm</code> data. This data is directly reflected in how the <code>entity</code>s are spawned (or not spawned) as <code>process-drawable</code>s in-game, with the actor's state being determined by the <code>entity</code> <code>perm</code> data.</p>
<p>Whenever a <code>level</code> is birthed (i.e. was already loaded, and is being made visible), the game will copy any relevant <code>perm</code> data from <code>*game-info*</code> over to the corresponding <code>entity</code> <code>perm</code> data in the <code>level</code>:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">birth</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">level</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Birth a level to make it alive! It must be loaded."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">copy-perms-to-level!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Whenever a <code>level</code> is deactivated (i.e. made invisible, but still loaded in memory), the game will copy all of the <code>entity</code> <code>perm</code> data for that level into the <code>*game-info*</code> <code>perm-list</code>:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">deactivate</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">level</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Kill the level. This won't remove it from memory."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">copy-perms-from-level!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Whenever you save the game, it will first copy <code>perm</code> data from active levels over to <code>*game-info*</code>'s <code>perm-list</code>. Then it will write the <code>perm-list</code> data into the save file:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">save-game!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Update the game-save to have the info from the current game state"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dotimes</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">3</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*level*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*level*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">level</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s3-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a1-1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">status</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">active</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">copy-perms-from-level!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a1-1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">v</span><span class="token number" style="color:#36acaa">1</span><span class="token number" style="color:#36acaa">-60</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-59</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">4</span><span class="token number" style="color:#36acaa">-2</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">pointer</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">a</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-39</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save-tag</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-60</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a0-39</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">elt-type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">game-save-elt</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a0-39</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">elt-count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s4-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">a0-39</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">elt-size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">uint</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">3</span><span class="token number" style="color:#36acaa">-4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">v1-60</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dotimes</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">2</span><span class="token number" style="color:#36acaa">-2</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s4-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">mem-copy!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save-tag</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s3-4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s2-2</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s2-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Whenever you start a new game or load a save file, first the <code>initialize!</code> method will run and make sure to reset <code>*game-info*</code>'s <code>perm-list</code> to length zero, effectively clearing it out:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">initialize!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">cause</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">save-to-load</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">continue-point-override</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Then if loading a file, it will read the <code>perm</code> data from the file and load it into <code>*game-info*</code>'s <code>perm-list</code>:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">load-game!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Copy save data from a game-save to a game-info"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">game-save-tag</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">tag</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">tag</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">user-int8</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">save</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">elt-type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">game-save-elt</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">3</span><span class="token number" style="color:#36acaa">-2</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">min</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">elt-count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">allocated-length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s3-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dotimes</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">s</span><span class="token number" style="color:#36acaa">2</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s3-2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">             </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">mem-copy!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm-list</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s2-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">&amp;+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pointer</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s2-0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>(And then any levels being birthed will get <code>perm-list</code> data from <code>*game-info*</code> copied into their <code>entity</code> <code>perm</code> data)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-whats-wrong">So what's wrong?<a href="https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch#so-whats-wrong" class="hash-link" aria-label="Direct link to So what's wrong?" title="Direct link to So what's wrong?" translate="no">​</a></h3>
<p>At first glance it seems like the data is passed between <code>level</code>s ↔️ <code>*game-info*</code> ↔️ <code>game-save</code> in a sensible way, so what's the issue?</p>
<p>Well, let's say you collect an Orb <code>money-2844</code> in <code>village2</code>, and then you walk into Precursor Basin such that <code>village2</code> is deactivated but still loaded in memory. If you load a save, or reset a speedrun to a checkpoint that has <code>village2</code> loaded, what happens?</p>
<ul>
<li class=""><code>village2</code> is still currently loaded, and has <code>perm</code> data for <code>money-2844</code> tracking that you collected it</li>
<li class=""><code>initialize!</code> runs and clears out <code>*game-info*</code>'s <code>perm-list</code></li>
<li class="">if loading a save, <code>*game-info*</code>'s <code>perm-list</code> is populated from the save file (let's assume the save file has no perm data about <code>money-2844</code>)</li>
<li class="">all relevant levels are loaded and birthed (<code>village2</code> becomes visible)<!-- -->
<ul>
<li class="">as part of this, <code>*game-info*</code>'s <code>perm-list</code> is copied over to <code>village2</code>'s <code>entity</code> <code>perm</code> data</li>
<li class="">BUT <code>*game-info*</code> has no <code>perm</code> data on <code>money-2844</code>, so whatever <code>perm</code> data <code>village2</code> had is left untouched!</li>
</ul>
</li>
</ul>
<p>There's an assumption baked in somewhere here that either a level being birthed will have no meaningful <code>perm</code> data, or that it can always be fully reset by copying over <code>perm</code> data from <code>*game-info*</code>. Either way you look at it, the end result is that you can end up with incorrect stale <code>perm</code> data in your level. So Orbs remain collected, buttons remain pushed - you can even cheat at Yakows by putting all but 1 in the pen, loading a save to trigger the glitch, and then you just need to put the 1 remaining Yakow in the pen to get the Cell reward.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fix">The Fix<a href="https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h3>
<p>So how do we fix the bug? This one's actually pretty simple - since <code>perm</code> data is written to <code>*game-info*</code> when a <code>level</code> is deactivated, and copied back into the <code>level</code> when it's birthed, we don't really need to hold onto any <code>perm</code> data in the <code>level</code> once it's been deactivated. This is true regardless of whether we birth the level by walking back over towards the <code>level</code>, loading a save, or resetting a speedrun.</p>
<p>Lucky for us, there is a debug function <code>reset-actors</code> that does pretty much exactly what we want - it clears out any modified <code>perm</code> data from the active levels. We can copy some of its code into the <code>deactivate</code> function, after we've copied the <code>perm</code> data over to <code>*game-info*</code>:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmethod</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">deactivate</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">level</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Kill the level. This won't remove it from memory."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">copy-perms-from-level!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*game-info*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">;; og:preserve-this fully clear entity perm status in the level itself (based on reset-actors)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">;;  it should be copied back out of game-info on birth to prevent "NG+ glitch"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">lev-ents</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">entity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">dotimes</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">idx</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">lev-ents</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">lev-ents</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">data</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">idx</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">entity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">update-perm!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ent</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">extra</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">perm</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">'</span><span class="token symbol" style="color:#36acaa">game</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">entity-perm-status</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1919</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>And voila, no more NG+ Glitch!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-05-16-patching-ngplus-glitch#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/3690" target="_blank" rel="noopener noreferrer" class="">PR #3690</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Barg's First OpenGOAL "Mod"]]></title>
            <link>https://jakmods.dev/blog/2026-05-15-my-first-mod</link>
            <guid>https://jakmods.dev/blog/2026-05-15-my-first-mod</guid>
            <pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[When I first learned about the OpenGOAL project, I was immediately excited for a couple reasons - I loved the Jak games growing up, and as a software engineer, I was curious to see the source code behind a video game engine. As a kid, I made some Flash games, Pokemon mods, custom Guitar Hero discs, etc, so I guess I was always kind of interested in building/modding games. And as I learned more about OpenGOAL, and more about the unique development of Jak 1, my fascination continued to grow.]]></description>
            <content:encoded><![CDATA[<p>When I first learned about the OpenGOAL project, I was immediately excited for a couple reasons - I loved the Jak games growing up, and as a software engineer, I was curious to see the source code behind a video game engine. As a kid, I made some Flash games, Pokemon mods, custom Guitar Hero discs, etc, so I guess I was always kind of interested in building/modding games. And as I learned more about OpenGOAL, and more about the unique development of Jak 1, my fascination continued to grow.</p>
<!-- -->
<p>I did some brief research about Lisp programming syntax (I had one college course where we wrote some Scheme, so I was loosely familiar), and then started reading through <a href="https://github.com/open-goal/jak-project/pulls" target="_blank" rel="noopener noreferrer" class="">Pull Requests on the <code>jak-project</code> GitHub</a>, trying to find smaller code changes that I could follow along with. Eventually I found a <a href="https://github.com/open-goal/jak-project/issues/386" target="_blank" rel="noopener noreferrer" class="">GitHub issue listing off various ideas and improvements for Jak 1</a>, and looked for something that sounded approachable, something I could attempt to complete and use to get my feet wet with OpenGOAL.</p>
<p>There was one bullet point that caught my attention as a possible candidate:</p>
<blockquote>
<p>Camera invert toggle (for the weirdos).</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-controller-inputs">Understanding controller inputs<a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#understanding-controller-inputs" class="hash-link" aria-label="Direct link to Understanding controller inputs" title="Direct link to Understanding controller inputs" translate="no">​</a></h3>
<p>Back in 2021/2022, I hadn't played Jak much in recent years, so I remember the camera controls tripping me up when I started playing regularly through OpenGOAL. Adding settings to toggle inverted camera seemed like something I might want for myself, so I decided I would try to add support for it (for the record, I have since learned the errors of my ways, and now play with default camera controls).</p>
<p>I browsed through all kinds of code, rather blindly at first, trying to understand how controller inputs were read. I saw <code>cpad-pressed?</code> and <code>cpad-hold?</code> throughout the code, and digging deeper I found where the game was reading individual button values, like <code>(-&gt; *cpad-list* 0 square)</code>. After awhile it clicked that <code>leftx</code>, <code>lefty</code>, <code>rightx</code>, and <code>righty</code> referred to the X and Y values for the two analog sticks (in particular, I kept seeing <code>rightx</code>/<code>righty</code> referenced in camera code).</p>
<p>99% of the time, these analog stick values were being evaluated using the <code>analog-input</code> function, for example:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">leftx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">128.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">48.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">110.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>With some experimentation I figured out that multiplying the result of <code>analog-input</code> by <code>-1</code> would invert the direction the camera moved.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="extending-pc-settings">Extending PC settings<a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#extending-pc-settings" class="hash-link" aria-label="Direct link to Extending PC settings" title="Direct link to Extending PC settings" translate="no">​</a></h3>
<p>In the PC port we've added a variety of settings that didn't exist in the original game - things like display resolution, aspect ratio, MSAA, cutscene skips, etc. Naturally, this is where it made sense to add settings to toggle inverted camera controls as well. Typically boolean (on/off) settings like this are represented as <code>symbol</code>s (either <code>#t</code> or <code>#f</code>).</p>
<p>Now the Jak games are a bit special, in that camera controls are already inverted compared to the defaults in most modern games - except for first person horizontal controls! At first I had only 2 settings, for vertical and horizontal camera inversion, but later I extended this to 4 settings so that players could configure first and third person controls independently:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">deftype</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">pc-settings</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">basic</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">first-camera-h-inverted?</span><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; first-person horizontal camera inverted</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">first-camera-v-inverted?</span><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; first-person vertical camera inverted</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">third-camera-h-inverted?</span><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; third-person horizontal camera inverted</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">third-camera-v-inverted?</span><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">symbol</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; third-person vertical camera inverted</span><br></div></code></pre></div></div>
<p>There are a few other bits of code to update when introducing new settings (resetting settings, reading from file, writing to file, etc), but they're not very interesting so I'll skip over them here. You can check my PRs <a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#references" class="">below</a> or search the codebase for one of the setting field names (e.g. <code>first-camera-h-inverted?</code>) if you're curious.</p>
<p>The other bit of work relating to adding new settings is hooking them up to options in the pause menu, known internally as the <code>progress</code> menu. I've written about this process before in the <a href="https://opengoal.dev/docs/developing/modding_examples/checkpoint_randomizer/#randomizer-options-in-pause-menu" target="_blank" rel="noopener noreferrer" class="">Checkpoint Randomizer modding example</a>, so I won't rehash it here.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="modifying-vanilla-code">Modifying vanilla code<a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#modifying-vanilla-code" class="hash-link" aria-label="Direct link to Modifying vanilla code" title="Direct link to Modifying vanilla code" translate="no">​</a></h3>
<p>Since the decompiler is always evolving and improving, occasionally we will re-decompile all or some of the GOAL source code. So whenever we make edits to OpenGOAL code, we try to edit the vanilla code as little as possible, or leave special comments as markers indicating that the code was changed in the PC port. This way if the code is decompiled again, our edits are either unaffected, or we can detect that markers were removed and go back and restore the PC port edits. For example, we adjusted some HUD sprite positioning for wider aspect ratios:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">;; og:preserve-this Positioning of first person hud sprites</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">when</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">use-vis?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">=</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s5-0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">spawn</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">this</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">particles</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">s5-0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">part</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*null-vector*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Originally I added some code directly in the <code>analog-input</code> function to check the PC settings and decide whether to multiply by <code>-1</code> and invert the controls, but the downside to this approach was that by the time we're in <code>analog-input</code>, we no longer have context as to whether we're evaluating the inputs for left/right or for up/down. Since we now have 4 separate PC settings for camera inversion, I would need to make some changes upstream of the <code>analog-input</code> function itself to support this properly.</p>
<p>The solution I landed on was to create 4 macros that wrapped the <code>analog-input</code> call:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmacro</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">analog-input-horizontal-first</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Same as analog-input but respects First-Person Horizontal camera control setting."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token reader-macro operator" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">#if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">PC_PORT</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; first-person horizontal is NOT inverted in original game</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">first-camera-h-inverted?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmacro</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">analog-input-vertical-first</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Same as analog-input but respects First-Person Vertical camera control setting."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token reader-macro operator" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">#if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">PC_PORT</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; first-person vertical is already inverted in original game</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">first-camera-v-inverted?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmacro</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">analog-input-horizontal-third</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Same as analog-input but respects Third-Person Horizontal camera control setting."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token reader-macro operator" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">#if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">PC_PORT</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; third-person horizontal is already inverted in original game</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">third-camera-h-inverted?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defmacro</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">analog-input-vertical-third</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">"Same as analog-input but respects Third-Person Vertical camera control setting."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token reader-macro operator" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">#if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">PC_PORT</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; third-person vertical is already inverted in original game</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">third-camera-v-inverted?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">in</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">offset</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">center-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">max-val</span><span class="token plain"> </span><span class="token reader-macro operator" style="color:#393A34">,</span><span class="token symbol" style="color:#36acaa">out-range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>From here, I just needed to replace the various camera-related <code>analog-input</code> calls throughout the codebase with the appropriate macro, based on the particular type of camera (first vs third person) and which controller input was being read (<code>rightx</code> vs <code>righty</code>, i.e. horizontal vs vertical).</p>
<p>For example, <code>cam-eye</code> which is the first person camera used when you press triangle to use your goggles:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">;; main first-person camera</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defstate</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cam-eye</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">camera-slave</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  :</span><span class="token symbol" style="color:#36acaa">code</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">behavior</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">30</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input-horizontal-first</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; og:preserve-this changed for pc port</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">rightx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-256</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">leftx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">48.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">110.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input-vertical-first</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; og:preserve-this changed for pc port</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">righty</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-256</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">lefty</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">48.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">110.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>And a more unique example, <code>cam-string-joystick</code>, the main third person camera used during 99% of gameplay:</p>
<div class="language-opengoal codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-opengoal codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defun</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cam-dist-analog-input</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">arg</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">cond</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">28</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmin</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.083333336</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">28.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">160</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">set!</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fmin</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0125</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-160.0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">arg1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">;; changed for pc port, only used for Third-Person Vertical camera</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token symbol" style="color:#36acaa">#if</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">PC_PORT</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*pc-settings*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">third-camera-v-inverted?</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">f0-0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">;; main third-person camera</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">defbehavior</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cam-string-joystick</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">camera-slave</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">logand!</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">self</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-257</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">28</span><span class="token number" style="color:#36acaa">-0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">cam-dist-analog-input</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">righty</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">f</span><span class="token number" style="color:#36acaa">0</span><span class="token number" style="color:#36acaa">-29</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">analog-input-horizontal-third</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">;; changed for pc port</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">int</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">-&gt;</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">*cpad-list*</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">cpads</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">rightx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">128.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">the-as</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">float</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">110.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">21845.334</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">seconds-per-frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>The original code for <code>cam-string-joystick</code> uses <code>cam-dist-analog-input</code> in place of the regular <code>analog-input</code> for vertical camera controls, but since this is the only place <code>cam-dist-analog-input</code> is used, we can get away with only checking our <code>third-camera-v-inverted?</code> setting here.</p>
<p>There were a few additional camera functions where I swapped out <code>analog-input</code>, but the changes were quite similar to the above. These included things like <code>cam-free-floating</code> for the debug freecam, and <code>cam-periscope</code> used for the FJ mirrors and Misty cannon.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="contributing-to-jak-project">Contributing to <code>jak-project</code><a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#contributing-to-jak-project" class="hash-link" aria-label="Direct link to contributing-to-jak-project" title="Direct link to contributing-to-jak-project" translate="no">​</a></h3>
<p>I want to briefly call out that I didn't get all these changes right on the first try 😅. I recall nervously opening a PR with my initial changes, and waiting around to see what would happen next.</p>
<p>The next day I got some good feedback from a couple of the main developers, water111 and dass. They gave me a quick breakdown of some compiler behavior I was misunderstanding, and we had some back and forth about how to improve my changes. A couple commits later and my changes were merged in!</p>
<p>I circled back with another cleanup PR a week or so later after talking more with dass. We changed some language in the pause menu (Inverted vs Flipped), added a "Restore Default" option, and added some sound effects.</p>
<p>I bring this all up because I really think the best way to get started is just to dive in and try something, even if you make mistakes along the way. The developers/community are really supportive and want to teach OpenGOAL to others who demonstrate that they are passionate about it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="looking-back">Looking back<a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#looking-back" class="hash-link" aria-label="Direct link to Looking back" title="Direct link to Looking back" translate="no">​</a></h3>
<p>When someone in the Discord asks how best to get started with OpenGOAL development/modding, I often bring this up as an example - it was small in scope, but still gave me an opportunity to explore the codebase. I got to see how controller inputs are processed and used for in-game logic, I got to work with PC settings and the pause menu, and (maybe most importantly) it forced me to just generally get familiar with the OpenGOAL development loop.</p>
<p>I still consider this my first OpenGOAL mod, even though the changes ended up in vanilla <code>jak-project</code>. Getting to write some new code, and then seeing my changes work in-game, it was a really exciting moment for me. Shortly after this I found out about Mike &amp; Zed's <a href="https://opengoal.dev/docs/developing/modding_examples/checkpoint_randomizer" target="_blank" rel="noopener noreferrer" class="">Checkpoint Randomizer</a>, and was able to connect with them and start contributing to the mod. To this day, that feeling of seeing my code changes reflected in-game is what keeps me interested in working on OpenGOAL.</p>
<p>I actually got to port these camera inversion changes over to Jak 3 recently, which was a nice little full-circle moment 😊</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://jakmods.dev/blog/2026-05-15-my-first-mod#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/1355" target="_blank" rel="noopener noreferrer" class="">PR #1355 (main changes)</a></li>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/1361" target="_blank" rel="noopener noreferrer" class="">PR #1361 (clean up)</a></li>
<li class=""><a href="https://github.com/open-goal/jak-project/pull/4171" target="_blank" rel="noopener noreferrer" class="">PR #4171 (Jak 3 changes)</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Forgotten Lands (Chapter 2)]]></title>
            <link>https://jakmods.dev/blog/2023-11-21-tfl</link>
            <guid>https://jakmods.dev/blog/2023-11-21-tfl</guid>
            <pubDate>Tue, 21 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[This blog will spoil a lot of the update’s content. I would encourage you to play it for yourself before reading.]]></description>
            <content:encoded><![CDATA[<p>This blog will spoil a lot of the update’s content. I would encourage you to play it for yourself before reading.</p>
<p>As this is a pretty big release with a lot of moving parts, I figured I’d write a little blog to answer some of the questions you might have or get some behind the scenes if you’re interested, as I’ve mostly been working in the dark. Only a select few have seen different steps in the progress of this project.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="faq">FAQ<a href="https://jakmods.dev/blog/2023-11-21-tfl#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ" translate="no">​</a></h2>
<p>I’ll start with a small Q&amp;A of probably the most likely questions:</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-long-have-you-been-working-on-this">How long have you been working on this?<a href="https://jakmods.dev/blog/2023-11-21-tfl#how-long-have-you-been-working-on-this" class="hash-link" aria-label="Direct link to How long have you been working on this?" title="Direct link to How long have you been working on this?" translate="no">​</a></h4>
<p>I started working on this mod in June 2022, with the first release being published on January 9th 2023.</p>
<p>After that first release, I gave myself a few weeks off the project before diving back into it.</p>
<p>There was around 350 hours of work done on the mod for the first release. Steam has actually stopped tracking my hours on Blender a while back, so I don’t have a precise estimate, but I’ve been working on this new release for over 10 months at a much higher density than the first release so I think I can confidently say that the total time I’ve been working on this for is easily over 1000 hours, probably in the ballpark of 1300 to 1600 hours total.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="are-you-working-on-this-on-your-own">Are you working on this on your own?<a href="https://jakmods.dev/blog/2023-11-21-tfl#are-you-working-on-this-on-your-own" class="hash-link" aria-label="Direct link to Are you working on this on your own?" title="Direct link to Are you working on this on your own?" translate="no">​</a></h4>
<p>Not completely nowadays, see the “New Contributors” section for more info on this.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-is-this-made">How is this made?<a href="https://jakmods.dev/blog/2023-11-21-tfl#how-is-this-made" class="hash-link" aria-label="Direct link to How is this made?" title="Direct link to How is this made?" translate="no">​</a></h4>
<p>I’ll try to be very concise here as this topic could be several pages in itself. In short, you need to create the level’s geometry using 3D modeling software. I’m using Blender myself, as the OpenGOAL devs made some tools specifically for Blender. And also because Blender is an amazing software, don’t listen to Zed.</p>
<p>Along with the level geometry, you’ll also need to edit some code in OpenGOAL to be able to add your level in but also to add collectables, water, sounds, particles and everything else.</p>
<p>Most of the geometry I’ve been using for my level was custom-made by me. The textures are a mix of edited textures from the main game and fully custom ones as well.</p>
<p>I’ve made an incomplete and now somewhat outdated tutorial which should give you a more complete idea.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="are-there-any-limitations">Are there any limitations?<a href="https://jakmods.dev/blog/2023-11-21-tfl#are-there-any-limitations" class="hash-link" aria-label="Direct link to Are there any limitations?" title="Direct link to Are there any limitations?" translate="no">​</a></h4>
<p>At the moment, some custom level tools are not available yet and are the reasons I haven’t implemented some specific aspects into the game. Hopefully, this release motivates some of the devs to work on these things. <em>wink wink</em></p>
<p>Here’s a short list of the main missing features at the moment:</p>
<ul>
<li class="">Importing custom models and skeleton to replace/create new actors.</li>
<li class="">Texture transparency for things like grass, ice and others.</li>
<li class="">Reflections for things like Precursor Metal.</li>
<li class="">Sky rendering in custom levels. (Need to use a “fake” sky sphere instead for the time being.)</li>
<li class="">Nav meshes, Jak 1’s system to make enemies or NPCs move around an area, avoid obstacles and path toward Jak. This means most enemies can’t be added at the moment. A few enemies don’t use nav meshes and can be implemented.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="are-you-looking-for-help-for-anything">Are you looking for help for anything?<a href="https://jakmods.dev/blog/2023-11-21-tfl#are-you-looking-for-help-for-anything" class="hash-link" aria-label="Direct link to Are you looking for help for anything?" title="Direct link to Are you looking for help for anything?" translate="no">​</a></h4>
<p>I don’t have anything in particular right now where I do need help. If you do have a specific skill set that you think would be useful for the project, then you can always contact me, but be aware that it’s pretty likely to be a negative answer. You can read the “New Contributors” section for more info on my mindset in this project overall.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="can-we-support-you-or-the-project-with-donations">Can we support you or the project with donations?<a href="https://jakmods.dev/blog/2023-11-21-tfl#can-we-support-you-or-the-project-with-donations" class="hash-link" aria-label="Direct link to Can we support you or the project with donations?" title="Direct link to Can we support you or the project with donations?" translate="no">​</a></h4>
<p>Short answer, no.</p>
<p>Long answer; this is a passion project for me, while I’ve put several hundreds of hours into this and will most likely put in a lot more, I do not see it as wasted time because I’m not making money out of it.</p>
<p>I’ve always loved making levels for games. (CS 1.6, LBP, Portal 2, Dustforce and I’m probably forgetting some) So even just this whole thing being possible thanks to OpenGOAL is a dream come true for me. Adding money to the mix would change how I view the project and it’d definitely lose something. I’m making money elsewhere. I'd rather this passion project stay exactly that.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-contributors">New Contributors<a href="https://jakmods.dev/blog/2023-11-21-tfl#new-contributors" class="hash-link" aria-label="Direct link to New Contributors" title="Direct link to New Contributors" translate="no">​</a></h2>
<p>I’ll start this part with some history on how this project started. As said in the FAQ, this project was started pretty much as soon as custom level became a possibility in OpenGOAL. I even started writing and drawing some ideas a bit before, while waiting for the feature.</p>
<p>My plans from the beginning have been stupidly large in terms of scope and… they’ve not really changed much. If anything, they’ve grown a bit over time.</p>
<p>I was, from the very start, fully committed to pretty much doing everything on my own. Which sounds kind of ridiculous when compared to the scale of the project, but the rate at which I’ve been able to work and complete things, this has actually been pretty good. I don’t think I could have expected to have done this much after 17 months.</p>
<p>The main reason for my mindset is pretty much an egotistical one. This is probably going to be one of my biggest, if not the biggest, projects I will ever make. I want this to be almost entirely my artistic vision and to have control over every minute detail. Having people help, even for work that seems to be repetitive and unimportant, felt like I’d lose some control over the creative process and, as silly as that might sound, was not something I was ready to accept.</p>
<p>When I released the first version with Crystal Cave, I got a lot of people messaging me, offering help. There was one aspect where I was actually willing to get some help, as it’s an aspect I don’t think I’d be able to do myself: Music.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pex">Pex<a href="https://jakmods.dev/blog/2023-11-21-tfl#pex" class="hash-link" aria-label="Direct link to Pex" title="Direct link to Pex" translate="no">​</a></h3>
<p>One of the people messaging me about offering help was none other than Pex. Maybe the name doesn’t ring a bell, but you’ve probably heard some of the soundtracks he’s made for the cut Jak and Daxter levels mentioned in the Design Bible. You can listen to them here.</p>
<p>I don’t think I could have hoped for someone better for the job, short of the original composer of Jak 1’s OST. Pex has done an incredible job on the soundtrack for TFL so far and been great to work with.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hat-kid">Hat Kid<a href="https://jakmods.dev/blog/2023-11-21-tfl#hat-kid" class="hash-link" aria-label="Direct link to Hat Kid" title="Direct link to Hat Kid" translate="no">​</a></h3>
<p>Another aspect of the project I was having trouble with was the programming aspect. While I have some very small basic knowledge on it, it was a huge undertaking to learn everything I needed to do some of the things I would like to be in the mod.</p>
<p>However, about 3 months ago. I decided to message Hat Kid to see if he wanted to be more involved in the project to help me achieve all those crazy things I wanted to implement.</p>
<p>I can’t thank him enough for all the work he’s been able to do in such a short period of time, which includes:</p>
<ul>
<li class="">Adding support for ambients in custom levels. Those allow us to add things like ambient audio, but also level name pop-ups.</li>
<li class="">Fixing many crashes related to custom levels.</li>
<li class="">Adding support to include models and textures in custom levels. This allows us to use almost every single actor in the game in custom levels. Probably the most significant change for custom levels over the whole year.</li>
<li class="">Creation of the custom “Super Eco” powers.</li>
<li class="">Custom music system roughly based on Barg and Zed’s audio hack with automatic level detection (only need to add the level name to a list and the corresponding audio file), pausing, volume adjustment based on the in-game music volume setting and fade in/fade out.</li>
<li class="">Custom level hint system, also using the same (heavily modified) audio hack that is very customisable (can repeat voice lines, interrupt other playing ones if wanted, only play based on special conditions/task status/etc.).</li>
<li class="">Sinking and emerging particle spawner for platforms.</li>
<li class="">And finally, a working gondola!</li>
</ul>
<p>I’ve probably even missed some other things, but as you can see, this list is getting really big. You can find a much more detailed explanation in Hat Kid’s written section later in this blog.</p>
<p>He’s also helped me a lot with some of the custom code I tried to write, such as the electric arcs in Crystal Cave.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="voice-actors">Voice Actors<a href="https://jakmods.dev/blog/2023-11-21-tfl#voice-actors" class="hash-link" aria-label="Direct link to Voice Actors" title="Direct link to Voice Actors" translate="no">​</a></h3>
<p>Maybe a bit of a controversial topic, but I’ll let you know how I got there to clear things up.</p>
<p>I’ve wanted to add voice lines to the project, both to tell the story I’ve been writing for this mod as well as to help the player, so they’re not completely lost and have a general idea of what they need to do.</p>
<p>To do so, I’ve actually had a few different ways to approach the situation. I first wanted to actually message the original voice actors of Jak 1. First, to see if they would be interested, but then, more realistically, if they were fine with me using AI voices trained on their work.</p>
<p>I was not really able to find contact information for Daxter’s and Keira’s voice actors and Samos’ VA, Warren Burton, is not with us anymore.</p>
<p>Which made me decide to look for impersonators instead. I’ve sent out a form on a few different platforms but got very few replies. One of the few I got was from a guy that has already been working on AI models for most of the characters in the franchise. At that point, it was one of my last options if I did want to have voice lines, so I’ve decided to go that route.</p>
<p>As mentioned earlier, I’m not going to make any money out of this project, so I’m not trying to profit off of these voice actors’ work, but if any of them wish for me to remove the lines from the mod, it’ll be done the next day.</p>
<p>The models have been trained by Netguard and here’s the voice actor for each of the characters, as you do still need someone acting out the lines for the best results:</p>
<ul>
<li class="">Michael Gross as Daxter</li>
<li class="">Nohmyy as Keira</li>
<li class="">Liam Donovan and Michael Gross as Samos</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="crystal-cave-improvements">Crystal Cave Improvements<a href="https://jakmods.dev/blog/2023-11-21-tfl#crystal-cave-improvements" class="hash-link" aria-label="Direct link to Crystal Cave Improvements" title="Direct link to Crystal Cave Improvements" translate="no">​</a></h2>
<p>Crystal Cave has received a few additions and finishing touches.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="paths">Paths!<a href="https://jakmods.dev/blog/2023-11-21-tfl#paths" class="hash-link" aria-label="Direct link to Paths!" title="Direct link to Paths!" translate="no">​</a></h3>
<p>Custom paths have been figured out by Luminar Light a couple of months ago. Thanks to them, I’ve been able to make good use of this feature. The platforms in Crystal Cave are now moving!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="functional-water">Functional Water<a href="https://jakmods.dev/blog/2023-11-21-tfl#functional-water" class="hash-link" aria-label="Direct link to Functional Water" title="Direct link to Functional Water" translate="no">​</a></h3>
<p>Another big missing part of Crystal Cave has now also been added, water will now work thanks to a change Hat Kid also made not too long after the first release. The visual for those is not proper yet as right now, I’d need to be able to match the area perfectly with some water that already exists in the game as those are just 3d models and we can’t import custom ones yet as mentioned earlier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="custom-jak-lighting">Custom Jak Lighting<a href="https://jakmods.dev/blog/2023-11-21-tfl#custom-jak-lighting" class="hash-link" aria-label="Direct link to Custom Jak Lighting" title="Direct link to Custom Jak Lighting" translate="no">​</a></h3>
<p>As some have noticed, some entities in Crystal Cave have custom lighting depending on the different areas of the level that have different lighting. This has also been added to Jak as he moves around the level. It’s a pretty small detail, but works really well, so Jak doesn’t look out of place.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="super-green-eco-and-new-cell">Super Green Eco and New Cell<a href="https://jakmods.dev/blog/2023-11-21-tfl#super-green-eco-and-new-cell" class="hash-link" aria-label="Direct link to Super Green Eco and New Cell" title="Direct link to Super Green Eco and New Cell" translate="no">​</a></h3>
<p>The first Super Green Eco you’ll stumble upon. It makes you invincible to most damage sources and temporarily increases the maximum Green Eco pill count to 100, even after the powerup runs out, allowing you to take five hits. It allows you to collect the extra cell without taking damage on the Dark Eco infected ground.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="electric-arc-in-the-precursor-ship">Electric Arc in the Precursor Ship<a href="https://jakmods.dev/blog/2023-11-21-tfl#electric-arc-in-the-precursor-ship" class="hash-link" aria-label="Direct link to Electric Arc in the Precursor Ship" title="Direct link to Electric Arc in the Precursor Ship" translate="no">​</a></h3>
<p>Mostly heavily edited code from the flame pots seen in the robot room of Spider Cave with custom particle work, adding a new hazard in the ship area.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ambients">Ambients<a href="https://jakmods.dev/blog/2023-11-21-tfl#ambients" class="hash-link" aria-label="Direct link to Ambients" title="Direct link to Ambients" translate="no">​</a></h3>
<p>Ambients allow us to display the name of the level as well as playing some ambient sounds all over the level. The waterfalls aren’t silent anymore!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-levels">New Levels!<a href="https://jakmods.dev/blog/2023-11-21-tfl#new-levels" class="hash-link" aria-label="Direct link to New Levels!" title="Direct link to New Levels!" translate="no">​</a></h2>
<p>I’m sure most people were expecting a new level in this update, but hopefully the second one was a nice surprise as well!</p>
<p>These new levels are obviously the bulk of the work of the past 10 months.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="crescent-top">Crescent Top<a href="https://jakmods.dev/blog/2023-11-21-tfl#crescent-top" class="hash-link" aria-label="Direct link to Crescent Top" title="Direct link to Crescent Top" translate="no">​</a></h3>
<p>When you come out of Crystal Cave, you will arrive on this crescent-shaped plateau, hence the name.</p>
<p>It serves as a hub to other levels. However, as opposed to the base game’s hubs that have pretty limited gameplay, I’ve wanted to make it a fully fleshed out level with loads of different gameplay features.</p>
<p>The level is built to be a mix of on-foot and zoomer gameplay within the same area. This has been quite a challenging idea, as a fun area to traverse on the zoomer isn’t as good on foot and vice versa. I’ve tried to make the level flow really well when you do know the “route”, so I hope it won’t feel too sluggish when playing through it for the first time.</p>
<p><a href="https://www.youtube.com/watch?v=XN9eEdFy4YQ" target="_blank" rel="noopener noreferrer" class="">Here’s a timelapse of a few recordings I took while working on Crescent Top played at 50 times the speed.</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="energy-bay">Energy Bay<a href="https://jakmods.dev/blog/2023-11-21-tfl#energy-bay" class="hash-link" aria-label="Direct link to Energy Bay" title="Direct link to Energy Bay" translate="no">​</a></h3>
<p>The second new level where you’ll have to go before being able to finish Crescent Top itself, as you’ll unlock the zoomer there.</p>
<p>A much smaller level than Crescent Top. Probably took me less than half of the time that Crescent Top took me.</p>
<p>It’s the home of the second Super Eco power you’ll find, Super Red Eco, which has many features, both on the zoomer and on foot that makes it hopefully a lot more useful than normal Red Eco!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hat-kids-section">Hat Kid’s Section<a href="https://jakmods.dev/blog/2023-11-21-tfl#hat-kids-section" class="hash-link" aria-label="Direct link to Hat Kid’s Section" title="Direct link to Hat Kid’s Section" translate="no">​</a></h2>
<p>This section was written by Hat Kid, where he goes into more detail on the custom music and hint systems and actors he implemented.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="music-player">Music Player<a href="https://jakmods.dev/blog/2023-11-21-tfl#music-player" class="hash-link" aria-label="Direct link to Music Player" title="Direct link to Music Player" translate="no">​</a></h3>
<p>When Kuitar and I started working on the audio aspect of this mod, we realized right away that Zed’s audio hack was not going to cut it, so I set out to write my own improved version. The idea was to try to mimic the original game’s music playback as closely as possible (i.e. automatically adjust volume via the in-game setting, fade in/fade out, play/pause automatically and automatically change when you switch levels).</p>
<p>Similar to Zed’s version, it also uses the SFML library to play back audio streams, but I also wrote a custom music player process in GOAL that interfaces with the C++ code doing the actual playback. This process keeps a list of all levels that have custom music and periodically checks the currently active level to see if it should start custom music playback, meaning all you need to do to add a new track is to add your custom level name to the list and place the corresponding audio stream in the right folder.</p>
<p>Once we are in a valid level, the music will start to fade in (achieved by simply lerping the volume from 0 up to what the current in-game volume setting is) and continuously loop until we leave the level, where it will start to fade out.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hint-system">Hint System<a href="https://jakmods.dev/blog/2023-11-21-tfl#hint-system" class="hash-link" aria-label="Direct link to Hint System" title="Direct link to Hint System" translate="no">​</a></h3>
<p>For the custom hint system, Kuitar approached me with some ideas on what exactly it should be able to do and, in the end, this is what we settled on:</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/hints1-e615710708c6d319ce23724dff62b3d7.png" width="1497" height="975" class="img_ev3q"></p>
<p>This is quite a bit to take in, but I’ll start from the top:</p>
<ul>
<li class=""><code>file-name</code> is the name of the file this hint is tied to and uniquely identifies said hint.</li>
<li class=""><code>alt-names</code> is an optional field to define multiple audio files that this hint will cycle through when triggered multiple times.</li>
<li class=""><code>text-id</code> is used to mark a hint as “seen”. This is also how the original game’s level hint system works in order to only display the level name popups a single time, so I abused the feature in order for custom hint progress to also be saved to the save file, even though we don’t really have any actual text associated with this <code>text-id</code>.</li>
<li class=""><code>voicebox?</code> simply tells the hint whether it should spawn the communicator.</li>
<li class=""><code>interrupt?</code> enables this hint to be interrupted by any other one being triggered while it’s still playing (you may have noticed this when using the launcher to get down to Energy Bay or when breaking the Super Green Eco Crystal).</li>
<li class=""><code>repeat?</code> allows this hint to be played back multiple times if enabled, with repeat-delay setting the cooldown for it in seconds.</li>
<li class=""><code>task</code> and <code>task-status</code> are used to specify a condition to only play this hint if said task is in a specific stage (an example would be the hints that play right after grabbing some of the Energy Bay turbine cells).</li>
<li class=""><code>force-playback?</code> makes the hint play regardless of the “Play Hints” setting when enabled.</li>
<li class=""><code>target-mode</code> is a bit of a complicated one, but this is used to only play when Jak is in a specific state. The documentation for its syntax can be seen in the comment below, but it’s a Lisp pair with up to three elements. This was used to, for example, only play the hint about the Super Green Eco Crystal once you destroy it and have the effect active, or to play Daxter’s scream during the launcher jump only if Jak is actually doing the launch jump, so you don’t accidentally trigger it somehow.</li>
<li class=""><code>close-task</code> and <code>close-task-status</code>, when used, will close said task’s cstage as defined by <code>close-task-status</code>. For example, you could place a hint right where a power cell is and, upon collecting it, the hint plays and closes the need-hint stage of the specified <code>close-task</code>, meaning the power cell associated with that task will now show up in the progress menu.</li>
</ul>
<p>I then wrote a macro to define these hints with a bunch of defaults set, so in the end, creating new data for the hints is as simple as this:</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/hints2-5b6d183559f8e2035c97d95b989a49f9.png" width="1500" height="1295" class="img_ev3q"></p>
<p>When adding these hints into a level, you simply define the position, <code>filename</code> and <code>radius</code> (for the sphere) and the game will associate the hint with the hint data based on the <code>filename</code>:</p>
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/hints3-ac2145d3013c2bc718a7445b8fe71db2.png" width="1497" height="595" class="img_ev3q"></p>
<p>The hints also play using the same rewritten version of the audio hack I made for the music player, which interfaces with the custom <code>tfl-hint</code> process I wrote, so it knows when a hint is playing and when it’s supposed to stop, in order to not interrupt any other hints (unless you want it to!).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="super-eco-crystal-and-super-eco">Super Eco Crystal and Super Eco<a href="https://jakmods.dev/blog/2023-11-21-tfl#super-eco-crystal-and-super-eco" class="hash-link" aria-label="Direct link to Super Eco Crystal and Super Eco" title="Direct link to Super Eco Crystal and Super Eco" translate="no">​</a></h3>
<p>The Super Eco Crystal was the first custom actor I made for TFL. The model is simply a shrunk down Dark Eco Crystal from Spider Cave that floats, slowly spins and bobs up and down. If you take a closer look at it, you will notice that the crystal’s glow “pulses”, similar to how Jak’s entire body glows while channelling any form of Super Eco (side note: you may not have noticed, but the eco meter HUD color also pulses while under the effects of Super Eco!). The amazing particles for when it’s idle and when it explodes were made by Kuitar. It also has some fancy logic to detect if its respawn would be blocked by Jak and waits until you move away.</p>
<p>The Super Eco powers themselves were a bit more involved to create since it required going through the codebase and changing lots of small cases to account for any special features.</p>
<p>Super Green Eco, as you probably surmised, gives you full invulnerability from any source of damage aside from three: <code>endlessfall</code>, <code>melt</code> and <code>dark-eco-pool</code>. It also “overheals” you by temporarily increasing your max eco pill count to 100, giving you 5 total hits.</p>
<p>Super Red Eco gives you the “helicopter spin”, the “trident” attack with two red spheres that damage anything they touch, allows you to break iron and steel crates and use a boost when on the zoomer, finally giving red eco its much needed buff.
We also had the idea to have a small sphere slowly orbit Jak while just moving around (<a href="https://www.youtube.com/watch?v=l9PlaAk2WVA" target="_blank" rel="noopener noreferrer" class="">sorta like this</a>), but it was more of an afterthought, so I ended up not implementing it. I haven’t scrapped the small bits of code I wrote for this, so who knows, it might end up being added at some point.</p>
<p>Eagle-eyed readers may have noticed the words <code>super-eco-blue</code> and <code>super-eco-yellow</code> in an earlier screenshot. While these have not seen any use in TFL yet, they do have some (slightly unfinished) functionality, but I will leave figuring out what exactly they do as an exercise to the reader.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lava-platforms">Lava Platforms<a href="https://jakmods.dev/blog/2023-11-21-tfl#lava-platforms" class="hash-link" aria-label="Direct link to Lava Platforms" title="Direct link to Lava Platforms" translate="no">​</a></h3>
<p>For the underground area below the observatory in Crescent Top, Kuitar had the idea to have the platforms spawn two different types of particles when sinking and emerging from the lava, so I made a custom platform type that takes a few parameters (<code>part-height</code>, <code>part-emerge-height</code>, <code>part-range</code>). The first one specifies at which height to spawn the sinking down particles, the second does the same for the emerging particles. The range determines how far away from the specified heights the platform can be for it to still spawn particles.</p>
<p>The math for it was a bit tricky to figure out at first, but I eventually got it working.</p>
<p>I’m overall pretty happy with the effect and not only is it just a cool little detail (with some amazing particle work from Kuitar as usual), it also serves as a very neat gameplay feature telling you where the platforms are while submerged in the lava, letting you time your jumps a bit easier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="crystal-cave-gondola">Crystal Cave Gondola<a href="https://jakmods.dev/blog/2023-11-21-tfl#crystal-cave-gondola" class="hash-link" aria-label="Direct link to Crystal Cave Gondola" title="Direct link to Crystal Cave Gondola" translate="no">​</a></h3>
<p>Unfortunately, custom actors with custom models remain an impossibility as of now, so for the gondola, we made do with a big invisible platform using similar logic as the eco platform used in the final boss level of the main game. The speed was adjusted a bit by lerp-master Kuitar so it isn’t as fast near the start and I made it only accessible once every power cell in Crystal Cave is collected.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What’s next?<a href="https://jakmods.dev/blog/2023-11-21-tfl#whats-next" class="hash-link" aria-label="Direct link to What’s next?" title="Direct link to What’s next?" translate="no">​</a></h2>
<p>More, a lot more.</p>
<p>As you’ve probably figured out by now. I’m mostly going to work on this in the dark and then do big releases like this. So I’m probably not going to show much more of what I’ll be working on until it’s pretty much ready to ship.</p>
<p>I’ll probably take a break for a few weeks before continuing work on the next levels, as this whole project has pretty much been eating most of my free time. After that, I’ll then slowly work on pre-production things to have a good base to work on.</p>
<p>Hope you’ve enjoyed the mod and reading this behind the scenes!</p>
<p>-- Kuitar</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="links">Links<a href="https://jakmods.dev/blog/2023-11-21-tfl#links" class="hash-link" aria-label="Direct link to Links" title="Direct link to Links" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://docs.google.com/document/d/1YBty5hZeAzFM2U7N3czlNhNrTFMq6NGaBxrtLF3n3nY" target="_blank" rel="noopener noreferrer" class="">Original blog doc</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bugged!]]></title>
            <link>https://jakmods.dev/blog/2023-10-31-bugged</link>
            <guid>https://jakmods.dev/blog/2023-10-31-bugged</guid>
            <pubDate>Tue, 31 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[A mod by Bin & Barg - see the trailer here!]]></description>
            <content:encoded><![CDATA[<p>A mod by Bin &amp; Barg - see the <a href="https://www.youtube.com/watch?v=BY9U9cM9vfU" target="_blank" rel="noopener noreferrer" class="">trailer here</a>!</p>
<p>Over 25 bugs added to the game to completely change the speedrun - think you can find them all?</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://jakmods.dev/assets/images/bugged_logo-47c351865ef9eecfbf6e10e8c9c738e6.png" width="1181" height="601" class="img_ev3q"></p>
<p>Note that it may be possible to "softlock" yourself out of some bugs, so you may want to create safety saves when you first enter a level.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bug-tracker">Bug Tracker<a href="https://jakmods.dev/blog/2023-10-31-bugged#bug-tracker" class="hash-link" aria-label="Direct link to Bug Tracker" title="Direct link to Bug Tracker" translate="no">​</a></h2>
<p>The input fields below are provided so you can name the bugs as you find them. Note that the website won't remember or verify your answers, it's just for your own tracking purposes.</p>
<p>Each "skip" in this list required some <em>unique</em> code modifications to make it possible. Other skips may also be possible simply by utilizing some of the "common tech", but those are not counted in this list.</p>
<p>We also made a little crossword puzzle out of this, which will verify your answers (at least against the names we came up with) - you can check that out <a href="https://docs.google.com/spreadsheets/d/1oIJmP-voFVfO-anVoyteOZDVKUb9LAlJ-hSKYmffoBQ/edit?usp=sharing" target="_blank" rel="noopener noreferrer" class="">here</a>!</p>
<div class="bugged_parent"><div class="bugged_child1"><center><h2>SKIPS</h2></center><table><tbody><tr><td colspan="3">Geyser Rock</td></tr><tr><td><b>#1</b> <input placeholder="???"></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 1m</p></div></div></details></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Follow hsblue's lamp</p></div></div></details></td></tr><tr><td colspan="3">Sandover Village</td></tr><tr><td><b>#2</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 25s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Which one is Bessie?</p></div></div></details></td></tr><tr><td colspan="3">Forbidden Jungle</td></tr><tr><td><b>#3</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 1m 20s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>!guess 0</p></div></div></details></td></tr><tr><td colspan="3">Misty Island</td></tr><tr><td><b>#4</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 30s</p></div></div></details></td><td></td></tr><tr><td colspan="3">Sentinel Beach</td></tr><tr><td><b>#5</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 15s</p></div></div></details></td><td></td></tr><tr><td colspan="3">Precursor Basin</td></tr><tr><td><b>#6</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 30s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Ultra shortcut</p></div></div></details></td></tr><tr><td><b>#7</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 50s</p></div></div></details></td><td></td></tr><tr><td><b>#8</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 25s</p></div></div></details></td><td></td></tr><tr><td><b>#9</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 50s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>This cell is already buggy in the vanilla game</p></div></div></details></td></tr><tr><td colspan="3">Lost Precursor City</td></tr><tr><td><b>#10</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 30s</p></div></div></details></td><td></td></tr><tr><td colspan="3">Boggy Swamp</td></tr><tr><td><b>#11</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 2m 30s</p></div></div></details></td><td></td></tr><tr><td><b>#12</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 1m</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Can be done 4 times</p></div></div></details></td></tr><tr><td colspan="3">Mountain Pass</td></tr><tr><td><b>#13</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 1m 50s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>♫ Ain't got no gun, Ain't got no knife, Don't you start no fight ♫</p></div></div></details></td></tr><tr><td colspan="3">Volcanic Crater</td></tr><tr><td><b>#14</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>You should probably figure out <b>#21</b> first</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Combined with <b>#25</b>, 990 orbs can be skipped</p></div></div></details></td></tr><tr><td colspan="3">Spider Cave</td></tr><tr><td><b>#15</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 50s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Only Up!</p></div></div></details></td></tr><tr><td colspan="3">Snowy Mountain</td></tr><tr><td><b>#16</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 15s</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Can be done 3 times</p></div></div></details></td></tr><tr><td colspan="3">Gol &amp; Maia's Citadel</td></tr><tr><td><b>#17</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Estimated total time save: 2m</p></div></div></details></td><td></td></tr></tbody></table></div><div class="bugged_child2"><center><h2>COMMON TECH</h2></center><table><tbody><tr><td><b>#18</b> <input placeholder="???"></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Think about speedrun tech from another PS2 platformer</p></div></div></details></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>2000s Mazda slogan</p></div></div></details></td></tr><tr><td><b>#19</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Can be done in certain places in vanilla (with different tech)</p></div></div></details></td><td></td></tr><tr><td><b>#20</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>You should probably figure out <b>#18</b> first</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Like an idle deload, but faster</p></div></div></details></td></tr><tr><td><b>#21</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Tom Holland wants to star in a Jak &amp; Daxter movie</p></div></div></details></td><td></td></tr><tr><td><b>#22</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Think about speedrun tech from another PS2 platformer (again)</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>You ever been inside Keira's room?</p></div></div></details></td></tr><tr><td><b>#23</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Be careful when you bonk</p></div></div></details></td><td></td></tr><tr><td><b>#24</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Hover glitch in Jak 1?</p></div></div></details></td><td></td></tr><tr><td><b>#25</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Can be done 6 times</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Combined with <b>#14</b>, 990 orbs can be skipped</p></div></div></details></td></tr></tbody></table><center><h2>MAJOR SKIPS</h2></center><table><tbody><tr><td><b>#26</b> <input placeholder="???"></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>You should probably figure out <b>#24</b> first</p></div></div></details></td><td style="width:45%"><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>What if we told you LTS is possible from Hub 2?</p></div></div></details></td></tr><tr><td><b>#27</b> <input placeholder="???"></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 1</summary><div><div class="collapsibleContent_i85q"><p>Any% is dead</p></div></div></details></td><td><details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hint 2</summary><div><div class="collapsibleContent_i85q"><p>Sub 1m WR?</p></div></div></details></td></tr></tbody></table></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="links">Links<a href="https://jakmods.dev/blog/2023-10-31-bugged#links" class="hash-link" aria-label="Direct link to Links" title="Direct link to Links" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://www.speedrun.com/Jakbugged" target="_blank" rel="noopener noreferrer" class="">Speedrun.com leaderboards</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=O8TlMDRek5I" target="_blank" rel="noopener noreferrer" class="">Bin's "every bug explained" walkthrough</a></li>
</ul>]]></content:encoded>
        </item>
    </channel>
</rss>