53 Commits

Author SHA1 Message Date
01f1b52086 ft: switch to click movement 2026-03-07 14:35:07 +01:00
a59255d9d6 (wip) settings menu almost works, just need to attach it to the button... 2026-03-07 14:34:24 +01:00
3c7e1a612d This should probably be part of last commit ...oops 2026-03-07 14:17:16 +01:00
df8c8c6c3b Added menu settings add-on 2026-03-07 14:16:44 +01:00
8b7a8e014f Merge remote-tracking branch 'refs/remotes/origin/stage/molecular' into stage/molecular 2026-03-07 14:06:30 +01:00
f78ad93191 cpp extension updated, fixing required not ... compile errors 2026-03-07 14:00:54 +01:00
6df9442b49 Updated cpp extension 2026-03-07 13:54:16 +01:00
1fbc2f6637 ft: made sprite behaviour consistent across entities 2026-03-07 13:12:18 +01:00
da15fb77a2 cleanup: comments 2026-03-07 12:22:17 +01:00
9d9ed22ea8 fx: wrapping 2026-03-01 17:41:15 +01:00
0e447f80ec Merge remote-tracking branch 'refs/remotes/origin/stage/molecular' into stage/molecular 2026-03-01 17:23:19 +01:00
fd636e2d9a Texture tail is now wrapping nicely but nothing else is... 2026-03-01 17:22:43 +01:00
27db6d6d27 fx: food wrappign sprites 2026-03-01 16:46:33 +01:00
a11805ad30 fx: readded merged function 2026-03-01 15:59:49 +01:00
241dfb3073 fx: sprite playing behaviour 2026-03-01 15:43:13 +01:00
9dd1d45698 Merge remote-tracking branch 'refs/remotes/origin/stage/molecular' into stage/molecular 2026-03-01 15:41:03 +01:00
6d8e8e3734 Added respawn logic ... and with it also a complete rehaul of how managing scene instances work to make it work 2026-03-01 15:40:56 +01:00
2b46fd18f7 fx: prey-predator interactions 2026-03-01 14:55:25 +01:00
f83b290f5c ft: wrapping manager overhaul 2026-03-01 14:11:48 +01:00
51ee5029e9 Fix: Double predator hitbox (can attack now after pixel rescale) 2026-03-01 12:20:21 +01:00
6b8af22d7a Hid debug cpp label 2026-03-01 12:15:17 +01:00
ac6549579e Fix: Molecular player sprite set explicitely now, should not be affected by editor anymore 2026-03-01 12:11:49 +01:00
99de1fc113 Fix: Molecular player starting on wrong sprite frame 2026-03-01 12:07:22 +01:00
8f1b301ece Fix: Added proper anchor use for main menu 2026-03-01 11:55:37 +01:00
d406fd2e30 Removed final (hopefully) lingering abstract tag 2026-03-01 11:29:14 +01:00
fc093533fd Globally rescaled pixel sizes to be uniform 2026-02-21 11:42:24 +01:00
a1e0ec07d7 Movement now snaps to pixels, player pixels match background 2026-02-21 10:42:45 +01:00
7bd85cc050 Fix: fleeing from a removed object not handled 2026-02-21 10:25:29 +01:00
1ff04ff3b3 Solved flushing querries errors 2026-02-21 10:20:02 +01:00
27540988fd Updated player sprite 2026-02-21 10:16:52 +01:00
cc0a4cb71b Added logs 2026-02-21 09:30:50 +01:00
f2b58d7314 Added basic player health and damage taking timeout 2026-02-20 19:19:17 +01:00
6482dab9b6 (wip) Removed @abstract tag that was causing issues 2026-02-20 19:02:22 +01:00
bc582efb90 ft (wip): tracking across boundaries 2026-02-07 18:13:32 +01:00
0f06846157 Food boundary + animation sprite 2026-02-07 17:50:29 +01:00
1dc1501b80 (wip) Food spawning on prey death 2026-02-07 17:42:57 +01:00
9c625edef4 (wip) Fix: Main menu now disappears 2026-02-07 17:42:46 +01:00
49d9489ede (wip) Rehauled main menu / game loading logic 2026-02-07 17:26:17 +01:00
bef1e9cb53 (wip) Food now moves with a world flow (which is updated using a manual timer in the GameManager 2026-02-07 16:24:37 +01:00
d5b33d66b8 Merge remote-tracking branch 'refs/remotes/origin/stage/molecular' into stage/molecular 2026-02-07 15:42:37 +01:00
2211130fd3 ft: extracted mirrorSprite to custom node 2026-02-07 15:42:24 +01:00
ceb0d90c20 (wip) Made food smaller 2026-02-07 15:38:41 +01:00
57c611a46d (wip) Food for other entities 2026-02-07 15:35:45 +01:00
232184afba (wip) Player food 2026-02-07 15:31:43 +01:00
2352a3aa07 fx: hopefully fixed error 2026-02-07 14:16:56 +01:00
098348b329 (wip) First food commit 2026-02-07 13:23:54 +01:00
22ffa3dac1 (wip) updated comment, starting to implement food 2026-02-07 12:48:16 +01:00
59c4818216 fx: variable loading prey script 2026-02-07 12:07:36 +01:00
8ee70854ea ft: predator mostly done 2026-02-06 12:43:37 +01:00
5f9a2fffb9 (wip) I guess this file also counts as part of the abstract food class 2026-02-03 21:11:17 +01:00
6094980d00 Added abstract food class 2026-02-03 21:10:39 +01:00
66233d8fad Added food assets 2026-02-03 21:10:14 +01:00
5751f25732 ft (wip): hammerhead predator 2026-02-02 01:41:01 +01:00
165 changed files with 16406 additions and 1558 deletions

1
.gitignore vendored
View File

@@ -49,4 +49,3 @@ export_presets.cfg
data_*/
mono_crash.*.json
addons/

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024-present Mark Velez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,7 @@
[plugin]
name="Modular Settings Menu"
description="A fully modular settings menu that can be customized with ease."
author="Mark Velez"
version="1.0.0"
script="plugin.gd"

View File

@@ -0,0 +1,13 @@
@tool
extends EditorPlugin
var pluginPath: String = get_script().resource_path.get_base_dir()
const settingsDataManagerPath: String = "/singletons/settings_data_manager.gd"
func _enter_tree():
add_autoload_singleton("SettingsDataManager", pluginPath + settingsDataManagerPath)
func _exit_tree():
remove_autoload_singleton("SettingsDataManager")

View File

@@ -0,0 +1 @@
uid://7kssh3jmekkk

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=4 format=3 uid="uid://pir75xaw2g08"]
[ext_resource type="Script" path="res://addons/modular-settings-menu/scripts/settings-elements-scripts/audio_setting.gd" id="1_n7p26"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="MasterVolume" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_n7p26")
AUDIO_BUS = "Master"
STEP_VALUE = 0.01
DISPLAY_PERCENT_VALUE = true
VALUE_SUFFIX = "%"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "MasterVolume"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Master"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=4 format=3 uid="uid://y6kuxwyem48c"]
[ext_resource type="Script" path="res://addons/modular-settings-menu/scripts/settings-elements-scripts/audio_setting.gd" id="1_05os8"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="MusicVolume" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_05os8")
AUDIO_BUS = "Music"
STEP_VALUE = 0.01
DISPLAY_PERCENT_VALUE = true
VALUE_SUFFIX = "%"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "MusicVolume"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Music"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=4 format=3 uid="uid://jl3iirunm0hq"]
[ext_resource type="Script" path="res://addons/modular-settings-menu/scripts/settings-elements-scripts/audio_setting.gd" id="1_b0uh7"]
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="SFXVolume" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_b0uh7")
AUDIO_BUS = "SFX"
STEP_VALUE = 0.01
DISPLAY_PERCENT_VALUE = true
VALUE_SUFFIX = "%"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "SFXVolume"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "SFX"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,38 @@
[gd_scene load_steps=2 format=3 uid="uid://bbtri7oyxvehi"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jjel1"]
[node name="InputButton" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="ActionLabel" type="Label" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 2
size_flags_vertical = 1
mouse_filter = 1
text = "action name"
horizontal_alignment = 1
vertical_alignment = 1
[node name="ActionInput" type="Button" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 1
theme_override_styles/focus = SubResource("StyleBoxEmpty_jjel1")
text = "input key"

View File

@@ -0,0 +1,305 @@
[gd_scene format=3 uid="uid://cqqhgk8ufkmqa"]
[ext_resource type="PackedScene" uid="uid://bbtri7oyxvehi" path="res://addons/modular-settings-menu/scenes/settings-elements/controls-elements/input-settings-panel/input-settings-panel-elements/input_button.tscn" id="1_1y7ch"]
[sub_resource type="GDScript" id="GDScript_0eq0v"]
resource_name = "input_settings_panel"
script/source = "extends Control
# Button scene for actions
@export var InputButtonScene: PackedScene
# Panel node references
@onready var ActionListRef: VBoxContainer = %ActionList
@onready var ResetButtonRef: Button = %ResetButton
@onready var BackButtonRef: Button = $VBoxContainer/HBoxContainer/BackButton
@onready var ApplyButtonRef: Button = $VBoxContainer/HBoxContainer/ApplyButton
# List of events for each action
var actionEvents_: Dictionary
# Cache for changed actions
var panelCache_: Dictionary
# Reference list to the button for each action
var actionReferenceList_: Dictionary
# Reference to the element this panel belongs to
var PanelOwnerRef: ButtonElement
# For remapping
var isRemapping: bool = false
var actionToRemap: String
var ActionInputButtonRef: Button
func _ready():
# Connect necessary signals
ResetButtonRef.connect(\"pressed\", on_reset_button_pressed)
BackButtonRef.connect(\"pressed\", on_back_button_pressed)
ApplyButtonRef.connect(\"pressed\", on_apply_button_pressed)
# Called to populate the action list with actions
func create_action_list(reset: bool = false) -> void:
# All the remappable actions
var INPUT_ACTIONS_: Dictionary = PanelOwnerRef.ACTION_LIST_.duplicate()
INPUT_ACTIONS_.make_read_only()
# Get the events for the actions
actionEvents_ = get_events(INPUT_ACTIONS_, reset)
# Check if the action list is already populated
if ActionListRef.get_child_count() > 0:
# Remove all the existing actions from the action list
for child in ActionListRef.get_children():
child.queue_free()
# Create the actions for the action list
for action in INPUT_ACTIONS_:
# Instantiate the button scene
var ButtonRef: PanelContainer = InputButtonScene.instantiate()
# Get references to the elements of the button scene
var ActionLabelRef: Label = ButtonRef.find_child(\"ActionLabel\")
var ActionInputRef: Button = ButtonRef.find_child(\"ActionInput\")
# Get the event for the action
var event = actionEvents_[action]
# Add a reference of the instantiated button to the reference list
actionReferenceList_[action] = ButtonRef
# Change the label text of the button to the action it corresponds to
ActionLabelRef.set_text(INPUT_ACTIONS_[action])
ActionInputRef.set_text(event.as_text())
ActionListRef.add_child(ButtonRef)
ActionInputRef.connect(
\"pressed\",
on_input_button_pressed.bind(action, ActionInputRef)
)
func on_input_button_pressed(action: String, ActionInputRef: Button) -> void:
# Check if a button is not being remapped currently
if not isRemapping:
isRemapping = true
actionToRemap = action
ActionInputButtonRef = ActionInputRef
ActionInputRef.set_text(\"Press key to bind...\")
func _input(event: InputEvent):
# Check if an action is being remapped currently
if isRemapping:
# Check if the desired input is either a keyboard or mouse input
if (
event is InputEventKey
or event is InputEventMouseButton
and event.pressed
):
# Check if the event is a mouse event and if it was a double click
if event is InputEventMouseButton and event.double_click:
# Disable the double click flag
event.double_click = false
# Set the input button's text to the inputted event
ActionInputButtonRef.set_text(event.as_text())
# Check if the inputted event is different compared to the saved one for the action
if not check_matching_event(event):
var duplicateEvent: String = check_duplicate_event(event)
actionEvents_[actionToRemap] = event
panelCache_[actionToRemap] = event
ApplyButtonRef.set_disabled(false)
if duplicateEvent:
var ActionInputRef: Button =\\
actionReferenceList_[duplicateEvent].find_child(
\"ActionInput\"
)
actionEvents_[duplicateEvent] = InputEventKey.new()
panelCache_[duplicateEvent] = actionEvents_[duplicateEvent]
ActionInputRef.set_text(
actionEvents_[duplicateEvent].as_text()
)
isRemapping = false
actionToRemap = \"\"
ActionInputButtonRef = null
accept_event()
# Checks if the inputted event matches the one saved for the action
func check_matching_event(event: InputEvent) -> bool:
var eventType: String = event.get_class()
var eventButton: int = get_event_button(event)
var currentEvent = actionEvents_[actionToRemap]
# Check if the event matches the action to be remapped
if (
currentEvent.is_class(eventType)
and get_event_button(currentEvent) == eventButton
):
return true
return false
# Checks if any other action has the inputted event as it's event
func check_duplicate_event(event: InputEvent) -> String:
var eventType: String = event.get_class()
var eventButton: int = get_event_button(event)
# Check for duplicates in other actions
for action in actionEvents_:
# Check if the action is not the one being remapped
if action != actionToRemap:
var storedEvent = actionEvents_[action]
# Check if the input type and event matches the inputted event
if (
storedEvent.is_class(eventType)
and get_event_button(storedEvent) == eventButton
):
return action
return \"\"
# Called to get the physical index for the inputted event depending on the input event's type
func get_event_button(event: InputEvent) -> int:
# Check for the input event's type
match event.get_class():
\"InputEventKey\":
return event.get_keycode()
\"InputEventMouseButton\":
return event.get_button_index()
return -1
# Called to update the action's events to the cached events
func update_action_events(actionList_: Dictionary) -> void:
# Itterate through all the recieved actions
for action in actionList_:
# Remove all events for the action
InputMap.action_erase_events(action)
# Check if the action has an event assigned to it
if actionList_[action]:
# Add the new event to the action
InputMap.action_add_event(action, actionList_[action])
# Called to get all the events for the remappable actions
func get_events(actions_: Dictionary, reset: bool = false) -> Dictionary:
var events_: Dictionary = {}
if PanelOwnerRef.noSaveFile or PanelOwnerRef.invalidSaveFile or reset:
InputMap.load_from_project_settings()
for action in actions_:
# Retrieve the first event for the action
events_[action] = InputMap.action_get_events(action)[0]
panelCache_ = events_.duplicate(true)
else:
# Retrieve the events from the loaded data
events_ = PanelOwnerRef.inputSettingsData_.duplicate()
return events_
func on_reset_button_pressed():
panelCache_.clear()
create_action_list(true)
ApplyButtonRef.set_disabled(false)
func on_back_button_pressed():
# Check if there have been any changes made
if ApplyButtonRef.is_disabled():
# Clear the cache and return normally
panelCache_.clear()
hide()
owner.SettingsPanelRef.show()
else:
# Display the discard changes popup
owner.display_discard_changes(self)
func on_apply_button_pressed():
# Update the input map
update_action_events(panelCache_)
panelCache_.clear()
# Send the new data to the parent element
PanelOwnerRef.panel_settings_changed(actionEvents_)
ApplyButtonRef.set_disabled(true)
"
[node name="InputSettingsPanel" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_0eq0v")
InputButtonScene = ExtResource("1_1y7ch")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -194.0
offset_top = -29.0
offset_right = 194.0
offset_bottom = 30.0
grow_horizontal = 2
grow_vertical = 2
[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 280)
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer"]
layout_mode = 2
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="ActionList" type="VBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="ResetButton" type="Button" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
text = "Reset to default"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="ApplyButton" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_direction = 1
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
disabled = true
text = "Apply"
[node name="BackButton" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_direction = 1
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
text = "Back"

View File

@@ -0,0 +1,165 @@
[gd_scene format=3 uid="uid://i0w5gftb0j16"]
[ext_resource type="PackedScene" uid="uid://cqqhgk8ufkmqa" path="res://addons/modular-settings-menu/scenes/settings-elements/controls-elements/input-settings-panel/input_settings_panel.tscn" id="1_6pc66"]
[sub_resource type="GDScript" id="GDScript_te2ja"]
resource_name = "input_settings"
script/source = "extends ButtonElement
##List of actions to make remappable and how the name they should display.
@export var ACTION_LIST_: Dictionary = {
\"ui_up\": \"Up\",
\"ui_down\": \"Down\",
\"ui_left\": \"Left\",
\"ui_right\": \"Right\",
}
# Path to the settings save file
var DATA_FOLDER: String = SettingsDataManager.DATA_FOLDER
const FILE_NAME: String = \"keybinds\"
const FILE_EXTENSION: String = \".json\"
var PATH: String = DATA_FOLDER + \"/\" + FILE_NAME + FILE_EXTENSION
# Currently stored input data
var inputSettingsData_: Dictionary
# Flag for checking if a save file exists for input settings
var noSaveFile: bool
# Flag for checking if an invalid value was found in the save file
var invalidSaveFile: bool = false
func _ready():
super._ready()
ACTION_LIST_.make_read_only()
SettingsDataManager.connect(\"settings_retrieved\", load_settings)
# Verify the directory
DirAccess.make_dir_absolute(DATA_FOLDER)
# Check if a save file exists
if not FileAccess.file_exists(PATH):
noSaveFile = true
# Loads the saved/default values of the element
func load_settings() -> void:
# Get the current value for the element
if noSaveFile:
# Get the events for the remappable actions from the project settings
inputSettingsData_ = ElementPanelRef.get_events(ACTION_LIST_)
save_input_settings()
else:
get_input_settings()
# Check if save file is valid
if invalidSaveFile:
# Get the events for the remappable actions from the project settings
inputSettingsData_ = ElementPanelRef.get_events(ACTION_LIST_)
save_input_settings()
else:
call_deferred(\"apply_settings\")
func pressed() -> void:
# Check if a save file exists
if not FileAccess.file_exists(PATH):
noSaveFile = true
ElementPanelRef.create_action_list()
# Switch panels
ParentRef.SettingsMenuRef.SettingsPanelRef.hide()
ElementPanelRef.show()
# Called to apply the settings in the settings cache
func apply_settings() -> void:
# Assign the events for the remappable actions
ElementPanelRef.update_action_events(inputSettingsData_)
# Called to update the input settings data
func panel_settings_changed(newValue_: Dictionary) -> void:
inputSettingsData_ = newValue_.duplicate()
save_input_settings()
# Called to save the input settings
func save_input_settings() -> void:
var file := FileAccess.open(PATH, FileAccess.WRITE)
var data_: Dictionary = {}
# Itterate through the input settings
for input in inputSettingsData_:
var inputEvent = inputSettingsData_[input]
# Serialize the InputEvent of the action
match inputEvent.get_class():
\"InputEventKey\":
data_[input] = {
\"type\": \"keyboard\",
\"key\": inputEvent.get_keycode()
}
\"InputEventMouseButton\":
data_[input] = {
\"type\": \"mouse\",
\"button\": inputEvent.get_button_index()
}
# Save the string to a json file
file.store_string(JSON.stringify(data_, \"\\t\", false))
file.close()
noSaveFile = false
# Called to retrieve the input settings data from the save file
func get_input_settings() -> void:
var file := FileAccess.open(PATH, FileAccess.READ)
# Parse the json string to a dictionary
var data_ = JSON.parse_string(file.get_as_text())
file.close()
# Check if there were any errors or if the save file is empty
if data_ == null or data_.is_empty():
push_error(\"Failed to parse input settings\")
invalidSaveFile = true
return
verify_settings_data(data_)
# Used for verifying the integrity of the save file
func verify_settings_data(data_: Dictionary) -> void:
# Itterate through all the retrieved data
for input in data_:
# Check if the action exists
if not ACTION_LIST_.has(input):
push_warning(\"Invalid input settings entry: \", input)
invalidSaveFile = true
continue
# Reconstruct the InputEvent for the action
match data_[input][\"type\"]:
\"keyboard\":
var key := InputEventKey.new()
key.set_keycode(data_[input][\"key\"])
inputSettingsData_[input] = key
\"mouse\":
var button := InputEventMouseButton.new()
button.set_button_index(data_[input][\"button\"])
inputSettingsData_[input] = button
# Itterate through all the expected actions
for action in ACTION_LIST_:
if not data_.has(action):
data_[action] = InputMap.action_get_events(action)[0]
invalidSaveFile = true
push_warning(\"Input action is missing: \", action)
"
[node name="InputSettings" type="Button"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
text = "Input Settings"
script = SubResource("GDScript_te2ja")
ElementPanelScene = ExtResource("1_6pc66")

View File

@@ -0,0 +1,37 @@
[gd_scene load_steps=3 format=3 uid="uid://c2tgt7fu0n5wp"]
[sub_resource type="GDScript" id="GDScript_te2ja"]
resource_name = "invert_y"
script/source = "extends ToggleElement
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="InvertY" type="HBoxContainer" node_paths=PackedStringArray("ToggleRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_te2ja")
ToggleRef = NodePath("Toggle")
IDENTIFIER = "InvertY"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Invert Y-Axis"
vertical_alignment = 1
[node name="Toggle" type="CheckButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,60 @@
[gd_scene load_steps=4 format=3 uid="uid://cy8n6yalxprb7"]
[sub_resource type="GDScript" id="GDScript_s1j2c"]
resource_name = "sensitivity"
script/source = "extends SliderElement
# Element specific script for applying its value to the game
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
"
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="Sensitivity" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_s1j2c")
MAX_VALUE = 5.0
STEP_VALUE = 0.01
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "Sensitivity"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Mouse Sensitivity"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,94 @@
[gd_scene load_steps=3 format=3 uid="uid://dvkksl3mrnoto"]
[sub_resource type="GDScript" id="GDScript_pvp7f"]
resource_name = "discard_changes_popup"
script/source = "extends PanelContainer
@onready var YesButtonRef: Button = %YesButton
@onready var NoButtonRef: Button = %NoButton
var CallerRef: Control
func _ready():
# Connect necessary signals
YesButtonRef.connect(\"pressed\", yes_button_pressed)
NoButtonRef.connect(\"pressed\", no_button_pressed)
connect(\"hidden\", on_hidden)
func yes_button_pressed() -> void:
# Check if the panel this belongs to has the \"discard changes\" function
if CallerRef.has_method(\"discard_changes\"):
CallerRef.discard_changes()
# Disable the panel's apply button
CallerRef.ApplyButtonRef.set_disabled(true)
CallerRef.on_back_button_pressed()
# Reset the changed elements count
SettingsDataManager.changedElementsCount = 0
hide()
func no_button_pressed() -> void:
hide()
func on_hidden() -> void:
CallerRef = null
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8kuxy"]
bg_color = Color(0.162066, 0.162066, 0.162065, 1)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[node name="DiscardChangesPopup" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_pvp7f")
[node name="PopupWindow" type="PanelContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
theme_override_styles/panel = SubResource("StyleBoxFlat_8kuxy")
[node name="MarginContainer" type="MarginContainer" parent="PopupWindow"]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="VBoxContainer" type="VBoxContainer" parent="PopupWindow/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 8
[node name="Text" type="Label" parent="PopupWindow/MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Would you like to discard
all changes done?"
[node name="HBoxContainer" type="HBoxContainer" parent="PopupWindow/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 8
[node name="YesButton" type="Button" parent="PopupWindow/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Yes"
[node name="NoButton" type="Button" parent="PopupWindow/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "No"

View File

@@ -0,0 +1,63 @@
[gd_scene load_steps=4 format=3 uid="uid://elu1vc0ox472"]
[sub_resource type="GDScript" id="GDScript_1rk38"]
resource_name = "fov"
script/source = "extends SliderElement
# Element specific script for applying its value to the game
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
"
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="FOV" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_1rk38")
MIN_VALUE = 30.0
MAX_VALUE = 120.0
STEP_VALUE = 1.0
DEFAULT_VALUE = 75.0
VALUE_SUFFIX = "°"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "FOV"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Field of View"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,83 @@
[gd_scene load_steps=3 format=3 uid="uid://csg6c3uuct1ls"]
[sub_resource type="GDScript" id="GDScript_5w56l"]
resource_name = "anti_aliasing"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = [
\"Disabled\",
\"FXAA\",
\"2x MSAA\",
\"4x MSAA\",
\"8x MSAA\",
\"TAA\"
]
func _apply_settings() -> void:
# Enables the selected anti aliasing
match currentValue:
\"Disabled\":
set_anti_aliasing_mode()
\"FXAA\":
set_anti_aliasing_mode(Viewport.SCREEN_SPACE_AA_FXAA)
\"2x MSAA\":
set_anti_aliasing_mode(
Viewport.SCREEN_SPACE_AA_DISABLED,
Viewport.MSAA_2X
)
\"4x MSAA\":
set_anti_aliasing_mode(
Viewport.SCREEN_SPACE_AA_DISABLED,
Viewport.MSAA_4X
)
\"8x MSAA\":
set_anti_aliasing_mode(
Viewport.SCREEN_SPACE_AA_DISABLED,
Viewport.MSAA_8X
)
\"TAA\":
set_anti_aliasing_mode(
Viewport.SCREEN_SPACE_AA_DISABLED,
Viewport.MSAA_DISABLED, true
)
# Sets the anti aliasing mode according to the values provided
func set_anti_aliasing_mode(
fxaaMode: Viewport.ScreenSpaceAA = Viewport.SCREEN_SPACE_AA_DISABLED,
msaaMode: Viewport.MSAA = Viewport.MSAA_DISABLED,
taaMode: bool = false
) -> void:
get_viewport().set_screen_space_aa(fxaaMode)
get_viewport().set_msaa_3d(msaaMode)
get_viewport().set_use_taa(taaMode)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="AntiAliasing" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_5w56l")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "AntiAliasing"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Anti Aliasing"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=3 format=3 uid="uid://3ckate3v614f"]
[sub_resource type="GDScript" id="GDScript_mnbyt"]
resource_name = "depth_of_field"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": null,
\"Low\": RenderingServer.DOF_BLUR_QUALITY_LOW,
\"Medium\": RenderingServer.DOF_BLUR_QUALITY_MEDIUM,
\"High\": RenderingServer.DOF_BLUR_QUALITY_HIGH
}
func _apply_settings() -> void:
if not apply_in_game_setting(currentValue):
if currentValue == \"Disabled\":
return
RenderingServer.camera_attributes_set_dof_blur_quality(
OPTION_LIST_[currentValue],
true
)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="DepthOfField" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_mnbyt")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "DepthOfField"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Depth of Field"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,109 @@
[gd_scene load_steps=3 format=3 uid="uid://byff0jwaicxvr"]
[sub_resource type="GDScript" id="GDScript_duwn0"]
resource_name = "display_mode"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Fullscreen\": DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
\"Borderless Windowed\": DisplayServer.WINDOW_MODE_WINDOWED,
\"Windowed\": DisplayServer.WINDOW_MODE_WINDOWED
}
func load_settings() -> void:
super.load_settings()
# Check if the resolution element should be disabled
call_deferred(\"check_resolution\")
func option_selected(index: int) -> void:
super.option_selected(index)
# Check if the resolution element should be disabled
check_resolution()
func _apply_settings() -> void:
DisplayServer.window_set_mode(OPTION_LIST_[currentValue])
if currentValue == \"Borderless Windowed\":
DisplayServer.window_set_flag(
DisplayServer.WINDOW_FLAG_BORDERLESS,
true
)
adjust_resolution()
else:
DisplayServer.window_set_flag(
DisplayServer.WINDOW_FLAG_BORDERLESS,
false
)
if currentValue == \"Windowed\":
# Set the resolution to 80% if the resolution setting doesnt exist
if not ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"Resolution\"):
adjust_resolution(0.8)
return
# Apply the selected resolution manually if it has not been changed
if not ParentRef.changedElements_.has(\"Resolution\"):
# Resolution change has to be delayed by at least 2 frames.
# Otherwise height of the window will be off by a bit.
await get_tree().process_frame
await get_tree().process_frame
# Apply the resolution settings manually
ParentRef.ELEMENT_REFERENCE_TABLE_[\"Resolution\"]._apply_settings()
# Called to check if the resolution element should be disabled
func check_resolution() -> void:
if not ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"Resolution\"):
return
var ResolutionRef: SettingsElement =\\
ParentRef.ELEMENT_REFERENCE_TABLE_[\"Resolution\"]
if currentValue != \"Windowed\":
ResolutionRef.OptionsRef.set_disabled(true)
else:
ResolutionRef.OptionsRef.set_disabled(false)
# Called to scale the resolution to the provided percentage
func adjust_resolution(sizeScale: float = 1.0) -> void:
var displaySize: Vector2i =\\
DisplayServer.screen_get_size(
DisplayServer.window_get_current_screen()
) * sizeScale
get_window().set_size(displaySize)
get_viewport().set_size(displaySize)
get_window().move_to_center()
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="DisplayMode" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_duwn0")
DEFAULT_VALUE = "Fullscreen"
OptionsRef = NodePath("Options")
IDENTIFIER = "DisplayMode"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Display Mode"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,55 @@
[gd_scene load_steps=3 format=3 uid="uid://du5urp5d2dyjb"]
[sub_resource type="GDScript" id="GDScript_7hlvr"]
resource_name = "glow_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = [
\"Disabled\",
\"Low\",
\"High\"
]
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
if currentValue == \"Disabled\":
return
# Toggle bicubic upscaling
match currentValue:
\"Low\":
RenderingServer.environment_glow_set_use_bicubic_upscale(false)
\"High\":
RenderingServer.environment_glow_set_use_bicubic_upscale(true)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="GlowQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_7hlvr")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "GlowQuality"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Glow Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,85 @@
[gd_scene load_steps=2 format=3 uid="uid://dghfxwcbi5ig"]
[sub_resource type="GDScript" id="GDScript_eitn6"]
resource_name = "max_fps"
script/source = "extends SliderElement
func init_element() -> void:
super.init_element()
# Increase UI max value by one for unlimited fps
SliderRef.set_max(MAX_VALUE + 1)
if ValueBoxRef is SpinBox:
ValueBoxRef.set_max(MAX_VALUE + 1)
func load_settings() -> void:
super.load_settings()
if currentValue == 0:
ValueBoxRef.set_text(\"Unlimited\")
SliderRef.set_value(MAX_VALUE + 1)
func value_changed(value: float) -> void:
if value > MAX_VALUE:
value = 0
ValueBoxRef.set_text(\"Unlimited\")
super.value_changed(value)
# Element specific script for applying its value to the game
func _apply_settings() -> void:
if (
ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"VSync\")
and ParentRef.ELEMENT_REFERENCE_TABLE_[\"VSync\"].currentValue != \"Disabled\"
):
return
Engine.set_max_fps(currentValue)
"
[node name="MaxFPS" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_eitn6")
MIN_VALUE = 30.0
MAX_VALUE = 240.0
STEP_VALUE = 1.0
DEFAULT_VALUE = 60.0
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "MaxFPS"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Max FPS"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="Label" parent="SliderValue"]
custom_minimum_size = Vector2(80, 31)
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 1
text = "240"
horizontal_alignment = 2
vertical_alignment = 1

View File

@@ -0,0 +1,64 @@
[gd_scene load_steps=2 format=3 uid="uid://cn8r63dmvd55c"]
[sub_resource type="GDScript" id="GDScript_utf5l"]
resource_name = "resolution"
script/source = "extends OptionElement
## Toggle window resizing by the user.
@export var RESIZABLE: bool = false
func _init() -> void:
OPTION_LIST_ = {
\"3840x2160\": Vector2i(3840, 2160),
\"2560x1440\": Vector2i(2560, 1440),
\"1920x1080\": Vector2i(1920, 1080),
\"1280x720\": Vector2i(1280, 720),
\"960x540\": Vector2i(960, 540)
}
func _ready() -> void:
super._ready()
# Toggle window resizing
DisplayServer.window_set_flag(
DisplayServer.WINDOW_FLAG_RESIZE_DISABLED,
!RESIZABLE
)
func _apply_settings() -> void:
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
return
if DisplayServer.window_get_flag(DisplayServer.WINDOW_FLAG_BORDERLESS):
return
# Change the window size to the selected resolution
get_window().set_size(OPTION_LIST_[currentValue])
get_viewport().set_size(OPTION_LIST_[currentValue])
get_window().move_to_center()
"
[node name="Resolution" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_utf5l")
DEFAULT_VALUE = "1920x1080"
OptionsRef = NodePath("Options")
IDENTIFIER = "Resolution"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Resolution"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0

View File

@@ -0,0 +1,41 @@
[gd_scene load_steps=3 format=3 uid="uid://5dydkttc2fww"]
[sub_resource type="GDScript" id="GDScript_usma2"]
resource_name = "fsr_options"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Ultra Quality\": 0.77,
\"Quality\": 0.67,
\"Balanced\": 0.59,
\"Performance\": 0.5
}
func _apply_settings() -> void:
get_viewport().set_scaling_3d_scale(OPTION_LIST_[currentValue])
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="FSRMode" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
size_flags_vertical = 3
script = SubResource("GDScript_usma2")
DEFAULT_VALUE = "Balanced"
OptionsRef = NodePath("Options")
IDENTIFIER = "FSRMode"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "FSR Mode"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,61 @@
[gd_scene load_steps=4 format=3 uid="uid://bdoaqhvw440eh"]
[sub_resource type="GDScript" id="GDScript_ng2w8"]
resource_name = "fsr_sharpness"
script/source = "extends SliderElement
# Element specific script for applying its value to the game
func _apply_settings() -> void:
get_viewport().set_fsr_sharpness((MAX_VALUE - currentValue) * 2.0)
"
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hae3h"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_0xoq4"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hae3h")
[node name="FSRSharpness" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_ng2w8")
STEP_VALUE = 0.01
DEFAULT_VALUE = 0.9
DISPLAY_PERCENT_VALUE = true
VALUE_SUFFIX = "%"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "FSRSharpness"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "FSR Sharpness"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 20
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_0xoq4")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,62 @@
[gd_scene load_steps=4 format=3 uid="uid://ha87l3hl643g"]
[sub_resource type="GDScript" id="GDScript_7dhl4"]
resource_name = "resolution_scale"
script/source = "extends SliderElement
# Element specific script for applying its value to the game
func _apply_settings() -> void:
get_viewport().set_scaling_3d_scale(currentValue)
"
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hae3h"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_0xoq4"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hae3h")
[node name="ResolutionScale" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_7dhl4")
MIN_VALUE = 0.5
MAX_VALUE = 2.0
STEP_VALUE = 0.01
DISPLAY_PERCENT_VALUE = true
VALUE_SUFFIX = "%"
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "ResolutionScale"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Resolution Scale"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 20
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_0xoq4")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,128 @@
[gd_scene format=3 uid="uid://b3w7qpn4nhmu2"]
[ext_resource type="PackedScene" uid="uid://ha87l3hl643g" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/scaler-sub-elements/resolution_scale.tscn" id="1_4irey"]
[ext_resource type="PackedScene" uid="uid://5dydkttc2fww" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/scaler-sub-elements/fsr_mode.tscn" id="2_w65g1"]
[ext_resource type="PackedScene" uid="uid://bdoaqhvw440eh" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/scaler-sub-elements/fsr_sharpness.tscn" id="3_v8pix"]
[sub_resource type="GDScript" id="GDScript_bn0b0"]
resource_name = "scaler"
script/source = "extends MultiElement
func _display_sub_elements() -> void:
match currentValue:
\"Disabled\":
for element in SUB_ELEMENTS_:
element.hide()
\"Bilinear\":
SUB_ELEMENTS_[0].show()
SUB_ELEMENTS_[1].hide()
SUB_ELEMENTS_[2].hide()
\"FSR 2.2\":
SUB_ELEMENTS_[0].hide()
SUB_ELEMENTS_[1].show()
SUB_ELEMENTS_[2].show()
"
[sub_resource type="GDScript" id="GDScript_mdyud"]
resource_name = "scaling_mode"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": Viewport.SCALING_3D_MODE_BILINEAR,
\"Bilinear\": Viewport.SCALING_3D_MODE_BILINEAR,
\"FSR 2.2\": Viewport.SCALING_3D_MODE_FSR2
}
# Loads the saved/default values of the element
func load_settings() -> void:
super.load_settings()
# Check if TAA is selected
call_deferred(\"check_anti_aliasing\")
func option_selected(index: int) -> void:
super.option_selected(index)
# Check if TAA is selected
check_anti_aliasing()
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
get_viewport().set_scaling_3d_mode(OPTION_LIST_[currentValue])
if currentValue == \"Disabled\":
get_viewport().set_scaling_3d_scale(1.0)
# Checks if TAA is selected while FSR 2.2 is enabled
func check_anti_aliasing() -> void:
if not ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"AntiAliasing\"):
return
var AntiAliasingRef: SettingsElement =\\
ParentRef.ELEMENT_REFERENCE_TABLE_[\"AntiAliasing\"]
var taaIndex: int = AntiAliasingRef.OPTION_LIST_.find(\"TAA\")
if currentValue != \"FSR 2.2\":
AntiAliasingRef.OptionsRef.set_item_disabled(
taaIndex,
false
)
return
AntiAliasingRef.OptionsRef.set_item_disabled(
taaIndex,
true
)
if AntiAliasingRef.currentValue == \"TAA\":
var disabledIndex: int = AntiAliasingRef.OPTION_LIST_.find(\"Disabled\")
# Reselect the anti aliasing mode
AntiAliasingRef.OptionsRef.select(disabledIndex)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="Scaler" type="VBoxContainer" node_paths=PackedStringArray("MainElementRef", "SUB_ELEMENTS_")]
offset_top = 51.0
offset_right = 1152.0
offset_bottom = 51.0
script = SubResource("GDScript_bn0b0")
MainElementRef = NodePath("ScalingMode")
SUB_ELEMENTS_ = [NodePath("ResolutionScale"), NodePath("FSRMode"), NodePath("FSRSharpness")]
[node name="ScalingMode" type="HBoxContainer" parent="." node_paths=PackedStringArray("OptionsRef")]
layout_mode = 2
size_flags_horizontal = 3
script = SubResource("GDScript_mdyud")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "ScalingMode"
[node name="Label" type="Label" parent="ScalingMode"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Scaling Mode"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="ScalingMode"]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")
[node name="ResolutionScale" parent="." instance=ExtResource("1_4irey")]
visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="FSRMode" parent="." instance=ExtResource("2_w65g1")]
visible = false
layout_mode = 2
[node name="FSRSharpness" parent="." instance=ExtResource("3_v8pix")]
visible = false
layout_mode = 2

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=3 format=3 uid="uid://ddykljx6ndodi"]
[sub_resource type="GDScript" id="GDScript_6k8bu"]
resource_name = "sdfgi_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": null,
\"Low\": RenderingServer.ENV_SDFGI_RAY_COUNT_8,
\"Medium\": RenderingServer.ENV_SDFGI_RAY_COUNT_32,
\"High\": RenderingServer.ENV_SDFGI_RAY_COUNT_96
}
# Called to apply the settings in the settings cache
func apply_settings() -> void:
apply_in_game_setting(currentValue)
if currentValue == \"Disabled\":
return
RenderingServer.environment_set_sdfgi_ray_count(OPTION_LIST_[currentValue])
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="SDFGIQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
tooltip_text = "Signed Distance Field Global Illumination"
script = SubResource("GDScript_6k8bu")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "SDFGIQuality"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "SDFGI Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,50 @@
[gd_scene load_steps=3 format=3 uid="uid://cgqiotob5aaoq"]
[sub_resource type="GDScript" id="GDScript_v3ktk"]
resource_name = "shadow_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Low\": RenderingServer.SHADOW_QUALITY_SOFT_LOW,
\"Medium\": RenderingServer.SHADOW_QUALITY_SOFT_MEDIUM,
\"High\": RenderingServer.SHADOW_QUALITY_SOFT_HIGH
}
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
RenderingServer.directional_soft_shadow_filter_set_quality(
OPTION_LIST_[currentValue]
)
RenderingServer.positional_soft_shadow_filter_set_quality(
OPTION_LIST_[currentValue]
)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="ShadowQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_v3ktk")
DEFAULT_VALUE = "Low"
OptionsRef = NodePath("Options")
IDENTIFIER = "ShadowQuality"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Shadow Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,78 @@
[gd_scene load_steps=3 format=3 uid="uid://bk5ky60jln7ag"]
[sub_resource type="GDScript" id="GDScript_h10e0"]
resource_name = "ssao_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": null,
\"Low\": {
\"quality\": RenderingServer.ENV_SSAO_QUALITY_LOW,
\"halfSize\": true,
\"blurPasses\": 1,
\"fadeOutFrom\": 25,
\"fadeOutTo\": 150
},
\"Medium\": {
\"quality\": RenderingServer.ENV_SSAO_QUALITY_MEDIUM,
\"halfSize\": true,
\"blurPasses\": 2,
\"fadeOutFrom\": 50,
\"fadeOutTo\": 300
},
\"High\": {
\"quality\": RenderingServer.ENV_SSAO_QUALITY_HIGH,
\"halfSize\": false,
\"blurPasses\": 4,
\"fadeOutFrom\": 100,
\"fadeOutTo\": 600
}
}
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
if currentValue == \"Disabled\":
return
RenderingServer.environment_set_ssao_quality(
OPTION_LIST_[currentValue][\"quality\"],
OPTION_LIST_[currentValue][\"halfSize\"],
0.5,
OPTION_LIST_[currentValue][\"blurPasses\"],
OPTION_LIST_[currentValue][\"fadeOutFrom\"],
OPTION_LIST_[currentValue][\"fadeOutTo\"]
)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="SSAOQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
tooltip_text = "Screen Space Ambient Occlusion"
script = SubResource("GDScript_h10e0")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "SSAOQuality"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "SSAO Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,78 @@
[gd_scene load_steps=3 format=3 uid="uid://bmnvig4gfqj0c"]
[sub_resource type="GDScript" id="GDScript_fwd72"]
resource_name = "ssil_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": null,
\"Low\": {
\"quality\": RenderingServer.ENV_SSIL_QUALITY_LOW,
\"halfSize\": true,
\"blurPasses\": 2,
\"fadeOutFrom\": 25,
\"fadeOutTo\": 150
},
\"Medium\": {
\"quality\": RenderingServer.ENV_SSIL_QUALITY_MEDIUM,
\"halfSize\": true,
\"blurPasses\": 4,
\"fadeOutFrom\": 50,
\"fadeOutTo\": 300
},
\"High\": {
\"quality\": RenderingServer.ENV_SSIL_QUALITY_HIGH,
\"halfSize\": false,
\"blurPasses\": 8,
\"fadeOutFrom\": 100,
\"fadeOutTo\": 600
}
}
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
apply_in_game_setting(currentValue)
if currentValue == \"Disabled\":
return
RenderingServer.environment_set_ssil_quality(
OPTION_LIST_[currentValue][\"quality\"],
OPTION_LIST_[currentValue][\"halfSize\"],
0.5,
OPTION_LIST_[currentValue][\"blurPasses\"],
OPTION_LIST_[currentValue][\"fadeOutFrom\"],
OPTION_LIST_[currentValue][\"fadeOutTo\"]
)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="SSILQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
tooltip_text = "Screen Space Indirect Lighting"
script = SubResource("GDScript_fwd72")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "SSILQuality"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "SSIL Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,70 @@
[gd_scene load_steps=3 format=3 uid="uid://cqfr01uk73ce5"]
[sub_resource type="GDScript" id="GDScript_v8qca"]
resource_name = "ssr_quality"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": null,
\"Low\": {
\"roughnessQuality\": RenderingServer.ENV_SSR_ROUGHNESS_QUALITY_LOW,
\"maxSteps\": 32,
\"fadeIn\": 0.3,
\"fadeOut\": 1.0
},
\"Medium\": {
\"roughnessQuality\": RenderingServer.ENV_SSR_ROUGHNESS_QUALITY_MEDIUM,
\"maxSteps\": 64,
\"fadeIn\": 0.15,
\"fadeOut\": 2.0
},
\"High\": {
\"roughnessQuality\": RenderingServer.ENV_SSR_ROUGHNESS_QUALITY_HIGH,
\"maxSteps\": 128,
\"fadeIn\": 0.075,
\"fadeOut\": 4.0
}
}
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
apply_in_game_setting(OPTION_LIST_[currentValue])
if currentValue == \"Disabled\":
return
RenderingServer.environment_set_ssr_roughness_quality(
OPTION_LIST_[currentValue][\"roughnessQuality\"]
)
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="SSRQuality" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
tooltip_text = "Screen Space Reflection"
script = SubResource("GDScript_v8qca")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "SSRQuality"
IS_IN_GAME_SETTING = true
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "SSR Quality"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,96 @@
[gd_scene load_steps=3 format=3 uid="uid://idtbqnsqlvb6"]
[sub_resource type="GDScript" id="GDScript_kwfgn"]
resource_name = "vsync"
script/source = "extends OptionElement
func _init() -> void:
OPTION_LIST_ = {
\"Disabled\": DisplayServer.VSYNC_DISABLED,
\"Enabled\": DisplayServer.VSYNC_ENABLED,
\"Adaptive\": DisplayServer.VSYNC_ADAPTIVE
}
# Loads the saved/default values of the element
func load_settings() -> void:
super.load_settings()
# Toggle the max fps element
call_deferred(\"toggle_max_fps\")
func option_selected(index: int) -> void:
super.option_selected(index)
# Toggle the max fps element
toggle_max_fps()
# Called to apply the settings in the settings cache
func _apply_settings() -> void:
# Set the vsync mode to the selected option
DisplayServer.window_set_vsync_mode(OPTION_LIST_[currentValue])
# Set the max fps to unlimited if vsync is being enabled
if currentValue != \"Disabled\":
Engine.set_max_fps(0)
# Check if the max fps element exists and if vsync is disabled
if (
ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"MaxFPS\")
and currentValue == \"Disabled\"
):
# Check if max fps has been changed
if not ParentRef.changedElements_.has(\"MaxFPS\"):
# Apply the max fps settings manually
ParentRef.ELEMENT_REFERENCE_TABLE_[\"MaxFPS\"]._apply_settings()
elif currentValue == \"Disabled\":
# Set the max fps to the max fps read from the project settings
Engine.set_max_fps(ProjectSettings.get_setting(\"application/run/max_fps\"))
# Disable/Enable the max fps element according to if vsync is on or not
func toggle_max_fps() -> void:
if not ParentRef.ELEMENT_REFERENCE_TABLE_.has(\"MaxFPS\"):
return
# Reference to the slider value node of the max fps element
var MaxFpsRef: SettingsElement =\\
ParentRef.ELEMENT_REFERENCE_TABLE_[\"MaxFPS\"]
if currentValue == \"Disabled\":
# Enable the max fps element
MaxFpsRef.SliderRef.set_editable(true)
MaxFpsRef.ValueBoxRef.modulate = Color.WHITE
else:
# Disable the max fps element
MaxFpsRef.SliderRef.set_editable(false)
MaxFpsRef.ValueBoxRef.modulate = Color.GRAY
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="VSync" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_kwfgn")
DEFAULT_VALUE = "Disabled"
OptionsRef = NodePath("Options")
IDENTIFIER = "VSync"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "V-Sync"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,14 @@
[gd_scene format=3 uid="uid://d0rkws47yf8l2"]
[ext_resource type="Script" uid="uid://d3nlqtnyyndrg" path="res://addons/modular-settings-menu/scripts/base-settings-elements/button_element.gd" id="1_cnjcn"]
[ext_resource type="PackedScene" uid="uid://cwhs0kpipkxfv" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/element-panel-template/element_panel_template.tscn" id="2_gbpfn"]
[node name="ButtonElementTemplate" type="Button"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
text = "Button Element"
script = ExtResource("1_cnjcn")
ElementPanelScene = ExtResource("2_gbpfn")

View File

@@ -0,0 +1,76 @@
[gd_scene format=3 uid="uid://cwhs0kpipkxfv"]
[ext_resource type="Script" uid="uid://cyig515shrgnb" path="res://addons/modular-settings-menu/scripts/element_panel.gd" id="1_3f2r5"]
[ext_resource type="PackedScene" uid="uid://bjcuw6amean2" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/slider_element_template.tscn" id="2_fpsnu"]
[node name="ElementPanelTemplate" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_3f2r5")
IDENTIFIER = "PanelTemplate"
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -194.0
offset_top = -29.0
offset_right = 194.0
offset_bottom = 30.0
grow_horizontal = 2
grow_vertical = 2
[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 280)
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/PanelContainer/MarginContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer/MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="SliderElementTemplate" parent="VBoxContainer/PanelContainer/MarginContainer/ScrollContainer/MarginContainer/ElementList" instance=ExtResource("2_fpsnu")]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="ApplyButton" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_direction = 1
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
disabled = true
text = "Apply"
[node name="BackButton" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_direction = 1
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
text = "Back"

View File

@@ -0,0 +1,46 @@
[gd_scene load_steps=3 format=3 uid="uid://vlmvkci3ptii"]
[sub_resource type="GDScript" id="GDScript_sk6j2"]
resource_name = "option_element_template"
script/source = "extends OptionElement
# Provide the options for the element
func _init() -> void:
OPTION_LIST_ = {
\"None\": null,
\"Slider\": null,
\"Toggle\": null
}
# Called to apply the setting to the game
func _apply_settings() -> void:
pass
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="MainElement" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_sk6j2")
DEFAULT_VALUE = "None"
OptionsRef = NodePath("Options")
IDENTIFIER = "MainElement"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Option Element"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,43 @@
[gd_scene format=3 uid="uid://csr6kawyf3t3i"]
[ext_resource type="PackedScene" uid="uid://vlmvkci3ptii" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/multi-element-template/main_element.tscn" id="1_mhqaj"]
[ext_resource type="PackedScene" uid="uid://bjcuw6amean2" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/slider_element_template.tscn" id="2_mf8gn"]
[ext_resource type="PackedScene" uid="uid://cxaxyqer0af21" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/toggle_element_template.tscn" id="3_gs1f8"]
[sub_resource type="GDScript" id="GDScript_sk6j2"]
resource_name = "multi_element_template"
script/source = "extends MultiElement
func _display_sub_elements() -> void:
match currentValue:
\"None\":
for element in SUB_ELEMENTS_:
element.hide()
\"Slider\":
SUB_ELEMENTS_[0].show()
SUB_ELEMENTS_[1].hide()
\"Toggle\":
SUB_ELEMENTS_[0].hide()
SUB_ELEMENTS_[1].show()
"
[node name="MultiElementTemplate" type="VBoxContainer" node_paths=PackedStringArray("MainElementRef", "SUB_ELEMENTS_")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
script = SubResource("GDScript_sk6j2")
MainElementRef = NodePath("MainElement")
SUB_ELEMENTS_ = [NodePath("SliderElementTemplate"), NodePath("ToggleElementTemplate")]
[node name="MainElement" parent="." instance=ExtResource("1_mhqaj")]
layout_mode = 2
[node name="SliderElementTemplate" parent="." instance=ExtResource("2_mf8gn")]
layout_mode = 2
[node name="ToggleElementTemplate" parent="." instance=ExtResource("3_gs1f8")]
layout_mode = 2

View File

@@ -0,0 +1,44 @@
[gd_scene load_steps=3 format=3 uid="uid://dc1yif146sxav"]
[sub_resource type="GDScript" id="GDScript_sk6j2"]
resource_name = "option_element_template"
script/source = "extends OptionElement
# Provide the options for the element
func _init() -> void:
OPTION_LIST_ = {
\"Example\": null
}
# Called to apply the setting to the game
func _apply_settings() -> void:
pass
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="OptionElementTemplate" type="HBoxContainer" node_paths=PackedStringArray("OptionsRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_sk6j2")
DEFAULT_VALUE = "Example"
OptionsRef = NodePath("Options")
IDENTIFIER = "OptionTemplate"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Option Element"
vertical_alignment = 1
[node name="Options" type="OptionButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,78 @@
[gd_scene format=3 uid="uid://ejplmui1twn4"]
[ext_resource type="Script" uid="uid://c3ssroxjs8wt" path="res://addons/modular-settings-menu/scripts/settings_section.gd" id="1_aglse"]
[ext_resource type="PackedScene" uid="uid://dc1yif146sxav" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/option_element_template.tscn" id="2_ws4gk"]
[ext_resource type="PackedScene" uid="uid://csr6kawyf3t3i" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/multi-element-template/multi_element_template.tscn" id="2_xre3j"]
[ext_resource type="PackedScene" uid="uid://bjcuw6amean2" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/slider_element_template.tscn" id="3_o11e6"]
[ext_resource type="PackedScene" uid="uid://cxaxyqer0af21" path="res://addons/modular-settings-menu/scenes/settings-elements/templates/toggle_element_template.tscn" id="4_41mog"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_e0spm"]
[node name="SectionTemplate" type="TabBar"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_e0spm")
script = ExtResource("1_aglse")
IDENTIFIER = "ExampleSection"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 24
[node name="ParentElementTemplate" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList" instance=ExtResource("2_xre3j")]
layout_mode = 2
[node name="SubSection" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Sub Section"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection"]
layout_mode = 2
[node name="OptionElementTemplate" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection/SubSectionElements" instance=ExtResource("2_ws4gk")]
layout_mode = 2
[node name="SliderElementTemplate" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection/SubSectionElements" instance=ExtResource("3_o11e6")]
layout_mode = 2
[node name="ToggleElementTemplate" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/SubSection/SubSectionElements" instance=ExtResource("4_41mog")]
layout_mode = 2

View File

@@ -0,0 +1,57 @@
[gd_scene load_steps=4 format=3 uid="uid://bjcuw6amean2"]
[sub_resource type="GDScript" id="GDScript_i57rt"]
resource_name = "slider_element_template"
script/source = "extends SliderElement
# Called to apply the setting to the game
func _apply_settings() -> void:
pass
"
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_hlot4"]
size = Vector2(0, 0)
[sub_resource type="Theme" id="Theme_4i2xw"]
SpinBox/icons/updown = SubResource("PlaceholderTexture2D_hlot4")
[node name="SliderElementTemplate" type="HBoxContainer" node_paths=PackedStringArray("SliderRef", "ValueBoxRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_i57rt")
SliderRef = NodePath("SliderValue/Slider")
ValueBoxRef = NodePath("SliderValue/Value")
IDENTIFIER = "SliderTemplate"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Slider Element"
vertical_alignment = 1
[node name="SliderValue" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Slider" type="HSlider" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
max_value = 0.0
step = 0.0
ticks_on_borders = true
[node name="Value" type="SpinBox" parent="SliderValue"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 4
theme = SubResource("Theme_4i2xw")
max_value = 0.0
step = 0.0
alignment = 2

View File

@@ -0,0 +1,36 @@
[gd_scene load_steps=3 format=3 uid="uid://cxaxyqer0af21"]
[sub_resource type="GDScript" id="GDScript_bpxmf"]
resource_name = "toggle_element_template"
script/source = "extends ToggleElement
# Called to apply the setting to the game
func _apply_settings() -> void:
pass
"
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_drjjf"]
[node name="ToggleElementTemplate" type="HBoxContainer" node_paths=PackedStringArray("ToggleRef")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_bpxmf")
ToggleRef = NodePath("Toggle")
IDENTIFIER = "ToggleTemplate"
[node name="Label" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Toggle Element"
vertical_alignment = 1
[node name="Toggle" type="CheckButton" parent="."]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 0
theme_override_styles/focus = SubResource("StyleBoxEmpty_drjjf")

View File

@@ -0,0 +1,74 @@
[gd_scene format=3 uid="uid://y5oxvao3aqtb"]
[ext_resource type="Script" uid="uid://c3ssroxjs8wt" path="res://addons/modular-settings-menu/scripts/settings_section.gd" id="1_e0sxq"]
[ext_resource type="PackedScene" uid="uid://pir75xaw2g08" path="res://addons/modular-settings-menu/scenes/settings-elements/audio-elements/master_volume.tscn" id="2_l5q4d"]
[ext_resource type="PackedScene" uid="uid://y6kuxwyem48c" path="res://addons/modular-settings-menu/scenes/settings-elements/audio-elements/music_volume.tscn" id="3_v08xa"]
[ext_resource type="PackedScene" uid="uid://jl3iirunm0hq" path="res://addons/modular-settings-menu/scenes/settings-elements/audio-elements/sfx_volume.tscn" id="4_j1ron"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_e0spm"]
[node name="Audio" type="TabBar"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_e0spm")
script = ExtResource("1_e0sxq")
IDENTIFIER = "Audio"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 24
[node name="VolumeSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Volume Settings"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings"]
layout_mode = 2
[node name="MasterVolume" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings/SubSectionElements" instance=ExtResource("2_l5q4d")]
layout_mode = 2
[node name="MusicVolume" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings/SubSectionElements" instance=ExtResource("3_v08xa")]
layout_mode = 2
[node name="SFXVolume" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/VolumeSettings/SubSectionElements" instance=ExtResource("4_j1ron")]
layout_mode = 2

View File

@@ -0,0 +1,86 @@
[gd_scene format=3 uid="uid://dfswr81erouhj"]
[ext_resource type="Script" uid="uid://c3ssroxjs8wt" path="res://addons/modular-settings-menu/scripts/settings_section.gd" id="1_vmore"]
[ext_resource type="PackedScene" uid="uid://cy8n6yalxprb7" path="res://addons/modular-settings-menu/scenes/settings-elements/controls-elements/sensitivity.tscn" id="2_qcqml"]
[ext_resource type="PackedScene" uid="uid://c2tgt7fu0n5wp" path="res://addons/modular-settings-menu/scenes/settings-elements/controls-elements/invert_y.tscn" id="3_8np8q"]
[ext_resource type="PackedScene" uid="uid://i0w5gftb0j16" path="res://addons/modular-settings-menu/scenes/settings-elements/controls-elements/input_settings.tscn" id="4_bejag"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_e0spm"]
[node name="Controls" type="TabBar"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_e0spm")
script = ExtResource("1_vmore")
IDENTIFIER = "Controls"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 24
[node name="MouseSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/MouseSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Mouse Settings"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/MouseSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/MouseSettings"]
layout_mode = 2
[node name="Sensitivity" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/MouseSettings/SubSectionElements" instance=ExtResource("2_qcqml")]
layout_mode = 2
[node name="InvertY" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/MouseSettings/SubSectionElements" instance=ExtResource("3_8np8q")]
layout_mode = 2
[node name="KeyboardSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/KeyboardSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Keyboard Settings"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/KeyboardSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/KeyboardSettings"]
layout_mode = 2
[node name="InputSettings" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/KeyboardSettings/SubSectionElements" instance=ExtResource("4_bejag")]
layout_mode = 2

View File

@@ -0,0 +1,66 @@
[gd_scene format=3 uid="uid://c66v42g6gs7gr"]
[ext_resource type="Script" uid="uid://c3ssroxjs8wt" path="res://addons/modular-settings-menu/scripts/settings_section.gd" id="1_i11k5"]
[ext_resource type="PackedScene" uid="uid://elu1vc0ox472" path="res://addons/modular-settings-menu/scenes/settings-elements/gameplay-elements/fov.tscn" id="2_c8t76"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_e0spm"]
[node name="Gameplay" type="TabBar"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_e0spm")
script = ExtResource("1_i11k5")
IDENTIFIER = "Gameplay"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 24
[node name="GeneralSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/GeneralSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "General Settings"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/GeneralSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/GeneralSettings"]
layout_mode = 2
[node name="FOV" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/GeneralSettings/SubSectionElements" instance=ExtResource("2_c8t76")]
layout_mode = 2

View File

@@ -0,0 +1,129 @@
[gd_scene format=3 uid="uid://buqq0kms2dbb4"]
[ext_resource type="Script" uid="uid://c3ssroxjs8wt" path="res://addons/modular-settings-menu/scripts/settings_section.gd" id="1_704sp"]
[ext_resource type="PackedScene" uid="uid://byff0jwaicxvr" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/display_mode.tscn" id="1_mr6e7"]
[ext_resource type="PackedScene" uid="uid://cn8r63dmvd55c" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/resolution.tscn" id="2_5bpi0"]
[ext_resource type="PackedScene" uid="uid://b3w7qpn4nhmu2" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/scaler.tscn" id="4_g2t2s"]
[ext_resource type="PackedScene" uid="uid://dghfxwcbi5ig" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/max_fps.tscn" id="5_82fyi"]
[ext_resource type="PackedScene" uid="uid://idtbqnsqlvb6" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/vsync.tscn" id="6_2rh1a"]
[ext_resource type="PackedScene" uid="uid://csg6c3uuct1ls" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/anti_aliasing.tscn" id="7_r8hxv"]
[ext_resource type="PackedScene" uid="uid://3ckate3v614f" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/depth_of_field.tscn" id="8_28f8l"]
[ext_resource type="PackedScene" uid="uid://cgqiotob5aaoq" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/shadow_quality.tscn" id="9_1x78v"]
[ext_resource type="PackedScene" uid="uid://cqfr01uk73ce5" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/ssr_quality.tscn" id="10_cfuis"]
[ext_resource type="PackedScene" uid="uid://du5urp5d2dyjb" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/glow_quality.tscn" id="10_m0pim"]
[ext_resource type="PackedScene" uid="uid://bk5ky60jln7ag" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/ssao_quality.tscn" id="11_xa1gl"]
[ext_resource type="PackedScene" uid="uid://bmnvig4gfqj0c" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/ssil_quality.tscn" id="12_jqvyp"]
[ext_resource type="PackedScene" uid="uid://ddykljx6ndodi" path="res://addons/modular-settings-menu/scenes/settings-elements/graphics-elements/sdfgi_quality.tscn" id="13_ifeh4"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_v0fou"]
[node name="Graphics" type="TabBar"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_v0fou")
script = ExtResource("1_704sp")
IDENTIFIER = "Graphics"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="ElementList" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 24
[node name="BasicSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Basic Settings"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings"]
layout_mode = 2
[node name="DisplayMode" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings/SubSectionElements" instance=ExtResource("1_mr6e7")]
layout_mode = 2
defaultValue = "Windowed"
[node name="Resolution" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings/SubSectionElements" instance=ExtResource("2_5bpi0")]
layout_mode = 2
defaultValue = "1280x720"
[node name="MaxFPS" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/BasicSettings/SubSectionElements" instance=ExtResource("5_82fyi")]
layout_mode = 2
[node name="AdvancedSettings" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList"]
layout_mode = 2
[node name="SubSectionName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Advanced Settings
"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings"]
layout_mode = 2
[node name="SubSectionElements" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings"]
layout_mode = 2
[node name="Scaler" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("4_g2t2s")]
layout_mode = 2
[node name="AntiAliasing" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("7_r8hxv")]
layout_mode = 2
[node name="VSync" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("6_2rh1a")]
layout_mode = 2
[node name="DepthOfField" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("8_28f8l")]
layout_mode = 2
[node name="ShadowQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("9_1x78v")]
layout_mode = 2
[node name="GlowQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("10_m0pim")]
layout_mode = 2
[node name="SSRQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("10_cfuis")]
layout_mode = 2
[node name="SSAOQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("11_xa1gl")]
layout_mode = 2
[node name="SSILQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("12_jqvyp")]
layout_mode = 2
[node name="SDFGIQuality" parent="MarginContainer/ScrollContainer/MarginContainer/ElementList/AdvancedSettings/SubSectionElements" instance=ExtResource("13_ifeh4")]
layout_mode = 2

View File

@@ -0,0 +1,97 @@
[gd_scene format=3 uid="uid://px6a2dg8cawb"]
[ext_resource type="Script" uid="uid://houdhjao14e7" path="res://addons/modular-settings-menu/scripts/settings_menu.gd" id="1_crapp"]
[ext_resource type="PackedScene" uid="uid://c66v42g6gs7gr" path="res://addons/modular-settings-menu/scenes/settings-sections/gameplay.tscn" id="2_kjxrg"]
[ext_resource type="PackedScene" uid="uid://buqq0kms2dbb4" path="res://addons/modular-settings-menu/scenes/settings-sections/graphics.tscn" id="3_8fcn6"]
[ext_resource type="PackedScene" uid="uid://dfswr81erouhj" path="res://addons/modular-settings-menu/scenes/settings-sections/controls.tscn" id="4_3a4pu"]
[ext_resource type="PackedScene" uid="uid://dvkksl3mrnoto" path="res://addons/modular-settings-menu/scenes/settings-elements/discard_changes_popup.tscn" id="4_yfemx"]
[ext_resource type="PackedScene" uid="uid://y5oxvao3aqtb" path="res://addons/modular-settings-menu/scenes/settings-sections/audio.tscn" id="5_ayxwc"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kwbsm"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ntlmy"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_qp71p"]
[node name="Settings" type="Control" unique_id=1487060885]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
script = ExtResource("1_crapp")
[node name="SettingsPanel" type="VBoxContainer" parent="." unique_id=481915833]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -320.0
offset_top = -270.0
offset_right = 320.0
offset_bottom = 270.0
grow_horizontal = 2
grow_vertical = 2
[node name="SettingsTabs" type="TabContainer" parent="SettingsPanel" unique_id=1124467818]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_kwbsm")
current_tab = 0
[node name="Gameplay" parent="SettingsPanel/SettingsTabs" unique_id=354139496 instance=ExtResource("2_kjxrg")]
layout_mode = 2
metadata/_tab_index = 0
[node name="Graphics" parent="SettingsPanel/SettingsTabs" unique_id=1265630621 instance=ExtResource("3_8fcn6")]
visible = false
layout_mode = 2
metadata/_tab_index = 1
[node name="Controls" parent="SettingsPanel/SettingsTabs" unique_id=1352859195 instance=ExtResource("4_3a4pu")]
visible = false
layout_mode = 2
metadata/_tab_index = 2
[node name="Audio" parent="SettingsPanel/SettingsTabs" unique_id=1839468677 instance=ExtResource("5_ayxwc")]
visible = false
layout_mode = 2
metadata/_tab_index = 3
[node name="HBoxContainer" type="HBoxContainer" parent="SettingsPanel" unique_id=1987062525]
layout_mode = 2
[node name="ApplyButton" type="Button" parent="SettingsPanel/HBoxContainer" unique_id=266503706]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/focus = SubResource("StyleBoxEmpty_ntlmy")
disabled = true
text = "Apply"
[node name="BackButton" type="Button" parent="SettingsPanel/HBoxContainer" unique_id=1620563241]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/focus = SubResource("StyleBoxEmpty_qp71p")
text = "Back"
[node name="ElementPanels" type="Control" parent="." unique_id=703442684]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="DiscardChangesPopup" parent="." unique_id=1211622821 instance=ExtResource("4_yfemx")]
visible = false
layout_mode = 1
[connection signal="visibility_changed" from="." to="." method="on_visibility_changed"]
[connection signal="pressed" from="SettingsPanel/HBoxContainer/ApplyButton" to="." method="on_apply_button_pressed"]
[connection signal="pressed" from="SettingsPanel/HBoxContainer/BackButton" to="." method="on_back_button_pressed"]

View File

@@ -0,0 +1,40 @@
extends Button
class_name ButtonElement
## Reference to the element's panel scene.
@export var ElementPanelScene: PackedScene
## Reference to the node the settings element is under.
@onready var ParentRef: Node = owner
## Reference to the element's panel.
var ElementPanelRef: Node
func _ready():
# Connect necessary signals
connect("pressed", pressed)
create_element_panel()
func pressed() -> void:
# Switch panels
ParentRef.SettingsMenuRef.SettingsPanelRef.hide()
ElementPanelRef.show()
# Populate the settings cache of the panel
ElementPanelRef.get_settings()
## Called to create the element's panel.
func create_element_panel() -> void:
var ElementPanelsRef: Control = ParentRef.SettingsMenuRef.ElementPanelsRef
ElementPanelRef = ElementPanelScene.instantiate()
# Check if the element panel exists
if not ElementPanelsRef.find_child(ElementPanelRef.name):
# Give a reference of the element
ElementPanelRef.PanelOwnerRef = self
ElementPanelRef.hide()
# Add the panel to the element panels list
ElementPanelsRef.add_child(ElementPanelRef)
ElementPanelRef.set_owner(ParentRef.SettingsMenuRef)

View File

@@ -0,0 +1,67 @@
extends Control
class_name MultiElement
## A wrapper node for multi elements.
@export var MainElementRef: SettingsElement
@export var SUB_ELEMENTS_: Array[SettingsElement]
var ParentRef: SettingsSection
var currentValue :
get:
return MainElementRef.currentValue
func _enter_tree() -> void:
ParentRef = owner
ParentRef.connect("setting_changed", update_element)
ParentRef.connect("apply_button_pressed", apply_settings)
ParentRef.SettingsMenuRef.connect("changes_discarded", load_settings)
SettingsDataManager.connect("settings_retrieved", load_settings)
init_main_element()
init_sub_elements()
func init_main_element() -> void:
var elementId: String = MainElementRef.IDENTIFIER
MainElementRef.IS_MULTI_ELEMENT = true
MainElementRef.ParentRef = ParentRef
ParentRef.ELEMENT_REFERENCE_TABLE_[elementId] = MainElementRef
## Used to initialize sub elements of the multi element.
func init_sub_elements() -> void:
for ElementRef in SUB_ELEMENTS_:
ElementRef.IS_MULTI_ELEMENT = true
ElementRef.IS_SUB_ELEMENT = true
ElementRef.ParentRef = ParentRef
ParentRef.ELEMENT_REFERENCE_TABLE_[ElementRef.IDENTIFIER] = ElementRef
## Called when settings are loaded to display the appropriate elements.
func load_settings() -> void:
call_deferred("_display_sub_elements")
## Called when the main element's value changes do display the appropriate elements.
func update_element(elementId: String) -> void:
if elementId == MainElementRef.IDENTIFIER:
call_deferred("_display_sub_elements")
## Called to update the visibility of sub elements based on the main elements's current value.
## This function is overwritten by the multi element wrapper.
func _display_sub_elements() -> void:
return
## Called when the apply button is pressed.
func apply_settings() -> void:
if ParentRef.changedElements_.has(MainElementRef.IDENTIFIER):
for SubElementRef in SUB_ELEMENTS_:
if (
not SubElementRef.is_visible_in_tree()
or ParentRef.changedElements_.has(SubElementRef.IDENTIFIER)
):
continue
SubElementRef._apply_settings()

View File

@@ -0,0 +1,72 @@
extends SettingsElement
class_name OptionElement
## A settings element specifically for elements that have option buttons.
## Default value for the element.
## Value has to exist in OPTION_LIST_ otherwise the first option will be used.
@export var DEFAULT_VALUE: String
## Element node references
@export var OptionsRef: OptionButton
## List of options related to the settings element
var OPTION_LIST_
## Index of the currently selected item
var selectedIndex: int
func _ready() -> void:
super._ready()
OPTION_LIST_.make_read_only()
OptionsRef.connect("item_selected", option_selected)
func init_element() -> void:
fill_options_button()
func get_valid_values() -> Dictionary:
if not OPTION_LIST_.has(DEFAULT_VALUE):
push_warning("Invalid default value for element '" + IDENTIFIER + "'.")
if OPTION_LIST_ is Dictionary:
DEFAULT_VALUE = OPTION_LIST_.keys()[0]
else:
DEFAULT_VALUE = OPTION_LIST_[0]
return {
"defaultValue": DEFAULT_VALUE,
"validOptions": OPTION_LIST_
}
## Used to initialize the option button element.
func fill_options_button() -> void:
var index: int = 0
# Get the current item count of the option button
var itemCount: int = OptionsRef.get_item_count()
# Add the options from the received option list of the element
for option in OPTION_LIST_:
# Check if the option button has not been initialized yet
if itemCount == 0:
OptionsRef.add_item(option, index)
# Select the option that was loaded
if option == currentValue:
OptionsRef.select(index)
selectedIndex = index
index += 1
func option_selected(index: int) -> void:
# Check if the settings menu is open
if ParentRef.settingsCache_.size() > 0:
# Update the settings cache with the selected option
ParentRef.settingsCache_[IDENTIFIER] = OptionsRef.get_item_text(index)
# Check if the selected value is different than the saved value
ParentRef.settings_changed(IDENTIFIER)
# Update the element's values
currentValue = OptionsRef.get_item_text(index)
selectedIndex = index

View File

@@ -0,0 +1,205 @@
extends Control
class_name SettingsElement
## The base script for settings elements.
## Identifier for the element.
## This value is used as the key in the settings data.
@export var IDENTIFIER: String = "Element"
## Toggle based on whether the element handles a setting that requires an in game node to exist.
@export var IS_IN_GAME_SETTING: bool
## The name of the section the element is under.
@onready var SECTION: String = ParentRef.IDENTIFIER
## Reference to the section the settings element is under.
var ParentRef: SettingsSection
# Multi element flags
## Flag to turn an element into the main element of a multi element.
var IS_MULTI_ELEMENT: bool = false
## Flag to turn an element into a sub element of a multi element.
var IS_SUB_ELEMENT: bool = false
## Current value of the element.
var currentValue
func _enter_tree() -> void:
if not IS_MULTI_ELEMENT:
ParentRef = owner
func _ready() -> void:
SettingsDataManager.connect("settings_retrieved", load_settings)
# Check if the element is a sub element
if not IS_SUB_ELEMENT:
ParentRef.connect("apply_button_pressed", _apply_settings)
# Add an entry of the settings element to the section's reference table
ParentRef.ELEMENT_REFERENCE_TABLE_[IDENTIFIER] = self
## Used to initialize a settings element.
## This function is overwritten by each [b]type[/b] of element.
func init_element() -> void:
return
## Loads the saved or default value of the element.
func load_settings() -> void:
# List of valid values for the element
var VALUES_: Dictionary = get_valid_values()
VALUES_.make_read_only()
# Check if no save file exists
if SettingsDataManager.noSaveFile:
# Assign default value as current value
currentValue = VALUES_["defaultValue"]
# Add default value of element to the settings data
SettingsDataManager.settingsData_[SECTION][IDENTIFIER] = currentValue
else:
# Verify the existance and validity of the element in the settings data
if verify_settings_data(VALUES_):
# Get the current value from the settings data
currentValue = SettingsDataManager.settingsData_[SECTION][IDENTIFIER]
else:
# Assign default value as current value
currentValue = VALUES_["defaultValue"]
# Add default value of the element to the settings data
SettingsDataManager.settingsData_[SECTION][IDENTIFIER] = currentValue
SettingsDataManager.invalidSaveFile = true
init_element()
# Check if the current element is in an in game menu or if it is a sub element
if (
ParentRef.SettingsMenuRef.IS_IN_GAME_MENU == IS_IN_GAME_SETTING
or not IS_SUB_ELEMENT
):
# Apply the loaded values to the game
call_deferred("_apply_settings")
## Used to get the valid values an element can have for validating settings data.
## This function is overwritten by each [b]type[/b] of element.
func get_valid_values() -> Dictionary:
return {}
## Checks if the loaded values are valid for the element.
## If a value is wrong, it will be fixed automatically.
func verify_settings_data(VALUES_: Dictionary) -> bool:
# Check if an entry exists for the element
if not entry_exists():
return false
# Get the value of the element
var RETRIEVED_VALUE = SettingsDataManager.settingsData_[SECTION][IDENTIFIER]
# Check if the retrieved value is the correct type
if not is_valid_type(VALUES_, RETRIEVED_VALUE):
return false
# Check if the retrieved value has the expected value
if not is_valid_value(VALUES_, RETRIEVED_VALUE):
return false
return true
## Used by verify_settings_data() to check if the element
## and the section it is under exists in the settings data.
func entry_exists() -> bool:
# Check if the section exists in settings data
if not SettingsDataManager.settingsData_.has(SECTION):
push_warning("Settings section missing: ", SECTION)
return false
# Check if the element exists in the settings data
if not SettingsDataManager.settingsData_[SECTION].has(IDENTIFIER):
push_warning("Settings element is missing: ", IDENTIFIER)
return false
return true
## Used by verify_settings_data() to check if the retrieved value has the correct type.
func is_valid_type(VALUES_: Dictionary, RETRIEVED_VALUE) -> bool:
if typeof(RETRIEVED_VALUE) != typeof(VALUES_["defaultValue"]):
push_warning(
"Invalid value type of '"
+ type_string(typeof(RETRIEVED_VALUE))
+ "' for element '"
+ IDENTIFIER
+ "' expected value type of '"
+ type_string(typeof(VALUES_["defaultValue"]))
+ "'"
)
return false
return true
## Used by verify_settings_data() to check if the retrieved value has a valid value.
func is_valid_value(VALUES_: Dictionary, RETRIEVED_VALUE) -> bool:
# Get the type of the valid value
match typeof(VALUES_["defaultValue"]):
# If the type is either string or bool
TYPE_STRING, TYPE_BOOL:
# Check if the retrieved value is valid
if not VALUES_["validOptions"].has(RETRIEVED_VALUE):
push_warning(
"Invalid value '"
+ str(RETRIEVED_VALUE)
+ "' for element '"
+ IDENTIFIER
+ "' expected values: "
+ str(VALUES_["validOptions"])
)
return false
# If the type is either int or float
TYPE_INT, TYPE_FLOAT:
# Check if the retrieved value is valid
if (
RETRIEVED_VALUE < VALUES_["minValue"]
or RETRIEVED_VALUE > VALUES_["maxValue"]
):
# Special check if max fps is set to 0 (unlimited)
if IDENTIFIER == "MaxFPS" and RETRIEVED_VALUE == 0:
return true
push_warning(
"Invalid value "
+ str(RETRIEVED_VALUE)
+ " for element '"
+ IDENTIFIER
+ "' expected values between "
+ str(VALUES_["minValue"])
+ " and "
+ str(VALUES_["maxValue"])
)
return false
return true
## Used to apply in game settings that require a node to exist to be applied,
## i.e., world environment related settings and or most gameplay settings.
func apply_in_game_setting(value = null) -> bool:
if ParentRef.SettingsMenuRef.IS_IN_GAME_MENU:
SettingsDataManager.call_deferred(
"emit_signal",
"applied_in_game_setting",
SECTION,
IDENTIFIER,
value
)
return true
return false
## Called to apply the setting to the game.
## This function is overwritten by each [b]element[/b].
func _apply_settings() -> void:
return

View File

@@ -0,0 +1,96 @@
extends SettingsElement
class_name SliderElement
## A settings element specifically for elements that have a slider.
# Default values for the element
@export var MIN_VALUE: float = 0
@export var MAX_VALUE: float = 1
@export var STEP_VALUE: float = 0.1
@export var DEFAULT_VALUE: float = 1
## If true, displays 0 to 100 instead of 0 to 1 in the settings,
## but the true value remains the same.
@export var DISPLAY_PERCENT_VALUE: bool = false
## An extra suffix for the value (optional).
@export var VALUE_SUFFIX: String = ""
## Reference to the slider of the element.
@export var SliderRef: HSlider
## Reference to the SpinBox or Label of the element.
@export var ValueBoxRef: Control
## Overwrite for SettingsElement.
func init_element() -> void:
init_slider(100 if DISPLAY_PERCENT_VALUE else 1)
## Called to initialize the slider element.
func init_slider(FACTOR: float) -> void:
# Apply the min/max/step/current value of the SliderRef
SliderRef.set_min(MIN_VALUE * FACTOR)
SliderRef.set_max(MAX_VALUE * FACTOR)
SliderRef.set_step(STEP_VALUE * FACTOR)
SliderRef.set_value(currentValue * FACTOR)
# Connect the value changed signal for the SliderRef
if not SliderRef.is_connected("value_changed", slider_value_changed):
SliderRef.connect("value_changed", slider_value_changed.bind(FACTOR))
# Check if the value box is a spin box or a label
if ValueBoxRef is SpinBox:
# Apply the min/max/step/current value of the spin box
ValueBoxRef.set_min(MIN_VALUE * FACTOR)
ValueBoxRef.set_max(MAX_VALUE * FACTOR)
ValueBoxRef.set_step(STEP_VALUE * FACTOR)
ValueBoxRef.set_value(currentValue * FACTOR)
# Add caret blink to spin box
ValueBoxRef.get_line_edit().set_caret_blink_enabled(true)
ValueBoxRef.set_suffix(VALUE_SUFFIX)
# Connect the value changed signal of the spin box
if not ValueBoxRef.is_connected("value_changed", value_box_value_changed):
ValueBoxRef.connect("value_changed", value_box_value_changed)
else:
# Set the text as the current value
ValueBoxRef.set_text(str(currentValue) + VALUE_SUFFIX)
## Gets the valid values from the element to be used for validating data.
func get_valid_values() -> Dictionary:
# Check if value is out of bounds
if DEFAULT_VALUE > MAX_VALUE or DEFAULT_VALUE < MIN_VALUE:
push_warning("Invalid default value for element '" + IDENTIFIER + "'.")
DEFAULT_VALUE = clampf(DEFAULT_VALUE, MIN_VALUE, MAX_VALUE)
return {
"defaultValue": DEFAULT_VALUE,
"minValue": MIN_VALUE,
"maxValue": MAX_VALUE,
}
## Used to update values of the section cache the element is under.
func value_changed(value: float) -> void:
# Check if the settings menu is open
if ParentRef.settingsCache_.size() > 0:
ParentRef.settingsCache_[IDENTIFIER] = value
# Check if the new value is different than the saved value
ParentRef.settings_changed(IDENTIFIER)
currentValue = value
func slider_value_changed(value: float, FACTOR: float) -> void:
if ValueBoxRef is SpinBox:
ValueBoxRef.set_value(value)
else:
ValueBoxRef.set_text(str(value) + VALUE_SUFFIX)
value_changed(value / FACTOR)
func value_box_value_changed(value: float) -> void:
SliderRef.set_value(value)

View File

@@ -0,0 +1,38 @@
extends SettingsElement
class_name ToggleElement
## A settings element specifically for elements that have a toggle button.
## Default value for the element
@export var DEFAULT_VALUE: bool = false
## Reference to the toggle button of the element.
@export var ToggleRef: Button
func _ready() -> void:
super._ready()
ToggleRef.connect("toggled", toggled)
## Overwrite for SettingsElement.
func init_element() -> void:
ToggleRef.set_pressed(currentValue)
## Gets the valid values from the element to be used for validating data.
func get_valid_values() -> Dictionary:
return {
"defaultValue": DEFAULT_VALUE,
"validOptions": [true, false]
}
## Used to update values of the section cache the element is under.
func toggled(state: bool) -> void:
# Check if the settings menu is open
if ParentRef.settingsCache_.size() > 0:
# Update the settings cache with the new toggle state
ParentRef.settingsCache_[IDENTIFIER] = state
# Check if the new state is different than the saved state
ParentRef.settings_changed(IDENTIFIER)
currentValue = state

View File

@@ -0,0 +1,54 @@
extends SettingsSection
# Panel node references
@onready var BackButtonRef: Button = $VBoxContainer/HBoxContainer/BackButton
@onready var ApplyButtonRef: Button = $VBoxContainer/HBoxContainer/ApplyButton
# Reference to the element this panel belongs to
var PanelOwnerRef: ButtonElement
func _ready():
# Connect neccessary signals
BackButtonRef.connect("pressed", on_back_pressed)
ApplyButtonRef.connect("pressed", on_apply_settings)
# Add a reference of the section to the reference table
SettingsDataManager.ELEMENT_PANEL_REFERENCE_TABLE_[IDENTIFIER] = self
# Check if a save file exists
if SettingsDataManager.noSaveFile:
# Add the section to the settings data dictionary
SettingsDataManager.settingsData_[IDENTIFIER] = {}
SettingsMenuRef = owner
# Load the settings of the elements inside of the panel
call_deferred("init_elements")
# Called to load the settings of the elements inside of the panel
func init_elements() -> void:
for element in ELEMENT_REFERENCE_TABLE_:
ELEMENT_REFERENCE_TABLE_[element].load_settings()
func settings_changed(elementId: String) -> void:
ApplyButtonRef.set_disabled(check_for_changes(elementId))
func on_back_pressed():
# Check if there have been any changes made
if ApplyButtonRef.is_disabled():
# Clear the cache and return normally
settingsCache_.clear()
hide()
SettingsMenuRef.SettingsPanelRef.show()
else:
SettingsMenuRef.display_discard_changes(self)
func on_apply_settings():
super.on_apply_settings()
# Save the updated settings data
SettingsDataManager.call_deferred("save_data")
ApplyButtonRef.set_disabled(true)

View File

@@ -0,0 +1 @@
uid://cyig515shrgnb

View File

@@ -0,0 +1,13 @@
extends SliderElement
## Name of the audio bus that the volume slider is assigned to.
@export var AUDIO_BUS: String
# Element specific script for applying its value to the game
func _apply_settings() -> void:
# Get the index of the audio bus
var busIndex: int = AudioServer.get_bus_index(AUDIO_BUS)
# Set the volume of the audio bus
AudioServer.set_bus_volume_db(busIndex, linear_to_db(currentValue))

View File

@@ -0,0 +1,20 @@
extends Node
@export var CameraRef: Camera3D
func _ready():
# Connect neccessary signal
SettingsDataManager.connect("applied_in_game_setting", apply_in_game_settings)
# Called to apply in game settings for the specific node
func apply_in_game_settings(section: String, element: String, value) -> void:
match element:
"FOV":
CameraRef.set_fov(value)
"DepthOfField":
var enabled: bool = false if value == "Disabled" else true
# Disable/Enable DOF
CameraRef.attributes.set_dof_blur_far_enabled(enabled)
CameraRef.attributes.set_dof_blur_near_enabled(enabled)

View File

@@ -0,0 +1,42 @@
extends Node
@export var WorldEnvRef: WorldEnvironment
@onready var EnvironmentRef: Environment = WorldEnvRef.environment
func _ready():
SettingsDataManager.connect("applied_in_game_setting", apply_in_game_settings)
# Called by elements to apply in game settings
func apply_in_game_settings(section: String, element: String, value) -> void:
match element:
"SSRQuality":
if SettingsDataManager.settingsData_[section][element] == "Disabled":
EnvironmentRef.set_ssr_enabled(false)
return
EnvironmentRef.set_ssr_enabled(true)
EnvironmentRef.set_ssr_max_steps(value["maxSteps"])
EnvironmentRef.set_ssr_fade_in(value["fadeIn"])
EnvironmentRef.set_ssr_fade_out(value["fadeOut"])
"SSAOQuality":
EnvironmentRef.set_ssao_enabled(
false if value == "Disabled" else true
)
"SSILQuality":
EnvironmentRef.set_ssil_enabled(
false if value == "Disabled" else true
)
"SDFGIQuality":
EnvironmentRef.set_sdfgi_enabled(
false if value == "Disabled" else true
)
"GlowQuality":
EnvironmentRef.set_glow_enabled(
false if value == "Disabled" else true
)

View File

@@ -0,0 +1,83 @@
extends Control
class_name SettingsMenu
## Emitted when the settings menu is made visible.
signal settings_menu_opened
## Emitted when the apply button is pressed.
signal apply_button_pressed
## Emitted when the settings menu is hidden.
signal settings_menu_closed
## Emitted when changes are discarded.
signal changes_discarded
## Used to check if gameplay related settings should be applied
@export var IS_IN_GAME_MENU: bool = true
## Reference to the parent node of the menu UI.
## The node this references will get set visible when pressing the back button.
@export var MenuPanelRef: Node
## List of settings sections that should be left out of the settings menu instance.
@export var IGNORED_SECTIONS_: Array[String]
@onready var DiscardChangesRef: PanelContainer = $DiscardChangesPopup
@onready var SettingsPanelRef: VBoxContainer = $SettingsPanel
@onready var ApplyButtonRef: Button = %ApplyButton
var ElementPanelsRef: Control
var SettingsTabsRef: TabContainer
func _enter_tree() -> void:
ElementPanelsRef = $ElementPanels
SettingsTabsRef = $SettingsPanel/SettingsTabs
ignore_sections()
func _ready():
# Load the settings menu
SettingsDataManager.call_deferred("emit_signal", "settings_retrieved")
func on_back_button_pressed() -> void:
# Check if there have been any changes made
if ApplyButtonRef.is_disabled():
hide()
MenuPanelRef.show()
# Clear the settings cache
emit_signal("settings_menu_closed")
else:
# Display the discard changes popup
display_discard_changes(self)
func on_apply_button_pressed() -> void:
ApplyButtonRef.set_disabled(true)
# Apply the settings of each section
emit_signal("apply_button_pressed")
# Save the updated settings data
SettingsDataManager.call_deferred("save_data")
# Reset the changed elements count
SettingsDataManager.changedElementsCount = 0
func on_visibility_changed() -> void:
if is_visible_in_tree():
emit_signal("settings_menu_opened")
# Called to discard the changes in the settings menu
func discard_changes() -> void:
emit_signal("changes_discarded")
for SectionRef in SettingsTabsRef.get_children():
SectionRef.discard_changes()
func display_discard_changes(CallerRef: Control) -> void:
DiscardChangesRef.show()
DiscardChangesRef.CallerRef = CallerRef
func ignore_sections() -> void:
for SectionRef in SettingsTabsRef.get_children():
if IGNORED_SECTIONS_.has(SectionRef.IDENTIFIER):
SectionRef.queue_free()

View File

@@ -0,0 +1 @@
uid://houdhjao14e7

View File

@@ -0,0 +1,123 @@
extends Control
class_name SettingsSection
## The base script for settings sections.
## Emitted when the apply button is pressed.
signal apply_button_pressed
## Emitted when a setting has it's value changed.
signal setting_changed(elementId: String)
## Identifier for the section.
## This value is used as the key in the settings data.
@export var IDENTIFIER: String
## Reference to the settings menu node.
var SettingsMenuRef: SettingsMenu
## Reference table of all elements under the section.
var ELEMENT_REFERENCE_TABLE_: Dictionary
## Cache of all the settings values for the section.
var settingsCache_: Dictionary
## A list of all the elements that were changed since the settings were last applied.
var changedElements_: Array[String]
func _enter_tree() -> void:
SettingsMenuRef = owner
func _ready():
# Connect neccessary signals from the central root node for the settings
SettingsMenuRef.connect("settings_menu_opened", get_settings)
SettingsMenuRef.connect("apply_button_pressed", on_apply_settings)
SettingsMenuRef.connect("settings_menu_closed", clear_cache)
# Add a reference of the section to the reference table
SettingsDataManager.SECTION_REFERENCE_TABLE_[IDENTIFIER] = self
# Check if a save file exists
if SettingsDataManager.noSaveFile:
# Add the section to the settings data dictionary
SettingsDataManager.settingsData_[IDENTIFIER] = {}
## Called when opening the settings menu to fill the settings cache.
func get_settings() -> void:
# Copy the settings data for the section into it's cache
settingsCache_ =\
SettingsDataManager.settingsData_[IDENTIFIER].duplicate(true)
# If no save file exists saves the default values retrieved from the section's elements
if SettingsDataManager.noSaveFile or SettingsDataManager.invalidSaveFile:
SettingsDataManager.call_deferred("save_data")
# Clear the changed elements array
changedElements_.clear()
## Called to clear the section's cache.
func clear_cache() -> void:
settingsCache_.clear()
## Called when a setting has been changed.
func settings_changed(elementId: String) -> void:
SettingsMenuRef.ApplyButtonRef.set_disabled(check_for_changes(elementId))
emit_signal("setting_changed", elementId)
## Called to check for changes between the cache and the settings data.
func check_for_changes(elementId: String) -> bool:
var cacheValue = settingsCache_[elementId]
var savedValue = SettingsDataManager.settingsData_[IDENTIFIER][elementId]
# Check if there are differences between the cache and the settings data
if cacheValue == savedValue:
# Check if the element is on the changed elements list
if changedElements_.has(elementId):
# Remove the element from the list
changedElements_.erase(elementId)
# Decrease the changed elements count
SettingsDataManager.changedElementsCount -= 1
# Check if there are any other changed elements
if SettingsDataManager.changedElementsCount == 0:
# Disabled the apply button
return true
# Check if the element is not the changed elements list
if not changedElements_.has(elementId):
# Add the element to the list
changedElements_.append(elementId)
# Increase the changed elements count
SettingsDataManager.changedElementsCount += 1
# Enable the apply button
return false
## Called to saved the data in the section's cache to the settings data
## and apply the settings to the game.
func on_apply_settings() -> void:
# Check if any of the sections elements have been changed
if changedElements_.size() > 0:
# Copy the section cache into the settings data dictionary
SettingsDataManager.settingsData_[IDENTIFIER] = settingsCache_.duplicate(true)
# Apply the settings for the changed elements
for element in changedElements_:
ELEMENT_REFERENCE_TABLE_[element]._apply_settings()
# Clear the changed elements array
changedElements_.clear()
## Called to discard changes that have been made since the last save.
func discard_changes() -> void:
# Check if any of the sections elements have been changed
if changedElements_.size() > 0:
# Load the saved settings for each element in the section
for element in changedElements_:
ELEMENT_REFERENCE_TABLE_[element].load_settings()
# Clear the changed elements array
changedElements_.clear()

View File

@@ -0,0 +1 @@
uid://c3ssroxjs8wt

View File

@@ -0,0 +1,212 @@
extends Node
## Handles the loading and saving of settings data.
## Emitted when loading the settings data.
signal settings_retrieved
## Emitted when applying specific settings to in game objects.
signal applied_in_game_setting(section: String, setting: String, value)
## PATH to the settings save file.
var DATA_FOLDER: String = OS.get_user_data_dir()
## Name of the file that the settings data is saved in.
const FILE_NAME: String = "settings"
## Extension for the save file.
const FILE_EXTENSION: String = ".cfg"
## The PATH to the save file on the computer.
var PATH: String = DATA_FOLDER + "/" + FILE_NAME + FILE_EXTENSION
## Dictionary that stores all settings data.
var settingsData_: Dictionary
## A reference table of all sections.
var SECTION_REFERENCE_TABLE_: Dictionary
## A reference table of all element panels.
var ELEMENT_PANEL_REFERENCE_TABLE_: Dictionary
## The number of elements that have been changed since the settings have been saved.
var changedElementsCount: int = 0
## Flag for checking if a save file exists.
var noSaveFile: bool
## Flag for checking if an invalid value was found in the save file.
var invalidSaveFile: bool = false
func _ready() -> void:
# Verify the directory
DirAccess.make_dir_absolute(DATA_FOLDER)
# Check if a save file exists
if FileAccess.file_exists(PATH):
# Proceed normally by retrieving data from the save file
call_deferred("get_data")
else:
# Enable the no save file flag
noSaveFile = true
push_warning("No save file found")
## Called to save the settings data to the save file.
func save_data() -> void:
# Create a new config instance
var config := ConfigFile.new()
# Add the data from the settings data dictionary
for section in settingsData_:
for element in settingsData_[section]:
config.set_value(section, element, settingsData_[section][element])
# Save the data to the specified directory
var err = config.save(PATH)
# Check for errors
if err != OK:
push_error("Failed to save data_: ", err)
return
# Disable the no save file flag if it was enabled
if noSaveFile:
noSaveFile = false
# Disable the invalid save file flag if it was enabled
if invalidSaveFile:
invalidSaveFile = false
## Called to retrieve data from the save file.
func get_data() -> void:
# Create a new config instance
var config := ConfigFile.new()
# Load the save data
var err := config.load(PATH)
# Temporary data dictionary
var data_: Dictionary = {}
# Check for errors
if err != OK:
push_error("Failed to load settings data_: ", err)
return
# Add the retrieved data to the settings data dictionary
for section in config.get_sections():
data_[section] = {}
for key in config.get_section_keys(section):
data_[section][key] = config.get_value(section, key)
# Verify the validity of the loaded data
verify_settings_data_(data_)
# Copy the retrieved data into the settings data dictionary
settingsData_ = data_.duplicate(true)
## Checks if the save file has any invalid entries and removes them or adds missing sections.
func verify_settings_data_(data_: Dictionary) -> void:
# List of valid entries to compare to
var validEntries_: Dictionary = SECTION_REFERENCE_TABLE_.duplicate()
# Merge the element panel references into the valid entries
validEntries_.merge(ELEMENT_PANEL_REFERENCE_TABLE_)
# List of invalid entries to be removed
var invalidEntries_: Dictionary = {}
# Itterate through the loaded settings data
for section in data_:
if is_valid_section(invalidEntries_, section):
verify_elements(data_, invalidEntries_, section)
# Check if there are any sections missing from the retrieved data
check_for_missing_sections(data_, validEntries_)
# Check if there are any invalid entries
if invalidEntries_.size() > 0:
# Set the invalid save file flag to true
invalidSaveFile = true
remove_invalid_entries(data_, invalidEntries_, validEntries_)
## Used by the verify_settings_data() function to verify the retrieved sections.
func is_valid_section(invalidEntries_: Dictionary, SECTION: String) -> bool:
# Check if the section is in either of the reference tables
if (
SECTION_REFERENCE_TABLE_.has(SECTION)
or ELEMENT_PANEL_REFERENCE_TABLE_.has(SECTION)
):
return true
# Add the invalid section to the invalid entries list
invalidEntries_[SECTION] = []
push_warning("Invalid section '", SECTION, "' found.")
return false
## Used by the verify_settings_data() function to verify the elements inside of the retrieved sections.
func verify_elements(
data_: Dictionary,
invalidEntries_: Dictionary,
SECTION: String
) -> void:
# Array of all elements under the section
var VALID_SECTION_ELEMENTS: Array = get_valid_elements(SECTION)
# Itterate through all the elements in the section
for element in data_[SECTION]:
# Check for invalid elements
if not VALID_SECTION_ELEMENTS.has(element):
# Check if the element is in a valid section
if not invalidEntries_.has(SECTION):
# Add the section to the invalid entries list
invalidEntries_[SECTION] = []
# Add the invalid element to the invalid entries list
invalidEntries_[SECTION].append(element)
push_warning(
"Invalid element '"
+ element
+ "' found in section '"
+ SECTION
+ "'."
)
## Used by the verify_elements() function to retrieve the valid elements for the retrieved section.
func get_valid_elements(SECTION: String) -> Array:
# Check if the section is a settings section
if SECTION_REFERENCE_TABLE_.has(SECTION):
return SECTION_REFERENCE_TABLE_[SECTION].ELEMENT_REFERENCE_TABLE_.keys()
# Check if the section is an element panel
if ELEMENT_PANEL_REFERENCE_TABLE_.has(SECTION):
return ELEMENT_PANEL_REFERENCE_TABLE_[SECTION].ELEMENT_REFERENCE_TABLE_.keys()
return []
## Used by the verify_settings_data() function to check if any expected sections are missing.
func check_for_missing_sections(data_: Dictionary, validEntries_: Dictionary) -> void:
# Itterate through all valid sections
for section in validEntries_:
# Check if the section is missing from the loaded data_
if not data_.has(section):
# Add an empty entry for the section
data_[section] = {}
# Set the invalid save file flag to true
invalidSaveFile = true
push_warning("Settings section is missing: ", section)
## Used by the verify_settings_data() function to remove invalid entires from the retrieved data.
func remove_invalid_entries(
data_: Dictionary,
invalidEntries_: Dictionary,
validEntries_: Dictionary
) -> void:
# Itterate through the sections in the invalid entries list
for section in invalidEntries_:
# Check if the section is valid
if validEntries_.has(section):
# Itterate through the invalid elements
for element in invalidEntries_[section]:
# Remove the invalid element
data_[section].erase(element)
else:
# Remove the invalid section
data_.erase(section)

View File

@@ -0,0 +1 @@
uid://c70d4w5cws2at

View File

@@ -1,24 +1,121 @@
extends Node
# Main scene
var mainSceneInstance
# Screen
var screen_size = Vector2(1920.0, 1080.0) # Default screen size (this is a float for some reason)
var viewport_size
@onready var extent: Rect2 = get_viewport().get_visible_rect()
var extent: Rect2
# utils.
var rng = RandomNumberGenerator.new()
var eps: float = 1e-4
var legacy_movement: bool = false
# managers
var foodManager: FoodManager2D
# A world "current"
# TODO: This should be moved to a different "game manager" specific to the molecular stage
# polar
var flow_dir: float # [0, 2pi)
var flow_mag: float # [0, 1]
# cartesian
var flow_x: float
var flow_y: float
# Swap period
var flowT: float = 10
# A game timer
var t: float = 0.0
func _ready() -> void:
# Start game
mainSceneInstance = get_tree().root.get_child(-1)
# Create viewport
viewport_size = get_viewport().get_visible_rect().size
# initial world current
get_new_flow()
func _physics_process(delta: float) -> void:
t += delta
# Flow current change
if abs(fmod(t, flowT)) < eps:
get_new_flow()
func switch_scene(name:String) -> void:
if name == "respawn":
mainSceneInstance.go_to_respawn_scene()
# TODO: This needs to be called from a script inheriting a CharacterBody2D (e.g. the player)
# Alternative would be to pass the player reference to this script (which might be better?)
func init_screen_size(x:float, y:float) -> void:
screen_size.x = x
screen_size.y = y
extent = Rect2(Vector2(-viewport_size.x/2, -viewport_size.y/2), viewport_size + screen_size)
# This can take a vector of any size (but should be 2d, other components are unused)
func get_boundaried_position(position):
## clamp
# clamp
#return position.clamp(Vector2.ZERO, screen_size)
## periodic
position.x = wrapf(position.x, 0, screen_size.x)
position.y = wrapf(position.y, 0, screen_size.y)
# periodic
position.x = wrapf(position.x, -viewport_size.x/2 , screen_size.x + viewport_size.x/2)
position.y = wrapf(position.y, -viewport_size.y/2, screen_size.y + viewport_size.y/2)
return position
func get_new_flow():
flow_dir = rng.randf()*2*PI
flow_mag = rng.randf()
flow_x = flow_mag * cos(flow_dir)
flow_y = flow_mag * sin(flow_dir)
func calc_distance(one, two) -> float:
var candidate = one.distance_to(two)
var onedup = one
if one.x < screen_size.x/2:
if one.y < screen_size.y/2:
# top left
onedup.y -= screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y += screen_size.y
onedup.x -= screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
# bottom left
onedup.y += screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y -= screen_size.y
onedup.x -= screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
if one.y < screen_size.y/2:
# top right
onedup.y -= screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y += screen_size.y
onedup.x += screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
# bottom right
onedup.y += screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y -= screen_size.y
onedup.x += screen_size.x
candidate = min(candidate, onedup.distance_to(two))
return candidate
func change_window_size(width: int, height: int) -> void:
# Do NOT remove this Godot, ffs!!!
var newSize = Vector2i(width, height)

View File

@@ -0,0 +1,3 @@
[gd_scene format=3 uid="uid://bsrxph8oa7uuj"]
[node name="Main" type="Node" unique_id=1994328839]

View File

@@ -1,5 +0,0 @@
extends Control
func _on_play_button_pressed() -> void:
get_tree().change_scene_to_file("res://molecular/molecular_stage.tscn")

View File

@@ -1 +0,0 @@
uid://dxc66bci2ivrj

View File

@@ -1,36 +1,65 @@
[gd_scene load_steps=3 format=3 uid="uid://drgv154ei1vrl"]
[gd_scene format=3 uid="uid://drgv154ei1vrl"]
[ext_resource type="Script" uid="uid://dxc66bci2ivrj" path="res://main_menu.gd" id="1_06t4h"]
[ext_resource type="PackedScene" uid="uid://px6a2dg8cawb" path="res://addons/modular-settings-menu/scenes/settings.tscn" id="1_06t4h"]
[sub_resource type="GDScript" id="GDScript_rhts7"]
script/source = "extends Control
func _on_play_button_pressed() -> void:
print(\"Starting game by pressing button...\")
GameManager.start_game()
self.queue_free()
"
[sub_resource type="LabelSettings" id="LabelSettings_rhts7"]
font_size = 64
[node name="MainMenu" type="Control"]
[node name="MainMenu" type="Control" unique_id=369570860]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_06t4h")
script = SubResource("GDScript_rhts7")
[node name="PlayButton" type="Button" parent="."]
layout_mode = 0
offset_left = 448.0
offset_top = 256.0
offset_right = 768.0
offset_bottom = 314.0
[node name="PlayButton" type="Button" parent="." unique_id=1831335357]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -160.0
offset_top = -29.0
offset_right = 160.0
offset_bottom = 29.0
grow_horizontal = 2
grow_vertical = 2
text = "Play"
[node name="MainMenuText" type="Label" parent="."]
layout_mode = 0
offset_left = 320.0
offset_top = 32.0
offset_right = 904.0
offset_bottom = 151.0
[node name="MainMenuText" type="Label" parent="." unique_id=1324657553]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -292.0
offset_right = 292.0
offset_bottom = 119.0
grow_horizontal = 2
text = "The Main Menu"
label_settings = SubResource("LabelSettings_rhts7")
horizontal_alignment = 1
vertical_alignment = 1
[connection signal="pressed" from="PlayButton" to="." method="_on_play_button_pressed"]
[node name="Settings" parent="." unique_id=112964493 node_paths=PackedStringArray("MenuPanelRef") instance=ExtResource("1_06t4h")]
layout_mode = 1
MenuPanelRef = NodePath("../SettingsWheelButton")
[node name="SettingsWheelButton" type="Button" parent="." unique_id=2147291321]
layout_mode = 0
offset_left = 898.0
offset_top = 46.0
offset_right = 954.0
offset_bottom = 93.0

View File

@@ -0,0 +1,61 @@
extends Node
# Main menu & game spawning
@onready
var mainMenuScene = preload("res://main_menu.tscn")
var mainMenuSceneInstance: Node
@onready
var gameScene = preload("res://molecular/molecular_stage.tscn")
var gameSceneInstance: Node
@onready
var respawnScene = preload("res://respawn_menu.tscn")
var respawnSceneInstance: Node
# Currently active scene (instance)
var currentSceneInstance:Node
# UI effects
var fadeEffect: CanvasLayer
func _ready() -> void:
# Instantiate effects
fadeEffect = $UI/Fade
fadeEffect.visible = false
# Create game (main menu)
print("Creating Main menu...")
mainMenuSceneInstance = mainMenuScene.instantiate()
currentSceneInstance = mainMenuSceneInstance
add_child(currentSceneInstance)
# Link MainMenu button to start_game
print("Linking Play button...")
mainMenuSceneInstance.get_node("PlayButton").connect("pressed", start_game)
func start_game() -> void:
print("Starting game...")
# Instatiate
currentSceneInstance.queue_free()
gameSceneInstance = gameScene.instantiate()
currentSceneInstance = gameSceneInstance
add_child(currentSceneInstance)
# Populate GameManager with game scene
GameManager.foodManager = gameSceneInstance.get_node("FoodManager")
func go_to_respawn_scene() -> void:
fadeEffect.visible = true
await fadeEffect.fade(1.0, 1.5).finished
print("Switching to Respawn scene.")
currentSceneInstance.queue_free()
respawnSceneInstance = respawnScene.instantiate()
respawnSceneInstance.get_node("RespawnButton").connect("pressed", start_game)
currentSceneInstance = respawnSceneInstance
add_child(currentSceneInstance)
await fadeEffect.fade(0.0, 1.5).finished
fadeEffect.visible = false

View File

@@ -0,0 +1 @@
uid://wiv0plsu04s

View File

@@ -0,0 +1,11 @@
[gd_scene format=3 uid="uid://b8gt8os2m28lv"]
[ext_resource type="Script" uid="uid://wiv0plsu04s" path="res://main_scene.gd" id="1_2c62f"]
[ext_resource type="PackedScene" uid="uid://3eg3a8ceom4l" path="res://molecular/fade.tscn" id="2_2c62f"]
[node name="MainScene" type="Node" unique_id=1897047571]
script = ExtResource("1_2c62f")
[node name="UI" type="Node2D" parent="." unique_id=1032224503]
[node name="Fade" parent="UI" unique_id=853660457 instance=ExtResource("2_2c62f")]

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://xaqiha5lh1vt"
path="res://.godot/imported/food-left-bolt.png-a2624c23f9c063886727de28bb8d45db.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/food/food-left-bolt.png"
dest_files=["res://.godot/imported/food-left-bolt.png-a2624c23f9c063886727de28bb8d45db.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dj4lyyloj1ke1"
path="res://.godot/imported/food-left-bottom-bolt.png-29b52334cce2caefb211e507cb88e1c1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/food/food-left-bottom-bolt.png"
dest_files=["res://.godot/imported/food-left-bottom-bolt.png-29b52334cce2caefb211e507cb88e1c1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dcedkos6vgv51"
path="res://.godot/imported/food-normal.png-b719473d7bef8a09701e78ab0f73d15b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/food/food-normal.png"
dest_files=["res://.godot/imported/food-normal.png-b719473d7bef8a09701e78ab0f73d15b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbynycukppmup"
path="res://.godot/imported/food-right-bottom-bolt.png-8eb8a35372e35c8de0cc432876ae5af5.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/food/food-right-bottom-bolt.png"
dest_files=["res://.godot/imported/food-right-bottom-bolt.png-8eb8a35372e35c8de0cc432876ae5af5.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cend5h3nmvq5l"
path="res://.godot/imported/food-right-up-bolt.png-c49edf3849bcaa575384d233173c9498.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/food/food-right-up-bolt.png"
dest_files=["res://.godot/imported/food-right-up-bolt.png-c49edf3849bcaa575384d233173c9498.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://du1lb4nr47kvl"
path="res://.godot/imported/player-sprite-attacking.png-6189c8275a257517c87cdb65abf5639a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/player-sprite-attacking.png"
dest_files=["res://.godot/imported/player-sprite-attacking.png-6189c8275a257517c87cdb65abf5639a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bsd1qtw50esai"
path="res://.godot/imported/player-sprite-attacking2.png-3e4e9a47129b5ac21e61c82e331ba03f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/player-sprite-attacking2.png"
dest_files=["res://.godot/imported/player-sprite-attacking2.png-3e4e9a47129b5ac21e61c82e331ba03f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cd0iv6hsi3fad"
path="res://.godot/imported/player-sprite.png-f2c28aefaf4a8e7293c46f17a4267bc1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://molecular/assets/player-sprite.png"
dest_files=["res://.godot/imported/player-sprite.png-f2c28aefaf4a8e7293c46f17a4267bc1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Some files were not shown because too many files have changed in this diff Show More