farming-simulator/scripts/util/game_state.gd
Dmitrii Bykov f8040fb399
Some checks failed
Godot CI / Import + smoke test (push) Failing after 13s
полная мета-структура игры: pause + settings + save/load
PAUSE-МЕНЮ (autoload PauseMenu CanvasLayer):
- ESC во время геймплея открывает оверлей с тёмным фоном
  и центральной панелью в Шиндовс-стиле.
- 4 кнопки: Продолжить / Настройки / В главное меню /
  Сохранить и выйти.
- process_mode = ALWAYS чтобы работать при tree.paused = true.
- При закрытии меню в 3D-сценах автоматически возвращает
  Input.mouse_mode = CAPTURED.
- В главном меню pause не активируется (_can_pause false).
- set_input_as_handled блокирует FPV-контроллер от перехвата
  ESC одновременно.

НАСТРОЙКИ (под-панель в Pause-меню):
- Слайдер «Чувствительность мыши» 0.001-0.010 (по умолчанию 0.0025).
- Слайдер «Громкость» 0-100% — связан с AudioServer Bus 0 Master
  через linear_to_db (audio ассетов пока нет, но шкала рабочая).
- Кнопки Назад / Применить. Применить пишет в user://settings.json.
- settings.json грузится автоматически при старте GameState._ready.

SAVE/LOAD:
- GameState.save_game(scene_path) → user://save01.json в JSON
  с player_name, прогрессом актов 1-2, ачивками, текущей сценой.
- GameState.load_game() читает и восстанавливает state, заполняет
  last_scene_path для перехода.
- has_save() / delete_save() помощники.
- Кнопка «Продолжить» в главном меню активна только если есть
  save и грузит игрока в сохранённую сцену.
- Кнопка «Новая игра» удаляет старый save и сбрасывает state.

AUTO-SAVE:
- При закрытии окна через X (NOTIFICATION_WM_CLOSE_REQUEST) —
  save_game перед quit.
- Кнопка Q в FPV-контроллере — save+quit.
- Кнопка «Сохранить и выйти» в pause-меню — save+quit.
- Кнопка «В главное меню» — save перед переходом.

FPV-КОНТРОЛЛЕР:
- mouse_sensitivity больше не локальное @export, читается из
  GameState.mouse_sensitivity каждый mouse_motion event
  (применяется живьём после Apply).
- _input → _unhandled_input чтобы PauseMenu мог перехватить ESC
  через set_input_as_handled.
- Убран ESC-toggle захвата мыши (теперь это делает PauseMenu).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 18:28:58 +03:00

165 lines
4.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
# Имя игрока (вводится в Акте 2)
var player_name: String = ""
# Прогресс Акта 1
var act1_gift_opened: bool = false
var act1_note_read: bool = false
var act1_father_left: bool = false
var act1_drive_taken: bool = false
# Кухня (опц. визит)
var wifi_password_found: bool = false
# Прогресс Акта 2 (установка Шиндовс)
var act2_license_read: bool = false
var act2_password_set: bool = false
var act2_timezone: String = "UTC+3 (С., страна Р)"
# Дата/время игровое
var game_date: String = "21 АВГ 2020 · ПТ"
var game_time: String = "10:30"
# Ачивки
var achievements: Array = []
# Сохранения
var last_scene_path: String = ""
# Настройки
var mouse_sensitivity: float = 0.0025
var master_volume: float = 0.5
const SAVE_PATH: String = "user://save01.json"
const SETTINGS_PATH: String = "user://settings.json"
func _ready() -> void:
load_settings()
func _notification(what: int) -> void:
if what == NOTIFICATION_WM_CLOSE_REQUEST:
var scene_path: String = ""
if get_tree() != null and get_tree().current_scene != null:
scene_path = get_tree().current_scene.scene_file_path
save_game(scene_path)
get_tree().quit()
func unlock_achievement(ach: String) -> void:
if achievements.has(ach):
return
achievements.append(ach)
print("[ACHIEVEMENT] %s" % ach)
func reset() -> void:
player_name = ""
act1_gift_opened = false
act1_note_read = false
act1_father_left = false
act1_drive_taken = false
wifi_password_found = false
act2_license_read = false
act2_password_set = false
achievements.clear()
game_date = "21 АВГ 2020 · ПТ"
game_time = "10:30"
# === SETTINGS ===
func save_settings() -> void:
var data: Dictionary = {
"mouse_sensitivity": mouse_sensitivity,
"master_volume": master_volume,
}
var file: FileAccess = FileAccess.open(SETTINGS_PATH, FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(data))
file.close()
func load_settings() -> void:
if not FileAccess.file_exists(SETTINGS_PATH):
return
var file: FileAccess = FileAccess.open(SETTINGS_PATH, FileAccess.READ)
if not file:
return
var raw: String = file.get_as_text()
file.close()
var data: Variant = JSON.parse_string(raw)
if data == null or not data is Dictionary:
return
mouse_sensitivity = data.get("mouse_sensitivity", 0.0025)
master_volume = data.get("master_volume", 0.5)
_apply_audio_volume()
func _apply_audio_volume() -> void:
# Bus 0 = Master
var db: float = linear_to_db(master_volume) if master_volume > 0.0 else -80.0
AudioServer.set_bus_volume_db(0, db)
# === SAVE / LOAD GAME ===
func save_game(current_scene_path: String = "") -> void:
var data: Dictionary = {
"player_name": player_name,
"act1_gift_opened": act1_gift_opened,
"act1_note_read": act1_note_read,
"act1_father_left": act1_father_left,
"act1_drive_taken": act1_drive_taken,
"wifi_password_found": wifi_password_found,
"act2_license_read": act2_license_read,
"act2_password_set": act2_password_set,
"act2_timezone": act2_timezone,
"game_date": game_date,
"game_time": game_time,
"achievements": achievements,
"scene_path": current_scene_path,
}
var file: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(data, " "))
file.close()
print("[SAVE] %s" % SAVE_PATH)
func load_game() -> bool:
if not FileAccess.file_exists(SAVE_PATH):
return false
var file: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.READ)
if not file:
return false
var raw: String = file.get_as_text()
file.close()
var data: Variant = JSON.parse_string(raw)
if data == null or not data is Dictionary:
return false
player_name = data.get("player_name", "")
act1_gift_opened = data.get("act1_gift_opened", false)
act1_note_read = data.get("act1_note_read", false)
act1_father_left = data.get("act1_father_left", false)
act1_drive_taken = data.get("act1_drive_taken", false)
wifi_password_found = data.get("wifi_password_found", false)
act2_license_read = data.get("act2_license_read", false)
act2_password_set = data.get("act2_password_set", false)
act2_timezone = data.get("act2_timezone", "UTC+3 (С., страна Р)")
game_date = data.get("game_date", "21 АВГ 2020 · ПТ")
game_time = data.get("game_time", "10:30")
achievements = data.get("achievements", [])
last_scene_path = data.get("scene_path", "")
return true
func has_save() -> bool:
return FileAccess.file_exists(SAVE_PATH)
func delete_save() -> void:
if FileAccess.file_exists(SAVE_PATH):
DirAccess.remove_absolute(ProjectSettings.globalize_path(SAVE_PATH))