Files
security-game/src/autoload/save.gd
T
zwitschi 883626a067
Full build / Import + headless tests (push) Successful in 42s
Full build / Full desktop build + optional release (push) Canceled after 4m58s
Ship the 0.9.0 production-quality milestone
Add mixer/accessibility settings, caption/reduce-motion hooks, audio volume
clamping, a 24-NPC performance smoke and shortened coat/dress stride for hem
clipping. Record the explicit Godot 4.3-CI/4.7-local matrix and the 0.9
production scope. Headless 250/250, Python 94/94, 24-NPC budget smoke passes.
2026-11-01 21:46:42 +01:00

105 lines
3.3 KiB
GDScript

extends Node
## Autoload for local persistence (JSON in user://). Wrapped in an autoload so UI
## nodes never touch the filesystem directly.
const SHIFT_RESULTS_FILE := "shift_results.json"
const SETTINGS_FILE := "settings.json"
const CAMPAIGN_FILE := "campaign.json"
const SHIFT_STATE_FILE := "shift_state.json"
func _load_json(file_name: String) -> Variant:
var path := "user://" + file_name
if not FileAccess.file_exists(path):
return null
var text := FileAccess.get_file_as_string(path)
if text.is_empty():
return null
var parsed = JSON.parse_string(text)
return parsed
func _save_json(file_name: String, data: Variant) -> Error:
var path := "user://" + file_name
var temp := path + ".tmp"
var text := JSON.stringify(data, "\t")
var f := FileAccess.open(temp, FileAccess.WRITE)
if f == null:
return FileAccess.get_open_error()
f.store_string(text)
f.close()
# Replace the target only after the complete JSON has been written, so a
# crash cannot leave a partial save in place.
return DirAccess.rename_absolute(ProjectSettings.globalize_path(temp),
ProjectSettings.globalize_path(path))
## Load previous shift results. Returns Array of Dictionaries or [].
func load_shift_results() -> Array:
var data = _load_json(SHIFT_RESULTS_FILE)
if data is Array:
return data
return []
## Append one shift result and save. Returns Error.
func save_shift_result(result: Dictionary) -> Error:
var results := load_shift_results()
results.append(result)
return _save_json(SHIFT_RESULTS_FILE, results)
## Campaign/session progression (separate from settings and shift history).
func load_campaign() -> Dictionary:
var data = _load_json(CAMPAIGN_FILE)
return data if data is Dictionary else {}
func save_campaign(data: Dictionary) -> Error:
return _save_json(CAMPAIGN_FILE, data)
func reset_campaign() -> Error:
return _save_json(CAMPAIGN_FILE, {})
## Mid-shift checkpoint. Kept separate so a completed shift history is never
## overwritten by a resumable snapshot.
func load_shift_state() -> Dictionary:
var data = _load_json(SHIFT_STATE_FILE)
return data if data is Dictionary else {}
func save_shift_state(data: Dictionary) -> Error:
return _save_json(SHIFT_STATE_FILE, data)
func clear_shift_state() -> Error:
return _save_json(SHIFT_STATE_FILE, {})
## Defaults for player settings. Kept in code so a missing or partial
## settings.json still produces a usable config.
const DEFAULT_SETTINGS := {
"look_sensitivity": 0.0022,
"hud_font_scale": 1.0,
"invert_y": false,
"head_bob": true,
"input_bindings": {},
"ai_difficulty": "normal",
"tutorial_completed": false,
"tutorial_skipped": false,
"master_volume": 1.0,
"sfx_volume": 1.0,
"ambience_volume": 1.0,
"reduce_motion": false,
"captions_enabled": true,
}
## Load player settings, merged over the defaults. Returns a Dictionary.
func load_settings() -> Dictionary:
var merged: Dictionary = DEFAULT_SETTINGS.duplicate(true)
var data = _load_json(SETTINGS_FILE)
if data is Dictionary:
for key in data.keys():
if merged.has(key):
merged[key] = data[key]
return merged
## Persist player settings. Returns Error.
func save_settings(settings: Dictionary) -> Error:
var merged: Dictionary = DEFAULT_SETTINGS.duplicate(true)
for key in settings.keys():
if merged.has(key):
merged[key] = settings[key]
return _save_json(SETTINGS_FILE, merged)