Files
whale-town-front-v2/_Core/managers/NotificationSoundManager.gd
2026-07-20 21:07:27 +08:00

152 lines
5.2 KiB
GDScript

extends Node
# ============================================================================
# NotificationSoundManager.gd - 提醒音效管理器
# ============================================================================
# 监听聊天和好友事件,播放项目内正式 UI 提醒音效资源。
# ============================================================================
const MIN_PLAY_INTERVAL_SEC: float = 0.18
const WORLD_NOTIFICATION_PATH: String = "res://assets/audio/ui/notify_world.wav"
const PRIVATE_NOTIFICATION_PATH: String = "res://assets/audio/ui/notify_private.wav"
const FRIEND_NOTIFICATION_PATH: String = "res://assets/audio/ui/notify_friend.wav"
var _player: AudioStreamPlayer
var _worldStream: AudioStreamWAV
var _privateStream: AudioStreamWAV
var _friendStream: AudioStreamWAV
var _lastPlayedAt: float = 0.0
var _seenFriendRequestKeys: Dictionary = {}
func _ready() -> void:
_build_player()
_load_streams()
_subscribe_to_events()
func _exit_tree() -> void:
var eventSystem := _get_event_system()
if eventSystem != null:
eventSystem.call("disconnect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
eventSystem.call("disconnect_event", EventNames.CHAT_FRIENDS_UPDATED, _on_friends_updated, self)
eventSystem.call("disconnect_event", EventNames.CHAT_ERROR_OCCURRED, _on_chat_error, self)
func play_notification(kind: String = "world") -> void:
if _settings_bool("mute_ui_sfx", false):
return
var now := Time.get_unix_time_from_system()
if now - _lastPlayedAt < MIN_PLAY_INTERVAL_SEC:
return
_lastPlayedAt = now
if not is_instance_valid(_player):
return
var stream := _stream_for_kind(kind)
if stream == null:
return
_player.stream = stream
_player.play()
func should_play_message_notification(data: Dictionary) -> bool:
if bool(data.get("is_self", false)):
return false
var scope := str(data.get("scope", "")).strip_edges().to_lower()
var isPrivate := bool(data.get("is_private", false)) or scope == "private"
if isPrivate:
return _settings_bool("private_notifications", true)
return _settings_bool("world_notifications", true)
func get_notification_asset_path(kind: String = "world") -> String:
match kind:
"private":
return PRIVATE_NOTIFICATION_PATH
"friend":
return FRIEND_NOTIFICATION_PATH
_:
return WORLD_NOTIFICATION_PATH
func _build_player() -> void:
_player = AudioStreamPlayer.new()
_player.name = "NotificationAudioPlayer"
_player.bus = "SFX"
add_child(_player)
func _load_streams() -> void:
_worldStream = _load_wav_stream(WORLD_NOTIFICATION_PATH)
_privateStream = _load_wav_stream(PRIVATE_NOTIFICATION_PATH)
_friendStream = _load_wav_stream(FRIEND_NOTIFICATION_PATH)
func _load_wav_stream(path: String) -> AudioStreamWAV:
var resource := load(path)
if not (resource is AudioStreamWAV):
push_warning("NotificationSoundManager: 提醒音效加载失败: %s" % path)
return null
return resource as AudioStreamWAV
func _stream_for_kind(kind: String) -> AudioStream:
match kind:
"private":
return _privateStream
"friend":
return _friendStream
_:
return _worldStream
func _subscribe_to_events() -> void:
var eventSystem := _get_event_system()
if eventSystem == null:
push_warning("NotificationSoundManager: EventSystem autoload is not available.")
return
eventSystem.call("connect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
eventSystem.call("connect_event", EventNames.CHAT_FRIENDS_UPDATED, _on_friends_updated, self)
eventSystem.call("connect_event", EventNames.CHAT_ERROR_OCCURRED, _on_chat_error, self)
func _on_chat_message_received(data: Dictionary) -> void:
if not should_play_message_notification(data):
return
var scope := str(data.get("scope", "")).strip_edges().to_lower()
var isPrivate := bool(data.get("is_private", false)) or scope == "private"
play_notification("private" if isPrivate else "world")
func _on_friends_updated(data: Dictionary) -> void:
if not _settings_bool("friend_request_notifications", true):
return
var requestsVariant: Variant = data.get("requests", [])
if not (requestsVariant is Array):
return
var shouldPlay := false
for requestVariant in requestsVariant:
if not (requestVariant is Dictionary):
continue
var key := _friend_request_key(requestVariant as Dictionary)
if key.is_empty() or _seenFriendRequestKeys.has(key):
continue
_seenFriendRequestKeys[key] = true
shouldPlay = true
if shouldPlay:
play_notification("friend")
func _on_chat_error(data: Dictionary) -> void:
var errorCode := str(data.get("error_code", ""))
if errorCode == "FRIEND_REQUEST_SENT" and _settings_bool("friend_request_notifications", true):
play_notification("friend")
func _friend_request_key(request: Dictionary) -> String:
var userId := str(request.get("user_id", request.get("userId", ""))).strip_edges()
if not userId.is_empty():
return userId
return str(request.get("username", "")).strip_edges()
func _settings_bool(key: String, defaultValue: bool) -> bool:
var settingsManager := get_node_or_null("/root/SettingsManager")
if settingsManager != null and settingsManager.has_method("get_bool"):
return bool(settingsManager.call("get_bool", key))
return defaultValue
func _get_event_system() -> Node:
return get_node_or_null("/root/EventSystem")