初次提交

This commit is contained in:
2026-07-20 10:56:52 +08:00
commit 7bcc0026e0
462 changed files with 50191 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
## SettingsManager — 全局设置 + 难度(Autoload: SettingsManager
## 权威来源:development_plan.md S6 P1(难度三档持久化)、certification_checklist.md ST-03(清除本地数据)
## 持久化 user://save_data.json{ difficulty:int, locale:String, master_volume:float }
extends Node
const SAVE_PATH: String = "user://save_data.json"
enum Difficulty { BEGINNER = 0, STANDARD = 1, CHALLENGE = 2 }
var difficulty: int = Difficulty.STANDARD
var locale: String = "zh_CN"
var master_volume: float = 1.0
var ftue_done: bool = false # 首次启动难度引导是否已完成(FTUE)
func _ready() -> void:
_load()
_load_balance()
_apply_audio()
if Locale:
Locale.set_locale(locale)
## 是否需要首启难度引导(FTUE
func needs_ftue() -> bool:
return not ftue_done
## 完成 FTUE:设定难度并持久化标志
func complete_ftue(chosen_difficulty: int) -> void:
difficulty = clampi(chosen_difficulty, 0, 2)
ftue_done = true
_save()
## 难度乘子 + Boss 血量:纯数据驱动,唯一来源 data/balance.json(游戏设计器「平衡」面板维护)
## get() 的默认仅为防崩溃,非内容副本
var _MULTS: Dictionary = {} # key → [初学者, 标准, 挑战]
var _BOSS_HP: Dictionary = {} # 原型→基础血量
const BALANCE_JSON: String = "res://data/balance.json"
func _mult(key: String) -> float:
var arr: Array = _MULTS.get(key, [1.0, 1.0, 1.0])
return float(arr[clampi(difficulty, 0, arr.size() - 1)])
func enemy_hp_mult() -> float: return _mult("enemy_hp")
func player_dmg_taken_mult() -> float: return _mult("player_dmg")
func wave_count_mult() -> float: return _mult("wave_count")
func boss_hp_mult() -> float: return _mult("boss_hp")
## Boss 基础血量(WaveManager._spawn_boss 查询;balance.json 可调)
func get_boss_base_hp(boss_type: int) -> float:
return float(_BOSS_HP.get(str(boss_type), 500.0))
## 数据驱动:加载 balance.json(难度乘子 + Boss 血量)
func _load_balance() -> void:
if not FileAccess.file_exists(BALANCE_JSON):
push_error("SettingsManager: 缺失 res://data/balance.json(难度乘子/Boss血量)")
return
var data = JSON.parse_string(FileAccess.get_file_as_string(BALANCE_JSON))
if not (data is Dictionary):
push_error("SettingsManager: balance.json 格式错误")
return
if data.has("difficulty_mults") and data["difficulty_mults"] is Dictionary:
for k in data["difficulty_mults"]:
_MULTS[k] = data["difficulty_mults"][k]
if data.has("boss_hp") and data["boss_hp"] is Dictionary:
_BOSS_HP = data["boss_hp"]
func difficulty_name() -> String:
return ["DIFF_BEGINNER", "DIFF_STANDARD", "DIFF_CHALLENGE"][difficulty]
# ── 设置变更(即时保存)──────────────────────────────────────
func set_difficulty(d: int) -> void:
difficulty = clampi(d, 0, 2)
_save()
func cycle_difficulty() -> int:
set_difficulty((difficulty + 1) % 3)
return difficulty
func set_locale(loc: String) -> void:
locale = loc
if Locale:
Locale.set_locale(loc)
_save()
func set_master_volume(v: float) -> void:
master_volume = clampf(v, 0.0, 1.0)
_apply_audio()
_save()
func _apply_audio() -> void:
var bus: int = AudioServer.get_bus_index("Master")
if bus >= 0:
AudioServer.set_bus_volume_db(bus, linear_to_db(maxf(0.0001, master_volume)))
# ── ST-03:清除所有本地数据 ─────────────────────────────────
func clear_all_local_data() -> void:
var files: Array = [
"user://crash_log.txt", "user://crash_log_prev.txt",
"user://run_a.json", "user://run_b.json",
"user://endless_records.json", "user://save_data.json",
]
for path in files:
if FileAccess.file_exists(path):
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
_remove_dir_recursive("user://runs")
# 重置内存态
difficulty = Difficulty.STANDARD
master_volume = 1.0
ftue_done = false # 清数据后重新触发首启难度引导
# 用路径引用避免编译时 autoload 依赖(打破 SettingsManager→ProfileManager→WaveManager→SettingsManager 循环)
var pm: Node = get_node_or_null("/root/ProfileManager")
if pm and pm.has_method("clear_run"):
pm.clear_run()
_apply_audio()
func _remove_dir_recursive(path: String) -> void:
var d := DirAccess.open(path)
if d == null:
return
d.list_dir_begin()
var fn := d.get_next()
while fn != "":
if not d.current_is_dir():
DirAccess.remove_absolute(ProjectSettings.globalize_path(path + "/" + fn))
fn = d.get_next()
d.list_dir_end()
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
# ── 持久化 ──────────────────────────────────────────────────
func _save() -> void:
var data: Dictionary = {
"difficulty": difficulty, "locale": locale,
"master_volume": master_volume, "ftue_done": ftue_done,
}
var f := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if f:
f.store_string(JSON.stringify(data))
f.close()
func _load() -> void:
if not FileAccess.file_exists(SAVE_PATH):
return
var parsed = JSON.parse_string(FileAccess.get_file_as_string(SAVE_PATH))
if not (parsed is Dictionary):
return
difficulty = clampi(int(parsed.get("difficulty", 1)), 0, 2)
locale = String(parsed.get("locale", "zh_CN"))
master_volume = clampf(float(parsed.get("master_volume", 1.0)), 0.0, 1.0)
ftue_done = bool(parsed.get("ftue_done", false))