# 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 ```json { "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 } } ``` ## 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 separate, live-only action, not a config value: it recenters using your *current* real head pose, so the game quad ends up directly ahead of wherever you're actually standing/ facing right now - not a plain "go back to `(0, 0, 0)`" (which would only undo thumbstick movement, not any physical drift/rotation since the session started). It's unrelated to `questshock.config.json` - it never reads or writes this file. ### `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)