Files
questshock/CONFIG.md
T
ml 70c43240bc
build / build (push) Successful in 3m42s
Add a Cheats menu tab with engine cheat buttons and unlimited-resource switches
The new Cheats tab exposes the engine's own cheat handlers as buttons:
give all items and weapons, toggle physics, next level and previous
level. Physics shows its state through the button highlight. The buttons
need a running game and show a message otherwise.

It also has four independent session-only switches for unlimited health,
energy, ammo and items (grenades and drugs). While a switch is on, its
resource is topped up every frame by qs_cheat_refill() from the new
engine patch 26. The switches are not saved to the config.
2026-09-20 14:59:33 +02:00

22 KiB

Persistent configuration (questshock.config.json)

QuestShock can load a persistent, human-editable configuration file at startup, in addition to (and independent of) the engine's own prefs.txt.

  • Location: /sdcard/questshock/questshock.config.json - the same folder you already copy res/data/res/sound into (see README.md section 5.1).

  • Format: plain JSON, grouped by concern. Each top-level key is a group; each key inside a group is one option.

  • Loading: read automatically at app startup, before the splash screen, if the file exists. If it doesn't, questshock.config.default.json (same folder) is used instead - a copy bundled with the app, refreshed on every launch, never overwriting your own saved questshock.config.json. If neither file exists, the engine behaves as it did before this file existed (driven by prefs.txt as usual).

  • Saving: never automatic. In-headset, open the menu (controller menu button) and press Save Configuration to write the file from your current live settings, overwriting it if present. Press Load Configuration to re-read the file and apply it without restarting the app.

  • Live vs. restart-required: every option below is tagged as either live-appliable (applying it while already playing takes effect immediately) or restart-required. Pressing Load Configuration applies every live-appliable change straight away; if the file also changes something restart-required, a dialog lists exactly which option(s) need it and offers:

    • Dismiss - keep playing with the live-appliable changes already applied; the restart-required option(s) stay as they were.
    • Restart Now - relaunches the app, which re-runs this same startup load and so applies every group in the file, including the restart-required ones.

    (Every option currently documented below is live-appliable, so this dialog won't actually appear yet - it's built in for options added in a future version that do need a restart.)

  • Unrecognized groups/keys are ignored (harmless if you're editing the file in advance for a future version). An invalid value for a specific option (see each option's possible values below) is logged and that one option is skipped - every other valid option in the file still applies. A file that isn't valid JSON at all is ignored entirely, with nothing applied.

  • questshock.config.json and prefs.txt are fully independent - loading or saving one never reads or writes the other.

Example

{
  "video_options": {
    "renderer": "opengl",
    "resolution": "1024x768",
    "initial_screen_width_m": 1.6,
    "initial_screen_distance_m": 2.0,
    "game_screen_width_m": 2.8,
    "game_screen_distance_m": 2.0,
    "screen_width_offset_m": 1.0,
    "screen_width_step_m": 0.6,
    "screen_distance_offset_m": 1.0,
    "screen_distance_step_m": 0.5
  },
  "general": {
    "language": "english"
  },
  "player_position": {
    "x": 0.0,
    "z": 0.0,
    "yaw_degrees": 0.0
  },
  "player_controls": {
    "move_forward": "left_thumbstick_up",
    "move_back": "left_thumbstick_down",
    "strafe_left": "left_thumbstick_left",
    "strafe_right": "left_thumbstick_right",
    "turn_left": "right_thumbstick_left",
    "turn_right": "right_thumbstick_right",
    "jump": "left_thumbstick_click",
    "mouse_click": "right_a",
    "mouse_double_click": "right_b",
    "stand": "right_thumbstick_up",
    "prone": "right_thumbstick_down",
    "map_view": "right_thumbstick_click",
    "fire_weapon": "right_trigger"
  }
}

Video Options (video_options)

renderer

Which renderer the game uses to draw the 3D view.

  • Type: enum
  • Possible values: "software", "opengl"
  • Restart required?: No (live-appliable)

resolution

The game's internal rendering resolution.

  • Type: enum
  • Possible values: "320x200", "320x400", "640x400", "640x480", "1024x768"
  • Restart required?: No (live-appliable)

initial_screen_width_m, initial_screen_distance_m, game_screen_width_m, game_screen_distance_m

The virtual screen's physical width and distance from the player (both in meters) - the game shows two independently configurable screen "profiles", switched automatically based on what's currently on screen:

  • Initial Screen (initial_screen_*) - the splash screen, intro cutscenes, and the game's own main menu.
  • Game Screen (game_screen_*) - actual gameplay (the 3D view, automap, cyberspace, etc.).

Switching happens automatically - no player action needed - the instant the game itself transitions between these (e.g. starting/loading a game, or returning to the menu).

  • Type: number (meters)
  • Possible values: the in-headset Video Options tab offers 5 presets each - widths 1.0, 1.6 (the original, pre-this-feature default), 2.2, 2.8, 3.4; distances 1.0, 1.5, 2.0 (the original, pre-this-feature default), 2.5, 3.0 - but a hand-edited value doesn't need to match a preset exactly (it's clamped to a sane range - width [0.2, 10.0], distance [0.5, 10.0] - rather than snapped or rejected; the Video Options tab just shows no button selected for an in-between value).
  • Restart required?: No (live-appliable)
  • Note: the screen is always flat - there's no curve option.

screen_width_offset_m, screen_width_step_m, screen_distance_offset_m, screen_distance_step_m

Reshapes the 5 preset buttons the Video Options tab offers for width and distance (above) - config-file-only, there's no in-headset control for these. Shared by both Initial Screen and Game Screen (both columns' buttons show the same 5 values per row) - this only changes what the buttons offer, not either profile's current value.

Preset button index (0-4) shows offset + step * index. For example, a distance offset of 0 and step of 2 shows buttons 0, 2, 4, 6, 8; the defaults below reproduce this feature's original launch presets exactly.

  • Type: number (meters)
  • Default values: width offset 1.0, step 0.6 (buttons 1.0, 1.6, 2.2, 2.8, 3.4); distance offset 1.0, step 0.5 (buttons 1.0, 1.5, 2.0, 2.5, 3.0).
  • Restart required?: No (live-appliable)
  • Note: not clamped - an unusual offset/step (e.g. negative step) is accepted as-is. A resulting preset value outside the current value's own clamp range (see above) still clamps normally when that preset button is actually pressed.

General (general)

language

The game's UI/text language - both the game's own resource strings and this project's own menu/keyboard chrome text (title bar, buttons, etc.).

  • Type: enum
  • Possible values: "english", "french", "german"
  • Restart required?: No (live-appliable) - applies the same way the game's own in-game options menu applies a language change (reloads the language-specific resource files and refreshes the inventory/MFD/side-icon UI immediately).
  • Note: French and German both need their own full resource pack (frnstrng.res/mfdfrn.res, gerstrng.res/mfdger.res under res/data/) - if any of those files aren't present, the switch is refused (logged, questshock.config.json and the current language are left unchanged) rather than leaving the game with no valid resource file open. See README.md section 3 ("Game assets") for where mfdfrn.res/ mfdger.res come from - the GOG Enhanced Edition installer alone doesn't include them.

Player Position (player_position)

Where the player is virtually standing/facing, independent of - and not reset by - starting a new game or loading a save (same independence from prefs.txt/savegames as every other group here). Set by moving around in-headset (menu open, left thumbstick snap-turns, right thumbstick smooth- moves) to line yourself up with the game quad ("virtual screen"). Height is never included here - it always comes from your real tracked headset height, not this file.

Startup fallback: only applied at startup if all three fields (x, z, yaw_degrees) are present - a config file with none of them (or none at all), or a partial/hand-edited one missing any of the three, falls back to an automatic recenter instead (see Reset Position below), using your real head pose at that moment. A complete saved position is applied as-is, with no such recenter.

Reset Position (Main Menu tab) is a live-only action, not a config value: it moves you to the centre in front of the game quad, based on where your head actually is right now. It resets only your position, not which way you're facing, so the screen does not rotate to follow your gaze - press it while looking off to one side and the screen stays off to the other side. The automatic recenter at startup (when there's no saved position) does face the screen towards where you're looking. Reset Position never reads or writes questshock.config.json.

x, z

Horizontal position, in meters, on the play space's floor plane.

  • Type: number
  • Restart required?: No (live-appliable)

yaw_degrees

Which way the player is virtually facing, in degrees.

  • Type: number
  • Restart required?: No (live-appliable)

Player Controls (player_controls)

Maps each of 57 in-game actions to a physical controller input: 9 movement/click actions, 2 free-view actions, 11 cyberspace actions, and 35 for the engine's own keyboard hotkeys and firing (posture, save/load, weapon select, MFD panels, etc. - see the hotkey table below). Some actions are bound by default (see the default-input columns); the rest are unbound and can be bound on demand. Assign them in the Controller Options menu tab: tap an action's binding button, then press the button or stick direction to use ("Press a button…"); tap it again, or wait 8 seconds, to cancel without changing it.

  • Type: one enum-string field per action (below)
  • Restart required?: No (live-appliable) - a rebind takes effect immediately, and persists across restarts once saved (Save Configuration, same as every other group here).
  • Not included here: opening/closing the VR menu itself is a fixed, non-remappable button (see "Menu button" below) - there's no menu_toggle field, and the Controller Options tab never lets you reassign it. This guarantees you can always reach this tab and press Reset to Defaults to recover from any bad rebind, no matter what else you've reassigned.
  • Reset to Defaults: restores the "Default input" column below for every action not overridden by a player_controls group in questshock.config.default.json - if that file defines one (it doesn't, out of the box), the values it specifies are applied on top of the table below instead, so you can customize what "reset" means without a rebuild. This is independent of your own questshock.config.json - resetting doesn't read or write that file, just the live bindings (Save Configuration afterward to persist them).

Actions

Action key Default input Effect
move_forward left_thumbstick_up Walk forward
move_back left_thumbstick_down Walk backward
strafe_left left_thumbstick_left Step sideways, left
strafe_right left_thumbstick_right Step sideways, right
turn_left right_thumbstick_left Turn left
turn_right right_thumbstick_right Turn right
jump left_thumbstick_click Jump
mouse_click right_a Click - operates the keyboard/game quad's cursor, whichever the pointing hand's ray is over
mouse_double_click right_b Double-click - game quad only (e.g. using an inventory item); has no effect over the menu or keyboard
free_left_thumbstick_view left_squeeze Hold to look around with the left thumbstick (see below)
free_right_thumbstick_view none Hold to look around with the right thumbstick (see below)

Movement actions bound to a thumbstick direction are suppressed while the VR menu is open, since the same physical stick already snap-turns/smooth- moves the player in that state (see player_position above) - this is the one case where a binding's effect depends on whether the menu is open, and it only ever affects the 8 thumbstick-direction inputs below.

Free view (free_left_thumbstick_view, free_right_thumbstick_view) is the engine's mouse look driven by a thumbstick. While the bound input is held, that stick looks around instead of doing its normal job: left/right turns the view, up/down looks up and down, in proportion to how far the stick is pushed. The stick's own direction bindings (move, strafe, turn, stand/prone) are suspended while it is held, and the cursor stays centred in the view. Release the input to return to normal. It has no effect while the VR menu is open.

Cyberspace actions

Cyberspace has its own set of movement actions, active only while the player is in cyberspace. While in cyberspace the normal movement actions (move_*, strafe_*, turn_*, jump, stand, crouch, prone) are inactive, and the cyberspace actions are inactive everywhere else. Each set has its own bindings, so one input can be bound in both (for example the left stick up is move_forward on foot and cyber_climb in cyberspace); the Controller Options tab only reports a conflict between actions that can be active at the same time. Free view, clicking and firing work in both.

Pitch and bank (cyber_climb, cyber_dive, cyber_bank_* and the four diagonals) are proportional when bound to a thumbstick direction: pushing the stick further turns faster, from 20% of full speed just past the trigger point to 60% at full deflection. Bound to a button they run at full speed. Thrust and roll are always full speed.

Action key Default input Keyboard equivalent Effect
cyber_thrust right_thumbstick_up s Thrust forward
cyber_climb left_thumbstick_up w Pitch up
cyber_dive left_thumbstick_down x Pitch down
cyber_bank_left left_thumbstick_left a Bank left
cyber_bank_right left_thumbstick_right d Bank right
cyber_roll_left right_thumbstick_left e Roll left
cyber_roll_right right_thumbstick_right q Roll right
cyber_climb_left none Keypad 7 Pitch up and bank left
cyber_climb_right none Keypad 9 Pitch up and bank right
cyber_dive_left none Keypad 1 Pitch down and bank left
cyber_dive_right none Keypad 3 Pitch down and bank right

mouse_click additionally behaves differently depending on the menu: while the VR menu is open, its "click" is always whichever hand's trigger is used to interact with the menu (so you can never lock yourself out of this settings screen by rebinding mouse_click elsewhere); once the menu is closed, both the on-screen keyboard (if it's still open) and the game quad use your actual mouse_click binding. The default (right_a) is a specific button on the right controller, so only the right hand's ray clicks; bind the trigger identifier (see below) for either hand to click.

Hotkey actions

The engine's own keyboard hotkeys (HotKeyLookup[] in Prefs.c) plus firing, as 35 bindable actions. stand (right_thumbstick_up), prone (right_thumbstick_down), map_view (right_thumbstick_click) and fire_weapon (right_trigger) are bound by default; the rest are unbound (none). Each fires once per press, like pressing the key on a real keyboard (there is no hold behavior).

Action key Keyboard equivalent Effect
stand t Stand up
crouch g Crouch
prone b Go prone
pause p Pause the game
full_view Ctrl+F Switch to the fullscreen view loop
normal_view Ctrl+D Switch to the normal gameplay view
map_view Ctrl+A Switch to the automap view
save_game Ctrl+S Save the game
load_game Ctrl+L Load the game
select_grenade Ctrl+' Select the next grenade type
arm_grenade Alt+' Arm the selected grenade
select_drug Ctrl+; Select the next drug/patch
use_drug Alt+; Use the selected drug/patch
toggle_online_help Ctrl+H Toggle the online help overlay
show_help Alt+O Show the help screen
toggle_music Ctrl+M Toggle music
quit Ctrl+Q Quit the game - opt-in only; nothing binds this by default
cycle_weapon_next Tab Ready the next weapon
cycle_weapon_prev Shift+Tab Ready the previous weapon
cycle_detail Ctrl+1 Cycle the graphics detail level
reload_weapon_primary Ctrl+Backspace Reload the current weapon's primary ammo
reload_weapon_secondary Alt+Backspace Reload the current weapon's secondary ammo
hud_color Alt+H Cycle the HUD color scheme
clear_fullscreen Backspace Close a fullscreen view (e.g. a datapad or email)
mfd_left_1-mfd_left_5 F1-F5 Set the left MFD panel to Weapon/Item/Map/Target/Info
mfd_right_1-mfd_right_5 F6-F10 Set the right MFD panel to Weapon/Item/Map/Target/Info
fire_weapon Enter Fire the readied weapon at the cursor (the engine's default fire key). Clicking in the 3D view with mouse_click also fires; this is a separate button for it

Not exposed (other HotKeyLookup[] entries):

  • keypad 0-keypad 9: a controller button per digit is not useful; use the on-screen keyboard.
  • toggle_freelook (F): the free-view actions cover mouse look.
  • The 4 cheat_* entries: debug cheats. They are on the menu's Cheats tab instead.
  • mac_help (disabled) and audiolog_cancel (its build flag is not defined): they are not real hotkeys in this build.
  • toggle_opengl (Ctrl+G): the Video Options tab's Renderer buttons already toggle the same setting.
  • The 10 numeric hardware-item hotkeys (bio scan, fullscreen, 360 view, lantern, shield, infrared, nav unit, data reader, booster, jumpjets): their registered keys never match a real keypress in the engine.

Raw input identifiers

Every raw physical input available to bind an action to, with its generic OpenXR interaction-profile component name and its Meta Quest Touch control name. Two symmetric identifiers (trigger, squeeze) are also accepted, but only for mouse_click/mouse_double_click: they mean "either hand's own trigger/grip, whichever hand is pointing". Every other identifier below refers to one specific control.

Identifier OpenXR component (Touch) Meta Quest control
left_trigger /user/hand/left/input/trigger/value Left Trigger
right_trigger /user/hand/right/input/trigger/value Right Trigger
left_squeeze /user/hand/left/input/squeeze/value Left Grip
right_squeeze /user/hand/right/input/squeeze/value Right Grip
left_x /user/hand/left/input/x/click X Button
left_y /user/hand/left/input/y/click Y Button
right_a /user/hand/right/input/a/click A Button
right_b /user/hand/right/input/b/click B Button
left_thumbstick_click /user/hand/left/input/thumbstick/click Left Thumbstick Click
right_thumbstick_click /user/hand/right/input/thumbstick/click Right Thumbstick Click
left_thumbstick_up/down/left/right /user/hand/left/input/thumbstick (digitized) Left Thumbstick, tilted
right_thumbstick_up/down/left/right /user/hand/right/input/thumbstick (digitized) Right Thumbstick, tilted
trigger either hand's trigger Either Trigger (mouse_click/mouse_double_click only)
squeeze either hand's grip Either Grip (mouse_click/mouse_double_click only)
none - Unbound - the action never fires

Menu button (left controller, Touch/Vive/generic alike) is not in this list - it always opens/closes the VR menu and can never be reassigned.

Other supported controllers: HTC Vive wands have no A/B/X/Y and no analog grip - left_x/left_y/right_a/right_b simply never fire on that profile, and left/right_squeeze are a plain click rather than analog. Their trackpad stands in for the thumbstick identifiers above (same digitization, just a trackpad instead of a stick). A bare-minimum OpenXR controller (khr/simple_controller, used as a fallback when neither Touch nor Vive bindings are suggested) only has left_trigger/ right_trigger (via its select/click component) and the menu button - every other identifier above simply never fires on it, which is why the Controller Options tab shows a note and hides irrelevant rows when this profile is active.

Controller profile files (controller_profiles/)

The Controller Options tab shows a labeled illustration of whichever controller is actually active, driven by res/controller_profiles/*.json (bundled with the app, copied to /sdcard/questshock/controller_profiles/ on every launch, refreshing the known bundled files without touching any extra ones you add there yourself). Each file:

{
  "name": "Meta Quest Touch",
  "interaction_profile": "/interaction_profiles/oculus/touch_controller",
  "image": "touch.png",
  "controls": [
    {"id": "right_b", "label": "B", "anchor_x": 0.76, "anchor_y": 0.22}
  ]
}
  • interaction_profile - the OpenXR interaction profile path this file describes; matched against whichever one is actually bound at runtime. Falls back to generic.json if nothing matches.
  • image - filename (same folder) of the controller illustration.
  • controls - one entry per labeled point on that illustration: id is a raw input identifier from the table above (thumbstick directions share one left_thumbstick/right_thumbstick entry, since they're the same physical stick); anchor_x/anchor_y are 0-1 fractions of the image for positioning that label's pointer line.

This is presentation data only - adding real support for a new controller still needs matching bindings in the app itself; a profile file with no corresponding code-level support just never gets matched.

Fatal startup error

If the OpenXR runtime rejects any of the three supported controller profiles' bindings at launch (an app bug, not something you did), a non-cancelable dialog explains this and exits - this should never happen in a normal build.