- C++ 74.1%
- Lua 17.7%
- Python 6.3%
- PowerShell 1%
- CMake 0.5%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Crouching is real state a sighted player reads off a HUD icon next to the ammo
counter, and it had no spoken form at all. HudStatusReader now announces every
settled change as "standing" / "sneaking", and the `/` read opens with
"sneaking" when V is low -- crouch only, since standing is the resting state and
its absence is the other half of the answer.
The game's own CrouchIndicatorGameController is the model: same
PlayerStateMachine.Locomotion field, and its crouched set {Crouch 1, Slide 10,
CrouchDodge 13} adopted verbatim. Two things the corpus settles:
- CrouchSprint (12) is never written. CrouchSprintEvents extends CrouchEvents
and writes 1, so sprinting while crouched IS Crouch. Not a gap to fill.
- The machine writes a transient standing 0 BETWEEN two crouched states, because
CrouchSprintEvents and SlideEvents both inherit CrouchEvents::OnExit:
crouch -> crouch-sprint is 1,0,1 and slide -> crouch is 10,0,1. Hence the
two-sample confirm; a single-sample detector says "standing" every time the
player sprints or slides while sneaking.
One deliberate deviation from the icon: airborne, workspot and Kerenzikov states
HOLD the last settled stance instead of reading as standing. A texture may
flicker mid-jump; a voice may not.
Gates hold rather than mute -- HighLevel outside {Default, SceneTier1,
SceneTier2} (tiers 3-5 are cinematic, Swimming has no stance since the dive
action IS the crouch key) and MountedToVehicle. A refused gate read is logged
once and the gate skipped, never closed: losing a gate costs an announcement the
player did not need, while a refusal read as a closed gate would silently cost
every one they did.
The read's module gate is the crouch indicator's own root widget, measured
through the same predicate as the other three: one instance on inkHUDLayer,
visible=true opacity=1 while standing. ReadPsmInt is split into PsmBoard +
ReadPsmIntOn/ReadPsmBoolOn so a three-field poll fetches the entity-local board
once; the handle is still never cached across polls.
Built and linked, NOT installed and NOT live-tested -- the USER was mid-session.
The test owed: crouch, sprint while crouched, then slide into a crouch.
Also carries other work in flight in this shared tree:
src/Scanner/ObjectScanner.{cpp,hpp}, another session's, compiled and linked
clean alongside this at the time of the commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
| .claude | ||
| assets | ||
| docs | ||
| scripts/CyberAccess | ||
| src | ||
| tests | ||
| tools | ||
| vendor | ||
| .clangd | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| .luarc.json | ||
| CLAUDE.md | ||
| CMakeLists.txt | ||
| LICENSE | ||
| README.md | ||
| todo.md | ||
CyberAccess
A screen-reader accessibility mod for Cyberpunk 2077, written as a C++ RED4ext plugin — one DLL plus a thin Redscript bridge. It adds its own files; it never edits the game's.
CyberAccess speaks the game's own UI and world state through the player's screen reader and adds the keyboard navigation that the ink UI does not expose. The goal is to make menus, HUD, dialogue, combat, looting and getting around Night City usable without sight.
Status
Pre-alpha. Under active development and not ready for general use: coverage grows menu by menu, interfaces and keys can change without notice, and screens that already work may break as neighbouring ones land. Engine-touching code is bound to specific game builds (see Compatibility). Expect rough edges, and back up your saves.
Test builds are published as a rolling dev release — the tag always points at the newest
build, so the same download link gives you the current one. Bug reports should name the commit the
release notes quote, since the tag alone never will.
What it does
Menus and screens. A key-driven navigation graph is built for each screen and read aloud on every move. It is our own cursor, not the engine's focus — most of these screens have no focusable widget at all. Covered:
- the first-launch user agreement — the 200-odd paragraphs read as a document,
Hjumps its section headings, and reaching the end unlocks Accept and Decline the way scrolling does - main menu, settings, the new-game flow and character creation, loading screens
- pause menu, death menu, the hub menus — plus the hub chrome, its five tabs and four counters, on every hub-hosted screen
- save and load, time skip, tutorial windows, modal message boxes and system modals
- quest journal, shards, the codex, and the database popup a journal link opens
- inventory: backpack, paperdoll, item mode, and the equip-slot chooser, quantity picker and sell-junk confirmations
- crafting, the wardrobe at an apartment closet, vendor and stash, ripperdoc
- perks, attributes and skills; the tarot gallery and its card preview
- in-world computers — the mail and document screens, the internet browser, and the local network's device tree
HUD, world and combat. Subtitles, dialogue choice hubs (with the reason a locked reply is locked), interaction prompts, notification banners and corner toast cards, quick-loot containers, the Kiroshi scanner panel and quickhack menu, Breach Protocol, braindance narration, device screens and elevators, camera takeover, radial menus, the vehicle radio selector and summon list, incoming calls and arriving texts, the tracked quest objective, and V's own vitals on demand. Aim assist steers the crosshair onto the nearest hostile with a clear shot while you hold the sights up — or hold right mouse on a vehicle-mounted weapon, where there are no sights to raise. It sources its target from the engine's own combat query, so a corpse or a friendly cannot be returned, and it claims no input of its own.
Getting around. A world-object scanner enumerates nearby entities, points of interest, named route points and quest markers, and can auto-walk or auto-drive to the selected one using the game's own navmesh and autodrive systems. Ladder and climb progress is spoken, doors report their real state, and a teleport fallback exists for destinations that carry no navmesh at all. The world map is read as a pin list per category, with fast travel, tracking and waypoints on the game's own verbs.
Speech goes through Prism, which drives NVDA, JAWS, ZDSR, ZoomText, SenseReader, PC-Talker or BoyPCReader when one is running, and falls back to Windows OneCore, SAPI or a UIA announcement when none is. It re-picks the backend by itself when a screen reader starts or stops mid-session.
Requirements
- Cyberpunk 2077 on PC (Windows), patch 2.31 / engine build 3.0.80.51928
- RED4ext
- redscript — the mod ships two
.redsfiles, and nothing compiles them without it. The DLL runs fine either way and says so out loud when the bridge is missing, but subtitles are not spoken and hits and kills give no audible confirmation until redscript is installed. - Audioware — the mod ships its own sound samples (the held on-target tone, and the hit / crit / kill markers) and nothing else can play them. The DLL runs fine without it and says so out loud: hits and kills fall back to the game's own audio events, and the held on-target tone is lost entirely, since the game ships no looping sound that means "the shot is lined up".
- A supported screen reader, or SAPI as the fallback
Optional: Input Loader — CyberAccess uses it to
move the two game actions that vanilla binds to Tab onto \, since the navigator owns Tab. Without
it those two actions stay on Tab and are unreachable while CyberAccess is active; everything else
works.
Installing
Download the archive from the
dev release and extract it at the
game root — the folder holding bin/, r6/ and archive/. It contains:
README.md
LICENSE
red4ext/plugins/CyberAccess/CyberAccess.dll
red4ext/plugins/CyberAccess/CyberAccess.pdb (symbols, so a crash dump can be read)
red4ext/plugins/CyberAccess/inputs.xml (the Input Loader overrides)
red4ext/plugins/CyberAccess/navlinks.bin (off-mesh link index, for climbing)
red4ext/plugins/CyberAccess/routepoints.bin (named route points for the scanner)
r6/scripts/CyberAccess/Bridge.reds
r6/scripts/CyberAccess/Subtitles.reds
r6/audioware/CyberAccess/CyberAccess.yml (the sound manifest)
r6/audioware/CyberAccess/base/sound/ui/*.wav (four samples: on-target tone, hit, crit, kill)
r6/scripts/CyberAccess/ and r6/audioware/CyberAccess/ are the only places the mod writes outside
its own plugin folder, and it only writes new files there. Neither is a choice: redscript compiles
what it finds under r6/scripts, and Audioware only loads a manifest from a folder named after the
mod inside one of its own depots — so there is no plugin-local option for either.
Through a mod manager, install the archive as its own mod with that same internal layout; both folders belong to it.
The .bin assets are optional at runtime. Without them the mod still runs, with the climb
follower on its geometric fallback and no named route points in the scanner.
Controls
Default keys. Where the game already has a verb, CyberAccess re-enters the game's own handler on the game's own key rather than inventing a parallel menu — so a rebound key stays rebound, and the game plays its own sounds and runs its own guards.
A key CyberAccess cannot serve makes no sound. Silence is a refusal, never a failure to hear you; the reason belongs to the readout you land on.
Menus and screens
| Key | Action |
|---|---|
| Arrow keys | Move within the current screen. Left / Right adjust a slider or option instead of moving when the cursor is on one |
| Ctrl + Left / Right | Adjust in large steps |
| Ctrl + Up / Down | Previous / next region of the screen |
| Tab / Shift+Tab | Next / previous stop (section) |
| Home / End | First / last item at this level |
| Enter | Activate — the left-click equivalent. Hold it where a screen defines a hold verb; Shift and Ctrl pick a screen's alternate activation where it defines one |
| Backspace | Right-click equivalent — open or close a node's flyout column, or run its secondary action |
| Space or F1 | Read the tooltip for the current item |
Item verbs are the game's own keys, claimed by the screen that offers them and dispatched
through the game's handler: on stock bindings F use, X drop, V preview, hold V favourite,
hold Z disassemble, R unequip. Each item announces the verbs it actually accepts — the game's
own hint bar, read out with the key names filled in from your live bindings.
In the computer's internet browser, four quick-navigation letters work the way they do in a browser, with Shift reversing each:
| Key | Action |
|---|---|
| K | Next link |
| H | Next heading |
| G | Next picture link |
| D | Next region of the page (the same move as Ctrl + Down) |
World scanner
| Key | Action |
|---|---|
| Page Up / Page Down | Previous / next entry |
| Shift + Page Up / Page Down | Previous / next category |
| Home (tap) | Centre the camera on the selection — on a device panel, aim at the selected button so the game's own interact prompt appears |
| Home (hold) | Travel to it — auto-walk on foot, auto-drive while mounted. A second hold cancels |
| Ctrl + Shift + Home (hold) | Teleport to it — the fallback for destinations no pathfinder can reach; can be switched off in the config |
| End | Step through the selected vehicle's parts, or the selected panel's buttons |
Inside a braindance the scanner stays live, and End steps the clue timeline instead: a tap moves
forward, a hold moves back.
World map
| Key | Action |
|---|---|
| Page Up / Page Down | Previous / next pin |
| Shift + Page Up / Page Down | Previous / next category |
| Home | Move the map focus to the selected pin — distances are then quoted from it |
| Shift + Home | What is around the focus |
| Space | Read the selected pin's full tooltip |
| End | Cycle how much the map shows: Essentials, Usual, Everything |
| Tab | Focus back to the player |
| Enter (hold) | Fast travel, on the game's own hold |
| Backspace | Track the pin, or drop a waypoint where it cannot be tracked |
Breach Protocol
The game keeps its own cursor here; ours is a read-only review cursor laid over it.
| Key | Action |
|---|---|
| Shift + arrow keys | Move the review cursor over the code matrix |
| Shift + Home | Snap the review cursor back to the game's |
| Tab / Shift+Tab | Next / previous daemon |
| Space | Buffer, time and progress |
| Shift + Space | Read the whole board |
Phone messages
The phone (hold T) already has a real keyboard selection model, so it keeps its own keys and
CyberAccess only narrates them. The one thing it has no cursor for is the message wall, which a
sighted player reads with their eyes — so stepping it is ours, while the SMS panel has focus:
| Key | Action |
|---|---|
| Page Up / Page Down | Previous / next message |
| Home / End | First / last message |
HUD
| Key | Action |
|---|---|
/ |
Health, overshield, RAM, ammo, stamina, wanted level, active effects, and the current objective |
Shift + / |
The same plus the long read — grenade, heal, launcher and camo charges, level and XP, and the objective in full |
Low health, a reload, a district change and every change of wanted level are spoken as they happen, without asking.
Configuration
On first run the plugin writes a commented TOML file at:
<game>/red4ext/plugins/CyberAccess/config.toml
It carries whether the load announcement is spoken and whether speech is mirrored to a log
([speech]), whether the scanner's teleport fallback is armed ([scanner]), and how braindance
clues are ordered ([braindance]). Delete the file to get the current commented version back.
Diagnostics are written by RED4ext itself to red4ext/logs/cyberaccess-<timestamp>.log; verbosity
and retention live in RED4ext's own red4ext/config.toml.
Building from source
Requires Visual Studio 2022 (or another MSVC toolset with C++20), CMake 3.23+, and the submodules.
git clone --recurse-submodules https://git.alex19ep.me/alex19ep/CyberAccess
cd CyberAccess
cmake -B build -G "Visual Studio 17 2022" -A x64 -DGAME_DIR="<deploy root>"
cmake --build build --config Release
cmake --install build --config Release
GAME_DIR is cached, so it only has to be passed once at configure time. It is the deploy root
— install writes the whole layout shown under Installing beneath it, so point it at
the game directory or at a mod-manager mod folder, whichever you deploy into.
Engine-neutral tests for the navigation graph live in tests/ and run under CTest:
ctest --test-dir build -C Release
pwsh tools/check_reds.ps1 compiles the Redscript half against a copy of the vanilla bundle and
fails on any error. It runs automatically before an install; run it by hand after editing a .reds
file or the native names it declares.
Compatibility
Engine-touching code is gated on the game build. On an unrecognised build every subsystem that
reads engine memory stays off and the plugin says so out loud instead of risking a crash — the
verified list is kVerifiedBuilds in src/InkSystemAccess.hpp. After a game patch, offsets have to
be re-verified before a new build is added.
License
MIT — see LICENSE.
Bundled dependencies are separate projects with their own licenses: RED4ext.SDK, RedLib, spdlog and toml11 are MIT.
Prism is Mozilla Public License 2.0 and is statically linked
into CyberAccess.dll. MPL-2.0 permits that combination and leaves this project's own sources under
MIT, but it is file-level copyleft: the source for Prism, including any modification we make to its
files, must stay available to anyone who receives the binary. We vendor it unmodified as the
vendor/prism submodule pinned to a published tag, so the upstream repository is that source. Prism
in turn bundles its own third-party code — see its NOTICE file.
Cyberpunk 2077 is a trademark of CD PROJEKT S.A. This is an unofficial fan project, not affiliated with or endorsed by CD PROJEKT.