├── addons └── ggs │ ├── plugin.gd.uid │ ├── core │ ├── component.gd.uid │ ├── setting.gd.uid │ ├── plugin_settings.gd.uid │ ├── save_manager.gd.uid │ ├── utilities │ │ ├── input_utils.gd.uid │ │ ├── window_utils.gd.uid │ │ ├── window_utils.gd │ │ └── input_utils.gd │ ├── global_manager │ │ ├── global_manager.gd.uid │ │ ├── global_manager.tscn │ │ └── global_manager.gd │ ├── input_databases │ │ ├── input_glyph_db.gd.uid │ │ ├── input_text_db.gd.uid │ │ ├── input_glyph_db.gd │ │ └── input_text_db.gd │ ├── setting.gd │ ├── plugin_settings.gd │ ├── save_manager.gd │ └── component.gd │ ├── plugin.cfg │ ├── plugin.gd │ ├── base_node.svg │ └── base_node.svg.import ├── ggs ├── scripts │ ├── display │ │ ├── setting_vsync.gd.uid │ │ ├── setting_maxfps.gd.uid │ │ ├── setting_display_mode.gd.uid │ │ ├── setting_display_scale.gd.uid │ │ ├── setting_display_size.gd.uid │ │ ├── setting_maxfps.gd │ │ ├── setting_vsync.gd │ │ ├── setting_display_mode.gd │ │ ├── setting_display_size.gd │ │ └── setting_display_scale.gd │ ├── setting_input_rebind.gd.uid │ ├── audio │ │ ├── setting_audio_mute.gd.uid │ │ ├── setting_audio_volume.gd.uid │ │ ├── setting_audio_mute.gd │ │ └── setting_audio_volume.gd │ └── setting_input_rebind.gd ├── components │ ├── checkbox │ │ ├── component_checkbox.gd.uid │ │ ├── component_checkbox.tscn │ │ ├── checkbox.svg │ │ ├── component_checkbox.gd │ │ └── checkbox.svg.import │ ├── slider │ │ ├── component_slider.gd.uid │ │ ├── component_slider.tscn │ │ ├── component_slider.gd │ │ ├── slider.svg.import │ │ └── slider.svg │ ├── spinbox │ │ ├── component_spinbox.gd.uid │ │ ├── component_spinbox.tscn │ │ ├── spinbox.svg │ │ ├── spinbox.svg.import │ │ └── component_spinbox.gd │ ├── switch │ │ ├── component_switch.gd.uid │ │ ├── component_switch.tscn │ │ ├── switch.svg │ │ ├── component_switch.gd │ │ └── switch.svg.import │ ├── apply_btn │ │ ├── component_apply_btn.gd.uid │ │ ├── component_apply_btn.tscn │ │ ├── apply_btn.svg │ │ ├── apply_btn.svg.import │ │ └── component_apply_btn.gd │ ├── arrow_list │ │ ├── component_arrow_list.gd.uid │ │ ├── component_arrow_list.tscn │ │ ├── arrow_list.svg.import │ │ ├── arrow_list.svg │ │ └── component_arrow_list.gd │ ├── input_btn │ │ ├── component_input_btn.gd.uid │ │ ├── component_input_btn.tscn │ │ ├── input_btn.svg.import │ │ ├── input_btn.svg │ │ └── component_input_btn.gd │ ├── radio_list │ │ ├── component_radio_list.gd.uid │ │ ├── component_radio_list.tscn │ │ ├── radio_list.svg │ │ ├── radio_list.svg.import │ │ └── component_radio_list.gd │ ├── reset_btn │ │ ├── component_reset_btn.gd.uid │ │ ├── component_reset_btn.tscn │ │ ├── reset_btn.svg │ │ ├── reset_btn.svg.import │ │ └── component_reset_btn.gd │ ├── text_field │ │ ├── component_text_field.gd.uid │ │ ├── component_text_field.tscn │ │ ├── text_field.svg │ │ ├── component_text_field.gd │ │ └── text_field.svg.import │ ├── toggle_btn │ │ ├── component_toggle_btn.gd.uid │ │ ├── component_toggle_btn.tscn │ │ ├── component_toggle_btn.gd │ │ ├── toggle_btn.svg │ │ └── toggle_btn.svg.import │ └── option_list │ │ ├── component_option_list.gd.uid │ │ ├── component_option_list.tscn │ │ ├── option_list.svg │ │ ├── option_list.svg.import │ │ └── component_option_list.gd ├── default_text_db.tres ├── default_glyph_db.tres ├── game_settings │ └── sample_setting.tres └── plugin_settings.tres ├── .gitattributes ├── .gitignore ├── icon.svg.import ├── LICENSE ├── README.md ├── project.godot └── icon.svg /addons/ggs/plugin.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cpeowjn2ymfvw 2 | -------------------------------------------------------------------------------- /addons/ggs/core/component.gd.uid: -------------------------------------------------------------------------------- 1 | uid://pp0u0pfmrjow 2 | -------------------------------------------------------------------------------- /addons/ggs/core/setting.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bnm32mu5imjv0 2 | -------------------------------------------------------------------------------- /addons/ggs/core/plugin_settings.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dkp8812ehmesd 2 | -------------------------------------------------------------------------------- /addons/ggs/core/save_manager.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bsh7xp4hfbt3r 2 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_vsync.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ilpk4a4xr7v0 2 | -------------------------------------------------------------------------------- /ggs/scripts/setting_input_rebind.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bru1qejws0quq 2 | -------------------------------------------------------------------------------- /addons/ggs/core/utilities/input_utils.gd.uid: -------------------------------------------------------------------------------- 1 | uid://0u6dqgqy61xg 2 | -------------------------------------------------------------------------------- /addons/ggs/core/utilities/window_utils.gd.uid: -------------------------------------------------------------------------------- 1 | uid://q611rld50lkp 2 | -------------------------------------------------------------------------------- /ggs/scripts/audio/setting_audio_mute.gd.uid: -------------------------------------------------------------------------------- 1 | uid://budwe14cqu4ih 2 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_maxfps.gd.uid: -------------------------------------------------------------------------------- 1 | uid://vi08js4kg335 2 | -------------------------------------------------------------------------------- /ggs/components/checkbox/component_checkbox.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dem3tkp32myx4 2 | -------------------------------------------------------------------------------- /ggs/components/slider/component_slider.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bxlftsqgacfqk 2 | -------------------------------------------------------------------------------- /ggs/components/spinbox/component_spinbox.gd.uid: -------------------------------------------------------------------------------- 1 | uid://be8u6s11l35uw 2 | -------------------------------------------------------------------------------- /ggs/components/switch/component_switch.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cu4hfegg777ng 2 | -------------------------------------------------------------------------------- /ggs/scripts/audio/setting_audio_volume.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bht1jxwv3w1sp 2 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_mode.gd.uid: -------------------------------------------------------------------------------- 1 | uid://brory0hphls2b 2 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_scale.gd.uid: -------------------------------------------------------------------------------- 1 | uid://p5dvt3a5jrcl 2 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_size.gd.uid: -------------------------------------------------------------------------------- 1 | uid://5obp3uutrmcb 2 | -------------------------------------------------------------------------------- /addons/ggs/core/global_manager/global_manager.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dh1wuc5ss3x1t 2 | -------------------------------------------------------------------------------- /addons/ggs/core/input_databases/input_glyph_db.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dgwfwelnb4jy0 2 | -------------------------------------------------------------------------------- /addons/ggs/core/input_databases/input_text_db.gd.uid: -------------------------------------------------------------------------------- 1 | uid://c8cf5jdbdd4d0 2 | -------------------------------------------------------------------------------- /ggs/components/apply_btn/component_apply_btn.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b0qfqgedsdqi5 2 | -------------------------------------------------------------------------------- /ggs/components/arrow_list/component_arrow_list.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cl5eom2au4pkw 2 | -------------------------------------------------------------------------------- /ggs/components/input_btn/component_input_btn.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on6pq1nljhji 2 | -------------------------------------------------------------------------------- /ggs/components/radio_list/component_radio_list.gd.uid: -------------------------------------------------------------------------------- 1 | uid://n87lxbii0tqk 2 | -------------------------------------------------------------------------------- /ggs/components/reset_btn/component_reset_btn.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bh2lpbbt6w00j 2 | -------------------------------------------------------------------------------- /ggs/components/text_field/component_text_field.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ck1l8j0eivxbl 2 | -------------------------------------------------------------------------------- /ggs/components/toggle_btn/component_toggle_btn.gd.uid: -------------------------------------------------------------------------------- 1 | uid://kv6jnjky8w1e 2 | -------------------------------------------------------------------------------- /ggs/components/option_list/component_option_list.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ddc66oavcpel0 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot specific ignores 2 | .godot/ 3 | 4 | # Text Editor/IDE specific ignores 5 | .vscode/ 6 | .idea/ 7 | 8 | # Project specific ignores 9 | test/ 10 | export_presets.cfg -------------------------------------------------------------------------------- /addons/ggs/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Godot Game Settings" 4 | description="Create and manage game settings in Godot Engine." 5 | author="PunchablePlushie" 6 | version="3.3.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_maxfps.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingMaxFPS 4 | ## Sets the max FPS of the game. 5 | 6 | func _init() -> void: 7 | type = TYPE_INT 8 | default = 60 9 | section = "display" 10 | 11 | 12 | func apply(value: int) -> void: 13 | Engine.max_fps = value 14 | -------------------------------------------------------------------------------- /ggs/components/apply_btn/component_apply_btn.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cww37aayk8gj6"] 2 | 3 | [ext_resource type="Script" uid="uid://b0qfqgedsdqi5" path="res://ggs/components/apply_btn/component_apply_btn.gd" id="1_dk1tm"] 4 | 5 | [node name="ApplyBtn" type="Button"] 6 | offset_right = 8.0 7 | offset_bottom = 8.0 8 | script = ExtResource("1_dk1tm") 9 | -------------------------------------------------------------------------------- /ggs/components/reset_btn/component_reset_btn.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dfepxjl44fyqo"] 2 | 3 | [ext_resource type="Script" uid="uid://bh2lpbbt6w00j" path="res://ggs/components/reset_btn/component_reset_btn.gd" id="1_pchyd"] 4 | 5 | [node name="ResetBtn" type="Button"] 6 | offset_right = 8.0 7 | offset_bottom = 8.0 8 | script = ExtResource("1_pchyd") 9 | -------------------------------------------------------------------------------- /ggs/default_text_db.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" script_class="GGSInputTextDB" load_steps=2 format=3 uid="uid://djbybuptgj4yj"] 2 | 3 | [ext_resource type="Script" uid="uid://c8cf5jdbdd4d0" path="res://addons/ggs/core/input_databases/input_text_db.gd" id="1_3hkey"] 4 | 5 | [resource] 6 | script = ExtResource("1_3hkey") 7 | metadata/_custom_type_script = "uid://c8cf5jdbdd4d0" 8 | -------------------------------------------------------------------------------- /ggs/default_glyph_db.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" script_class="GGSInputGlyphDB" load_steps=2 format=3 uid="uid://rnyvstcxehvv"] 2 | 3 | [ext_resource type="Script" uid="uid://dgwfwelnb4jy0" path="res://addons/ggs/core/input_databases/input_glyph_db.gd" id="1_u3p2c"] 4 | 5 | [resource] 6 | script = ExtResource("1_u3p2c") 7 | metadata/_custom_type_script = "uid://dgwfwelnb4jy0" 8 | -------------------------------------------------------------------------------- /ggs/game_settings/sample_setting.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" script_class="SettingDisplayMode" load_steps=2 format=3 uid="uid://cty14giu4etx8"] 2 | 3 | [ext_resource type="Script" uid="uid://brory0hphls2b" path="res://ggs/scripts/display/setting_display_mode.gd" id="1_c6rmd"] 4 | 5 | [resource] 6 | resource_name = "sample" 7 | script = ExtResource("1_c6rmd") 8 | key = "sample" 9 | section = "display" 10 | default = 2 11 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_vsync.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingVSync 4 | ## Changes VSync mode. 5 | 6 | func _init() -> void: 7 | type = TYPE_INT 8 | hint = PROPERTY_HINT_ENUM 9 | hint_string = "Disabled,Enabled,Adaptive,Mailbox" 10 | default = DisplayServer.VSYNC_ENABLED 11 | section = "display" 12 | 13 | 14 | func apply(value: int) -> void: 15 | DisplayServer.window_set_vsync_mode(value) 16 | -------------------------------------------------------------------------------- /ggs/components/switch/component_switch.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cha8xesfthpfk"] 2 | 3 | [ext_resource type="Script" uid="uid://cu4hfegg777ng" path="res://ggs/components/switch/component_switch.gd" id="1_s7nl3"] 4 | 5 | [node name="Switch" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_s7nl3") 9 | 10 | [node name="Btn" type="CheckButton" parent="."] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /ggs/components/checkbox/component_checkbox.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bhkyf3l4ee800"] 2 | 3 | [ext_resource type="Script" uid="uid://dem3tkp32myx4" path="res://ggs/components/checkbox/component_checkbox.gd" id="1_wjrcm"] 4 | 5 | [node name="Checkbox" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_wjrcm") 9 | 10 | [node name="Btn" type="CheckBox" parent="."] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /ggs/components/spinbox/component_spinbox.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bqi00h7i7sg3u"] 2 | 3 | [ext_resource type="Script" uid="uid://be8u6s11l35uw" path="res://ggs/components/spinbox/component_spinbox.gd" id="1_ovbvt"] 4 | 5 | [node name="SpinBox" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_ovbvt") 9 | 10 | [node name="SpinBox" type="SpinBox" parent="."] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /addons/ggs/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const _MANAGER_NAME: String = "GGS" 5 | const _MANAGER_UID: String = "uid://esw7j7or7gpd" 6 | 7 | 8 | func _enter_tree() -> void: 9 | if ProjectSettings.has_setting("autoload/" + _MANAGER_NAME): 10 | return 11 | 12 | var manager_id: int = ResourceUID.text_to_id(_MANAGER_UID) 13 | var manager_path: String = ResourceUID.get_id_path(manager_id) 14 | add_autoload_singleton(_MANAGER_NAME, manager_path) -------------------------------------------------------------------------------- /ggs/components/option_list/component_option_list.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://b7m6l0lvojrsj"] 2 | 3 | [ext_resource type="Script" uid="uid://ddc66oavcpel0" path="res://ggs/components/option_list/component_option_list.gd" id="1_5yk06"] 4 | 5 | [node name="OptionList" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_5yk06") 9 | 10 | [node name="Btn" type="OptionButton" parent="."] 11 | layout_mode = 2 12 | -------------------------------------------------------------------------------- /ggs/components/toggle_btn/component_toggle_btn.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://d10row618jwcs"] 2 | 3 | [ext_resource type="Script" uid="uid://kv6jnjky8w1e" path="res://ggs/components/toggle_btn/component_toggle_btn.gd" id="1_m2okj"] 4 | 5 | [node name="ToggleBtn" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_m2okj") 9 | 10 | [node name="Btn" type="Button" parent="."] 11 | layout_mode = 2 12 | toggle_mode = true 13 | -------------------------------------------------------------------------------- /ggs/components/slider/component_slider.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ds06mwhee8ygm"] 2 | 3 | [ext_resource type="Script" uid="uid://bxlftsqgacfqk" path="res://ggs/components/slider/component_slider.gd" id="1_4rmgf"] 4 | 5 | [node name="Slider" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_4rmgf") 9 | 10 | [node name="Slider" type="HSlider" parent="."] 11 | custom_minimum_size = Vector2(100, 0) 12 | layout_mode = 2 13 | size_flags_horizontal = 0 14 | size_flags_vertical = 4 15 | -------------------------------------------------------------------------------- /ggs/components/radio_list/component_radio_list.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c11tbsolk6dnu"] 2 | 3 | [ext_resource type="Script" uid="uid://n87lxbii0tqk" path="res://ggs/components/radio_list/component_radio_list.gd" id="1_47eay"] 4 | 5 | [node name="RadioList" type="MarginContainer"] 6 | offset_bottom = 648.0 7 | script = ExtResource("1_47eay") 8 | 9 | [node name="HList" type="HBoxContainer" parent="."] 10 | layout_mode = 2 11 | mouse_filter = 2 12 | 13 | [node name="VList" type="VBoxContainer" parent="."] 14 | layout_mode = 2 15 | mouse_filter = 2 16 | -------------------------------------------------------------------------------- /ggs/components/text_field/component_text_field.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://di8r6amxunq7q"] 2 | 3 | [ext_resource type="Script" uid="uid://ck1l8j0eivxbl" path="res://ggs/components/text_field/component_text_field.gd" id="1_u6s1s"] 4 | 5 | [node name="TextField" type="MarginContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_u6s1s") 9 | 10 | [node name="TextField" type="LineEdit" parent="."] 11 | custom_minimum_size = Vector2(100, 0) 12 | layout_mode = 2 13 | context_menu_enabled = false 14 | caret_blink = true 15 | caret_blink_interval = 0.5 16 | -------------------------------------------------------------------------------- /ggs/plugin_settings.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" script_class="GGSPluginSettings" load_steps=4 format=3 uid="uid://dhewfdfjnlrvc"] 2 | 3 | [ext_resource type="Script" uid="uid://dkp8812ehmesd" path="res://addons/ggs/core/plugin_settings.gd" id="1_677d5"] 4 | [ext_resource type="Resource" uid="uid://rnyvstcxehvv" path="res://ggs/default_glyph_db.tres" id="1_xdksr"] 5 | [ext_resource type="Resource" uid="uid://djbybuptgj4yj" path="res://ggs/default_text_db.tres" id="3_tnt32"] 6 | 7 | [resource] 8 | script = ExtResource("1_677d5") 9 | text_db = ExtResource("3_tnt32") 10 | glyph_db = ExtResource("1_xdksr") 11 | metadata/_custom_type_script = "uid://bnm32mu5imjv0" 12 | -------------------------------------------------------------------------------- /ggs/components/reset_btn/reset_btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | -------------------------------------------------------------------------------- /ggs/components/apply_btn/apply_btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | -------------------------------------------------------------------------------- /addons/ggs/core/input_databases/input_glyph_db.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | class_name GGSInputGlyphDB 4 | ## Resource class for storing and managing image data for mouse and joypad events. 5 | 6 | @export var mouse: Dictionary[MouseButton, Texture2D] 7 | 8 | @export_group("Xbox") 9 | @export var xbox_button: Dictionary[JoyButton, Texture2D] 10 | @export var xbox_axis: Dictionary[GGSInputUtils.AxisDirection, Texture2D] 11 | 12 | @export_group("Playstation") 13 | @export var playstation_button: Dictionary[JoyButton, Texture2D] 14 | @export var playstation_axis: Dictionary[GGSInputUtils.AxisDirection, Texture2D] 15 | 16 | @export_group("Switch") 17 | @export var switch_button: Dictionary[JoyButton, Texture2D] 18 | @export var switch_axis: Dictionary[GGSInputUtils.AxisDirection, Texture2D] 19 | -------------------------------------------------------------------------------- /ggs/components/arrow_list/component_arrow_list.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://1qpe22ky2s6y"] 2 | 3 | [ext_resource type="Script" uid="uid://cl5eom2au4pkw" path="res://ggs/components/arrow_list/component_arrow_list.gd" id="1_cifu3"] 4 | 5 | [node name="ArrowList" type="MarginContainer"] 6 | offset_left = 143.0 7 | offset_right = 143.0 8 | offset_bottom = 40.0 9 | script = ExtResource("1_cifu3") 10 | 11 | [node name="HBox" type="HBoxContainer" parent="."] 12 | layout_mode = 2 13 | 14 | [node name="LeftBtn" type="Button" parent="HBox"] 15 | layout_mode = 2 16 | text = "<" 17 | 18 | [node name="OptionLabel" type="Label" parent="HBox"] 19 | layout_mode = 2 20 | text = "OptionLabel" 21 | 22 | [node name="RightBtn" type="Button" parent="HBox"] 23 | layout_mode = 2 24 | text = ">" 25 | -------------------------------------------------------------------------------- /ggs/components/text_field/text_field.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | -------------------------------------------------------------------------------- /ggs/components/switch/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | -------------------------------------------------------------------------------- /ggs/components/input_btn/component_input_btn.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dm1av7skxvp1j"] 2 | 3 | [ext_resource type="Script" uid="uid://on6pq1nljhji" path="res://ggs/components/input_btn/component_input_btn.gd" id="1_crgnj"] 4 | 5 | [node name="InputBtn" type="MarginContainer"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | script = ExtResource("1_crgnj") 12 | 13 | [node name="Btn" type="Button" parent="."] 14 | custom_minimum_size = Vector2(100, 0) 15 | layout_mode = 2 16 | toggle_mode = true 17 | text_overrun_behavior = 3 18 | clip_text = true 19 | icon_alignment = 1 20 | 21 | [node name="ListenTime" type="Timer" parent="."] 22 | one_shot = true 23 | 24 | [node name="AcceptDelay" type="Timer" parent="."] 25 | one_shot = true 26 | -------------------------------------------------------------------------------- /ggs/components/option_list/option_list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | -------------------------------------------------------------------------------- /ggs/components/checkbox/checkbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | -------------------------------------------------------------------------------- /ggs/components/spinbox/spinbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | -------------------------------------------------------------------------------- /addons/ggs/base_node.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | -------------------------------------------------------------------------------- /ggs/scripts/audio/setting_audio_mute.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingAudioMute 4 | ## Sets the mute state of an audio bus. 5 | 6 | ## Target audio bus. 7 | var audio_bus: String = "Master" 8 | 9 | 10 | func _init() -> void: 11 | type = TYPE_BOOL 12 | default = false 13 | section = "audio" 14 | 15 | 16 | func _get_property_list() -> Array: 17 | return [ 18 | { 19 | "name": "audio_bus", 20 | "type": TYPE_STRING, 21 | "hint": PROPERTY_HINT_ENUM, 22 | "hint_string": ",".join(_get_audio_bus_list()), 23 | } 24 | ] 25 | 26 | 27 | func apply(value: bool) -> void: 28 | var bus_idx: int = AudioServer.get_bus_index(audio_bus) 29 | AudioServer.set_bus_mute(bus_idx, value) 30 | 31 | 32 | func _get_audio_bus_list() -> PackedStringArray: 33 | var buses: PackedStringArray 34 | for bus_idx: int in range(AudioServer.bus_count): 35 | var bus: String = AudioServer.get_bus_name(bus_idx) 36 | buses.append(bus) 37 | return buses 38 | -------------------------------------------------------------------------------- /addons/ggs/core/global_manager/global_manager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://esw7j7or7gpd"] 2 | 3 | [ext_resource type="Script" uid="uid://dh1wuc5ss3x1t" path="res://addons/ggs/core/global_manager/global_manager.gd" id="1_6v3cu"] 4 | [ext_resource type="Resource" uid="uid://dhewfdfjnlrvc" path="res://ggs/plugin_settings.tres" id="2_g5ayl"] 5 | 6 | [node name="GGS" type="Node" node_paths=PackedStringArray("audio_mouse_entered", "audio_focus_entered", "audio_activated")] 7 | script = ExtResource("1_6v3cu") 8 | plugin_settings = ExtResource("2_g5ayl") 9 | audio_mouse_entered = NodePath("Audio/MouseEntered") 10 | audio_focus_entered = NodePath("Audio/FocusEntered") 11 | audio_activated = NodePath("Audio/Activated") 12 | 13 | [node name="Audio" type="Node" parent="."] 14 | 15 | [node name="MouseEntered" type="AudioStreamPlayer" parent="Audio"] 16 | 17 | [node name="FocusEntered" type="AudioStreamPlayer" parent="Audio"] 18 | 19 | [node name="Activated" type="AudioStreamPlayer" parent="Audio"] 20 | -------------------------------------------------------------------------------- /icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b16vjyxohf7mc" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/ggs/core/global_manager/global_manager.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | ## The core GGS singleton. Handles everything that needs a persistent and global instance to function. 4 | 5 | ## Emitted when any setting is applied. 6 | signal setting_applied(setting: GGSSetting, value: Variant) 7 | 8 | ## The plug settings instance that should be used 9 | @export var plugin_settings: GGSPluginSettings 10 | 11 | ## Audio players used to play the sound effects of the user interface components. 12 | @export_group("Audio Players") 13 | @export var audio_mouse_entered: AudioStreamPlayer 14 | @export var audio_focus_entered: AudioStreamPlayer 15 | @export var audio_activated: AudioStreamPlayer 16 | 17 | 18 | func _ready() -> void: 19 | GGSSaveManager.clean_up_file() 20 | if not Engine.is_editor_hint(): 21 | _apply_all() 22 | 23 | 24 | func _apply_all() -> void: 25 | for setting: GGSSetting in GGSSaveManager.get_all_settings(): 26 | var value: Variant = GGSSaveManager.load_setting_value(setting) 27 | setting.apply(value) 28 | setting_applied.emit(setting, value) 29 | -------------------------------------------------------------------------------- /ggs/scripts/audio/setting_audio_volume.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingAudioVolume 4 | ## Sets the volume of an audio bus. 5 | 6 | ## Target audio bus. 7 | var audio_bus: String = "None" 8 | 9 | 10 | func _init() -> void: 11 | type = TYPE_FLOAT 12 | hint = PROPERTY_HINT_RANGE 13 | hint_string = "0,100" 14 | default = 80.0 15 | section = "audio" 16 | 17 | 18 | func _get_property_list() -> Array: 19 | return [ 20 | { 21 | "name": "audio_bus", 22 | "type": TYPE_STRING, 23 | "hint": PROPERTY_HINT_ENUM, 24 | "hint_string": ",".join(_get_audio_bus_list()), 25 | } 26 | ] 27 | 28 | 29 | func apply(value: float) -> void: 30 | var bus_idx: int = AudioServer.get_bus_index(audio_bus) 31 | var volume_db: float = linear_to_db(value / 100) 32 | AudioServer.set_bus_volume_db(bus_idx, volume_db) 33 | 34 | 35 | func _get_audio_bus_list() -> PackedStringArray: 36 | var buses: PackedStringArray 37 | for bus_idx: int in range(AudioServer.bus_count): 38 | var bus: String = AudioServer.get_bus_name(bus_idx) 39 | buses.append(bus) 40 | return buses 41 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_mode.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingDisplayMode 4 | ## Changes display mode between fullscreen, borderless, and windowed. 5 | 6 | ## A setting that can handle window size. Used to set the game window to the correct size after its state changes. 7 | @export var size_setting: GGSSetting 8 | 9 | 10 | func _init() -> void: 11 | type = TYPE_INT 12 | hint = PROPERTY_HINT_ENUM 13 | hint_string = "Fullscreen,Borderless,Windowed" 14 | default = 2 15 | section = "display" 16 | 17 | 18 | func apply(value: int) -> void: 19 | var window_mode: DisplayServer.WindowMode 20 | match value: 21 | 0: 22 | window_mode = DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN 23 | 1: 24 | window_mode = DisplayServer.WINDOW_MODE_FULLSCREEN 25 | 2: 26 | window_mode = DisplayServer.WINDOW_MODE_WINDOWED 27 | DisplayServer.window_set_mode(window_mode) 28 | 29 | if size_setting != null: 30 | var size_value: int = GGSSaveManager.load_setting_value(size_setting) 31 | GGS.setting_applied.emit(size_setting, size_value) 32 | size_setting.apply(size_value) 33 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_size.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingDisplaySize 4 | ## Sets the window size. The window will be resized by setting its size to provided values. 5 | 6 | ## List of available sizes. 7 | @export var sizes: Array[Vector2]: set = _set_sizes 8 | 9 | 10 | func _init() -> void: 11 | type = TYPE_INT 12 | hint = PROPERTY_HINT_ENUM 13 | default = 0 14 | section = "display" 15 | 16 | 17 | func _set_sizes(value: Array[Vector2]) -> void: 18 | sizes = value 19 | 20 | if Engine.is_editor_hint(): 21 | hint_string = ",".join(_get_size_strings()) 22 | notify_property_list_changed() 23 | 24 | 25 | func apply(value: int) -> void: 26 | var size: Vector2 = sizes[value] 27 | GGSWindowUtils.clamp_to_screen() 28 | DisplayServer.window_set_size(size) 29 | GGSWindowUtils.center() 30 | 31 | 32 | func _get_size_strings() -> PackedStringArray: 33 | var result: PackedStringArray 34 | for size: Vector2 in sizes: 35 | var formatted_size: String = str(size).trim_prefix("(").trim_suffix(")").replace(",", " x") 36 | result.append(formatted_size) 37 | return result 38 | -------------------------------------------------------------------------------- /ggs/components/switch/component_switch.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/switch/switch.svg") 3 | extends GGSComponent 4 | 5 | @onready var _btn: Button = $Btn 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_BOOL] 10 | if not Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _btn.toggled.connect(_on_btn_toggled) 15 | _btn.mouse_entered.connect(_on_btn_mouse_entered) 16 | _btn.focus_entered.connect(_on_btn_focus_entered) 17 | 18 | 19 | func init_value() -> void: 20 | value = GGSSaveManager.load_setting_value(setting) 21 | _btn.set_pressed_no_signal(value) 22 | 23 | 24 | func reset_setting() -> void: 25 | super() 26 | _btn.set_pressed_no_signal(value) 27 | 28 | 29 | func _on_btn_toggled(btn_state: bool) -> void: 30 | value = btn_state 31 | GGS.audio_activated.play() 32 | if can_apply_on_changed(): 33 | apply_setting() 34 | 35 | 36 | func _on_btn_mouse_entered() -> void: 37 | GGS.audio_mouse_entered.play() 38 | if can_grab_focus_on_mouseover(): 39 | _btn.grab_focus() 40 | 41 | 42 | func _on_btn_focus_entered() -> void: 43 | GGS.audio_focus_entered.play() 44 | -------------------------------------------------------------------------------- /ggs/components/checkbox/component_checkbox.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/checkbox/checkbox.svg") 3 | extends GGSComponent 4 | 5 | @onready var _btn: Button = $Btn 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_BOOL] 10 | if not Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _btn.toggled.connect(_on_btn_toggled) 15 | _btn.mouse_entered.connect(_on_btn_mouse_entered) 16 | _btn.focus_entered.connect(_on_btn_focus_entered) 17 | 18 | 19 | func init_value() -> void: 20 | value = GGSSaveManager.load_setting_value(setting) 21 | _btn.set_pressed_no_signal(value) 22 | 23 | 24 | func reset_setting() -> void: 25 | super() 26 | _btn.set_pressed_no_signal(value) 27 | 28 | 29 | func _on_btn_toggled(btn_state: bool) -> void: 30 | value = btn_state 31 | GGS.audio_activated.play() 32 | if can_apply_on_changed(): 33 | apply_setting() 34 | 35 | 36 | func _on_btn_mouse_entered() -> void: 37 | GGS.audio_mouse_entered.play() 38 | if can_grab_focus_on_mouseover(): 39 | _btn.grab_focus() 40 | 41 | 42 | func _on_btn_focus_entered() -> void: 43 | GGS.audio_focus_entered.play() 44 | -------------------------------------------------------------------------------- /ggs/components/slider/component_slider.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/slider/slider.svg") 3 | extends GGSComponent 4 | 5 | @onready var _slider: HSlider = $Slider 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_INT, TYPE_FLOAT] 10 | if Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _slider.value_changed.connect(_on_slider_value_changed) 15 | _slider.mouse_entered.connect(_on_slider_mouse_entered) 16 | _slider.focus_entered.connect(_on_slider_focus_entered) 17 | 18 | 19 | func init_value() -> void: 20 | value = GGSSaveManager.load_setting_value(setting) 21 | _slider.set_value_no_signal(value) 22 | 23 | 24 | func reset_setting() -> void: 25 | super() 26 | _slider.value = value 27 | 28 | 29 | func _on_slider_value_changed(new_value: float) -> void: 30 | value = new_value 31 | if can_apply_on_changed(): 32 | apply_setting() 33 | 34 | 35 | func _on_slider_mouse_entered() -> void: 36 | GGS.audio_mouse_entered.play() 37 | if can_grab_focus_on_mouseover(): 38 | _slider.grab_focus() 39 | 40 | 41 | func _on_slider_focus_entered() -> void: 42 | GGS.audio_focus_entered.play() 43 | -------------------------------------------------------------------------------- /ggs/components/toggle_btn/component_toggle_btn.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/toggle_btn/toggle_btn.svg") 3 | extends GGSComponent 4 | 5 | @onready var _btn: CheckButton = $Btn 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_BOOL] 10 | if Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _btn.toggled.connect(_on_btn_toggled) 15 | _btn.mouse_entered.connect(_on_btn_mouse_entered) 16 | _btn.focus_entered.connect(_on_btn_focus_entered) 17 | 18 | 19 | func init_value() -> void: 20 | value = GGSSaveManager.load_setting_value(setting) 21 | _btn.set_pressed_no_signal(value) 22 | 23 | 24 | func reset_setting() -> void: 25 | super() 26 | _btn.set_pressed_no_signal(value) 27 | 28 | 29 | func _on_btn_toggled(toggled_on: bool) -> void: 30 | value = toggled_on 31 | GGS.audio_activated.play() 32 | if can_apply_on_changed(): 33 | apply_setting() 34 | 35 | 36 | func _on_btn_mouse_entered() -> void: 37 | GGS.audio_mouse_entered.play() 38 | if can_grab_focus_on_mouseover(): 39 | _btn.grab_focus() 40 | 41 | 42 | func _on_btn_focus_entered() -> void: 43 | GGS.audio_focus_entered.play() 44 | -------------------------------------------------------------------------------- /ggs/components/toggle_btn/toggle_btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) PunchablePlushie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ggs/components/text_field/component_text_field.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/text_field/text_field.svg") 3 | extends GGSComponent 4 | 5 | @onready var _field: LineEdit = $TextField 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_STRING] 10 | if Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _field.text_submitted.connect(_on_field_text_submitted) 15 | _field.mouse_entered.connect(_on_field_mouse_entered) 16 | _field.focus_entered.connect(_on_field_focus_entered) 17 | 18 | 19 | func init_value() -> void: 20 | value = GGSSaveManager.load_setting_value(setting) 21 | _field.text = value 22 | 23 | 24 | func reset_setting() -> void: 25 | super() 26 | _field.text = value 27 | 28 | 29 | func _on_field_text_submitted(submitted_text: String) -> void: 30 | value = submitted_text 31 | GGS.audio_activated.play() 32 | if can_apply_on_changed(): 33 | apply_setting() 34 | 35 | 36 | func _on_field_mouse_entered() -> void: 37 | GGS.audio_mouse_entered.play() 38 | if can_grab_focus_on_mouseover(): 39 | _field.grab_focus() 40 | 41 | 42 | func _on_field_focus_entered() -> void: 43 | GGS.audio_focus_entered.play() -------------------------------------------------------------------------------- /ggs/components/radio_list/radio_list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | -------------------------------------------------------------------------------- /addons/ggs/base_node.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://3tisg7ksch0r" 6 | path="res://.godot/imported/base_node.svg-9da2866d9a560d9d780a1cada5a51e85.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/ggs/base_node.svg" 14 | dest_files=["res://.godot/imported/base_node.svg-9da2866d9a560d9d780a1cada5a51e85.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /addons/ggs/core/utilities/window_utils.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends RefCounted 3 | class_name GGSWindowUtils 4 | ## Provides simple game window utilities. 5 | 6 | ## Centers game window on the current screen. 7 | static func center() -> void: 8 | var screen_id: int = DisplayServer.window_get_current_screen() 9 | var screen_position: Vector2i = DisplayServer.screen_get_position(screen_id) 10 | var screen_center: Vector2i = DisplayServer.screen_get_usable_rect(screen_id).size / 2 11 | var window_center: Vector2i = DisplayServer.window_get_size() / 2 12 | var target_position: Vector2 = screen_position + screen_center - window_center 13 | DisplayServer.window_set_position(target_position) 14 | 15 | 16 | ## Clamps the game window to the current screen size. 17 | static func clamp_to_screen() -> void: 18 | var screen_id: int = DisplayServer.window_get_current_screen() 19 | var screen_size: Vector2i = DisplayServer.screen_get_usable_rect(screen_id).size 20 | var window_size: Vector2i = DisplayServer.window_get_size() 21 | var window_width: int = mini(screen_size.x, window_size.x) 22 | var window_height: int = mini(screen_size.y, window_size.y) 23 | DisplayServer.window_set_size(Vector2i(window_width, window_height)) 24 | -------------------------------------------------------------------------------- /ggs/components/slider/slider.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://j6pcn2avkysi" 6 | path="res://.godot/imported/slider.svg-5aa92268eeac630a64462bb90042fc87.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/slider/slider.svg" 14 | dest_files=["res://.godot/imported/slider.svg-5aa92268eeac630a64462bb90042fc87.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/switch/switch.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bn1m6etm3ivk6" 6 | path="res://.godot/imported/switch.svg-eed4323ad3e44db3c19d023c9c3db034.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/switch/switch.svg" 14 | dest_files=["res://.godot/imported/switch.svg-eed4323ad3e44db3c19d023c9c3db034.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/spinbox/spinbox.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bv0nul1sxpi7i" 6 | path="res://.godot/imported/spinbox.svg-18164324ee377d2bd126e0a1bae80819.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/spinbox/spinbox.svg" 14 | dest_files=["res://.godot/imported/spinbox.svg-18164324ee377d2bd126e0a1bae80819.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/checkbox/checkbox.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b0t5rvtuib0i" 6 | path="res://.godot/imported/checkbox.svg-7706511fab4642be11120d7aa7c38c35.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/checkbox/checkbox.svg" 14 | dest_files=["res://.godot/imported/checkbox.svg-7706511fab4642be11120d7aa7c38c35.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/scripts/display/setting_display_scale.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingDisplayScale 4 | ## Sets the window scale. The window will be resized by multiplying its dimensions by a flat number. 5 | 6 | ## List of available scales. 7 | @export var scales: Array[float]: set = _set_scales 8 | 9 | 10 | func _init() -> void: 11 | type = TYPE_INT 12 | hint = PROPERTY_HINT_ENUM 13 | default = 0 14 | section = "display" 15 | 16 | 17 | func _set_scales(value: Array[float]) -> void: 18 | scales = value 19 | 20 | if Engine.is_editor_hint(): 21 | hint_string = ",".join(_get_scale_strings()) 22 | notify_property_list_changed() 23 | 24 | 25 | func apply(value: int) -> void: 26 | var scale: float = scales[value] 27 | var base_w: int = ProjectSettings.get_setting("display/window/size/viewport_width") 28 | var base_h: int = ProjectSettings.get_setting("display/window/size/viewport_height") 29 | var size: Vector2 = Vector2(base_w, base_h) * scale 30 | GGSWindowUtils.clamp_to_screen() 31 | DisplayServer.window_set_size(size) 32 | GGSWindowUtils.center() 33 | 34 | 35 | func _get_scale_strings() -> PackedStringArray: 36 | var result: PackedStringArray = [] 37 | for scale: float in scales: 38 | result.append("x%s" % [scale]) 39 | return result 40 | -------------------------------------------------------------------------------- /ggs/components/apply_btn/apply_btn.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://1mbgim41cfh8" 6 | path="res://.godot/imported/apply_btn.svg-75d45300c55d98b6b8409ca3cdc55d68.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/apply_btn/apply_btn.svg" 14 | dest_files=["res://.godot/imported/apply_btn.svg-75d45300c55d98b6b8409ca3cdc55d68.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/input_btn/input_btn.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ct4owhaxq3ees" 6 | path="res://.godot/imported/input_btn.svg-eb0dc4400c9fbda76f1aacb3054c116f.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/input_btn/input_btn.svg" 14 | dest_files=["res://.godot/imported/input_btn.svg-eb0dc4400c9fbda76f1aacb3054c116f.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/reset_btn/reset_btn.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d0jkq30mvtqti" 6 | path="res://.godot/imported/reset_btn.svg-868debf90a60eed0d3dd4f8b9f61df9c.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/reset_btn/reset_btn.svg" 14 | dest_files=["res://.godot/imported/reset_btn.svg-868debf90a60eed0d3dd4f8b9f61df9c.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/arrow_list/arrow_list.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d0566jcvm0uxg" 6 | path="res://.godot/imported/arrow_list.svg-d5e66a38aac7e905005f5c7bf982014b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/arrow_list/arrow_list.svg" 14 | dest_files=["res://.godot/imported/arrow_list.svg-d5e66a38aac7e905005f5c7bf982014b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/radio_list/radio_list.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dfvtvylsulxgf" 6 | path="res://.godot/imported/radio_list.svg-31a41d3b5415d87f19905863c17798d7.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/radio_list/radio_list.svg" 14 | dest_files=["res://.godot/imported/radio_list.svg-31a41d3b5415d87f19905863c17798d7.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/slider/slider.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | -------------------------------------------------------------------------------- /ggs/components/text_field/text_field.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dwd05hntm3p3x" 6 | path="res://.godot/imported/text_field.svg-221ab179124a5df4e4ceb3b3aca5af96.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/text_field/text_field.svg" 14 | dest_files=["res://.godot/imported/text_field.svg-221ab179124a5df4e4ceb3b3aca5af96.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/toggle_btn/toggle_btn.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c4cjgyjpd05pv" 6 | path="res://.godot/imported/toggle_btn.svg-908b50bbad233e51a020f8561ae45b82.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/toggle_btn/toggle_btn.svg" 14 | dest_files=["res://.godot/imported/toggle_btn.svg-908b50bbad233e51a020f8561ae45b82.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/option_list/option_list.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://csyligm2xb7r3" 6 | path="res://.godot/imported/option_list.svg-9a79a50e7c5c551a99ac7aeb8d13e560.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://ggs/components/option_list/option_list.svg" 14 | dest_files=["res://.godot/imported/option_list.svg-9a79a50e7c5c551a99ac7aeb8d13e560.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | svg/scale=1.0 42 | editor/scale_with_editor_scale=false 43 | editor/convert_colors_with_editor_theme=false 44 | -------------------------------------------------------------------------------- /ggs/components/spinbox/component_spinbox.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/spinbox/spinbox.svg") 3 | extends GGSComponent 4 | 5 | @onready var _spinbox: SpinBox = $SpinBox 6 | @onready var _field: LineEdit = _spinbox.get_line_edit() 7 | 8 | 9 | func _ready() -> void: 10 | compatible_types = [TYPE_INT, TYPE_FLOAT] 11 | if Engine.is_editor_hint(): 12 | return 13 | 14 | init_value() 15 | _spinbox.value_changed.connect(_on_spinBox_value_changed) 16 | _field.mouse_entered.connect(_on_field_mouse_entered) 17 | _field.focus_entered.connect(_on_field_focus_entered) 18 | _field.context_menu_enabled = false 19 | 20 | 21 | func init_value() -> void: 22 | value = GGSSaveManager.load_setting_value(setting) 23 | _spinbox.set_value_no_signal(value) 24 | _field.text = str(value) 25 | 26 | 27 | func reset_setting() -> void: 28 | super() 29 | _spinbox.value = value 30 | _field.text = str(value) 31 | 32 | 33 | func _on_spinBox_value_changed(new_value: float) -> void: 34 | value = new_value 35 | GGS.audio_activated.play() 36 | if can_apply_on_changed(): 37 | apply_setting() 38 | 39 | 40 | func _on_field_mouse_entered() -> void: 41 | GGS.audio_mouse_entered.play() 42 | if can_grab_focus_on_mouseover(): 43 | _field.grab_focus() 44 | 45 | 46 | func _on_field_focus_entered() -> void: 47 | GGS.audio_focus_entered.play() -------------------------------------------------------------------------------- /ggs/components/option_list/component_option_list.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/option_list/option_list.svg") 3 | extends GGSComponent 4 | 5 | @onready var _btn: OptionButton = $Btn 6 | 7 | 8 | func _ready() -> void: 9 | compatible_types = [TYPE_BOOL, TYPE_INT] 10 | if Engine.is_editor_hint(): 11 | return 12 | 13 | init_value() 14 | _btn.item_selected.connect(_on_btn_item_selected) 15 | _btn.pressed.connect(_on_btn_pressed) 16 | _btn.mouse_entered.connect(_on_btn_mouse_entered) 17 | _btn.focus_entered.connect(_on_btn_focus_entered) 18 | _btn.item_focused.connect(_on_btn_item_focused) 19 | 20 | 21 | func init_value() -> void: 22 | value = GGSSaveManager.load_setting_value(setting) 23 | _btn.select(value) 24 | 25 | 26 | func reset_setting() -> void: 27 | super() 28 | _btn.select(value) 29 | 30 | 31 | func _on_btn_item_selected(item_index: int) -> void: 32 | GGS.audio_activated.play() 33 | value = item_index 34 | if can_apply_on_changed(): 35 | apply_setting() 36 | 37 | 38 | func _on_btn_pressed() -> void: 39 | GGS.audio_focus_entered.play() 40 | 41 | 42 | func _on_btn_mouse_entered() -> void: 43 | GGS.audio_mouse_entered.play() 44 | 45 | if can_grab_focus_on_mouseover(): 46 | _btn.grab_focus() 47 | 48 | 49 | func _on_btn_focus_entered() -> void: 50 | GGS.audio_focus_entered.play() 51 | 52 | 53 | func _on_btn_item_focused(_index: int) -> void: 54 | GGS.audio_focus_entered.play() 55 | -------------------------------------------------------------------------------- /ggs/components/apply_btn/component_apply_btn.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/apply_btn/apply_btn.svg") 3 | extends Button 4 | 5 | ## Node group associated with the button. When pressed, the button calls [method GGSComponent.apply_setting] on 6 | ## all nodes in this node group. 7 | @export var group: String 8 | 9 | @export_group("Override", "override_") 10 | ## If enabled, plugin settings can be overriden for this specific component. 11 | @export_custom(PROPERTY_HINT_GROUP_ENABLE, "feature") var override_plugin_settings: bool = false : set = _set_override_plugin_settings 12 | @export var override_grab_focus_on_mouseover: bool = false 13 | 14 | 15 | func _ready() -> void: 16 | pressed.connect(_on_pressed) 17 | mouse_entered.connect(_on_mouse_entered) 18 | focus_entered.connect(_on_focus_entered) 19 | 20 | 21 | func _set_override_plugin_settings(value: bool) -> void: 22 | override_plugin_settings = value 23 | if not override_plugin_settings: 24 | override_grab_focus_on_mouseover = false 25 | 26 | 27 | func _can_grab_focus_on_mouseover() -> bool: 28 | if override_plugin_settings: 29 | return override_grab_focus_on_mouseover 30 | else: 31 | return GGS.plugin_settings.components_grab_focus_on_mouseover 32 | 33 | 34 | func _on_pressed() -> void: 35 | get_tree().call_group(group, "apply_setting") 36 | GGS.audio_activated.play() 37 | 38 | 39 | func _on_mouse_entered() -> void: 40 | GGS.audio_mouse_entered.play() 41 | if _can_grab_focus_on_mouseover(): 42 | grab_focus() 43 | 44 | 45 | func _on_focus_entered() -> void: 46 | GGS.audio_focus_entered.play() 47 | -------------------------------------------------------------------------------- /ggs/components/reset_btn/component_reset_btn.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/reset_btn/reset_btn.svg") 3 | extends Button 4 | 5 | ## Node group associated with the button. When pressed, the button calls [method GGSComponent.reset_setting] on 6 | ## all nodes in this node group. 7 | @export var group: String 8 | 9 | @export_group("Override", "override_") 10 | ## If enabled, plugin settings can be overriden for this specific component. 11 | @export_custom(PROPERTY_HINT_GROUP_ENABLE, "feature") var override_plugin_settings: bool = false : set = _set_override_plugin_settings 12 | @export var override_grab_focus_on_mouseover: bool = false 13 | 14 | 15 | func _ready() -> void: 16 | pressed.connect(_on_pressed) 17 | mouse_entered.connect(_on_mouse_entered) 18 | focus_entered.connect(_on_focus_entered) 19 | 20 | 21 | func _set_override_plugin_settings(value: bool) -> void: 22 | override_plugin_settings = value 23 | if not override_plugin_settings: 24 | override_grab_focus_on_mouseover = false 25 | 26 | 27 | func _can_grab_focus_on_mouseover() -> bool: 28 | if override_plugin_settings: 29 | return override_grab_focus_on_mouseover 30 | else: 31 | return GGS.plugin_settings.components_grab_focus_on_mouseover 32 | 33 | 34 | func _on_pressed() -> void: 35 | get_tree().call_group(group, "reset_setting") 36 | GGS.audio_activated.play() 37 | 38 | 39 | func _on_mouse_entered() -> void: 40 | GGS.audio_mouse_entered.play() 41 | if _can_grab_focus_on_mouseover(): 42 | grab_focus() 43 | 44 | 45 | func _on_focus_entered() -> void: 46 | GGS.audio_focus_entered.play() 47 | -------------------------------------------------------------------------------- /addons/ggs/core/setting.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @abstract 3 | extends Resource 4 | class_name GGSSetting 5 | ## Resource that contains the necessary information for a specific game setting such as its default 6 | ## value or its script. 7 | 8 | ## The key name used to save and load the value. The setting will not be saved if this is empty. 9 | @export var key: String = "": set = _set_key 10 | 11 | ## The section name used to save and load the value. It can be empty. 12 | @export var section: String = "" 13 | 14 | ## Default value of the setting. 15 | var default: Variant = false: set = _set_default 16 | 17 | ## The value type this setting uses. 18 | var type: Variant.Type = TYPE_BOOL 19 | 20 | ## Property hint of [member default]. Use it to customize how [member default] is exported. 21 | var hint: PropertyHint = PROPERTY_HINT_NONE 22 | 23 | ## Property hint string of [member default]. Use it alongside [member hint] to customize how 24 | ## [member default] is exported. 25 | var hint_string: String = "" 26 | 27 | 28 | func _get_property_list() -> Array: 29 | var properties: Array 30 | properties.append_array([ 31 | { 32 | "name": "default", 33 | "type": type, 34 | "hint": hint, 35 | "hint_string": hint_string, 36 | }, 37 | ]) 38 | return properties 39 | 40 | 41 | func _set_default(value: Variant) -> void: 42 | default = value 43 | if Engine.is_editor_hint() and not key.is_empty(): 44 | GGSSaveManager.save_setting_value(self, value) 45 | 46 | 47 | func _set_key(value: String) -> void: 48 | key = value 49 | resource_name = value 50 | 51 | 52 | ## This method is called when a setting needs to be applied. In other words, it should contain the setting logic. 53 | @abstract func apply(value) -> void 54 | -------------------------------------------------------------------------------- /ggs/components/arrow_list/arrow_list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | -------------------------------------------------------------------------------- /addons/ggs/core/plugin_settings.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | class_name GGSPluginSettings 4 | ## Resource for storing and managing GGS plugin settings. 5 | 6 | ## The directory where the game setting resources are located. 7 | @export_dir var settings_directory: String = "res://ggs/game_settings" 8 | 9 | @export_group("Input") 10 | ## Text and glyphs will be shown in this layout if the connected joypad device is not recognized. 11 | @export_enum("Xbox", "Playstation", "Switch") var joypad_fallback_layout: String = "Xbox" 12 | ## Path to the [GGSInputTextDB] that should be used for text data. 13 | @export var text_db: GGSInputTextDB 14 | ## Path to the [GGSInputGlyphDB] that should be used for image data. 15 | @export var glyph_db: GGSInputGlyphDB 16 | 17 | 18 | @export_group("Components", "components_") 19 | ## If true, the setting is applied when components are activated successfully. Otherwise, an ApplyBtn component is required. 20 | @export var components_apply_on_changed: bool = true 21 | ## If true, the main control node(s) of components will grab focus on mouse over. 22 | @export var components_grab_focus_on_mouseover: bool = true 23 | 24 | @export_subgroup("Input Button", "input_btn_") 25 | ## The time the input component listens for input before automatically stopping. 26 | @export_range(0.001, 4096, 0.001, "exp", "suffix:s") var input_btn_listen_time: float = 3.0 27 | 28 | ## Delay before accepting the chosen input. Mainly used to create enough time for keyboard and mouse modifiers to get processed.[br] 29 | ## If you don't plan to accept modifiers, you can set this to its minimum value. If you do, choosing a number that's too low may 30 | ## prevent the users from using modifiers. 31 | @export_range(0.001, 4096, 0.001, "exp", "suffix:s") var input_btn_accept_delay: float = 0.33 32 | 33 | ## The duration of one loop of the input button listening state animation. Higher values mean slower animation. 34 | @export_range(0.001, 4096, 0.001, "exp", "suffix:s") var input_btn_anim_duration: float = 1.5 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot Game Settings (GGS) 2 | 3 | Godot Game Settings allows you to create and manage game settings for small to medium sized projects. It includes predefined logic for common game settings (display, audio, input) in addition to a framework for creating and managing custom settings and user interface components. 4 | 5 | View the [documentation](https://punchableplushie.github.io/godot-game-settings-docs) for information on how to use the plugin. 6 | 7 |

8 | GGS icon 9 |

10 | 11 | ## Changelog 12 | 13 | **3.3.0** is a small update primarily ensuring compatibility with Godot 4.5. It also comes with small feature revisions. 14 | 15 | ### 3.3.0 16 | 17 | - Updated for Godot 4.5. 18 | - Improved how the glyph database entries are exported. 19 | - Added a text database resource which allows customizing the text data used for converting mouse and joypad input events into text. 20 | - The name and extension of the save file can no longer be changed. 21 | - Type, hint, and hint string of a setting value can no longer be changed from the editor and should be set in its script instead. 22 | - Removed the ability to use IDs instead of indices from all list components. 23 | - Added the option to disable selection wrapping for arrow lists. 24 | - Added global settings for `apply_on_changed` and `grab_focus_on_mouseover` component properties. Individual component instances can override this if needed. 25 | - Plugin settings is now a resource saved on disc instead of being part of the main singleton. 26 | - Moved setting scripts and components outside of the plugin directory to allow users to use them from the "Quick Load" window without having to toggle the "Addons" filtering option. 27 | - Added a vsync toggle setting. 28 | - Added a max FPS setting. 29 | - Changed display fullscreen setting to allow changing between borderless and exclusive fullscreen modes. The setting is renamed to "display mode". 30 | - Other general codebase improvements. 31 | -------------------------------------------------------------------------------- /ggs/components/input_btn/input_btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 41 | -------------------------------------------------------------------------------- /ggs/components/radio_list/component_radio_list.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/radio_list/radio_list.svg") 3 | extends GGSComponent 4 | 5 | enum Lists {HLIST, VLIST} 6 | 7 | ## The children of the active list will be used as the list items. 8 | @export var list_type: Lists = Lists.HLIST 9 | 10 | var _active_list: BoxContainer 11 | 12 | @onready var _hlist: HBoxContainer = $HList 13 | @onready var _vlist: VBoxContainer = $VList 14 | @onready var _btn_group: ButtonGroup = ButtonGroup.new() 15 | 16 | 17 | func _ready() -> void: 18 | compatible_types = [TYPE_BOOL, TYPE_INT] 19 | if Engine.is_editor_hint(): 20 | return 21 | 22 | @warning_ignore("incompatible_ternary") 23 | _active_list = _hlist if list_type == Lists.HLIST else _vlist 24 | 25 | init_value() 26 | _btn_group.pressed.connect(_on_btn_group_pressed) 27 | for child: Button in _active_list.get_children(): 28 | child.button_group = _btn_group 29 | child.mouse_entered.connect(_on_any_btn_mouse_entered.bind(child)) 30 | child.focus_entered.connect(_on_any_btn_focus_entered) 31 | 32 | 33 | func init_value() -> void: 34 | value = GGSSaveManager.load_setting_value(setting) 35 | _set_button_pressed(value, true) 36 | 37 | 38 | func reset_setting() -> void: 39 | super() 40 | _set_button_pressed(value, true) 41 | 42 | 43 | func _set_button_pressed(btn_index: int, pressed: bool) -> void: 44 | _active_list.get_child(btn_index).set_pressed_no_signal(pressed) 45 | 46 | 47 | func _get_child_index(target_child: BaseButton) -> int: 48 | var i: int = 0 49 | for child: Button in _active_list.get_children(): 50 | if child == target_child: 51 | return i 52 | i += 1 53 | return -1 54 | 55 | 56 | func _on_btn_group_pressed(button: BaseButton) -> void: 57 | GGS.audio_activated.play() 58 | value = _get_child_index(button) 59 | if can_apply_on_changed(): 60 | apply_setting() 61 | 62 | 63 | func _on_any_btn_mouse_entered(Btn: Button) -> void: 64 | GGS.audio_mouse_entered.play() 65 | if can_grab_focus_on_mouseover(): 66 | Btn.grab_focus() 67 | 68 | 69 | func _on_any_btn_focus_entered() -> void: 70 | GGS.audio_focus_entered.play() 71 | -------------------------------------------------------------------------------- /ggs/components/arrow_list/component_arrow_list.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/arrow_list/arrow_list.svg") 3 | extends GGSComponent 4 | 5 | signal option_selected(option_index: int) 6 | 7 | ## Options of the list. Note that the option index or id is saved, not its string label. 8 | @export var options: PackedStringArray 9 | ## If enabled, the selection will wrap around when reaching the end of options from either side. 10 | @export var wrap_selection: bool = true 11 | 12 | @onready var _left_btn: Button = $HBox/LeftBtn 13 | @onready var _option_label: Label = $HBox/OptionLabel 14 | @onready var _right_btn: Button = $HBox/RightBtn 15 | 16 | 17 | func _ready() -> void: 18 | compatible_types = [TYPE_BOOL, TYPE_INT] 19 | if Engine.is_editor_hint(): 20 | return 21 | 22 | _init_value() 23 | option_selected.connect(_on_option_selected) 24 | _left_btn.pressed.connect(_on_left_btn_pressed) 25 | _right_btn.pressed.connect(_on_right_btn_pressed) 26 | _left_btn.mouse_entered.connect(_on_any_btn_mouse_entered.bind(_left_btn)) 27 | _right_btn.mouse_entered.connect(_on_any_btn_mouse_entered.bind(_right_btn)) 28 | _left_btn.focus_entered.connect(_on_any_btn_focus_entered) 29 | _right_btn.focus_entered.connect(_on_any_btn_focus_entered) 30 | 31 | 32 | func reset_setting() -> void: 33 | _select(setting.default) 34 | apply_setting() 35 | 36 | 37 | func _init_value() -> void: 38 | value = GGSSaveManager.load_setting_value(setting) 39 | _select(value, false) 40 | 41 | 42 | func _select(new_index: int, emit_selected: bool = true) -> void: 43 | value = new_index % options.size() 44 | if emit_selected: 45 | option_selected.emit(value) 46 | 47 | _option_label.text = options[value] 48 | if not wrap_selection: 49 | _left_btn.disabled = (value == 0) 50 | _right_btn.disabled = (value == options.size() - 1) 51 | 52 | 53 | func _on_option_selected(_option_index: int) -> void: 54 | if can_apply_on_changed(): 55 | apply_setting() 56 | 57 | 58 | func _on_left_btn_pressed() -> void: 59 | _select(value - 1) 60 | GGS.audio_activated.play() 61 | 62 | 63 | func _on_right_btn_pressed() -> void: 64 | _select(value + 1) 65 | GGS.audio_activated.play() 66 | 67 | 68 | func _on_any_btn_mouse_entered(Btn: Button) -> void: 69 | GGS.audio_mouse_entered.play() 70 | if can_grab_focus_on_mouseover(): 71 | Btn.grab_focus() 72 | 73 | 74 | func _on_any_btn_focus_entered() -> void: 75 | GGS.audio_focus_entered.play() 76 | -------------------------------------------------------------------------------- /ggs/scripts/setting_input_rebind.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GGSSetting 3 | class_name SettingInputRebind 4 | ## Rebinds the chosen input event of a specific input action. 5 | 6 | ## The action to be rebinded. 7 | var action: String: set = _set_action 8 | 9 | ## Index of the input event to be replaced. 10 | var event_index: int: set = _set_event_index 11 | 12 | 13 | func _init() -> void: 14 | type = TYPE_ARRAY 15 | default = [] 16 | section = "input" 17 | 18 | 19 | func _get_property_list() -> Array: 20 | return [ 21 | { 22 | "name": "action", 23 | "type": TYPE_STRING, 24 | "hint": PROPERTY_HINT_INPUT_NAME, 25 | }, 26 | { 27 | "name": "event_index", 28 | "type": TYPE_INT, 29 | "usage": _get_usage(), 30 | "hint": PROPERTY_HINT_ENUM, 31 | "hint_string": ",".join(_get_event_list()), 32 | }, 33 | ] 34 | 35 | 36 | func _set_action(value: String) -> void: 37 | action = value 38 | event_index = 0 39 | notify_property_list_changed() 40 | 41 | 42 | func _set_event_index(value: int) -> void: 43 | event_index = value 44 | 45 | var events: Array = GGSInputUtils.action_get_events(action) 46 | if events.is_empty(): 47 | default = [] 48 | return 49 | default = GGSInputUtils.serialize_event(events[value]) 50 | 51 | 52 | func apply(value: Array) -> void: 53 | var event: InputEvent = GGSInputUtils.deserialize_event(value) 54 | 55 | var new_events: Array[InputEvent] = InputMap.action_get_events(action) 56 | new_events.remove_at(event_index) 57 | new_events.insert(event_index, event) 58 | 59 | InputMap.action_erase_events(action) 60 | for input_event: InputEvent in new_events: 61 | InputMap.action_add_event(action, input_event) 62 | 63 | 64 | func _get_usage() -> int: 65 | if action.is_empty() or GGSInputUtils.action_get_events(action).is_empty(): 66 | return PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY 67 | else: 68 | return PROPERTY_USAGE_DEFAULT 69 | 70 | 71 | func _get_event_list() -> PackedStringArray: 72 | if action.is_empty(): 73 | return ["No action is selected."] 74 | 75 | var events: Array = GGSInputUtils.action_get_events(action) 76 | if events.is_empty(): 77 | return ["Selected action has no events."] 78 | 79 | var event_names: PackedStringArray 80 | var event_idx: int = 0 81 | for event: InputEvent in events: 82 | var event_text: String = GGSInputUtils.event_get_text(event) 83 | event_names.append("%d. %s" % [event_idx, event_text]) 84 | event_idx += 1 85 | 86 | return event_names 87 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Godot Game Settings" 14 | config/description="Create and manage game settings in Godot Engine." 15 | config/version="3.3" 16 | config/features=PackedStringArray("4.5", "GL Compatibility") 17 | config/icon="res://icon.svg" 18 | 19 | [audio] 20 | 21 | buses/default_bus_layout="" 22 | 23 | [autoload] 24 | 25 | GGS="*res://addons/ggs/core/global_manager/global_manager.tscn" 26 | 27 | [debug] 28 | 29 | gdscript/warnings/untyped_declaration=1 30 | 31 | [display] 32 | 33 | window/size/viewport_width=640 34 | window/size/viewport_height=360 35 | window/size/resizable=false 36 | window/stretch/mode="canvas_items" 37 | 38 | [editor] 39 | 40 | export/convert_text_resources_to_binary=false 41 | 42 | [editor_plugins] 43 | 44 | enabled=PackedStringArray("res://addons/ggs/plugin.cfg") 45 | 46 | [filesystem] 47 | 48 | import/blender/enabled=false 49 | 50 | [input] 51 | 52 | move_left={ 53 | "deadzone": 0.2, 54 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) 55 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null) 56 | , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) 57 | ] 58 | } 59 | move_right={ 60 | "deadzone": 0.2, 61 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) 62 | ] 63 | } 64 | move_up={ 65 | "deadzone": 0.2, 66 | "events": [] 67 | } 68 | 69 | [internationalization] 70 | 71 | locale/translation_remaps={} 72 | 73 | [rendering] 74 | 75 | renderer/rendering_method="gl_compatibility" 76 | environment/glow/upscale_mode=0 77 | -------------------------------------------------------------------------------- /addons/ggs/core/save_manager.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends RefCounted 3 | class_name GGSSaveManager 4 | 5 | const _FILE_PATH: String = "user://settings.cfg" 6 | 7 | 8 | ## Saves the value of the given setting to the disc. 9 | static func save_setting_value(setting: GGSSetting, value: Variant) -> void: 10 | var file: ConfigFile = _get_file() 11 | file.set_value(setting.section, setting.key, value) 12 | file.save(_FILE_PATH) 13 | 14 | 15 | ## Loads the value of the given setting from the disc. 16 | static func load_setting_value(setting: GGSSetting) -> Variant: 17 | var file: ConfigFile = _get_file() 18 | return file.get_value(setting.section, setting.key, setting.default) 19 | 20 | 21 | ## Gets all settings located in the settings directory (recursive). 22 | static func get_all_settings() -> Array[GGSSetting]: 23 | var result: Array[GGSSetting] 24 | 25 | var settings: PackedStringArray = _get_settings_in_dir(GGS.plugin_settings.settings_directory) 26 | for setting: String in settings: 27 | # ".remap" is trimmed to prevent resource loader errors when the project is exported. 28 | var obj: Resource = load(setting.trim_suffix(".remap")) 29 | if obj is not GGSSetting: 30 | continue 31 | 32 | result.append(obj) 33 | 34 | return result 35 | 36 | 37 | ## Removes unused keys and adds missing ones to the save file. 38 | static func clean_up_file() -> void: 39 | var file: ConfigFile = _get_file() 40 | 41 | # 1. Save the current keys in a temp variable for later. 42 | var temp: Dictionary[String, Dictionary] 43 | for section: String in file.get_sections(): 44 | temp[section] = {} 45 | for key: String in file.get_section_keys(section): 46 | temp[section][key] = file.get_value(section, key) 47 | 48 | # 2. Clear the file. 49 | file.clear() 50 | 51 | # 3. Recreate keys from the default value of settings. 52 | for setting: GGSSetting in get_all_settings(): 53 | if setting.key.is_empty(): 54 | continue 55 | file.set_value(setting.section, setting.key, setting.default) 56 | 57 | # 4. If the key exists in this new file, use temp to restore the value it had before clearing the file. 58 | for section: String in temp: 59 | if not file.has_section(section): 60 | continue 61 | for key: String in temp[section]: 62 | if not file.has_section_key(section, key): 63 | continue 64 | file.set_value(section, key, temp[section][key]) 65 | 66 | file.save(_FILE_PATH) 67 | 68 | 69 | static func _get_file() -> ConfigFile: 70 | var file: ConfigFile = ConfigFile.new() 71 | if FileAccess.file_exists(_FILE_PATH): 72 | file.load(_FILE_PATH) 73 | else: 74 | file.save(_FILE_PATH) 75 | return file 76 | 77 | 78 | static func _get_settings_in_dir(path: String) -> PackedStringArray: 79 | var result: PackedStringArray 80 | var dir_access: DirAccess = DirAccess.open(path) 81 | 82 | for file: String in dir_access.get_files(): 83 | if file.get_extension() != "tres": 84 | continue 85 | 86 | var file_path: String = path.path_join(file) 87 | result.append(file_path) 88 | 89 | for dir: String in dir_access.get_directories(): 90 | var dir_path: String = path.path_join(dir) 91 | var dir_settings: PackedStringArray = _get_settings_in_dir(dir_path) 92 | result.append_array(dir_settings) 93 | 94 | return result 95 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 40 | -------------------------------------------------------------------------------- /addons/ggs/core/component.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @abstract 3 | extends MarginContainer 4 | class_name GGSComponent 5 | ## A components is a control/user interface node that allow the user to change the value of its assigned setting. 6 | 7 | const _WARNING_NO_SETTING: String = "No setting is assigned." 8 | const _WARNING_INVALID: String = "The assigned setting is invalid. Ensure that it's save on disc and is in the settings directory." 9 | const _WARNING_EMPTY_KEY: String = "Setting key is empty and won't be saved to or loaded from the file." 10 | const _WARNING_INCOMPATIBLE_SETTING: String = "The type of the assigned setting is not compatible with this component." 11 | 12 | ## The setting this component will handle. The setting type must be compatible with the component. See [member compatible_types]. 13 | @export var setting: GGSSetting: set = _set_setting 14 | 15 | @export_group("Override", "override_") 16 | ## If enabled, plugin settings can be overriden for this specific component. 17 | @export_custom(PROPERTY_HINT_GROUP_ENABLE, "feature") var override_plugin_settings: bool = false: set = _set_override_plugin_settings 18 | @export var override_apply_on_changed: bool = false 19 | @export var override_grab_focus_on_mouseover: bool = false 20 | 21 | ## The current value of the setting associated with this component. 22 | var value: Variant 23 | 24 | ## Value type(s) that are compatible with this component. 25 | var compatible_types: Array[Variant.Type] = [] 26 | 27 | 28 | func _get_configuration_warnings() -> PackedStringArray: 29 | if setting == null: 30 | return [_WARNING_NO_SETTING] 31 | 32 | var warnings: PackedStringArray 33 | if ( 34 | setting.resource_path.is_empty() 35 | or not setting.resource_path.begins_with(GGS.plugin_settings.settings_directory) 36 | ): 37 | warnings.append(_WARNING_INVALID) 38 | 39 | if setting.key.is_empty(): 40 | warnings.append(_WARNING_EMPTY_KEY) 41 | 42 | if ( 43 | not compatible_types.is_empty() 44 | and not compatible_types.has(setting.type) 45 | ): 46 | warnings.append(_WARNING_INCOMPATIBLE_SETTING) 47 | 48 | return warnings 49 | 50 | 51 | func _set_setting(value: GGSSetting) -> void: 52 | # Disconnect signal of the previous setting 53 | if ( 54 | setting != null 55 | and setting.changed.is_connected(_on_setting_resource_changed) 56 | ): 57 | setting.changed.disconnect(_on_setting_resource_changed) 58 | 59 | setting = value 60 | update_configuration_warnings() 61 | if setting != null: 62 | setting.changed.connect(_on_setting_resource_changed) 63 | 64 | 65 | func _set_override_plugin_settings(value: bool) -> void: 66 | override_plugin_settings = value 67 | if not override_plugin_settings: 68 | override_apply_on_changed = false 69 | override_grab_focus_on_mouseover = false 70 | 71 | 72 | ## Whether the setting should be applied on value change, depending on the override value. 73 | func can_apply_on_changed() -> bool: 74 | if override_plugin_settings: 75 | return override_apply_on_changed 76 | else: 77 | return GGS.plugin_settings.components_apply_on_changed 78 | 79 | 80 | ## Whether the component should grab focus on mouseover, depending on the override value. 81 | func can_grab_focus_on_mouseover() -> bool: 82 | if override_plugin_settings: 83 | return override_grab_focus_on_mouseover 84 | else: 85 | return GGS.plugin_settings.components_grab_focus_on_mouseover 86 | 87 | 88 | ## Saves the setting value to the save file and applies it to the game.[[br] 89 | ## An ApplyBtn calls this method to apply its associated settings. 90 | func apply_setting() -> void: 91 | GGSSaveManager.save_setting_value(setting, value) 92 | GGS.setting_applied.emit(setting, value) 93 | setting.apply(value) 94 | 95 | 96 | ## Saves the default value of the setting to the save file and applies it to the game, effectively reseting it.[br] 97 | ## A ResetBtn calls this method to reset its associated settings. 98 | func reset_setting() -> void: 99 | GGSSaveManager.save_setting_value(setting, setting.default) 100 | GGS.setting_applied.emit(setting, value) 101 | setting.apply(value) 102 | 103 | 104 | func _on_setting_resource_changed() -> void: 105 | update_configuration_warnings() 106 | -------------------------------------------------------------------------------- /addons/ggs/core/input_databases/input_text_db.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | class_name GGSInputTextDB 4 | ## Resource class for storing text data for mouse and joypad input events. 5 | 6 | @export var mouse: Dictionary[MouseButton, String] = { 7 | MOUSE_BUTTON_LEFT: "LMB", 8 | MOUSE_BUTTON_RIGHT: "RMB", 9 | MOUSE_BUTTON_MIDDLE: "MMB", 10 | MOUSE_BUTTON_WHEEL_UP: "MW Up", 11 | MOUSE_BUTTON_WHEEL_DOWN: "MW Down", 12 | MOUSE_BUTTON_WHEEL_LEFT: "MW Left", 13 | MOUSE_BUTTON_WHEEL_RIGHT: "MW Right", 14 | MOUSE_BUTTON_XBUTTON1: "MB1", 15 | MOUSE_BUTTON_XBUTTON2: "MB2", 16 | } 17 | 18 | @export_group("Xbox") 19 | @export var xbox_button: Dictionary[JoyButton, String] = { 20 | JOY_BUTTON_A: "A", 21 | JOY_BUTTON_B: "B", 22 | JOY_BUTTON_X: "X", 23 | JOY_BUTTON_Y: "Y", 24 | JOY_BUTTON_BACK: "Back", 25 | JOY_BUTTON_GUIDE: "Home", 26 | JOY_BUTTON_START: "Start", 27 | JOY_BUTTON_LEFT_STICK: "LS", 28 | JOY_BUTTON_RIGHT_STICK: "RS", 29 | JOY_BUTTON_LEFT_SHOULDER: "LB", 30 | JOY_BUTTON_RIGHT_SHOULDER: "RB", 31 | JOY_BUTTON_DPAD_UP: "D-Pad Up", 32 | JOY_BUTTON_DPAD_DOWN: "D-Pad Down", 33 | JOY_BUTTON_DPAD_LEFT: "D-Pad Left", 34 | JOY_BUTTON_DPAD_RIGHT: "D-Pad Right", 35 | JOY_BUTTON_MISC1: "Share", 36 | JOY_BUTTON_PADDLE1: "PAD1", 37 | JOY_BUTTON_PADDLE2: "PAD2", 38 | JOY_BUTTON_PADDLE3: "PAD3", 39 | JOY_BUTTON_PADDLE4: "PAD4", 40 | JOY_BUTTON_TOUCHPAD: "Touchpad", 41 | } 42 | @export var xbox_axis: Dictionary[GGSInputUtils.AxisDirection, String] = { 43 | GGSInputUtils.AxisDirection.LEFT_STICK_LEFT: "LStick Left", 44 | GGSInputUtils.AxisDirection.LEFT_STICK_RIGHT: "LStick Right", 45 | GGSInputUtils.AxisDirection.LEFT_STICK_UP: "LStick Up", 46 | GGSInputUtils.AxisDirection.LEFT_STICK_DOWN: "LStick Down", 47 | GGSInputUtils.AxisDirection.RIGHT_STICK_LEFT: "RStick Left", 48 | GGSInputUtils.AxisDirection.RIGHT_STICK_RIGHT: "RStick Right", 49 | GGSInputUtils.AxisDirection.RIGHT_STICK_UP: "RStick Up", 50 | GGSInputUtils.AxisDirection.RIGHT_STICK_DOWN: "RStick Down", 51 | GGSInputUtils.AxisDirection.LEFT_TRIGGER: "LT", 52 | GGSInputUtils.AxisDirection.RIGHT_TRIGGER: "RT", 53 | } 54 | 55 | @export_group("Playstation") 56 | @export var playstation_button: Dictionary[JoyButton, String] = { 57 | JOY_BUTTON_A: "Cross", 58 | JOY_BUTTON_B: "Circle", 59 | JOY_BUTTON_X: "Square", 60 | JOY_BUTTON_Y: "Triangle", 61 | JOY_BUTTON_BACK: "Select", 62 | JOY_BUTTON_GUIDE: "PS", 63 | JOY_BUTTON_START: "Start", 64 | JOY_BUTTON_LEFT_STICK: "L3", 65 | JOY_BUTTON_RIGHT_STICK: "R3", 66 | JOY_BUTTON_LEFT_SHOULDER: "L1", 67 | JOY_BUTTON_RIGHT_SHOULDER: "R1", 68 | JOY_BUTTON_DPAD_UP: "D-Pad Up", 69 | JOY_BUTTON_DPAD_DOWN: "D-Pad Down", 70 | JOY_BUTTON_DPAD_LEFT: "D-Pad Left", 71 | JOY_BUTTON_DPAD_RIGHT: "D-Pad Right", 72 | JOY_BUTTON_MISC1: "Microphone", 73 | JOY_BUTTON_PADDLE1: "PAD1", 74 | JOY_BUTTON_PADDLE2: "PAD2", 75 | JOY_BUTTON_PADDLE3: "PAD3", 76 | JOY_BUTTON_PADDLE4: "PAD4", 77 | JOY_BUTTON_TOUCHPAD: "Touchpad", 78 | } 79 | @export var playstation_axis: Dictionary[GGSInputUtils.AxisDirection, String] = { 80 | GGSInputUtils.AxisDirection.LEFT_STICK_LEFT: "LStick Left", 81 | GGSInputUtils.AxisDirection.LEFT_STICK_RIGHT: "LStick Right", 82 | GGSInputUtils.AxisDirection.LEFT_STICK_UP: "LStick Up", 83 | GGSInputUtils.AxisDirection.LEFT_STICK_DOWN: "LStick Down", 84 | GGSInputUtils.AxisDirection.RIGHT_STICK_LEFT: "RStick Left", 85 | GGSInputUtils.AxisDirection.RIGHT_STICK_RIGHT: "RStick Right", 86 | GGSInputUtils.AxisDirection.RIGHT_STICK_UP: "RStick Up", 87 | GGSInputUtils.AxisDirection.RIGHT_STICK_DOWN: "RStick Down", 88 | GGSInputUtils.AxisDirection.LEFT_TRIGGER: "L2", 89 | GGSInputUtils.AxisDirection.RIGHT_TRIGGER: "R2", 90 | } 91 | 92 | @export_group("Switch") 93 | @export var switch_button: Dictionary[JoyButton, String] = { 94 | JOY_BUTTON_A: "B", 95 | JOY_BUTTON_B: "A", 96 | JOY_BUTTON_X: "Y", 97 | JOY_BUTTON_Y: "X", 98 | JOY_BUTTON_BACK: "Minus", 99 | JOY_BUTTON_GUIDE: "Home", 100 | JOY_BUTTON_START: "Plus", 101 | JOY_BUTTON_LEFT_STICK: "LS", 102 | JOY_BUTTON_RIGHT_STICK: "RS", 103 | JOY_BUTTON_LEFT_SHOULDER: "L", 104 | JOY_BUTTON_RIGHT_SHOULDER: "R", 105 | JOY_BUTTON_DPAD_UP: "D-Pad Up", 106 | JOY_BUTTON_DPAD_DOWN: "D-Pad Down", 107 | JOY_BUTTON_DPAD_LEFT: "D-Pad Left", 108 | JOY_BUTTON_DPAD_RIGHT: "D-Pad Right", 109 | JOY_BUTTON_MISC1: "Capture", 110 | JOY_BUTTON_PADDLE1: "PAD1", 111 | JOY_BUTTON_PADDLE2: "PAD2", 112 | JOY_BUTTON_PADDLE3: "PAD3", 113 | JOY_BUTTON_PADDLE4: "PAD4", 114 | JOY_BUTTON_TOUCHPAD: "Touchpad", 115 | } 116 | @export var switch_axis: Dictionary[GGSInputUtils.AxisDirection, String] = { 117 | GGSInputUtils.AxisDirection.LEFT_STICK_LEFT: "LStick Left", 118 | GGSInputUtils.AxisDirection.LEFT_STICK_RIGHT: "LStick Right", 119 | GGSInputUtils.AxisDirection.LEFT_STICK_UP: "LStick Up", 120 | GGSInputUtils.AxisDirection.LEFT_STICK_DOWN: "LStick Down", 121 | GGSInputUtils.AxisDirection.RIGHT_STICK_LEFT: "RStick Left", 122 | GGSInputUtils.AxisDirection.RIGHT_STICK_RIGHT: "RStick Right", 123 | GGSInputUtils.AxisDirection.RIGHT_STICK_UP: "RStick Up", 124 | GGSInputUtils.AxisDirection.RIGHT_STICK_DOWN: "RStick Down", 125 | GGSInputUtils.AxisDirection.LEFT_TRIGGER: "ZL", 126 | GGSInputUtils.AxisDirection.RIGHT_TRIGGER: "ZR", 127 | } 128 | -------------------------------------------------------------------------------- /ggs/components/input_btn/component_input_btn.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://ggs/components/input_btn/input_btn.svg") 3 | extends GGSComponent 4 | 5 | enum State {NORMAL, LISTENING} 6 | enum AcceptedTypes { 7 | KEYBOARD = 1 << 0, 8 | MOUSE = 1 << 1, 9 | JOYPAD_BUTTON = 1 << 2, 10 | JOYPAD_AXIS = 1 << 3, 11 | } 12 | 13 | const TEXT_ANIM: PackedStringArray = [".", "..", "...", "."] 14 | 15 | ## Types of input this component listens to. 16 | @export_flags("Keyboard", "Mouse", "Joypad Button", "Joypad Axis") var _accepted_types: int = 1 17 | 18 | ## Whether modifiers should be accepted. Only relevant for keyboard and mouse events. 19 | @export var _accept_modifiers: bool 20 | 21 | ## Whether the component should show use glyphs to show mouse and joypad events. Uses the [GGSGlyphDB] select in the plugin 22 | ## settings for its image data. 23 | @export var _use_glyph: bool 24 | 25 | var _state: State = State.NORMAL: set = _set_state 26 | var _new_event: InputEvent 27 | var _tween: Tween 28 | var _anim_frame: int: set = _set_anim_frame 29 | 30 | @onready var _btn: Button = $Btn 31 | @onready var _listen_timer: Timer = $ListenTime 32 | @onready var _accept_delay: Timer = $AcceptDelay 33 | 34 | 35 | func _ready() -> void: 36 | compatible_types = [TYPE_ARRAY] 37 | if Engine.is_editor_hint(): 38 | return 39 | 40 | _btn.toggled.connect(_on_btn_toggled) 41 | _btn.mouse_entered.connect(_on_btn_mouse_entered) 42 | _btn.focus_entered.connect(_on_btn_focus_entered) 43 | _listen_timer.timeout.connect(_on_listen_timer_timeout) 44 | _accept_delay.timeout.connect(_on_accept_delay_timeout) 45 | Input.joy_connection_changed.connect(_on_input_joy_connection_changed) 46 | 47 | init_value() 48 | _listen_timer.wait_time = GGS.plugin_settings.input_btn_listen_time 49 | _accept_delay.wait_time = GGS.plugin_settings.input_btn_accept_delay 50 | 51 | 52 | func _input(event: InputEvent) -> void: 53 | if _state != State.LISTENING: 54 | return 55 | 56 | if not _event_is_acceptable(event): 57 | return 58 | 59 | _new_event = event 60 | accept_event() 61 | _accept_delay.start() 62 | 63 | 64 | func _set_state(new_state: State) -> void: 65 | _state = new_state 66 | match new_state: 67 | State.NORMAL: 68 | _btn.set_pressed_no_signal(false) 69 | _tween.kill() 70 | _update_btn_display() 71 | State.LISTENING: 72 | _btn.icon = null 73 | _tween = create_tween() 74 | _tween.set_loops() 75 | _tween.tween_property( 76 | self, 77 | "_anim_frame", 78 | TEXT_ANIM.size() - 1, 79 | GGS.plugin_settings.input_btn_anim_duration 80 | ).from(0) 81 | 82 | 83 | func _set_anim_frame(frame: int) -> void: 84 | _anim_frame = frame 85 | _btn.text = TEXT_ANIM[frame] 86 | 87 | 88 | func init_value() -> void: 89 | value = GGSSaveManager.load_setting_value(setting) 90 | _update_btn_display() 91 | 92 | 93 | func reset_setting() -> void: 94 | super() 95 | _update_btn_display() 96 | 97 | 98 | func _event_is_acceptable(event: InputEvent) -> bool: 99 | if ( 100 | event is not InputEventKey 101 | and event is not InputEventMouseButton 102 | and event is not InputEventJoypadButton 103 | and event is not InputEventJoypadMotion 104 | ): 105 | return false 106 | 107 | if not event.is_pressed(): 108 | return false 109 | 110 | if event.is_echo(): 111 | return false 112 | 113 | if ( 114 | event is InputEventMouse 115 | and event.double_click 116 | ): 117 | return false 118 | 119 | if ( 120 | event is InputEventWithModifiers 121 | and not _accept_modifiers 122 | and (event.shift_pressed or event.ctrl_pressed or event.alt_pressed) 123 | ): 124 | return false 125 | 126 | if ( 127 | event is InputEventKey 128 | and not (_accepted_types & AcceptedTypes.KEYBOARD) 129 | ): 130 | return false 131 | 132 | if ( 133 | event is InputEventMouseButton 134 | and not (_accepted_types & AcceptedTypes.MOUSE) 135 | ): 136 | return false 137 | 138 | if ( 139 | event is InputEventJoypadButton 140 | and not (_accepted_types & AcceptedTypes.JOYPAD_BUTTON) 141 | ): 142 | return false 143 | 144 | if ( 145 | event is InputEventJoypadMotion 146 | and not (_accepted_types & AcceptedTypes.JOYPAD_AXIS) 147 | ): 148 | return false 149 | 150 | return true 151 | 152 | 153 | 154 | func _accepted_type_has_glyph() -> bool: 155 | return ( 156 | _accepted_types & AcceptedTypes.MOUSE 157 | or _accepted_types & AcceptedTypes.JOYPAD_BUTTON 158 | or _accepted_types & AcceptedTypes.JOYPAD_AXIS 159 | ) 160 | 161 | 162 | func _update_btn_display() -> void: 163 | var event: InputEvent = GGSInputUtils.deserialize_event(value) 164 | 165 | if _use_glyph and _accepted_type_has_glyph(): 166 | _btn.icon = GGSInputUtils.event_get_glyph(event) 167 | if _btn.icon == null: 168 | _btn.text = GGSInputUtils.event_get_text(event) 169 | else: 170 | _btn.text = "" 171 | return 172 | 173 | _btn.icon = null 174 | _btn.text = GGSInputUtils.event_get_text(event) 175 | 176 | 177 | func _on_btn_toggled(toggled_on: bool) -> void: 178 | if toggled_on: 179 | GGS.audio_activated.play() 180 | _state = State.LISTENING 181 | _listen_timer.start() 182 | 183 | 184 | func _on_listen_timer_timeout() -> void: 185 | _state = State.NORMAL 186 | 187 | 188 | func _on_accept_delay_timeout() -> void: 189 | _state = State.NORMAL 190 | value = GGSInputUtils.serialize_event(_new_event) 191 | if can_apply_on_changed(): 192 | apply_setting() 193 | 194 | _new_event = null 195 | _update_btn_display() 196 | GGS.audio_activated.play() 197 | 198 | 199 | func _on_btn_mouse_entered() -> void: 200 | GGS.audio_mouse_entered.play() 201 | if can_grab_focus_on_mouseover(): 202 | _btn.grab_focus() 203 | 204 | 205 | func _on_btn_focus_entered() -> void: 206 | GGS.audio_focus_entered.play() 207 | 208 | 209 | func _on_input_joy_connection_changed(_device: int, _connected: bool) -> void: 210 | _update_btn_display() 211 | -------------------------------------------------------------------------------- /addons/ggs/core/utilities/input_utils.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends RefCounted 3 | class_name GGSInputUtils 4 | ## Provides various input utilities. 5 | 6 | enum InputType {KEYBOARD, MOUSE, JOYPAD_BUTTON, JOYPAD_MOTION} 7 | 8 | enum AxisDirection { 9 | LEFT_STICK_LEFT, LEFT_STICK_RIGHT, LEFT_STICK_UP, LEFT_STICK_DOWN, 10 | RIGHT_STICK_LEFT, RIGHT_STICK_RIGHT, RIGHT_STICK_UP, RIGHT_STICK_DOWN, 11 | LEFT_TRIGGER, RIGHT_TRIGGER, 12 | } 13 | 14 | const _AXIS_TO_DIRECTION: Dictionary[JoyAxis, Dictionary] = { 15 | JOY_AXIS_LEFT_X: { 16 | -1: AxisDirection.LEFT_STICK_LEFT, 17 | 1: AxisDirection.LEFT_STICK_RIGHT, 18 | }, 19 | JOY_AXIS_LEFT_Y: { 20 | -1: AxisDirection.LEFT_STICK_UP, 21 | 1: AxisDirection.LEFT_STICK_DOWN, 22 | }, 23 | JOY_AXIS_RIGHT_X: { 24 | -1: AxisDirection.RIGHT_STICK_LEFT, 25 | 1: AxisDirection.RIGHT_STICK_RIGHT, 26 | }, 27 | JOY_AXIS_RIGHT_Y: { 28 | -1: AxisDirection.RIGHT_STICK_UP, 29 | 1: AxisDirection.RIGHT_STICK_DOWN, 30 | }, 31 | JOY_AXIS_TRIGGER_LEFT: { 32 | 1: AxisDirection.LEFT_TRIGGER, 33 | }, 34 | JOY_AXIS_TRIGGER_RIGHT: { 35 | 1: AxisDirection.RIGHT_TRIGGER, 36 | }, 37 | } 38 | 39 | const JOYPAD_DEVICE_LAYOUTS: Dictionary = { 40 | "XInput Gamepad": "xbox", 41 | "Xbox Series Controller": "xbox", 42 | "Sony DualSense": "playstation", 43 | "PS5 Controller": "playstation", 44 | "PS4 Controller": "playstation", 45 | "Switch": "switch", 46 | } 47 | 48 | const MODIFIERS_MASK: int = KEY_MASK_SHIFT | KEY_MASK_CTRL | KEY_MASK_ALT 49 | 50 | 51 | ## Serializes the given event by saving its key properties in an array. 52 | static func serialize_event(event: InputEvent) -> Array: 53 | var type: int = -1 54 | var id: int = -1 55 | var axis_dir: int = 0 56 | 57 | if event is InputEventKey: 58 | type = InputType.KEYBOARD 59 | id = event.physical_keycode | event.get_modifiers_mask() 60 | 61 | if event is InputEventMouseButton: 62 | type = InputType.MOUSE 63 | id = event.button_index | event.get_modifiers_mask() 64 | 65 | if event is InputEventJoypadButton: 66 | type = InputType.JOYPAD_BUTTON 67 | id = event.button_index 68 | 69 | if event is InputEventJoypadMotion: 70 | type = InputType.JOYPAD_MOTION 71 | id = event.axis 72 | axis_dir = event.axis_value 73 | 74 | return [type, id, axis_dir] 75 | 76 | 77 | ## Recreates an event serialized via [method serialize_event]. 78 | static func deserialize_event(data: Array) -> InputEvent: 79 | var type: int = data[0] 80 | var id: int = data[1] 81 | var axis_dir: int = data[2] 82 | 83 | var event: InputEvent 84 | if type == InputType.KEYBOARD: 85 | event = InputEventKey.new() 86 | event.physical_keycode = id & ~MODIFIERS_MASK 87 | event.shift_pressed = bool(id & KEY_MASK_SHIFT) 88 | event.ctrl_pressed = bool(id & KEY_MASK_CTRL) 89 | event.alt_pressed = bool(id & KEY_MASK_ALT) 90 | 91 | if type == InputType.MOUSE: 92 | event = InputEventMouseButton.new() 93 | event.button_index = id & ~MODIFIERS_MASK 94 | event.shift_pressed = bool(id & KEY_MASK_SHIFT) 95 | event.ctrl_pressed = bool(id & KEY_MASK_CTRL) 96 | event.alt_pressed = bool(id & KEY_MASK_ALT) 97 | 98 | if type == InputType.JOYPAD_BUTTON: 99 | event = InputEventJoypadButton.new() 100 | event.button_index = id 101 | 102 | if type == InputType.JOYPAD_MOTION: 103 | event = InputEventJoypadMotion.new() 104 | event.axis = id 105 | event.axis_value = axis_dir 106 | 107 | return event 108 | 109 | 110 | ## Retrieves the input events associated with the given action. Unlike [method InputMap.action_get_events()], it works in the 111 | ## editor. 112 | static func action_get_events(action: String) -> Array: 113 | var project_file: ConfigFile = ConfigFile.new() 114 | project_file.load("res://project.godot") 115 | var action_properties: Dictionary = project_file.get_value("input", action) 116 | var action_events: Array = action_properties["events"] 117 | return action_events 118 | 119 | 120 | ## Returns a string representation of the event. Uses the [GGSInputTextDB] selected in the plugin settings for its text data. 121 | ## The returned text for joypad events depends on the joypad device selected in the plugin settings. 122 | static func event_get_text(event: InputEvent) -> String: 123 | var text: String = "INVALID EVENT" 124 | var db: GGSInputTextDB = GGS.plugin_settings.text_db 125 | 126 | if event is InputEventKey: 127 | var keycode_with_modifiers: int = event.get_physical_keycode_with_modifiers() 128 | text = OS.get_keycode_string(keycode_with_modifiers) 129 | 130 | if event is InputEventMouseButton: 131 | var modifiers_string: String = _event_get_modifiers_string(event) 132 | text = modifiers_string + db.mouse[event.button_index] 133 | 134 | if event is InputEventJoypadButton: 135 | var layout: String = _joypad_get_device_layout(event) 136 | var property: String = "%s_button"%[layout] 137 | text = db.get(property)[event.button_index] 138 | 139 | if event is InputEventJoypadMotion: 140 | var layout: String = _joypad_get_device_layout(event) 141 | var property: String = "%s_axis"%[layout] 142 | var axis_direction: AxisDirection = _event_get_axis_direction(event) 143 | text = db.get(property)[axis_direction] 144 | 145 | return text 146 | 147 | 148 | ## Returns an image representation of the event. Uses the [GGSInputGlyphDB] selected in the plugin settings for its image data. 149 | ## The returned image for joypad events depends on the joypad device selected in the plugin settings. 150 | static func event_get_glyph(event: InputEvent) -> Texture2D: 151 | var db: GGSInputGlyphDB = GGS.plugin_settings.glyph_db 152 | var glyph: Texture2D = null 153 | 154 | if event is InputEventMouseButton: 155 | glyph = db.mouse[event.button_index] 156 | 157 | if event is InputEventJoypadButton: 158 | var layout: String = _joypad_get_device_layout(event) 159 | var property: String = "%s_button"%[layout] 160 | glyph = db.get(property)[event.button_index] 161 | 162 | if event is InputEventJoypadMotion: 163 | var layout: String = _joypad_get_device_layout(event) 164 | var property: String = "%s_axis"%[layout] 165 | var axis_direction: AxisDirection = _event_get_axis_direction(event) 166 | glyph = db.get(property)[axis_direction] 167 | 168 | return glyph 169 | 170 | 171 | static func _event_get_modifiers_string(event: InputEventWithModifiers) -> String: 172 | var modifiers: PackedStringArray 173 | if event.shift_pressed: 174 | modifiers.append("Shift") 175 | 176 | if event.ctrl_pressed: 177 | modifiers.append("Ctrl") 178 | 179 | if event.alt_pressed: 180 | modifiers.append("Alt") 181 | 182 | if modifiers.is_empty(): 183 | return "" 184 | else: 185 | return "+".join(modifiers) + "+" 186 | 187 | 188 | static func _event_get_axis_direction(event: InputEventJoypadMotion) -> AxisDirection: 189 | var axis_direction: int = sign(event.axis_value) 190 | return _AXIS_TO_DIRECTION[event.axis][axis_direction] 191 | 192 | 193 | static func _joypad_get_device_layout(event: InputEvent) -> String: 194 | var device_name: String = Input.get_joy_name(event.device) 195 | if JOYPAD_DEVICE_LAYOUTS.has(device_name): 196 | return JOYPAD_DEVICE_LAYOUTS[device_name] 197 | else: 198 | return GGS.plugin_settings.joypad_fallback_layout.to_lower() 199 | --------------------------------------------------------------------------------