# Aidyn ReChronicled *Aidyn Chronicles: The First Mage* (NTSC-U 1.0), enhanced by Spitzdano: a native Linux and Windows build of *AIDYN_CHRONICLES* (NAYE, version 0), made by static recompilation with [N64Recomp](https://github.com/N64Recomp/N64Recomp) and run on [N64ModernRuntime](https://github.com/N64Recomp/N64ModernRuntime) with the [RT64](https://github.com/rt64/rt64) renderer. **Working with MDoulos.** MDoulos, author of the Action Edition mod, is working on a project of his own built on a new engine. The two stay separate and share art, fixes and findings. Aidyn ReChronicled remains the original game and its mods, fixed and improved. ![Aidyn ReChronicled: The First Mage](docs/banner.jpg) **1.0.0 is the main release.** New features stop here: spitzdano is putting the work down to play the game. Bug fixes will follow as they turn up, and maybe some quality-of-life additions. Updates come through the game itself (Updates on the title menu), so there is nothing to download again. ## Features - **A native game for Linux and Windows**, not an emulator. Linux: one download for any current distribution, with an installer that adds it to the app menu and to Steam (Steam Deck and ROG Ally friendly). Windows: a folder to unpack and play (Windows 10 or 11, nothing to install), with an Add to Steam button. - **Your ROMs in one folder**: put the 1.0 ROM, and the Aidyn Patch, Aidyn Plus and Action Edition ones if you have them, in the download's `ROMs` folder under any names; the game tells them apart by their contents and lists what it found on the title screen. - **Aidyn Patch, Aidyn Plus and Action Edition**: community rebalances play as more versions next to the original. Bring your own ROM (checked by its hash; nothing of them ships here), pick one in the list Y opens on the title screen, and each version keeps its own saves. - **Sharp at any resolution**: drawn at 1080 lines or more, supersampled, with anti-aliasing. - **Widescreen**: 16:9, 21:9 or any width, with the HUD at the screen edges; the menus stay 4:3, as they were made. - **Smooth**: the game runs at 60 frames a second (the N64 managed 15 to 20), with frames drawn in between up to your display's rate, and walk cycles without their stutter. - **Newly painted art**: 2,113 of the game's 2,115 world textures, every portrait, an icon of its own for every item (each potion its own colour, scrolls and wands by school), the HUD, the map page's land and a repainted pause scroll, plus HD lettering and Xbox button prompts; what isn't repainted yet is upscaled. F2 shows what was there before. - **Fuller sound**: the game's music and sounds as loud as other games' (they played very softly) and a little clearer, through a limiter, and the music's instruments restored (their lost treble back and the 8-bit hiss gone); the same music and sounds, each switchable back to the original. - **Further draw distance**: more of the land drawn, trees and objects showing from much further away. - **A free camera** on the right stick, kept out of walls. - **Quicksave, autosaves, Quick Load and Continue**, in files of their own beside the Controller Pak; Load Game and Quick Load during fights too, and Quit to Title. - **Fast-forward** (3x) and a **faster walk** (1.5x by default, up to 2x). - **Clearer combat**: a turn-order bar, a combat log, framed portraits with HP, stamina and a tired mark, easier aiming, what a spell, skill or potion does and costs shown while you choose it, and faster turns if you like (your party's and the enemies'). - **Better inventory, spells and skills lists**: tabs on LB and RB (weapons, armour, jewellery, magic, potions, items; the spell schools; skills and arms), a sort on Y (the game's order, by type, by name), group headings, and a Details switch on X: a card for the highlighted item, spell or skill in place of the paper doll, with what it is and does, how it compares with what the character wears, and what the character is wearing. The right stick turns the menu's pages. With a mouse, point at a row and click a tab. - **Help built in**: a controls card on screen, button hints in the menus, a ReChronicled topic in the help book and a PC edition of the manual (F1). - **Changed while you play**: the Display and ReChronicled pages of the pause menu hold all of this edition's options, windowed or borderless full screen included. - **Safe saves**: the Controller Pak's last 20 states are kept, an emulator's save (Project64, ares, Mupen64Plus, RetroArch) can be copied in, and a problem report gathers what's needed when something goes wrong. **This repository contains no game data** (only the pictures on this page). You need your own dump of the game. The build checks it against this SHA-1: ``` 47ae795a2b12783a13e2b8d03439ce9a39fc826c baserom.z64 ``` > **Work in progress.** This is a hobby project I'm making for myself, in my own time, with no schedule. Expect some > rough edges. New features stop with 1.0.0 so I can play the game; I'll fix bugs as they turn up, and maybe add small > quality-of-life things. I haven't played very far into the game yet, so playtests and problem reports from further > along are very welcome (see [Found a problem?](#found-a-problem)); I'll work on them when I can. > **Unofficial fan project.** Not affiliated with or endorsed by THQ, H2O Entertainment or Nintendo. *Aidyn > Chronicles: The First Mage* and its characters, art and music belong to their rights holders. > **Made with heavy AI involvement.** Nearly all of the code, tools and documentation here were written by an AI > coding assistant (Claude Code), directed and play-tested by Spitzdano. The redrawn portraits, icons and world > textures in `art/` were made with an AI image generator from the game's originals, each checked against its > original; the HD menu and world texture packs are AI upscales (Real-ESRGAN) of the game's own pictures. Scaffolded > by the n64-recomp-toolkit's `new-game.sh`. ## Screenshots ![The same spot on the Nintendo 64 and in ReChronicled](docs/screenshots/n64-vs-rechronicled.jpg) | | | |---|---| | ![The title screen](docs/screenshots/title.jpg) | ![The title menu](docs/screenshots/title-menu.jpg) | | The new logo, and the version switch | Continue, Display, Controls, Achievements and Updates before the game starts | | ![The title menu's Display page](docs/screenshots/title-display.jpg) | ![Updates](docs/screenshots/updates-panel.jpg) | | Frame rate, anti-aliasing and the rest, set before starting a game | In-game updates: what's new, then only the changed files are downloaded | | ![Walking about in the first forest](docs/screenshots/field.jpg) | ![The coast](docs/screenshots/painted-world.jpg) | | The new party bar, the compass, the controls card, widescreen | Newly painted world textures, the right-stick camera | | ![A fight](docs/screenshots/combat.jpg) | ![The party and its inventory](docs/screenshots/inventory.jpg) | | The turn order bar, framed portraits, the combat log and its controls card | The repainted scroll, the new portraits, HD lettering and Xbox prompts | | ![The map and calendar page](docs/screenshots/map-and-calendar.jpg) | ![The Display page](docs/screenshots/display-page.jpg) | | The map page's calendar with its moon phases | Frame rate, separate anti-aliasing and supersampling, changed while you play | | ![The ReChronicled page](docs/screenshots/rechronicled-page.jpg) | ![Achievements](docs/screenshots/title-achievements.jpg) | | This edition's options, changed while you play | Achievements for each version of the game | ## Download and play [Releases](https://github.com/spitzdano/AidynReChronicled/releases) have ready-to-run builds: - **Linux** (`AidynReChronicled--linux-x86_64.tar.gz`: x86_64, glibc 2.38 or newer, a Vulkan driver). Unpack it and run `bash install.sh` (app menu entries and, if wanted, a Steam shortcut) or `game/aidyn-rechronicled.sh` in place. - **Windows** (`AidynReChronicled--windows-x86_64.zip`: Windows 10 or 11, 64-bit, a Vulkan driver; the Visual C++ runtime is included, nothing to install). Unpack it anywhere and run `AidynReChronicled.exe`; `Add to Steam.bat` adds it to Steam with its library art. **Your ROMs:** put them in the download's `ROMs` folder: the NTSC-U 1.0 ROM, and the same ROM with Aidyn Patch or Aidyn Plus applied if you have them, under any names (`.z64`, `.n64` or `.v64`). At each start the game looks there for any version it doesn't have yet, recognises each by its hash, keeps a copy in its data folder and lists it on the title screen; on Linux, `install.sh` copies them to the installed game's `ROMs` folder and keeps that folder through updates. If the version you played last has no ROM, the game plays one that has. Without any ROM there, the first start asks for one in a file dialog. The README in the download covers controls, Aidyn Patch, Aidyn Plus and settings, and RELEASE-NOTES.md what each release changed (`tools/release/notes`). Releases carry no ROM (nor either patch). The newly painted art, the HD letters and the title branding ship as three `.rtz` texture packs, and two things made from the game's own data, kept in private repositories (spitzdano's call to ship them): the HD menu and world packs (`hd-ui`, `hd-world`: the `hd/` submodule, Real-ESRGAN upscales; only the pictures the painted art doesn't replace yet go in, `tools/release/rtz.py --without`, about 45 MB of their 1.3 GB) and the restored instrument samples (the `sound/` submodule). The data folder (settings, saves, logs, problem reports, the stored ROMs) is `~/.local/share/AidynReChronicled` on Linux and `%APPDATA%\AidynReChronicled` on Windows. **Updates come through the game.** At each start the title screen asks GitHub (in the background, through the system's `curl`) whether a newer release is out; if so, a line at the top says so, and **Updates** on the title menu shows what's new and updates: only the files that changed are downloaded, checked, and put in at the next start (or Restart now), the old ones kept so **Undo the last update** can put them back. Saves, settings and ROMs aren't touched. **Check at start** turns the checking off (Updates then looks only when opened); **Test builds too** also offers test builds (pre-releases). Works on Linux (including Steam Deck / Bazzite Game Mode) and Windows 10 / 11; under Wine or Proton, which have no `curl.exe`, it shows the release page instead. A release is made with `tools/release/make_release.sh --windows --publish` (`--test` for a test build, a pre-release): it builds the portable Linux executable (`tools/ally/build_portable.sh`) and the Windows one (`tools/windows/build_windows.sh`, cross-compiled here), packages each with its launcher or Add to Steam script, the icon, the Steam art and the texture packs, makes the in-game updater's signed manifest (`tools/release/update_manifest.py`, "In-game updates" below; the signing key in `~/Vault` must be open), tags the pushed `main` and creates the GitHub release with all of it. ## Found a problem? Please report it: a crash, a freeze, a glitch, a wrong picture, anything that looks off. 1. **Make a problem report.** If the game crashed, it already made one and said where; if it froze, or you had to close it by force, it makes one the next time it starts (a notice at the top says so). Otherwise pause as close as you can to the problem and choose **Report a problem** at the bottom of the **ReChronicled** page (on the pause menu's Menu page): it also keeps a picture of the screen and a save of that moment. 2. **Open an issue** with the [Problem report form](https://github.com/spitzdano/AidynReChronicled/issues/new?template=problem-report.yml) and drag in the newest `.zip` from `~/.local/share/AidynReChronicled/reports` on Linux (on a Steam Deck or an Ally, switch to Desktop Mode to reach it) or `%APPDATA%\AidynReChronicled\reports` on Windows (type that into File Explorer's address bar). 3. **Say what happened** and what you were doing just before. A report holds the game's logs, your settings and your saves, nothing from the game's ROM, and your home folder's name is left out of it. ## Building Building it yourself isn't supported: play the [releases](https://github.com/spitzdano/AidynReChronicled/releases). For the record, a release is built from this repository and the [n64-recomp-toolkit](https://github.com/spitzdano/n64-recomp-toolkit) at commit `e1e4e40`, cloned and built on the machine (`install.sh`, `build.sh`, `doctor.sh`). `tools/toolkit.env` says where it is (`~/Work/n64-recomp-toolkit`); set `N64RECOMP_TOOLKIT` to override. The redrawn art (`art/`) is a submodule from its own private repository; players get it in the releases' texture packs. ```sh git submodule update --init --recursive ln -s /path/to/your/dump.z64 baserom.z64 # never copied, never committed tools/split.sh # splat: split/asm, split/bin, linker script tools/build_elf.sh # assemble + link -> build/aidyn.us.elf; must rebuild baserom.z64 byte for byte tools/recomp.sh # N64Recomp -> RecompiledFuncs/, RSPRecomp -> rsp/ tools/check_recomp.sh # compile-only check of the generated code ``` Everything under `split/asm`, `split/bin`, `RecompiledFuncs/`, `rsp/` and `build/` is derived from the ROM, so it is gitignored and regenerated by the commands above. The Windows build is cross-compiled on Linux: `tools/windows/build_windows.sh` (clang-cl and lld-link against Microsoft's C runtime and Windows SDK, fetched by a pinned [xwin](https://github.com/Jake-Shadle/xwin); Vulkan, as on Linux) puts the exe and its DLLs in `build/windows/dist`. It is tested under GE-Proton (`tools/dev/windows-steam.sh` adds it to Steam on the Linux PC) and on Windows. ## Stubs, ignored functions and patches Every entry in `aidyn.us.toml`'s `[patches]` and every hand-written stub in `src/` is listed here with the reason for it. | Function | Kind | Why | |---|---|---| | func_800B7B50, func_800B7BC0, func_800B7C6C, func_800B7C88, func_800B7CBC, func_800B7D40, func_800B81C8, func_800B828C | stub (`aidyn.us.toml`) | libultra exception machinery (cop0, cache ops, handler vector); ultramodern provides interrupts and threads | | osPfsInitPak, osPfsFreeBlocks, osPfsAllocateFile, osPfsDeleteFile, osPfsFileState, osPfsReadWriteFile, osPfsRepairId, osPfsFindFile, __osCheckId, __osCheckPackId, __osGetId, __osPfsCheckRamArea, __osPfsRWInode, __osPfsSelectBank, __osRepairPackId | renamed `aidyn_*` (`tools/symbol_renames.txt`) | N64Recomp substitutes the runtime's "no Controller Pak" versions for these names. Renamed, the game's own libultra file system is recompiled and runs over the pack image below | | Graphics__EndList | ignored; `src/game/patches.cpp` | ends each frame's display list; the C version draws the game's black CRT-overscan frame only when `overscan_borders` is on (it would sit in the middle of a 16:9 picture) | | func_800A68E0 | ignored; `src/game/patches.cpp` | the walking camera's "behind the player" point; the C version keeps the camera's own direction after the right stick has turned it | | func_800AA2B0 | ignored; `src/game/patches.cpp` | Font::GetButtonColor; white while the HD UI pack is loaded, so its Xbox button icons keep their colours | | __osSiGetAccess/RelAccess, __osSpDeviceBusy, __osGetSR, osUnmapTLBAll | ignored by N64Recomp (runtime-provided) | the runtime lacks `__osSiGetAccess/RelAccess`, `__osSpDeviceBusy`, `__osGetSR`: stubbed in `src/game/stubs.cpp` | | osPfsIsPlug, __osPfsGetStatus, osPfsChecker, __osContRamRead/Write | `src/game/pak.cpp` | the Controller Pak (below) | ## Controller Pak saves The game keeps its saves on a Controller Pak. `src/game/pak.cpp` emulates one pack in port 1 as a 32 KiB image in the standard libultra layout, `~/.local/share/AidynReChronicled/aidyn.mpk` (`AIDYN_MPK=/path` overrides it). Only the two raw block accesses (`__osContRamRead` / `__osContRamWrite`, 32 bytes each) plus the PIF-level `osPfsIsPlug`, `__osPfsGetStatus` and `osPfsChecker` are ours; the file system on top (ID and inode tables, note directory, free-page accounting, the `PFS_ERR_*` codes) is the game's own libultra code, recompiled. A new image is created formatted (as `osPfsReFormat` leaves one). Every write is flushed at once through a temp file and a rename. An existing file must be exactly 32768 bytes (Project64's `.mpk` and ares' `.pak` work as they are), or a file holding four controllers' packs, which is converted when the game starts: Mupen64Plus' 128 KiB `.mpk` (RMG, simple64) and RetroArch's `.srm` (Mupen64Plus-Next, ParaLLEl-N64). The pack with an Aidyn save (else the first formatted one) becomes the 32 KiB file and the original is kept in `backups/`. Anything else stops the program and is left untouched. Ports 2 to 4 report no pack. `osPfsChecker` always reports "consistent": it talks to the PIF directly. Runtime-shell stubs (`src/game/stubs.cpp`): `__osSiGetAccess` / `__osSiRelAccess` (no-op), `__osSpDeviceBusy` (never busy), `__osGetSR` (context status register), and `aidyn_park_boot_thread` (hooked at the entrypoint's trailing `break`: bootproc returns on the runtime but never on hardware). `src/main/audio.cpp` caps the reported audio queue at one VI so the game's unsigned buffer-size arithmetic can't wrap, and keeps the game's sound three VIs ahead (52 to 68 ms queued; it was one, 18 to 35 ms, and a hold-up of the game's thread longer than that played silence: a pop). The log notes each time the queue was found empty ("Sound: ran dry"); `AIDYN_AUDIO_AHEAD=` (testing). Split fix-ups (all in `tools/`, applied by `tools/split.sh`; `decomp/` is never edited): `yaml_options.txt` + `yaml_fixes.txt` (segment boundary moves and replacements), `symbol_fixes.txt` (named/sized symbols), `symbol_fixes_auto.txt` (functions reached only through data, from `find_missing_funcs.py`), `symbol_drops.txt` (bogus decomp symbols inside other functions), `symbol_renames.txt` (see above). `find_fallthrough.py` must report 0: a function that falls off its end into the next one is recompiled as a function that returns early and leaves the stack pointer wrong. ## Running ```sh cmake -S . -B build/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ cmake --build build/cmake --target AidynReChronicled build/cmake/AidynReChronicled --rom /path/to/aidyn-chronicles-ntsc-u-1.0.z64 # remembered after the first run ``` F1: the manual. F2: the redrawn art (`rechronicled-art` pack) off and on, to compare ("Redrawn art off" shows top right while it's off). F11 / Alt+Enter (or the Display page's Window row): windowed or borderless, remembered. Shift+Esc: quit. The game starts at the title screen (`skip_intro`). Data (settings, saves, the stored ROMs, texture packs) lives in `~/.local/share/AidynReChronicled` (Windows: `%APPDATA%\AidynReChronicled`); ROMs put in `ROMs` beside the executable (or one folder up) are found and stored at the start; a folder from the project's earlier name, `AidynRecompiled`, is moved there on the first run. **Aidyn Patch and Aidyn Plus.** Two community rebalances by halibabica (u/halibabica) can be played instead of the original: Aidyn Patch and the bigger Aidyn Plus (2023: a massive overhaul that corrects many issues and makes the game more challenging; new weapon skills and items, every spell learnable, elements and resistances reworked; read its FAQ). Aidyn Plus first came out as a ROM download on a site that is gone; it now exists as a patch for the 1.0 ROM (SHA-1 `47ae795a...`, CRC32 `b1f18186`). Neither comes with this project: get the patch yourself and apply it to your own 1.0 ROM (for Aidyn Plus, `AidynPlus.bps` with a BPS patcher such as Flips or Floating IPS), then run the game once with `--rom /path/to/that.z64`, or put it in the `ROMs` folder. Each is checked by its hash and only a match is stored, next to the original ROM: | Version | XXH3-64 | SHA-1 | |---|---|---| | Original (NTSC-U 1.0) | `79127d0e9caf38bc` | `47ae795a2b12783a13e2b8d03439ce9a39fc826c` | | Aidyn Patch | `aee228c7fb42f968` | `1f95ad78824f491ea3d94e3ac5d36901cef09e4e` | | Aidyn Plus | `a72109e848aff75e` | `d57f9459a77c66490ff09b3d9d97545c7a65fad2` | | Action Edition (`AidynActionFeb072026.z64`) | `802056dbbadc0fc0` | `4bbb9fcd0f3124b145b8b54e7be927baae4e262e` | **Action Edition**, by MDoulos, is a data mod of its own (faster combat, a rebalanced economy and items, shops stocked to their themes, spell schools redistributed among the party; see its page): a whole ROM, not a patch, so it is used as it is, and a new release of it has another hash and needs a new release of the port. Like the others it changes only game data (its differences from the 1.0 ROM are in the same places as the other two's), so it runs on the same recompiled code. All of them change only game data (potion recipes and prices, skill and weapon XP rates, the entity, weapon, armour, spell, item and chest databases; compared byte by byte with the 1.0 ROM), no code, so they run on the same recompiled code. You can have any of them. Each game keeps its own Controller Pak: `aidyn-patch.mpk`, `aidyn-plus.mpk`, `action-edition.mpk`; copy `aidyn.mpk` over one to carry a save across (not into Aidyn Plus: its databases differ enough that such a save can crash it, #31). Aidyn Plus's own FAQ warns that some of its items and spells freeze the game when you look at their description; that is the romhack, not the port. Switching versions: - **Title screen**: the bottom left names the running version with a copper marker, and under it *Versions* with Y (keyboard I). Y opens a list: *Original* first, then *Aidyn Patch*, *Aidyn Plus* and *Action Edition*, the running one marked *playing*. Up and Down choose, A plays the chosen one (saved in `settings.json`; the program restarts in place into that version, back at the title), B, Start or Y close it (`title_versions_panel` in `src/game/patches.cpp`; the game gets no input meanwhile). A version whose ROM isn't stored says *not found* and can't be chosen; if the version in `settings.json` is one of those, the game plays one whose ROM is there instead, for that run, and names that one. The list is on the title card: from the menu Start opens (New Game, Load Game, Options), B goes back to it (the game had no way back; `aidyn_title_back` calls its own `IntroMenu::InitTitleCard`). - **Version number**: the bottom right of the title screen shows the build's version above the credit: "v0.4.0" for a release (`make_release.sh` passes it to the build as `AIDYN_VERSION`), "v0.4.0 dev" for a build after that tag (from `git describe`, `tools/version.cmake`, on every build). - **Launch options**: `--original`, `--patch`, `--plus` or `--action` picks the version for that run (otherwise `game.rom_patch`). - **App launcher**: `tools/install-launchers.sh` adds "Aidyn ReChronicled" (`--original`), "Aidyn ReChronicled (Aidyn Patch)" (`--patch`) and "Aidyn ReChronicled (Aidyn Plus)" (`--plus`) to `~/.local/share/applications` (`--remove` takes them out). | N64 | Xbox controller | Keyboard | In the game | |---|---|---|---| | Control stick | Left stick | W A S D, arrows | walk, move in combat, menus | | (camera) | Right stick | mouse; [ ] ; ' | turn the camera around the party (left/right), tilt it up or down (up/down), both at the same speed, 30 degrees each way from its resting height; once turned, the camera keeps that direction while walking (`camera_invert_x` / `camera_invert_y` flip them) | | (camera) | Right stick click | middle mouse button | camera back behind the party at its resting height (the original follow camera) | | (fast-forward) | Left stick click | hold Tab | fast-forward on / off: everything runs `fast_forward_speed` times faster (3); a marker shows at the top right. In combat it doesn't add to Combat speed / Enemy turns: the faster of the two is used | | A | A | Z, left mouse button | talk, open, confirm; in combat: attack, then act on the target | | B | B | X, right mouse button | back, cancel; walking: show the party's portraits; title menu: back to the title card | | C-down | X | K | Details (pause menu); in combat: spells (tap: the last spell again, hold + stick: pick one); title screen: switch to the version marked X | | C-up | Y | I | in combat: items and potions (tap: the last one again, hold + stick: pick one); title screen: switch to the version marked Y | | C-left | View | J | walking and in combat: show / hide the controls card (not passed to the game there; in combat it cycled targets, which the stick and RT still do); the menus: nothing | | C-right | RT | L, mouse forward button | pause menu, shops, trainers: next party member | | Z | LT | C, mouse back button | walking: hold + stick to sneak; combat: end the turn; pause menu, shops, trainers: previous party member (passed to the game as C-left; the game's Z turned the page back, as LB does) | | L | LB | Q | walking: quicksave; in combat: nothing (not passed to the game in either; in combat it was a second End turn); pause menu: previous page | | R | RB | E | pause menu: next page; hold + stick: the game's own camera control | | Start | Menu | Enter | pause menu; back to the game | | D-pad | D-pad | keypad 8 4 5 6 | menus | The keyboard and mouse column is the default: every key and mouse button (left, right, middle, back and forward, the wheel) can be changed, two to an action, on the Controls page (the title menu's Controls, or the pause menu's ReChronicled page, last row; `src/main/controls.cpp`, kept in settings.json's `controls` section: `bindings` by action id, `mouse_look`, `mouse_sensitivity`, `mouse_invert_y`). The mouse turns and tilts the camera in the field (SDL's relative mouse mode, captured only while walking about and the window has the focus). While the keyboard or mouse was used last (`aidyn::input::keyboard_last`) every picture prompt shows your keys instead of the Xbox icons: each picture with an icon in it has a keyboard twin (`_k`, `tools/branding/make_branding.py`: the icon left as a blank slot and the header's `kKeyed` saying where) and `draw_rgba16` puts a key cap (or a mouse picture) in each slot (`draw_key_slots`). The button symbols in the game's own text (`\x7F`..`\x89`, the font's cells 0x5C..0x66: the Help book, messages) become caps too: N64BorgImageDraw's entry hook (`glyph_as_key`) draws the cap in place of the cell, and each frame `update_glyph_widths` sets those cells' widths in the font's tables to the caps' so the text lays out around them (the originals come back with the pad). The Controls page's hints change too, and a cap covers the DETAILS button painted into the inventory page. The caps and mouse pictures are placeholders (`key_cap_*`, `key_mouse_*`). No two pictures may have the same pixels (the renderer stops on equal texture hashes: make_branding.py checks). The letters on the caps are pictures too (`key_g_`, one per character, 12 texels high like the cap with the capitals centred on its face, `kKeyGlyphs` giving each ink's width): the game's text engine places a letter by whole units, which left them off the cap's centre by a different amount on each page. While a conversation is up (one the story starts, or talking to someone in the field: the game's own test, `gGlobals.diaClass`'s menu), "Continue [A]" ("Choose" when it offers answers; the key bound to A while the keyboard was used last) shows at the bottom right of its band (`draw_dialogue_hint`; `AIDYN_DIALOGUE_LOG=1`), with "Skip [Start]": Start (as in the game's four cinematics, which skip with B or Start) skips the conversation by pressing A for you about eight times a second (`src/main/input.cpp`), so every line goes by and everything it does still happens, until it offers answers or ends; Start again stops it ("Stop [Start]"). In a conversation the game sees neither Start (held past the end, it doesn't open the pause menu) nor View, and LB doesn't quicksave. The Help book (a conversation on the journal's page) keeps its own hints. `AIDYN_INPUT_FILE=` (testing: a line of inputs held, "W, Mouse Left", and `look=dx,dy`), `AIDYN_TEST_PAD=1` with `tools/dev/vpad.py` (a virtual Xbox pad for the real pad path), `AIDYN_CONTROLS_LOG=1` (mouse capture changes). These are the game's own controls (from its 2001 instruction booklet) on the port's buttons; the manual has them per screen. **Handheld (Bazzite, ROG Xbox Ally X).** `tools/ally/` builds a copy that runs on other Linux systems (in a container, glibc 2.38, its own SDL2), Steam art, and the installer that adds the game to Steam (the release README has the steps, and how to add it by hand). **Manual.** `tools/manual/build_manual.py` builds a PC edition of the instruction booklet (HTML and an A5 PDF) into `~/.local/share/AidynReChronicled/manual`: new text following the original booklet's chapters, with the spell list, portraits and icons read from your own ROM (and tables of what Aidyn Patch and Aidyn Plus change: spells, weapons, armour, items, creatures, training and potions, when those ROMs are stored), and screenshots from `tools/manual/shots.sh`. F1 in the game and the launchers' Manual action open it. See `tools/manual/README.md`. With the HD UI pack, the button icons in the game's text are drawn as these Xbox buttons. ### Settings `~/.local/share/AidynReChronicled/settings.json` (Windows: `%APPDATA%\AidynReChronicled\settings.json`) is written with these defaults on the first run: ```json { "game": { "rom_patch": "none", "skip_intro": true, "fast_forward_speed": 3, "combat_enemy_speed": 2, "combat_party_speed": 1, "save_backups": 20 }, "audio": { "enhanced": true, "loudness_db": 18.0, "brightness_db": 6.0, "restored_samples": true }, "graphics": { "render_height": 1080, "aspect": "16:9", "widescreen": true, "msaa": 4, "supersampling": 1, "fps_limit": 240, "draw_distance": 3.0, "walk_speed": 1.5, "fullscreen": false, "menus_4_3": true, "right_stick_camera": true, "camera_sensitivity": 1.0, "battle_camera_sensitivity": 0.5, "camera_invert_x": false, "camera_invert_y": true, "overscan_borders": false, "smooth_animation_loops": true, "textures": true, "vsync": true, "texture_packs": ["hd-ui", "hd-world", "rechronicled-text", "rechronicled-art", "rechronicled"] }, "updates": { "check": true, "test_builds": false } } ``` | Key | Values | |---|---| | `rom_patch` | `"none"` (the 1.0 ROM), `"aidyn-patch"`, `"aidyn-plus"` or `"action-edition"` (see Aidyn Patch and Aidyn Plus above); the title screen's version list writes it | | `skip_intro` | `false` puts back the start-up screens: Expansion Pak notice, copyright, Nintendo, THQ and H2O logos | | `fast_forward_speed` | 2 to 6: how much faster the game runs while fast-forward (left stick click, held Tab) is on. In combat the faster of this and `combat_enemy_speed` / `combat_party_speed` is used, not both multiplied | | `combat_enemy_speed` | 1 (off) to 6: enemy turns in combat run this much faster on their own (the ReChronicled page's Enemy turns: Original, 2x, 3x, 4x) | | `combat_party_speed` | 1 (off) to 3: the party's turns in combat run this much faster while they move and act, never while you choose (the ReChronicled page's Combat speed: Original, 1.5x, 2x, 3x) | | `enhanced` (audio) | `false`: the sound exactly as the game makes it. `true` (the ReChronicled page's Sound, Enhanced): louder, with a little more treble, through a limiter that holds the peaks at -1 dBFS (`src/main/audio.cpp`) | | `loudness_db` (audio) | 0 to 24: how much louder Enhanced plays (the game's own mix measures -35 to -39 LUFS; +18 brings it to about -17 to -19, like other games) | | `brightness_db` (audio) | 0 to 12: Enhanced's treble lift above about 3 kHz (the game's instrument samples have almost nothing above 4 kHz) | | `restored_samples` (audio) | `true` (the ReChronicled page's Instruments, Restored; shown when the pack is there): the music's instrument samples from the `rechronicled-samples` pack in the data folder's `sample-packs`, each one redrawn whole by a restoration model (AudioSR): the lost treble back and the 8-bit hiss gone, at the original's length, loop points and level (`tools/audio/sample_pack.py`); the game's own mixer plays them, so notes, loops, volumes and pans are unchanged. `false`: the game's own samples | | `save_backups` | how many copies of the Controller Pak to keep in `~/.local/share/AidynReChronicled/backups` (made at start and before each save; 0 = none) | | `render_height` | internal resolution in lines: 1080 = 1440x1080 at 4:3 or 1920x1080 at 16:9; 0 = auto: the window's own height in lines, at most 2160 (4K), so a 1440-line screen renders 1440 and a 1080-line one 1080 | | `aspect` | `"4:3"` (original, bars at the sides); any wider `"width:height"` such as `"16:9"`, `"21:9"` or `"43:18"` (exactly 3440x1440): a wider 3D view, HUD and menus stay 4:3 in the middle; `"window"` (as wide as the window) | | `widescreen` | `false`: 4:3, the N64's picture, whatever `aspect` says (the Display page's switch; turning it on with `"aspect": "4:3"` sets `"16:9"`) | | `fullscreen` | `true`: borderless fullscreen on the display the window is on (F11, Alt+Enter or the Display page's Window row switch while playing, and save it); with `"aspect": "window"` the picture takes that display's shape (21:9 on an ultrawide). `AIDYN_WINDOWED=1` starts in a window whatever this says (`tools/dev/pad.sh` uses it) | | `fps_limit` | frames shown a second, 30 to 1000, or 0 for unlimited (the Display page's Frame rate: 60, 90, 100, 120, 144, 175, 240, Unlimited; below 60 changes nothing, as the game makes 60); never above the display's rate (default 240: the display's own rate, up to 240), except under gamescope (Steam Deck / Bazzite Game Mode), whose XWayland reports 60 Hz whatever the screen runs at: there the rate set is used as it is. The game makes 60; above that RT64 draws in-between frames, interpolating the camera and every model (see below). On Linux the game opens its window through Wayland where there is one (XWayland too reads 60 Hz); `SDL_VIDEODRIVER=x11` goes back to X11. The log's `Display:` line says the rate RT64 read | | `msaa` | 0, 2, 4 or 8 | | `supersampling` | 1 (off), 2, 3 or 4: renders at that many times `render_height` and scales down, smoothing the edges MSAA leaves (hair, leaves, thin parts). Works with `render_height` 0 too (the window's height times this) | | `draw_distance` | 1 (original) to 3: outdoors, rings of map cells beyond the game's own 3x3 are loaded and drawn (5x5 up to 9x9, 2 = 9x9: ground reaching about 400 units) and the far clip follows the loaded edge up to 180 x `draw_distance`; props (trees, rocks, objects) are drawn up to that many times farther, with their detail levels; monsters, people and items (chests, bags, herb patches) reach twice as far as props: 160 units at 2, 240 at 3 (originally 20 and 5 to 30) | | `walk_speed` | 1 (original) to 2: in the field Alaron's walk and run cycles play this many times faster, and as the game moves him by them, his pace with them; nothing else is sped up (`1.5`) | | `controls_card` | the card of button prompts at the left edge while walking (top) and in combat (bottom); View shows and hides it and this remembers which (`true`) | | `button_prompts` | the port's other button prompts: the menus' hints (Equip, Use, Lists, Take, Put back, Buy, Train, the combat menus' Choose / Release to use), LB and RB at the pause pages' arrows and the party switch beside the portraits (`false`: none; the controls card has its own switch) | | `tabbed_lists` | the pause menu's Inventory, Spells and Character pages drawn by the port: tabs, sorting, headings, the Details switch, and the Character page (the attributes and the skills together; Up from the first skill moves onto the attributes, A trains one with the experience you have) (`false`: the game's own lists and Stats page, X still switches Details) | | `inventory_sort` | the order of those lists (Y): 0 the game's, 1 by type or school (default), 2 by name | | `details` | Details on (X): a card for the highlighted entry beside the list; off in a new game, remembered | | `combat_log` | combat: the log of the fight at the bottom left (see "Combat log" below; `false`: none) | | `combat_descriptions` | combat: what the highlighted spell, skill or item does, beside its menu (the ReChronicled page's Combat descriptions; `false`: none) | | `fps_counter` | `true`: the frames shown a second at the top right (left of the fast-forward marker when that shows), over everything, in the game's font (the Display page; default `false`) | | `turn_order` | combat: the round's turn order across the top of the screen, then the next round's (see "Turn order" below; `false`: none, and the game's own order within each speed group) | | `hud_at_edges` | with a wider aspect: the time-of-day dial at the picture's left edge, the compass at its right edge; the party portraits stay centred (`false`: all in the middle 4:3, as the original) | | `overscan_borders` | `true` puts back the black frame the game drew for CRT overscan | | `right_stick_camera` | `false` turns the right-stick camera off | | `camera_sensitivity` | 0.5 to 2: the right stick's camera speed walking (1: 150 degrees a second at full tilt); the mouse has its own (`controls`) (`1.0`) | | `battle_camera_sensitivity` | 0.25 to 2: the same in a fight (`0.5`: 1 felt too quick there) | | `camera_invert_x`, `camera_invert_y` | `true` turns the camera the other way left/right, or tilts it the other way up/down (inverted look) | | `smooth_animation_loops` | `false` puts back the original stop in a walk or run cycle each time it loops (see below) | | `textures` | `false`: every custom texture off, as the N64 drew it: the texture packs (HD menus, letters, buttons, portraits, world, title art), the HD save pictures and the redrawn page words; the letters go back to the game's own widths. The packs stay listed in `texture_packs` | | `vsync` | `true`: each frame waits for the display's refresh (no tearing); `false`: shown as soon as it's drawn, for a G-Sync or FreeSync display (on a fixed-rate one it can tear) | | `menus_4_3` | with `16:9` or `window`: the pause menu (all pages, also the combat inventory) and shop screens switch to 4:3 while they're up. Their layouts mix 2D draws that RT64 widens with ones it keeps centred, so the equipment page fell apart widened | | `updates.check` | `false`: no look for a newer release at start (the title menu's Updates still looks when opened) | | `updates.test_builds` | `true`: Updates offers test builds (GitHub pre-releases) too, not only full releases | | `texture_packs` | RT64 texture packs in `~/.local/share/AidynReChronicled/texture-packs` (folders or `.rtz`), later ones win. The default `hd-ui` and `hd-world` are built by `tools/hdui` (see its README), `rechronicled-text` (the HD letters and Xbox buttons, drawn from fonts; it decides the letter widths, see "HD text") by `tools/hdui/text_pack.py`, `rechronicled-art` (the redrawn portraits and icons in `art/`) by `tools/hdui/art_pack.py`, `rechronicled` by `tools/branding/make_branding.py`; a missing pack is skipped with a message. A settings file from before `rechronicled-art` or `rechronicled-text` existed gets each added once | **In the game.** The pause menu's Menu page has two more entries below Theater (in a battle, after Flee), each a page of the switches above that can change while playing: **Display** (`widescreen`, `textures`, `vsync`, `fullscreen` as Window, `fps_counter`, `hud_at_edges`, `draw_distance` from 1 to 3 in halves, Anti-aliasing as `msaa` (Off, 2x, 4x, 8x) and Supersampling as `supersampling` (Off, 2x; 3 and 4 only in the file); the title menu has the same page as **Display**, before a game is started) and **ReChronicled** (`enhanced` as Sound, `restored_samples` as Instruments, `turn_order`, `walk_speed` from 1 to 2 in quarters, `combat_party_speed` as Combat speed, `combat_enemy_speed` as Enemy turns, `controls_card`, `button_prompts`, `combat_log`, `smooth_animation_loops`, `right_stick_camera`, `camera_sensitivity` as Walking camera sensitivity from 0.5x to 2x in quarters, `battle_camera_sensitivity` as Battle camera sensitivity from 0.25x, `camera_invert_x` and `_y`, and Report a problem). Up / down pick one, A or left / right change it, B goes back. Each change takes effect at once and is written into `settings.json` (the rest of the file is kept); turning `textures` back on holds the game for a few seconds while the packs load, as F2 does. The game's own Options screen no longer has its Graphics row (Normal / High Resolution): the game always runs in the normal mode. RT64 already renders at `render_height` in full colour, and the High Resolution mode's 512-wide frame only stretched the HUD and menus off the side of the picture (its unused third mode, 32-bit colour, looked the same here). The game itself submits a new frame every 1/60 s (its logic is delta-timed, so it simply runs at 60 fps here, where the N64 managed 15 to 20). `AIDYN_FPS=1` prints the rate every two seconds. A file with a bad value stops the program with the reason. On Hyprland, Omarchy's default window opacity lets the desktop show through; a rule like `o.window("^AidynReChronicled$", { opacity = "1 1" })` makes the window opaque. ### Changes to the game's behaviour All in `src/game/patches.cpp`, hooked from `aidyn.us.toml`: - **Achievements (#42).** 72 milestones in eight groups: Story (21), Side quests (5: Trahern's sword, Pandara's amulet, a strange letter, the wild man of the bog, Elisheva's tomb), Bosses (3), Optional bosses (8), Companions (15), Exploration (2), Growth (6) and Feats (12: five, and the seven fair play ones; Action Edition has no Fair Trader, and only the Original has Old Stock). Most are read from the event flags the game keeps in its saves (journal entries, bosses' "killed" flags, companions joining, maps, keys, a spell learned, stats and skills), a few counted by the port (a potion brewed: `Party::CraftPotion`'s result; fights won, a flawless fight, a camp ambush survived, a fight fled, gold, levels, a full party). **Only what is done in play counts**: when a game starts or a save is loaded, what it already holds (flags, level, gold, party size: the first 3 seconds) is its starting point, and an achievement unlocks only when its flag comes on after that (or a level, gold or party size crosses its threshold), so loading a late save never unlocks the story so far. Each version (Original, Aidyn Patch, Aidyn Plus, Action Edition) keeps its own progress beside its saves (`achievements-.txt`, not in a save), so it adds up across playthroughs; nothing unlocks in a game where a cheat code was used (below). Each unlock shows a toast at the top right while walking about (the turn bar's pieces with the group's medal, painted by Codex; its metal is the version's: bronze for Original, silver for Aidyn Patch, gold for Aidyn Plus, rose copper for Action Edition). The story, the side quests, the companions and the optional bosses show as `???` until earned (so each group can still be counted); the rest (the story's bosses, exploration, growth, feats, fair play) show their names from the start. Under the list, the highlighted one says how it is earned (a secret one once it has been). The title menu's **Achievements** shows all four versions with a progress bar and their counts, A opens one's list; the pause menu's Menu page has **Achievements** (after Help) with this version's list: grouped, a medal and the day each was earned. `AIDYN_ACHIEVEMENTS=0` turns them off; `AIDYN_ACH_LOG=1` logs each unlock; `AIDYN_ACH_FORCE=id,id` unlocks those once and `AIDYN_SET_FLAGS=f,f` sets event flags as a game begins (tests). - **Fair play.** New Game opens a panel of five fixes for the original's loopholes, one help and better shops, each fixed for that playthrough (shown on the ReChronicled page's Fair play row). The fixes are on by default and each has an achievement for finishing the game with it on, and the panel says that turning one off rules that achievement out: - *Healer rule* (Honest Healer): a healer can't lay hands on someone who healed them since the party last rested (camping or an inn, `Party::CampHeal`), so two healers can't heal each other up for free (#33). - *Fair trade* (Fair Trader): a shop never sells an item for less than it would pay for it. The game prices with f = (the keeper's Merchant - the party's best) x 0.05, buying at price x markup x (1 + f) and selling at base price x (1 - f) / 2, so a party that out-ranks the keeper (by 7, or at all on half-price stock) could buy and sell back at a profit for ever. Not offered in Action Edition (its shops are its own fix), so it has no Fair Trader; Aidyn Plus keeps the original's shopkeepers and prices them the same way, so it has the rule. - *Honest alchemy* (True Alchemist): a potion sells for no more than its ingredients' base price (the original sold a Restore Potion brewed from 150 gold of reagents for about 1,200; Aidyn Patch and Action Edition repriced potions). - *Fights stay won* (Settled Scores): a group of monsters beaten stays beaten. The game brings back the groups that have no "defeated" flag of their own (52 of its 1,085, such as the Gwernia inn cellar's bats, and townsfolk who fight once spoken to) after three trips through a door or to another map (its voxel chart). With the rule each such win over a group with no conversation is kept with the save (`battle_setup_func`, `WriteTo_VoxelChart`), and `some_monsterparty_checker` keeps that group away; townsfolk keep the game's way. Camping still brings night ambushes, so there is always a fight to be had. Every other group (1,033 of 1,085) has a "defeated" flag and never came back in the original either. - *Chests stay empty* (Fair Finder): a chest emptied stays empty. A container with no "opened" flag of its own (46 of them hold gold or items, two 3,000 and 5,000 gold) went on the voxel chart for 10 trips through a door or to another map and was then full again. With the rule each such chest opened is kept with the save (`loot_func`, `WriteTo_VoxelChart`) and `some_ref_obj_lookup_func` answers that it is still empty; herb, spice and gem patches still regrow. `AIDYN_FAIR_TEST_LOOTED=key,key` starts a new game with those emptied (developers). - *Treasure sparkle* (off by default; Keen Eye for finishing the game without it): a pulsing star over every container within about 20 units still to be opened and holding something (chests, gold, herb, spice and gem patches), where the camera can see it: the game's `CheckCollision` on the line from the camera to it (a hit within a unit of it is its own wall). It can be turned on later on the ReChronicled page's Treasure sparkle row (chosen twice), never off. `AIDYN_SPARKLE=1` shows it always and `AIDYN_SPARKLE_DEBUG=1` logs it (developers). - *Better shops* (the Original only, off by default; Old Stock for finishing the game with it off, the original's own shops): the 75 shops stock what Action Edition stocks them with (MDoulos's mod, with his agreement), fuller and fitting each trade, in place of the original's thin stock. The same shops, shopkeepers and only items the original has (checked by `tools/data/action_shops.py`, which writes `src/game/action_shops.h` from the Action Edition ROM); the game's shop table in memory (`gShopDBP`) holds them while the rule is in force and its own otherwise. Not offered in the mods: each stocks its shops its own way (Aidyn Patch 34 of the 75, Plus 52), and Action Edition's are where the stock comes from. `AIDYN_BETTER_SHOPS_TEST=1` puts them in any version and game (developers). The choice goes with each save the port writes (`save-rules.txt` beside the saves, by the save's hash, with the groups beaten and the chests emptied for good); a save made before has none of the rules and its playthrough can't earn their achievements. `AIDYN_FAIR_LOG=1` logs prices, fights and the voxel chart (developers). The game's cheat codes (typed as a name: `!cheater`, `!bingo`, `!slashing`, `!darkside`) stop all achievements in that playthrough. - **Sholeh's intelligence.** Saves keep intelligence, willpower, dexterity and strength in 5 bits (at most 31): Sholeh starts with 35 intelligence (the original, Aidyn Patch) and came back from every save with 3. A stat whose starting value is 32 or more and that a save cut below 32 gets its 32 back (`fair_fix_stats`, once a second). - **The mouse pointer on the port's own pages.** On the ReChronicled, Display, Controls, Achievements and Versions pages, moving the pointer over a row highlights it (a click is A as usual: it then acts on that row); `aidyn::controls::pointer` maps the window to the menus' 320 x 240 units. The game's own scroll lists (the pause menu's Menu page, the title menu, the Options lists: WidgetScrollMenu::Tick, hooked) follow it the same way (`AIDYN_LIST_LOG=1` prints each list's entries as the pointer moves). Other game menus (shops, the inventory's lists) don't yet. - **Help book.** The pause menu's Help (a dialogue, `BORG13_HelpMenu`) is copied as it loads with its words fitted to this edition: the BUTTON after each button icon dropped (they're Xbox icons now), "the left stick" for the Control Stick, RT (not View, which shows the controls card) to pick a target. A **ReChronicled** topic is added before Exit Help: the camera, fast-forward, the controls card, the menus, saving, combat (turn order, hold menus, D-pad targeting) and the keyboard and PC keys. - **Pause scroll ends.** The pause pages are one long parchment drawn through a clip, and the scroll's rollers are a 3D model moved sideways per page. On the Menu page the parchment ran on past the left roller to the screen's edge (and past the right one on the map page); the clip is now kept between the rollers' middles, which follow the page turns (measured in game; only with the pause menu at 4:3, `menus_4_3`). - **Titles.** The pause pages' and menus' titles (Menu, Options, Theater, Pause, Enter Name) are drawn like the ReChronicled page's (`art/titles`), and the title card is Spitzdano's redesigned logo (`art/titles/source`, fitted by `tools/branding/make_logo.py`, with its shadow), RECHRONICLED included: the game's own "Rechronicled" under the card is drawn only while the redrawn art is off (F2, or textures off). - **ReChronicled page.** The pause menu's list (`WidgetOptionsMenu::MakeScrollList`) gets one more entry, made the game's way (a `WidgetClipText` with its own id, appended to the scroll menu) after Theater, or at the end of a battle's shorter list. The scroll menu is made with room for exactly six entries and `Append` doesn't check, so its size is patched to eight (a seventh entry overran the array and hung the game when the pause menu was freed). The select callback would stop the game on an id it doesn't know, so it's hooked where it reads the id: ours opens the page instead. The page is drawn at the end of each frame over the parchment, with the list faded out beneath it (`Utilities::SetAlpha`) and the game given no input while it's up, so no button can reach the hidden list; it fades back when the page closes. Turning `textures` off reloads RT64's replacements with no packs (and no dynamic ones: the HD save pictures and redrawn page words) and puts back the fonts' original letter widths; `vsync` sets the swap chain's present mode, rebuilt at the next frame. - **Pause menu edges.** The menu's opening reveal ends a few pixels short of the screen edges (a clip of 17..303 when it opens on the equipment page), hidden on a TV by overscan and the game's black border. It now finishes at the full width; before, the narrower clip also cut the journal page into texture strips the HD pack had no match for, so it showed the original parchment after paging to it from the equipment page. - **Camera.** See the right stick above. The walking camera eases towards a point behind the party; after the right stick turns it, that point follows the camera's own direction instead (movement is relative to the camera, so a fixed angle from the party's facing would chase itself round). Tilting moves the height angle 30 degrees either way from where the camera rests (walking: -15 to 45); the game's own limits, 0 to 45, left only a third of that room upwards. `AIDYN_CAMERA_LOG=1` prints the angle. - **Fast-forward.** Every mode measures its frame's time step by counting the controller samples queued since the last frame (1 to 6 ticks of 1/60 s, built for the N64's 10 to 20 frames a second). While fast-forward is on, each real sample is followed by quiet copies of it (same stick and held buttons, no new presses), so the game takes that many ticks a frame and no press is handled twice; enemy turns in combat get `combat_enemy_speed` the same way, and the party's moves and attacks `combat_party_speed` (not while a menu or a target is being chosen; a fraction is carried to the next frame). The speeds don't multiply: each frame takes the larger of fast-forward's and the combat one's, so with Enemy turns at 4x fast-forward (3x) changes nothing on an enemy's turn. `AIDYN_FPS=1` prints the game ticks a second (60, or 180 at 3x). - **Save backups.** The Controller Pak is copied to `backups/` when the game starts and before each save (the first write after 30 s without writes), unless the copy would be the same as the newest one; the newest `save_backups` copies of each pak are kept. - **Walk speed.** The game moves a character by its walk or run cycle (each animation step moves it as far as the cycle's root moves), stepping each one's animation once per game tick in ProcessPlayers. For Alaron walking or running while walking about, that loop is run `walk_speed` times the frame's ticks (the fraction carried over), so legs and pace quicken together and the clock, monsters and camera don't. - **Problem reports.** `reports/report--