@tool extends Node #region configuration @export_file var user_settings_path:String = "usr://user_settings.json" @export_group("Acessability") @export var screen_reader_enabled:bool = false @export var rendering_disabled: bool = false @export var use_simplified_navigation:bool = false @export var show_navigation_buttons: bool = false @export_enum("disabled", "text", "cc") var subtitles: int = false @export var reduce_motion: bool = false @export var show_content_notes: bool = false @export var provide_summaries: bool = false @export var allow_skipping: bool = false @export_group("AudioSettings") @export var main_volume:float = 1: set(volume): main_volume = volume AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear_to_db(volume)) @export var sfx_muted:bool = false: set(mute): sfx_muted = mute AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(mute)) @export var sfx_volume:float = 1: set(volume): sfx_volume = volume AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(volume/2)) @export var music_muted:bool = false: set(mute): sfx_muted = mute AudioServer.set_bus_volume_db(AudioServer.get_bus_index("sfx"), linear_to_db(mute)) @export var music_volume:float = 1: set(volume): music_volume = volume AudioServer.set_bus_volume_db(AudioServer.get_bus_index("music"), linear_to_db(volume/2.5)) @export var speech_volume: float = 1: set(volume): speech_volume = volume AudioServer.set_bus_volume_db(AudioServer.get_bus_index("text"), linear_to_db(volume)) @export_group("Gameplay Settings") @export var input_sensitivity: float @export var inverty_y_axis: bool @export_enum("off", "top_left", "top_right", "bottom_left", "bottom_right") var stream_overlay_position: int var ssil_enable:bool = false: set(value): ssil_enable = value environment_settings_changed.emit() var sdfgi_enable:bool = false: set(value): sdfgi_enable = value environment_settings_changed.emit() var stage_list:Array = [] var focus_locked: bool = false signal environment_settings_changed signal theme_changed var current_main_theme:Theme = preload("res://logic-scenes/themes/easy-handwriting.theme"): set(theme): current_main_theme = theme emit_signal("theme_changed", theme) func load_user_settings(): if FileAccess.file_exists(user_settings_path): var file = FileAccess.open(user_settings_path, FileAccess.READ) var raw_json = FileAccess.get_file_as_string(user_settings_path) file.close() var parsed: Dictionary = JSON.parse_string(raw_json) for kategory in parsed.keys(): for key in parsed[kategory].keys(): set(key, parsed[key]) else: if OS.has_feature("macos"): var out: Array OS.execute("defaults", ["read", "/Users/$loggedInUser/Library/Preferences/com.apple.universalaccess.plist", "reduceMotion"], out) if out[0] == "reduce": reduce_motion = true func save_settings(): var out_dict = { "accessability": { "screen_reader_enabled": screen_reader_enabled, "rendering_disabled": rendering_disabled, "use_simplified_navigation": use_simplified_navigation, "show_navigation_buttons": show_navigation_buttons, "subtitles": subtitles, "reduce_motion:": reduce_motion, "show_content_notes:": show_content_notes, "provide_summaries:": provide_summaries, "allow_skipping:": allow_skipping }, "audio": { "main_volume": main_volume, "sfx_muted": sfx_muted, "sfx_volume": sfx_volume, "music_muted": music_muted, "music_volume": music_volume, "speech_volume": speech_volume }, "gameplay": { "input_sensitivity": input_sensitivity, "inverty_y_axis": inverty_y_axis, "stream_overlay_position": stream_overlay_position } } var file = FileAccess.open(user_settings_path, FileAccess.WRITE) file.store_string(JSON.stringify(out_dict)) file.close() #endregion func _ready(): for child in get_parent().get_children(): if "has_stage" in child: pass_stage_to(child) music_volume = music_volume #region focus handling (called staging to avoid name colisions) # Intented for use when an actor wants focus for itself, can reclaim focus, thus dropping the stack that focused. func take_stage(actor: Object, reclaim: bool = false) -> bool: if focus_locked: return false if reclaim: stage_list.front().has_stage = false if stage_list.has(actor): while stage_list.pop_front() != actor: break actor.has_stage = true stage_list.push_front(actor) return actor.has_stage push_warning(actor, " wanted to reclaim focus, but was not on list.") return pass_stage_to(actor) # Element no longer wants focus, if Element itself is also dropped, this option can be chosen aswell. func leave_stage(actor:Object, dropObject: bool = false) -> bool: if get_tree().paused: push_error(actor, " wanted to drop focus while tree is paused.") if not dropObject: actor.has_stage = false focus_locked = false stage_list.erase(actor) if stage_list != []: stage_list.front().has_stage = true else: get_tree().quit() return false func get_current_actor(): return stage_list.front() # Used to put a new target on top of the Focus Stack. func pass_stage_to(target:Object, force = false, lock_focus = true) -> bool: if "pass_to_actor" in target: pass_stage_to(target.pass_to_actor) if (focus_locked or get_tree().paused) and not force: push_error(target, " requested focus while it was locked or tree is paused.") elif !is_instance_valid(target): push_error("Focus instance not valid") elif !"has_stage" in target: push_error(target, " has no has focus method.") else: if stage_list.size() > 0: if stage_list.front() == target: push_warning(target, " is already target. Abort passing focus.") return false if not stage_list.size() == 0: stage_list.front().has_stage = false target.has_stage = true if target.has_stage: stage_list.push_front(target) assert(stage_list.size() < 100) return true return false # Currently focused element loses focus, but remains in stack. func free_focus(): if focus_locked: return false if stage_list.size() > 0: stage_list.front().has_stage = false func transition_stage_to(thief: Object, lock_focus = false): focus_locked = lock_focus if stage_list.size() > 0: if stage_list.front().has_stage: stage_list.pop_front().has_stage = false return pass_stage_to(thief, true) func queue_for_stage(target: Object, index: int = 1): stage_list.insert(index, target) #endregion #region play state enum rooms { MENU, DRAVEN, YOUTH, TRANSITION_VOL, VOLUNTARY, TRANSITION_UNI, UNIVERSITY, ENDING } var current_room = rooms.MENU