add reusable options menu plugin with localized UI

- Settings autoloads (Game/Input/Graphics/Audio) backed by FileManager,
  persisted to user:// JSON files
- OptionsMenu scene with tabs wired to each autoload: Game (locale),
  Input (invert vertical axis), Graphics (window mode, borderless,
  screen), Audio (master/music/fx volume)
- GraphicsSettings reads DisplayServer state when running in editor
  to avoid clobbering the editor's window mode; exported builds load
  from disk and apply via set_screen_and_apply with a _loading guard
  to suppress redundant writes
- Built-in translations for en/es/fr/de/ja/zh_CN registered at runtime
  from translations/gd_options.csv
- Control-prompt icon assets for keyboard, PlayStation, Xbox, Nintendo
- Flesh out README with scope and audio bus layout requirement

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Samson 2026-04-28 18:06:45 +01:00
parent 810e5a6da0
commit f0f8bdfafb
No known key found for this signature in database
GPG Key ID: A9EB2589C60F7268
74 changed files with 1995 additions and 1 deletions

View File

@ -1,3 +1,31 @@
# gd-options
re-usable settings plugin for godot (gd scripts), to configure game, graphics and sound settings.
Re-usable settings/options menu for godot (gd scripts) to configure game, input, graphics and sound settings.
- GameSettings global
- InputSettings global
- GraphicsSettings global
- AudioSettings global
- Saves and loads states from [user://](https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html) application directory
## Pre-Requisites
Game:
- TBD
Input:
- TBD
Graphics:
- TBD
Audio:
- Use the default_audio_bus_layout
- "Master" bus
- "Music" bus
- "FX" bus

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

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

7
plugin.cfg Normal file
View File

@ -0,0 +1,7 @@
[plugin]
name="gd-options"
description="Re-usable settings menu for godot (gd scripts) to configure game, input, graphics and sound settings."
author="Daniel Samson"
version="0.1.0"
script="plugin.gd"

12
plugin.gd Normal file
View File

@ -0,0 +1,12 @@
@tool
extends EditorPlugin
func _enter_tree() -> void:
add_autoload_singleton("GameSettings", "res://addons/gd-options/src/common/game_settings.gd")
add_autoload_singleton("InputSettings", "res://addons/gd-options/src/common/input_settings.gd")
add_autoload_singleton("GraphicsSettings", "res://addons/gd-options/src/common/graphics_settings.gd")
add_autoload_singleton("AudioSettings", "res://addons/gd-options/src/common/audio_settings.gd")
func _exit_tree() -> void:
pass

1
plugin.gd.uid Normal file
View File

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

View File

@ -0,0 +1,72 @@
extends Node
# CLAMP Settings in decebels
const MIN_DB = -80
const MAX_DB = 20
# file path
const _SETTINGS_PATH = "user://audio.settings.json"
# audio server buses:
const _MASTER_BUS = "Master"
const _MUSIC_BUS = "Music"
const _FX_BUS = "FX"
# _state keys:
const _KEY_MASTER_VOLUME = "master_volume"
const _KEY_MUSIC_VOLUME = "music_volume"
const _KEY_FX_VOLUME = "fx_volume"
var _state_defaults: Dictionary = {
_KEY_MASTER_VOLUME: 0.0,
_KEY_MUSIC_VOLUME: -20.0,
_KEY_FX_VOLUME: -6.0
}
var _state: Dictionary
func _ready():
_state = FileManager.load_file(_SETTINGS_PATH, _state_defaults)
_set_audio_server_volume(_MASTER_BUS, _state[_KEY_MASTER_VOLUME])
_set_audio_server_volume(_MUSIC_BUS, _state[_KEY_MUSIC_VOLUME])
_set_audio_server_volume(_FX_BUS, _state[_KEY_FX_VOLUME])
func get_master_volume() -> float:
return _state[_KEY_MASTER_VOLUME]
func set_master_volume(decebels: float) -> void:
var volume = _clamp_volume(decebels)
_state[_KEY_MASTER_VOLUME] = volume
FileManager.save_file(_SETTINGS_PATH, _state)
_set_audio_server_volume(_MASTER_BUS, volume)
func get_music_volume() -> float:
return _state[_KEY_MUSIC_VOLUME]
func set_music_volume(decebels: float) -> void:
var volume = _clamp_volume(decebels)
_state[_KEY_MUSIC_VOLUME] = volume
FileManager.save_file(_SETTINGS_PATH, _state)
_set_audio_server_volume(_MUSIC_BUS, volume)
func get_fx_volume() -> float:
return _state[_KEY_FX_VOLUME]
func set_fx_volume(decebels: float) -> void:
var volume = _clamp_volume(decebels)
_state[_KEY_FX_VOLUME] = volume
FileManager.save_file(_SETTINGS_PATH, _state)
_set_audio_server_volume(_FX_BUS, volume)
func from_db_to_percent(decebels: float) -> float:
return 100 - ((decebels / MIN_DB) * 100)
func from_percent_to_db(percentage: float) -> float:
return MIN_DB - ((percentage * .01) * MIN_DB)
func _clamp_volume(value: float) -> float:
return clampf(value, MIN_DB, MAX_DB)
func _set_audio_server_volume(bus_name: String, volume: float):
var bus_index = AudioServer.get_bus_index(bus_name)
if bus_index == -1:
printerr("Missing AudioServer bus: " + bus_name + ". Please add bus to audio tab.")
AudioServer.set_bus_volume_db(bus_index, volume)

View File

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

View File

@ -0,0 +1,20 @@
extends Node
class_name FileManager
static func load_file(path: String, initial_data: Dictionary = {}) -> Dictionary:
if not FileAccess.file_exists(path):
save_file(path, initial_data)
var save_game = FileAccess.get_file_as_string(path)
var parse_result = JSON.parse_string(save_game)
if parse_result == null:
printerr("JSON Parse Error: " + path)
return parse_result
static func save_file(path: String, data: Dictionary) -> void:
var save_game = FileAccess.open(path, FileAccess.WRITE)
var json_string = JSON.stringify(data)
save_game.store_line(json_string)
save_game.close()

View File

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

View File

@ -0,0 +1,64 @@
extends Node
const _TRANSLATIONS_CSV = "res://addons/gd-options/translations/gd_options.csv"
# file path
const _SETTINGS_PATH = "user://game.settings.json"
# _state keys:
const _KEY_LOCALE = "locale"
var _state: Dictionary
func _ready():
_load_addon_translations()
var defaults = {
_KEY_LOCALE: _resolve_locale(TranslationServer.get_locale())
}
_state = FileManager.load_file(_SETTINGS_PATH, defaults)
TranslationServer.set_locale(_state[_KEY_LOCALE])
func get_locale() -> String:
return _state[_KEY_LOCALE]
func set_locale(locale: String) -> void:
_state[_KEY_LOCALE] = locale
FileManager.save_file(_SETTINGS_PATH, _state)
TranslationServer.set_locale(locale)
# Maps a system or saved locale to the closest locale we actually ship a
# translation for, so the dropdown selection and tr() lookups agree.
func _resolve_locale(locale: String) -> String:
var available = TranslationServer.get_loaded_locales()
if locale in available:
return locale
var prefix = locale.split("_", false, 1)[0]
if prefix in available:
return prefix
if available.size() > 0:
return available[0]
return locale
func _load_addon_translations() -> void:
var file = FileAccess.open(_TRANSLATIONS_CSV, FileAccess.READ)
if file == null:
return
var header = file.get_csv_line()
if header.size() < 2:
file.close()
return
var translations: Array[Translation] = []
for i in range(1, header.size()):
var t = Translation.new()
t.locale = header[i]
translations.append(t)
while not file.eof_reached():
var row = file.get_csv_line()
if row.size() < 2 or row[0] == "":
continue
for i in range(1, header.size()):
if i < row.size():
translations[i - 1].add_message(row[0], row[i])
file.close()
for t in translations:
TranslationServer.add_translation(t)

View File

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

View File

@ -0,0 +1,113 @@
extends Node
signal screen_count_changed
# file path
const _SETTINGS_PATH = "user://graphics.settings.json"
# _state keys:
const _KEY_WINDOW_MODE = "window_mode"
const _KEY_BORDERLESS = "borderless"
const _KEY_USE_PRIMARY_SCREEN = "use_primary_screen"
const _KEY_USE_SCREEN = "use_screen"
var _state: Dictionary
var _current_screen_count: int
var _loading: bool = false
func _ready():
var current_screen = DisplayServer.window_get_current_screen()
var defaults = {
_KEY_WINDOW_MODE: DisplayServer.window_get_mode(),
_KEY_BORDERLESS: DisplayServer.window_get_flag(DisplayServer.WINDOW_FLAG_BORDERLESS),
_KEY_USE_PRIMARY_SCREEN: current_screen == DisplayServer.get_primary_screen(),
_KEY_USE_SCREEN: current_screen
}
_current_screen_count = DisplayServer.get_screen_count()
if OS.has_feature("editor"):
_state = defaults
else:
_state = FileManager.load_file(_SETTINGS_PATH, defaults)
_loading = true
set_screen_and_apply(_state[_KEY_USE_SCREEN])
_loading = false
func _save() -> void:
if _loading:
return
FileManager.save_file(_SETTINGS_PATH, _state)
func _process(_delta):
var screen_count = DisplayServer.get_screen_count()
if screen_count < _current_screen_count:
var primary_screen = DisplayServer.get_primary_screen()
set_screen_and_apply(primary_screen)
_current_screen_count = screen_count
emit_signal("screen_count_changed")
func get_window_mode() -> int:
return _state[_KEY_WINDOW_MODE]
func set_window_mode(mode: int) -> void:
_state[_KEY_WINDOW_MODE] = mode
_save()
_apply()
func get_borderless() -> bool:
return _state[_KEY_BORDERLESS]
func set_borderless(is_borderless: bool) -> void:
_state[_KEY_BORDERLESS] = is_borderless
_save()
_apply()
func get_use_primary_screen() -> bool:
return _state[_KEY_USE_PRIMARY_SCREEN]
func set_use_primary_screen(use_primary_screen: bool) -> void:
_state[_KEY_USE_PRIMARY_SCREEN] = use_primary_screen
_save()
_apply()
func get_use_screen() -> int:
return _state[_KEY_USE_SCREEN]
func set_screen_and_apply(screen: int) -> void:
var old_window_mode = get_window_mode()
set_window_mode(0)
var screen_limit = get_screen_count() - 1
_state[_KEY_USE_SCREEN] = clampi(screen, 0, screen_limit)
_save()
DisplayServer.window_set_current_screen(_state[_KEY_USE_SCREEN])
DisplayServer.get_window_list()
var window = get_window()
window.grab_focus()
set_window_mode(old_window_mode)
func get_screen_count() -> int:
return DisplayServer.get_screen_count()
func _apply():
var screen = clampi(_state[_KEY_USE_SCREEN], 0, get_screen_count() - 1)
DisplayServer.window_set_current_screen(screen)
_state[_KEY_USE_SCREEN] = screen
var window_mode = _state[_KEY_WINDOW_MODE]
if window_mode == 0:
get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
elif window_mode == 2:
get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_DISABLED
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MAXIMIZED)
elif window_mode == 3:
get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_DISABLED
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, _state[_KEY_BORDERLESS])

View File

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

View File

@ -0,0 +1,22 @@
extends Node
# file path
const _SETTINGS_PATH = "user://input.settings.json"
# _state keys:
const _KEY_INVERT_VERTICAL_AXIS = "invert_vertical_axis"
var _state_defaults: Dictionary = {
_KEY_INVERT_VERTICAL_AXIS: false
}
var _state: Dictionary
func _ready():
_state = FileManager.load_file(_SETTINGS_PATH, _state_defaults)
func get_invert_vertical_axis() -> bool:
return _state[_KEY_INVERT_VERTICAL_AXIS]
func set_invert_vertical_axis(value: bool) -> void:
_state[_KEY_INVERT_VERTICAL_AXIS] = value
FileManager.save_file(_SETTINGS_PATH, _state)

View File

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

View File

@ -0,0 +1,15 @@
[gd_resource type="AudioBusLayout" format=3 uid="uid://g36xq7ydus0j"]
[resource]
bus/1/name = &"Music"
bus/1/solo = false
bus/1/mute = false
bus/1/bypass_fx = false
bus/1/volume_db = 0.0
bus/1/send = &"Master"
bus/2/name = &"FX"
bus/2/solo = false
bus/2/mute = false
bus/2/bypass_fx = false
bus/2/volume_db = 0.0
bus/2/send = &"Master"

View File

@ -0,0 +1,202 @@
extends Node
signal device_changed(device_category: DeviceCategory, device_type: DeviceType, device_id: int, device_name: String)
enum DeviceCategory {
UNKOWN,
KEYBOARD,
POINTER,
CONTROLLER,
TOUCH,
}
enum DeviceType {
UNKOWN,
KEYBOARD,
MOUSE,
PC_CONTROLLER,
XBOX_CONTROLLER,
SONY_CONTROLLER,
NINTENDO_CONTROLLER,
TOUCH,
}
var _state: InputState
func _init():
_state = InputState.new()
_state.load_state()
func _ready():
_notifify_device_change()
func _input(event: InputEvent):
# to help info, on we need to detect the input type
var last_device_category = _state.device_category
var device_category = _detect_input_category(event)
# let ignore the mouse
if device_category == DeviceCategory.POINTER:
return
# Controller is a special case
if device_category == DeviceCategory.CONTROLLER:
_on_controller_input(event)
return
if device_category == last_device_category:
return
if device_category == DeviceCategory.KEYBOARD:
_on_keyboard_input(event)
#elif device_category == DeviceCategory.POINTER:
#_on_pointer_input(event)
elif device_category == DeviceCategory.TOUCH:
_on_touch_input(event)
func _detect_input_category(event: InputEvent) -> DeviceCategory:
if event is InputEventKey:
return DeviceCategory.KEYBOARD
elif (event is InputEventMouse or event is InputEventMouseButton or event is InputEventMouseMotion):
return DeviceCategory.POINTER
elif (event is InputEventScreenTouch or event is InputEventScreenDrag):
return DeviceCategory.TOUCH
elif event is InputEventJoypadButton:
return DeviceCategory.CONTROLLER
else:
return DeviceCategory.UNKOWN
func _on_controller_input(event: InputEvent):
var last_device_id = _state.device_id
var device_id = event.get_device()
var type = _detect_controller(event)
var _last_type = _state.device_type
if (last_device_id == device_id and _last_type == type):
return
_state.device_category = DeviceCategory.CONTROLLER
var joy_name = Input.get_joy_name(device_id)
if type == DeviceType.XBOX_CONTROLLER:
_state.device_id = device_id
_state.device_type = DeviceType.XBOX_CONTROLLER
_state.device_name = joy_name
_state.save_state()
_notifify_device_change()
elif type == DeviceType.SONY_CONTROLLER:
_state.device_id = device_id
_state.device_type = DeviceType.SONY_CONTROLLER
_state.device_name = joy_name
_state.save_state()
_notifify_device_change()
elif type == DeviceType.NINTENDO_CONTROLLER:
_state.device_id = device_id
_state.device_type = DeviceType.NINTENDO_CONTROLLER
_state.device_name = joy_name
_state.save_state()
_notifify_device_change()
if type == DeviceType.PC_CONTROLLER:
_state.device_id = device_id
_state.device_type = DeviceType.PC_CONTROLLER
_state.device_name = "PC Controller"
_state.save_state()
_notifify_device_change()
func _detect_controller(event: InputEvent) -> DeviceType:
var device_id = event.get_device()
var joy_name = Input.get_joy_name(device_id)
if joy_name.to_lower().contains("xbox"):
return DeviceType.XBOX_CONTROLLER
elif joy_name.to_lower().contains("sony") or joy_name.to_lower().contains("ps"):
return DeviceType.SONY_CONTROLLER
elif joy_name.to_lower().contains("nintendo"):
return DeviceType.NINTENDO_CONTROLLER
else:
return DeviceType.PC_CONTROLLER
func _on_keyboard_input(event: InputEvent):
var device_id = event.get_device()
_state.device_category = DeviceCategory.KEYBOARD
_state.device_id = device_id
_state.device_type = DeviceType.KEYBOARD
_state.device_name = "Keyboard"
_state.save_state()
_notifify_device_change()
#func _on_pointer_input(event: InputEvent):
#var device_id = event.get_device()
#_state.device_category = DeviceCategory.POINTER
#_state.device_category = DeviceCategory.POINTER
#_state.device_id = device_id
#_state.device_type = DeviceType.MOUSE
#_state.device_name = "Mouse"
#_state.save_state()
#_notifify_device_change()
func _on_touch_input(event: InputEvent):
var device_id = event.get_device()
_state.device_category = DeviceCategory.TOUCH
_state.device_category = DeviceCategory.TOUCH
_state.device_id = device_id
_state.device_type = DeviceType.TOUCH
_state.device_name = "Touch"
_state.save_state()
_notifify_device_change()
func _notifify_device_change():
device_changed.emit(_state.device_category, _state.device_type, _state.device_id, _state.device_name)
func get_current_device_category() -> DeviceCategory:
return _state.device_category
func get_current_device_type() -> DeviceType:
return _state.device_type
func get_current_device_id() -> int:
return _state.device_id
class InputState:
var device_category: DeviceCategory = DeviceCategory.UNKOWN
var device_type: DeviceType = DeviceType.UNKOWN
var device_id: int = -2
var device_name: String = ""
func save_state():
var save = FileAccess.open("user://controls.save", FileAccess.WRITE)
var dict = {
device_category = device_category,
device_type = device_type,
device_id = device_id,
device_name = device_name,
}
var json_string = JSON.stringify(dict)
save.store_line(json_string)
save.close()
func load_state():
if not FileAccess.file_exists("user://controls.save"):
save_state()
var save = FileAccess.open("user://controls.save", FileAccess.READ)
var json_string = save.get_line()
var parse_result = JSON.parse_string(json_string)
if parse_result == null:
print("JSON Parse Error: savegame ")
var data = parse_result
device_category = data["device_category"]
device_type = data["device_type"]
device_id = data["device_id"]
device_name = data["device_name"]
save.close()

View File

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

20
src/menus/options_menu.gd Normal file
View File

@ -0,0 +1,20 @@
extends Control
signal close
func _ready() -> void:
pass
func _input(event):
if event.is_action_pressed("ui_cancel"):
emit_signal("close")
func _on_back_button_pressed() -> void:
print_verbose("OptionsMenu->emit_signal('close')")
emit_signal("close")
func _on_visibility_changed() -> void:
if visible:
grab_focus()

View File

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

View File

@ -0,0 +1,82 @@
[gd_scene format=3 uid="uid://b4m67tt3kxa2u"]
[ext_resource type="PackedScene" uid="uid://bmnajdu3p7ibb" path="res://addons/gd-options/src/menus/tabs/game/game.tscn" id="1_5kpk4"]
[ext_resource type="Script" uid="uid://wrvmx8n6s3lw" path="res://addons/gd-options/src/menus/options_menu.gd" id="1_j2roj"]
[ext_resource type="PackedScene" uid="uid://cfehf2hk2hls4" path="res://addons/gd-options/src/menus/tabs/input/input.tscn" id="1_jiull"]
[ext_resource type="PackedScene" uid="uid://n400hyjbel7a" path="res://addons/gd-options/src/menus/tabs/graphics/graphics.tscn" id="1_t5ygu"]
[ext_resource type="PackedScene" uid="uid://d03u63g71l71o" path="res://addons/gd-options/src/menus/tabs/audio/audio.tscn" id="2_jiull"]
[node name="OptionsMenu" type="Control" unique_id=9417709]
process_mode = 3
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
focus_mode = 2
script = ExtResource("1_j2roj")
[node name="MarginContainer" type="MarginContainer" parent="." unique_id=42402491]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 24
theme_override_constants/margin_top = 32
theme_override_constants/margin_right = 32
theme_override_constants/margin_bottom = 64
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer" unique_id=716295331]
layout_mode = 2
[node name="TabContainer" type="TabContainer" parent="MarginContainer/VBoxContainer" unique_id=1505369780]
custom_minimum_size = Vector2(2.08165e-12, 400)
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 11.0
focus_mode = 2
tab_alignment = 1
current_tab = 0
[node name="Game" parent="MarginContainer/VBoxContainer/TabContainer" unique_id=1350286854 instance=ExtResource("1_5kpk4")]
layout_mode = 2
[node name="Input" parent="MarginContainer/VBoxContainer/TabContainer" unique_id=409493361 instance=ExtResource("1_jiull")]
visible = false
layout_mode = 2
[node name="Graphics" parent="MarginContainer/VBoxContainer/TabContainer" unique_id=465215553 instance=ExtResource("1_t5ygu")]
visible = false
layout_mode = 2
[node name="Audio" parent="MarginContainer/VBoxContainer/TabContainer" unique_id=1363743964 instance=ExtResource("2_jiull")]
visible = false
layout_mode = 2
[node name="Panel" type="Panel" parent="MarginContainer/VBoxContainer" unique_id=1638010937]
layout_mode = 2
size_flags_vertical = 3
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/Panel" unique_id=1518177746]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_top = -20.0
offset_right = 49.0
offset_bottom = 20.0
grow_vertical = 2
[node name="BackButton" type="Button" parent="MarginContainer/VBoxContainer/Panel/HBoxContainer" unique_id=1205872950]
layout_mode = 2
focus_next = NodePath("../../../TabContainer")
focus_previous = NodePath("../../../TabContainer")
text = "Back"
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/Panel/HBoxContainer/BackButton" to="." method="_on_back_button_pressed"]

View File

@ -0,0 +1,32 @@
extends TabBar
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
_initilize()
func _initilize() ->void:
%MasterFader.set_value_no_signal(AudioSettings.from_db_to_percent(AudioSettings.get_master_volume()))
%MusicFader.set_value_no_signal(AudioSettings.from_db_to_percent(AudioSettings.get_music_volume()))
%FxFader.set_value_no_signal(AudioSettings.from_db_to_percent(AudioSettings.get_fx_volume()))
func _on_focus_entered() -> void:
pass
func _on_visibility_changed() -> void:
pass
func _on_master_fader_value_changed(value: float) -> void:
var volume = AudioSettings.from_percent_to_db(value)
AudioSettings.set_master_volume(volume)
func _on_music_fader_value_changed(value: float) -> void:
var volume = AudioSettings.from_percent_to_db(value)
AudioSettings.set_music_volume(volume)
func _on_fx_fader_value_changed(value: float) -> void:
var volume = AudioSettings.from_percent_to_db(value)
AudioSettings.set_fx_volume(volume)

View File

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

View File

@ -0,0 +1,123 @@
[gd_scene format=3 uid="uid://d03u63g71l71o"]
[ext_resource type="Script" uid="uid://dldnuk2iq2qth" path="res://addons/gd-options/src/menus/tabs/audio/audio.gd" id="1_k0ads"]
[node name="Audio" type="TabBar" unique_id=1363743964]
process_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_neighbor_bottom = NodePath("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MasterBus/MasterControl/MasterFader")
script = ExtResource("1_k0ads")
metadata/_tab_index = 3
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=642262788]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=115895340]
layout_mode = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=1723475598]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1990975030]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 10
[node name="MasterBus" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=2094937390]
layout_mode = 2
size_flags_vertical = 0
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MasterBus" unique_id=1298612268]
custom_minimum_size = Vector2(128, 2.08165e-12)
layout_mode = 2
size_flags_horizontal = 3
text = "Master"
horizontal_alignment = 1
[node name="MasterControl" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MasterBus" unique_id=1383754360]
layout_mode = 2
[node name="MasterFader" type="HSlider" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MasterBus/MasterControl" unique_id=1000966012]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
size_flags_stretch_ratio = 11.0
focus_neighbor_top = NodePath("../../../FxBus/FxControl/FxFader")
focus_neighbor_bottom = NodePath("../../../MusicBus/MusicControl/MusicFader")
[node name="Spacer" type="Control" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1551453563]
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 0.05
[node name="MusicBus" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1891481128]
layout_mode = 2
size_flags_vertical = 0
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MusicBus" unique_id=2143726600]
custom_minimum_size = Vector2(128, 2.08165e-12)
layout_mode = 2
size_flags_horizontal = 3
text = "Music"
horizontal_alignment = 1
[node name="MusicControl" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MusicBus" unique_id=870240695]
layout_mode = 2
[node name="MusicFader" type="HSlider" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MusicBus/MusicControl" unique_id=1461713735]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
size_flags_stretch_ratio = 11.0
focus_neighbor_top = NodePath("../../../MasterBus/MasterControl/MasterFader")
focus_neighbor_bottom = NodePath("../../../FxBus/FxControl/FxFader")
[node name="Spacer2" type="Control" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=864259381]
layout_mode = 2
size_flags_vertical = 3
size_flags_stretch_ratio = 0.05
[node name="FxBus" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1379604575]
layout_mode = 2
size_flags_vertical = 2
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/FxBus" unique_id=1515945810]
custom_minimum_size = Vector2(128, 2.08165e-12)
layout_mode = 2
size_flags_horizontal = 3
text = "FX"
horizontal_alignment = 1
[node name="FxControl" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/FxBus" unique_id=2130252155]
layout_mode = 2
[node name="FxFader" type="HSlider" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/FxBus/FxControl" unique_id=1418984511]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
size_flags_stretch_ratio = 11.0
focus_neighbor_top = NodePath("../../../MusicBus/MusicControl/MusicFader")
focus_neighbor_bottom = NodePath("../../../MasterBus/MasterControl/MasterFader")
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="value_changed" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MasterBus/MasterControl/MasterFader" to="." method="_on_master_fader_value_changed"]
[connection signal="value_changed" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/MusicBus/MusicControl/MusicFader" to="." method="_on_music_fader_value_changed"]
[connection signal="value_changed" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/FxBus/FxControl/FxFader" to="." method="_on_fx_fader_value_changed"]

View File

@ -0,0 +1,43 @@
extends TabBar
func _ready() -> void:
_initialize()
func _initialize() -> void:
var button: OptionButton = %LanguageOptionButton
button.clear()
var locales = _get_available_locales()
for i in locales.size():
var locale = locales[i]
button.add_item(TranslationServer.get_locale_name(locale))
button.set_item_metadata(i, locale)
var current = GameSettings.get_locale()
var selected_index = 0
for i in button.item_count:
if button.get_item_metadata(i) == current:
selected_index = i
break
if button.item_count > 0:
button.select(selected_index)
func _get_available_locales() -> PackedStringArray:
var locales = TranslationServer.get_loaded_locales()
if locales.is_empty():
var fallback = ProjectSettings.get_setting("internationalization/locale/fallback", "en_US")
locales = PackedStringArray([fallback])
return locales
func _on_focus_entered() -> void:
pass
func _on_visibility_changed() -> void:
pass
func _on_language_option_button_item_selected(index: int) -> void:
var locale = (%LanguageOptionButton as OptionButton).get_item_metadata(index)
GameSettings.set_locale(locale)

View File

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

View File

@ -0,0 +1,58 @@
[gd_scene format=3 uid="uid://bmnajdu3p7ibb"]
[ext_resource type="Script" uid="uid://cqmylsiqrpge1" path="res://addons/gd-options/src/menus/tabs/game/game.gd" id="1_7vn2t"]
[node name="Game" type="TabBar" unique_id=1350286854]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
focus_neighbor_bottom = NodePath("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/LanguageControl/LanguageOptionButton")
script = ExtResource("1_7vn2t")
metadata/_tab_index = 0
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1741770121]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=1884352437]
layout_mode = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=1838175574]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1818428831]
layout_mode = 2
size_flags_horizontal = 3
[node name="LanguageControl" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=507373924]
layout_mode = 2
size_flags_stretch_ratio = 2.0
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/LanguageControl" unique_id=1986420736]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 8.0
text = "Language"
[node name="LanguageOptionButton" type="OptionButton" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/LanguageControl" unique_id=2121649320]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 4.0
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="item_selected" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/LanguageControl/LanguageOptionButton" to="." method="_on_language_option_button_item_selected"]

View File

@ -0,0 +1,54 @@
extends TabBar
func _ready() -> void:
_initialize()
func _initialize() -> void:
_initialize_window_mode()
_initialize_borderless()
_initialize_display_on()
func _initialize_window_mode() -> void:
var button: OptionButton = %WindowModeOptionButton
var current = GraphicsSettings.get_window_mode()
for i in button.item_count:
if button.get_item_id(i) == current:
button.select(i)
break
func _initialize_borderless() -> void:
%CheckButton.set_pressed_no_signal(GraphicsSettings.get_borderless())
func _initialize_display_on() -> void:
var button: OptionButton = %DisplayOnOptionButton
button.clear()
for i in GraphicsSettings.get_screen_count():
button.add_item(tr("Screen %d") % i, i)
var current = GraphicsSettings.get_use_screen()
for i in button.item_count:
if button.get_item_id(i) == current:
button.select(i)
break
func _on_focus_entered() -> void:
pass
func _on_visibility_changed() -> void:
pass
func _on_window_mode_option_button_item_selected(index: int) -> void:
var mode = (%WindowModeOptionButton as OptionButton).get_item_id(index)
GraphicsSettings.set_window_mode(mode)
func _on_check_borderless_toggled(toggled_on: bool) -> void:
GraphicsSettings.set_borderless(toggled_on)
func _on_display_on_option_button_item_selected(index: int) -> void:
var screen = (%DisplayOnOptionButton as OptionButton).get_item_id(index)
GraphicsSettings.set_screen_and_apply(screen)

View File

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

View File

@ -0,0 +1,104 @@
[gd_scene format=3 uid="uid://n400hyjbel7a"]
[ext_resource type="Script" uid="uid://qpnlr1xbrqyt" path="res://addons/gd-options/src/menus/tabs/graphics/graphics.gd" id="1_41qjv"]
[node name="Graphics" type="TabBar" unique_id=465215553]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_neighbor_bottom = NodePath("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/WindowMode/WindowModeOptionButton")
script = ExtResource("1_41qjv")
metadata/_tab_index = 2
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1780038364]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=943852944]
layout_mode = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=328920069]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=957301241]
layout_mode = 2
size_flags_horizontal = 3
[node name="WindowMode" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1393620897]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/WindowMode" unique_id=334423763]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 8.0
text = "Window Mode"
[node name="WindowModeOptionButton" type="OptionButton" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/WindowMode" unique_id=738661340]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 4.0
focus_neighbor_top = NodePath("../../DisplayOn/DisplayOnOptionButton")
focus_neighbor_bottom = NodePath("../../Borderless/CheckButton")
button_mask = 3
allow_reselect = true
item_count = 3
popup/item_0/text = "Windowed"
popup/item_0/id = 0
popup/item_1/text = "Windowed (Maximized)"
popup/item_1/id = 2
popup/item_2/text = "Fullscreen"
popup/item_2/id = 3
[node name="Borderless" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1151972511]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/Borderless" unique_id=531435484]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 8.0
text = "Borderless"
[node name="CheckButton" type="CheckButton" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/Borderless" unique_id=586231003]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
focus_neighbor_top = NodePath("../../WindowMode/WindowModeOptionButton")
focus_neighbor_bottom = NodePath("../../DisplayOn/DisplayOnOptionButton")
[node name="DisplayOn" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=287471882]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/DisplayOn" unique_id=1857499090]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 8.0
text = "Screen"
[node name="DisplayOnOptionButton" type="OptionButton" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/DisplayOn" unique_id=1008900572]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 4.0
focus_neighbor_top = NodePath("../../Borderless/CheckButton")
focus_neighbor_bottom = NodePath("../../WindowMode/WindowModeOptionButton")
selected = 0
item_count = 1
popup/item_0/text = "0"
popup/item_0/id = 0
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="item_selected" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/WindowMode/WindowModeOptionButton" to="." method="_on_window_mode_option_button_item_selected"]
[connection signal="toggled" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/Borderless/CheckButton" to="." method="_on_check_borderless_toggled"]
[connection signal="item_selected" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/DisplayOn/DisplayOnOptionButton" to="." method="_on_display_on_option_button_item_selected"]

View File

@ -0,0 +1,20 @@
extends TabBar
func _ready() -> void:
_initialize()
func _initialize() -> void:
%InvertVerticalAxisCheckButton.set_pressed_no_signal(InputSettings.get_invert_vertical_axis())
func _on_focus_entered() -> void:
pass
func _on_visibility_changed() -> void:
pass
func _on_invert_vertical_axis_check_button_toggled(toggled_on: bool) -> void:
InputSettings.set_invert_vertical_axis(toggled_on)

View File

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

View File

@ -0,0 +1,54 @@
[gd_scene format=3 uid="uid://cfehf2hk2hls4"]
[ext_resource type="Script" uid="uid://bwflvl6t3k0hc" path="res://addons/gd-options/src/menus/tabs/input/input.gd" id="1_26ax5"]
[node name="Input" type="TabBar" unique_id=409493361]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_neighbor_bottom = NodePath("PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/InvertVerticalAxisControl/InvertVerticalAxisCheckButton")
script = ExtResource("1_26ax5")
metadata/_tab_index = 1
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1987401442]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=1919944062]
layout_mode = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=549462347]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=604645589]
layout_mode = 2
size_flags_horizontal = 3
[node name="InvertVerticalAxisControl" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer" unique_id=1832651151]
layout_mode = 2
size_flags_stretch_ratio = 2.0
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/InvertVerticalAxisControl" unique_id=920012428]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 8.0
text = "Invert Vertical Axis"
[node name="InvertVerticalAxisCheckButton" type="CheckButton" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/InvertVerticalAxisControl" unique_id=1008018201]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="toggled" from="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/InvertVerticalAxisControl/InvertVerticalAxisCheckButton" to="." method="_on_invert_vertical_axis_check_button_toggled"]

View File

@ -0,0 +1,18 @@
keys,en,es,fr,de,ja,zh_CN
Game,Game,Juego,Jeu,Spiel,ゲーム,游戏
Input,Input,Entrada,Entrée,Eingabe,入力,输入
Graphics,Graphics,Gráficos,Graphismes,Grafik,グラフィック,图像
Audio,Audio,Audio,Audio,Audio,オーディオ,音频
Back,Back,Atrás,Retour,Zurück,戻る,返回
Master,Master,General,Principal,Master,マスター,主音量
Music,Music,Música,Musique,Musik,音楽,音乐
FX,FX,Efectos,Effets,Effekte,効果音,音效
Window Mode,Window Mode,Modo de ventana,Mode de fenêtre,Fenstermodus,ウィンドウモード,窗口模式
Borderless,Borderless,Sin bordes,Sans bordure,Randlos,ボーダーレス,无边框
Screen,Screen,Pantalla,Écran,Bildschirm,画面,屏幕
Windowed,Windowed,Ventana,Fenêtré,Fenster,ウィンドウ,窗口
Windowed (Maximized),Windowed (Maximized),Ventana (Maximizada),Fenêtré (Maximisé),Fenster (Maximiert),ウィンドウ(最大化),窗口(最大化)
Fullscreen,Fullscreen,Pantalla completa,Plein écran,Vollbild,フルスクリーン,全屏
Invert Vertical Axis,Invert Vertical Axis,Invertir eje vertical,Inverser l'axe vertical,Vertikale Achse umkehren,縦軸を反転,反转垂直轴
Language,Language,Idioma,Langue,Sprache,言語,语言
Screen %d,Screen %d,Pantalla %d,Écran %d,Bildschirm %d,画面 %d,屏幕 %d
1 keys en es fr de ja zh_CN
2 Game Game Juego Jeu Spiel ゲーム 游戏
3 Input Input Entrada Entrée Eingabe 入力 输入
4 Graphics Graphics Gráficos Graphismes Grafik グラフィック 图像
5 Audio Audio Audio Audio Audio オーディオ 音频
6 Back Back Atrás Retour Zurück 戻る 返回
7 Master Master General Principal Master マスター 主音量
8 Music Music Música Musique Musik 音楽 音乐
9 FX FX Efectos Effets Effekte 効果音 音效
10 Window Mode Window Mode Modo de ventana Mode de fenêtre Fenstermodus ウィンドウモード 窗口模式
11 Borderless Borderless Sin bordes Sans bordure Randlos ボーダーレス 无边框
12 Screen Screen Pantalla Écran Bildschirm 画面 屏幕
13 Windowed Windowed Ventana Fenêtré Fenster ウィンドウ 窗口
14 Windowed (Maximized) Windowed (Maximized) Ventana (Maximizada) Fenêtré (Maximisé) Fenster (Maximiert) ウィンドウ(最大化) 窗口(最大化)
15 Fullscreen Fullscreen Pantalla completa Plein écran Vollbild フルスクリーン 全屏
16 Invert Vertical Axis Invert Vertical Axis Invertir eje vertical Inverser l'axe vertical Vertikale Achse umkehren 縦軸を反転 反转垂直轴
17 Language Language Idioma Langue Sprache 言語 语言
18 Screen %d Screen %d Pantalla %d Écran %d Bildschirm %d 画面 %d 屏幕 %d

View File

@ -0,0 +1,19 @@
[remap]
importer="csv_translation"
type="Translation"
uid="uid://ve0c3i7bkxwt"
[deps]
files=["res://addons/gd-options/translations/gd_options.en.translation", "res://addons/gd-options/translations/gd_options.es.translation", "res://addons/gd-options/translations/gd_options.fr.translation", "res://addons/gd-options/translations/gd_options.de.translation", "res://addons/gd-options/translations/gd_options.ja.translation", "res://addons/gd-options/translations/gd_options.zh_CN.translation"]
source_file="res://addons/gd-options/translations/gd_options.csv"
dest_files=["res://addons/gd-options/translations/gd_options.en.translation", "res://addons/gd-options/translations/gd_options.es.translation", "res://addons/gd-options/translations/gd_options.fr.translation", "res://addons/gd-options/translations/gd_options.de.translation", "res://addons/gd-options/translations/gd_options.ja.translation", "res://addons/gd-options/translations/gd_options.zh_CN.translation"]
[params]
compress=1
delimiter=0
unescape_keys=false
unescape_translations=true