This fixes the metal head tower resolution cutscene having an awkward
amount of blackout at the start unless the framerate dipped to at least
(most?) 33FPS, since its frame num starts in the negative. There may be
other cutscenes that start at negative or higher than zero, but I don't
know of any (did not check thoroughly as that's boring as hell).
I don't really understand what the purpose of this check is in the first
place to be honest.
Fixes for:
- `prebot` sword attack and projectile speed (fixes#4050)
- Fast first person camera turning speed (fixes#4051)
- Slow turret camera turning speed (fixes#4052)
- `flyingsaw` movement and rotation speed (fixes#4053)
- Green eco drain rate on jetboard during `forest-kill-plants` mission
(fixes#4054)
- `skeet` rotation speed
- `maker-grenade` tumble speed
- Jetboard spin speed
- `hud-skill` rotation speed (also for jak2)
- `gun-dark-shot` projectile speed (also for jak2)
- `target-float` up/down speed (also for jak2)
- Texscroll speed
- Slow walk anim after landing from a jump
The dust storm will now always behave as if it was at 60 FPS. Particles
only spawn every 1/60th of a second and the push vector is converted as
if it was originally equivalent to 1/60th of a second.
All stereo VAG commands would write to an out-of-bounds array element
and corrupt the whole VAG queue list.
Fixes random sound-related crashes including a consistent Light Jak
Freeze crash.
Fix textures on the bomb (just a simple typo fix):
<img width="1638" height="1067" alt="2025-10-05_18-22"
src="https://github.com/user-attachments/assets/d31fedd4-b4f3-4e65-8457-a67068b137be"
/>
Fix issue with ocean culling when in the war factory. In this level, the
make the background rotate by defining a different camera matrix. We
need to use that matrix when culling the ocean, since it rotates too.
This is different on PC because the original game stashed the camera
frustum planes in some vf registers for background drawing.
<img width="1638" height="671" alt="image"
src="https://github.com/user-attachments/assets/feb5f931-e71f-411e-8a29-e8d970f70078"
/>
Fixes#4023
This fixes a duplicated entry in the Jak 2 keybind details page where
"Right Analog Stick Left" appears twice.
File: goal_src/jak2/pc/progress/progress-static-pc.gc
Change: corrected the :entries list to include r-analog-right once
and removed the duplicate r-analog-left, resulting in the 8 expected
analog directions (L and R sticks).
Repro: Options → Input → Reassign Binds → Keyboard (Jak 2)
Before: Right stick shows duplicate "Left"
After: Right stick shows Left/Right correctly
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/407">actions/download-artifact#407</a></li>
<li>BREAKING fix: inconsistent path behavior for single artifact
downloads by ID by <a
href="https://github.com/GrantBirki"><code>@GrantBirki</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/416">actions/download-artifact#416</a></li>
</ul>
<h2>v5.0.0</h2>
<h3>🚨 Breaking Change</h3>
<p>This release fixes an inconsistency in path behavior for single
artifact downloads by ID. <strong>If you're downloading single artifacts
by ID, the output path may change.</strong></p>
<h4>What Changed</h4>
<p>Previously, <strong>single artifact downloads</strong> behaved
differently depending on how you specified the artifact:</p>
<ul>
<li><strong>By name</strong>: <code>name: my-artifact</code> → extracted
to <code>path/</code> (direct)</li>
<li><strong>By ID</strong>: <code>artifact-ids: 12345</code> → extracted
to <code>path/my-artifact/</code> (nested)</li>
</ul>
<p>Now both methods are consistent:</p>
<ul>
<li><strong>By name</strong>: <code>name: my-artifact</code> → extracted
to <code>path/</code> (unchanged)</li>
<li><strong>By ID</strong>: <code>artifact-ids: 12345</code> → extracted
to <code>path/</code> (fixed - now direct)</li>
</ul>
<h4>Migration Guide</h4>
<h5>✅ No Action Needed If:</h5>
<ul>
<li>You download artifacts by <strong>name</strong></li>
<li>You download <strong>multiple</strong> artifacts by ID</li>
<li>You already use <code>merge-multiple: true</code> as a
workaround</li>
</ul>
<h5>⚠️ Action Required If:</h5>
<p>You download <strong>single artifacts by ID</strong> and your
workflows expect the nested directory structure.</p>
<p><strong>Before v5 (nested structure):</strong></p>
<pre lang="yaml"><code>- uses: actions/download-artifact@v4
with:
artifact-ids: 12345
path: dist
# Files were in: dist/my-artifact/
</code></pre>
<blockquote>
<p>Where <code>my-artifact</code> is the name of the artifact you
previously uploaded</p>
</blockquote>
<p><strong>To maintain old behavior (if needed):</strong></p>
<pre lang="yaml"><code></tr></table>
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/download-artifact/commit/634f93cb2916e3fdff6788551b99b062d0335ce0"><code>634f93c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/416">#416</a>
from actions/single-artifact-id-download-path</li>
<li><a
href="https://github.com/actions/download-artifact/commit/b19ff4302770b82aa4694b63703b547756dacce6"><code>b19ff43</code></a>
refactor: resolve download path correctly in artifact download tests
(mainly ...</li>
<li><a
href="https://github.com/actions/download-artifact/commit/e262cbee4ab8c473c61c59a81ad8e9dc760e90db"><code>e262cbe</code></a>
bundle dist</li>
<li><a
href="https://github.com/actions/download-artifact/commit/bff23f9308ceb2f06d673043ea6311519be6a87b"><code>bff23f9</code></a>
update docs</li>
<li><a
href="https://github.com/actions/download-artifact/commit/fff8c148a8fdd56aa81fcb019f0b5f6c65700c4d"><code>fff8c14</code></a>
fix download path logic when downloading a single artifact by id</li>
<li><a
href="https://github.com/actions/download-artifact/commit/448e3f862ab3ef47aa50ff917776823c9946035b"><code>448e3f8</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/407">#407</a>
from actions/nebuk89-patch-1</li>
<li><a
href="https://github.com/actions/download-artifact/commit/47225c44b359a5155efdbbbc352041b3e249fb1b"><code>47225c4</code></a>
Update README.md</li>
<li>See full diff in <a
href="https://github.com/actions/download-artifact/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
During the iso creation process it's possible that this file in will be
internally renamed to `WATER_AN.CGO`, despite the external file name
displaying `WATER-AN.CGO`. We expect the file to be named
`WATER-AN.CGO`.
We can't control how our users create their iso files. We can alleviate
future friction regarding this matter by adopting this simple change.
---------
Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
Fixes#3997: The script for the orb that is spawned when getting a medal
in the satellite game was using the wrong entity name.
Fixes#3998: The `spider-manager` process was invalid for one frame,
causing the score to be set to the value of `#f`, winning the game
instantly.
Also doubled the PC port texture count, which should hopefully fix the
crash that happens when playing the game all the way through to the end
of the Destroy Dark Eco Tanks mission in one sitting.
Resolves#3075
TODO before merge:
- [x] Properly draw non-korean strings while in korean mode (language
selection)
- [x] Check jak 3
- [x] Translation scaffolding (allow korean characters, add to Crowdin,
fix japanese locale, etc)
- [x] Check translation of text lines
- [x] Check translation of subtitle lines
- [x] Cleanup PR / some performance optimization (it's take a bit too
long to build the text and it shouldn't since the information is in a
giant lookup table)
- [x] Wait until release is cut
I confirmed the font textures are identical between Jak 2 and Jak 3, so
thank god for that.
Some examples of converting the korean encoding to utf-8. These show off
all scenarios, pure korean / korean with ascii and japanese / korean
with replacements (flags):
<img width="316" height="611" alt="Screenshot 2025-07-26 191511"
src="https://github.com/user-attachments/assets/614383ba-8049-4bf4-937e-24ad3e605d41"
/>
<img width="254" height="220" alt="Screenshot 2025-07-26 191529"
src="https://github.com/user-attachments/assets/1f6e5a6c-8527-4f98-a988-925ec66e437d"
/>
And it working in game. `Input Options` is a custom not-yet-translated
string. It now shows up properly instead of a disgusting block of
glyphs, and all the original strings are hopefully the same
semantically!:
<img width="550" height="493" alt="Screenshot 2025-07-26 202838"
src="https://github.com/user-attachments/assets/9ebdf6c0-f5a3-4a30-84a1-e5840809a1a2"
/>
Quite the challenge. The crux of the problem is -- Naughty Dog came up
with their own encoding for representing korean syllable blocks, and
that source information is lost so it has to be reverse engineered.
Instead of trying to figure out their encoding from the text -- I went
at it from the angle of just "how do i draw every single korean
character using their glyph set".
One might think this is way too time consuming but it's important to
remember:
- Korean letters are designed to be composable from a relatively small
number of glyphs (more on this later)
- Someone at naughty dog did basically this exact process
- There is no other way! While there are loose patterns, there isn't an
overarching rhyme or reason, they just picked the right glyph for the
writing context (more on this later). And there are even situations
where there IS NO good looking glyph, or the one ND chose looks awful
and unreadable (we could technically fix this by adjusting the
positioning of the glyphs but....no more)!
Information on their encoding that gets passed to `convert-korean-text`:
- It's a raw stream of bytes
- It can contain normal font letters
- Every syllable block begins with: `0x04 <num_glyphs> <...the glyph
bytes...>`
- DO NOT confuse `num_glyphs` with num jamo, because some glyphs can
have multiple jamo!
- Every section of normal text starts with `0x03`. For example a space
would be `0x03 0x20`
- There are a very select few number of jamo glyphs on a secondary
texture page, these glyph bytes are preceeded with a `0x05`. These jamo
are a variant of some of the final vowels, moving them as low down as
possible.
Crash course on korean writing:
- Nice resource as this is basically what we are doing -
https://glyphsapp.com/learn/creating-a-hangeul-font
- Korean syllable blocks have either 2 or 3 jamo. Jamo are basically
letters and are the individual pieces that make up the syllable blocks.
- The jamo are split up into "initial", "medial" and "final" categories.
Within the "medial" category there are obvious visual variants:
- Horizontal
- Vertical
- Combination (horizontal + a vertical)
- These jamo are laid out in 6 main pre-defined "orientations":
- initial + vertical medial
- initial + horizontal medial
- initial + combination
- initial + vertical medial + final
- initial + horizontal medial + final
- initial + combination + final
- Sometimes, for stylistic reasons, jamo will be written in different
ways (ie. if there is nothing below a vertical vowel will be extended).
- Annoying, and ND's glyph set supports this stylistic choice!
- There are some combination of jamo that are never used, and some that
are only used for a single word in the entire language!
With all that in mind, my basic process was:
- Scan the game's entire corpus of korean text, that includes subtitles.
It's very easy to look at the font texture's glyphs and assign them to
their respective jamo
- This let me construct a mapping and see which glyphs were used under
which context
- I then shoved this information into a 2-D matrix in excel, and created
an in-game tool to check every single jamo permutation to fill in the
gaps / change them if naughty dogs was bad. Most of the time, ND's
encoding was fine.
-
https://docs.google.com/spreadsheets/d/e/2PACX-1vTtyMeb5-mL5rXseS9YllVj32BGCISOGZFic6nkRV5Er5aLZ9CLq1Hj_rTY7pRCn-wrQDH1rvTqUHwB/pubhtml?gid=886895534&single=true
anything in red is an addition / modification on my part.
- This was the most lengthy part but not as long as you may think, you
can do a lot of pruning. For example if you are checking a 3-jamo
variant (the ones with the most permutations) and you've verified that
the medial jamo is as far up vertically as it can be, and you are using
the lowest final jamo that are available -- there is nothing to check or
improve -- for better or worse! So those end up being the permutations
between the initial and medial instead of a three-way permutation
nightmare.
- Also, while it is a 2d matrix, there's a lot of pruning even within
that. For example, for the first 3 orientations, you dont have to care
about final vowels at all.
- At the end, I'm left with a lookup table that I can use the encode the
best looking korean syllable blocks possible given the context of the
jamo combination.
Similar issue to the orbs-per-level counts, while loading a save (in
blackout) these values are reset to 0 briefly. This can cause extra
autosplits (see https://github.com/open-goal/speedrunning/pull/25)
The context for this is that i was symlinking files from a mod
installation that were identical to the unmodded game, for space
considerations, because i remembered having success with that in the
past, before the project made the switch to ghc's filesystem library, it
seems. <sub>(I was also much more pressed for space back then, but well,
i thought writing a small program to check for matches and link them for
me would be a good, not-so-difficult exercise...)</sub>
However i quickly started getting weird errors that it couldn't find
GAME.CGO or KERNEL.CGO even when i hadn't touched them. Initially since
i found that it occurred only when symlinking any files that were
earlier alphabetically, i assumed it was some kind of index mismatch,
but it was actually caused by a bug in an older version of
filesystem.hpp causing the `directory_iterator` to read everything after
a symlink as also a symlink, making them fail to be read properly:
https://github.com/gulrak/filesystem/pull/162
Adding `f.is_symlink()` is not as much of a change to the loop's
condition from the previous behavior as it might first appear. It seems
that even without that condition the iterator *was* reading symlinks
before, just incorrectly (and before ghc::filesystem, they were being
read just fine, iirc). With the new version, though, symlinks do have to
be accounted for explicitly.
Given that the library's major and minor version are both the same, i
don't expect there should be any breaking changes to the API, and i
didn't find any when i was testing but there might need to be more
investigation into that before merging. Also, without the added check
for `f.exists()`, the behavior is exactly the same as if, for some
reason, a fakeiso file the game needs just doesn't exist in the first
place (which has no real reason to happen in a normal installation of
the game): there's a decent chance it will either crash at some point
later during runtime, or something will happen like a level failing to
load causing Jak to trip infinitely. I thought it might be helpful to
have it fail right away in the case that, say, someone moved their
vanilla installation and staled their symlinks, but i'll defer to
maintainers' judgement on that point.
Taking the suggestion from @Calinou
(https://github.com/open-goal/jak-project/pull/3943#issuecomment-3017359144),
this replaces the resolve/render framebuffer -> window framebuffer blit
with an actual drawn tri-strip which covers the entire viewport, which
the PCRTC blackout already does.
It appears we have no guarantee what state the internal window
framebuffer will be in, so drawing an actual primitive and letting the
fragment shader do all the work seems to be the more
compatible/functional solution here.
Thanks for the suggestion!
Some monitors are very specific about their reported refresh rates:
```
[54:37] [debug] [DISPLAY]: Skipping 640x480 as it requires 360.11hz but the monitor is currently set to 360hz
```
The current code truncated the display mode, but would compare against
the reported refresh rate. As per the log, they are obviously not equal.
- Retain the float component and only round it off when sending the
value to GOAL
- Make the comparison more generous, +/- 1.0hz
The main bug this fixes is not being able to use level select when your
language isn't translated. Why? Because they use the result from
`lookup-text!` to determine if a menu item should be drawn or not. No
text found, no menu item.
However this uncovered a long-standing bug in my fallback extension to
this code. If you call this function with a text-id that doesnt even
have an english string, it will recursively keep calling itself until it
crashes. Of course the first few tasks in the game are dummies and have
no valid text-id so this had to be fixed.
Should be fixed for all 3 games.
Compilation with the NO_ASSERT flag set results in errors.
This happens because some the macros are not defined in the else branch
of #ifndef NO_ASSERT.
If long_name is too long, it will result in the level being invisible
but the collide loading, lets detect this and point the user in correct
direction to solve it with a clear error message.
---------
Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
Fixes#3151 by applying the same fix as #2686 to another Mips2C
function.
Tested by mashing Jak's body into the bad collision walls for 30 minutes
without a single crash.
Fixes a small bug introduced in #3902.
Without this change, whenever you load a save it is treated as if you
started a new game and resets the autosplitter back to 0s.