Initial project sync
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
extends Node
|
||||
|
||||
## Persistent player-facing settings (AnchorV1.0 §9.5 difficulty windows,
|
||||
## §20.5 music/sfx volumes). Sole writer of the Music / SFX buses and of the
|
||||
## RhythmManager judgement windows; every menu goes through this node.
|
||||
|
||||
signal difficulty_changed(difficulty: StringName)
|
||||
signal volumes_changed(music_volume: int, sfx_volume: int)
|
||||
|
||||
const SETTINGS_PATH := "user://settings.cfg"
|
||||
|
||||
const MUSIC_BUS_NAME := "Music"
|
||||
const SFX_BUS_NAME := "SFX"
|
||||
|
||||
const DIFFICULTY_ORDER: Array[StringName] = [&"easy", &"normal", &"hard"]
|
||||
|
||||
## Windows in seconds per AnchorV1.0 §21.8.
|
||||
const DIFFICULTY_WINDOWS := {
|
||||
&"easy": Vector3(0.090, 0.160, 0.260),
|
||||
&"normal": Vector3(0.060, 0.115, 0.215),
|
||||
&"hard": Vector3(0.030, 0.070, 0.170),
|
||||
}
|
||||
|
||||
## 小怪血量难度倍率:普通 3 倍、困难 5 倍,简单保持基准值。
|
||||
const DIFFICULTY_MINION_HEALTH_MULTIPLIER := {
|
||||
&"easy": 1.0,
|
||||
&"normal": 3.0,
|
||||
&"hard": 5.0,
|
||||
}
|
||||
|
||||
const DEFAULT_DIFFICULTY: StringName = &"normal"
|
||||
const DEFAULT_MUSIC_VOLUME := 85
|
||||
const DEFAULT_SFX_VOLUME := 80
|
||||
|
||||
var difficulty: StringName = DEFAULT_DIFFICULTY
|
||||
var music_volume := DEFAULT_MUSIC_VOLUME
|
||||
var sfx_volume := DEFAULT_SFX_VOLUME
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_ensure_audio_buses()
|
||||
_load_settings()
|
||||
apply_difficulty_windows()
|
||||
_apply_bus_volumes()
|
||||
|
||||
|
||||
func set_difficulty(next: StringName) -> void:
|
||||
if not DIFFICULTY_WINDOWS.has(next):
|
||||
return
|
||||
if next == difficulty:
|
||||
return
|
||||
difficulty = next
|
||||
apply_difficulty_windows()
|
||||
_save_settings()
|
||||
difficulty_changed.emit(difficulty)
|
||||
|
||||
|
||||
func set_music_volume(value: int) -> void:
|
||||
music_volume = clampi(value, 0, 100)
|
||||
_apply_bus_volumes()
|
||||
_save_settings()
|
||||
volumes_changed.emit(music_volume, sfx_volume)
|
||||
|
||||
|
||||
func set_sfx_volume(value: int) -> void:
|
||||
sfx_volume = clampi(value, 0, 100)
|
||||
_apply_bus_volumes()
|
||||
_save_settings()
|
||||
volumes_changed.emit(music_volume, sfx_volume)
|
||||
|
||||
|
||||
func difficulty_windows(for_difficulty: StringName = &"") -> Vector3:
|
||||
var key := for_difficulty if DIFFICULTY_WINDOWS.has(for_difficulty) else difficulty
|
||||
return DIFFICULTY_WINDOWS.get(key, DIFFICULTY_WINDOWS[DEFAULT_DIFFICULTY])
|
||||
|
||||
|
||||
func minion_health_multiplier(for_difficulty: StringName = &"") -> float:
|
||||
var key := for_difficulty if DIFFICULTY_MINION_HEALTH_MULTIPLIER.has(for_difficulty) else difficulty
|
||||
return float(DIFFICULTY_MINION_HEALTH_MULTIPLIER.get(key, 1.0))
|
||||
|
||||
|
||||
func difficulty_display_name(value: StringName = &"") -> String:
|
||||
match (value if not value.is_empty() else difficulty):
|
||||
&"easy":
|
||||
return "简单"
|
||||
&"hard":
|
||||
return "困难"
|
||||
return "普通"
|
||||
|
||||
|
||||
func apply_difficulty_windows() -> void:
|
||||
var rhythm := _rhythm_manager_or_null()
|
||||
if rhythm == null or not rhythm.has_method("apply_judgement_windows"):
|
||||
return
|
||||
var windows := difficulty_windows()
|
||||
rhythm.call("apply_judgement_windows", windows.x, windows.y, windows.z)
|
||||
|
||||
|
||||
func _ensure_audio_buses() -> void:
|
||||
for bus_name: String in [MUSIC_BUS_NAME, SFX_BUS_NAME]:
|
||||
if AudioServer.get_bus_index(bus_name) != -1:
|
||||
continue
|
||||
AudioServer.add_bus()
|
||||
var bus_index := AudioServer.bus_count - 1
|
||||
AudioServer.set_bus_name(bus_index, bus_name)
|
||||
AudioServer.set_bus_send(bus_index, "Master")
|
||||
|
||||
|
||||
func _apply_bus_volumes() -> void:
|
||||
_set_bus_volume(MUSIC_BUS_NAME, music_volume)
|
||||
_set_bus_volume(SFX_BUS_NAME, sfx_volume)
|
||||
|
||||
|
||||
func _set_bus_volume(bus_name: String, value: int) -> void:
|
||||
var bus_index := AudioServer.get_bus_index(bus_name)
|
||||
if bus_index == -1:
|
||||
return
|
||||
var linear := clampf(float(value) / 100.0, 0.0, 1.0)
|
||||
AudioServer.set_bus_mute(bus_index, linear <= 0.0)
|
||||
AudioServer.set_bus_volume_db(bus_index, linear_to_db(maxf(0.0001, linear)))
|
||||
|
||||
|
||||
func _load_settings() -> void:
|
||||
var config := ConfigFile.new()
|
||||
if config.load(SETTINGS_PATH) != OK:
|
||||
return
|
||||
var stored_difficulty := StringName(str(config.get_value("gameplay", "difficulty", str(DEFAULT_DIFFICULTY))))
|
||||
if DIFFICULTY_WINDOWS.has(stored_difficulty):
|
||||
difficulty = stored_difficulty
|
||||
music_volume = clampi(int(config.get_value("audio", "music_volume", DEFAULT_MUSIC_VOLUME)), 0, 100)
|
||||
sfx_volume = clampi(int(config.get_value("audio", "sfx_volume", DEFAULT_SFX_VOLUME)), 0, 100)
|
||||
|
||||
|
||||
func _save_settings() -> void:
|
||||
var config := ConfigFile.new()
|
||||
config.set_value("gameplay", "difficulty", str(difficulty))
|
||||
config.set_value("audio", "music_volume", music_volume)
|
||||
config.set_value("audio", "sfx_volume", sfx_volume)
|
||||
config.save(SETTINGS_PATH)
|
||||
|
||||
|
||||
func _rhythm_manager_or_null() -> Node:
|
||||
if not is_inside_tree():
|
||||
return null
|
||||
return get_tree().root.get_node_or_null("RhythmManager")
|
||||
Reference in New Issue
Block a user