diff --git a/export_presets.cfg b/export_presets.cfg index 18530ee..aebb1e7 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -8,21 +8,20 @@ export_filter="all_resources" include_filter="" exclude_filter="" export_path="../../Rhythm.x86_64" -patch_list=PoolStringArray( ) script_export_mode=1 script_encryption_key="" [preset.0.options] +custom_template/debug="" +custom_template/release="" +binary_format/architecture="x86_64" +binary_format/embed_pck=false texture_format/bptc=false texture_format/s3tc=true texture_format/etc=false texture_format/etc2=false texture_format/no_bptc_fallbacks=true -binary_format/64_bits=true -binary_format/embed_pck=false -custom_template/release="" -custom_template/debug="" [preset.1] @@ -34,50 +33,55 @@ export_filter="all_resources" include_filter="" exclude_filter="" export_path="../../Rhythm.apk" -patch_list=PoolStringArray( ) script_export_mode=1 script_encryption_key="" [preset.1.options] -graphics/32_bits_framebuffer=true -xr_features/xr_mode=0 -xr_features/degrees_of_freedom=0 -xr_features/hand_tracking=0 -one_click_deploy/clear_previous_install=false custom_template/debug="" custom_template/release="" -custom_template/use_custom_build=false -command_line/extra_args="" -version/code=1 -version/name="1.0" -package/unique_name="au.ufeff.rhythmgame" -package/name="Rhythm Game" -package/signed=true -screen/immersive_mode=true -screen/orientation=0 -screen/support_small=true -screen/support_normal=true -screen/support_large=true -screen/support_xlarge=true -screen/opengl_debug=false -launcher_icons/main_192x192="" -launcher_icons/adaptive_foreground_432x432="" -launcher_icons/adaptive_background_432x432="" +custom_build/use_custom_build=false +custom_build/export_format=0 +custom_build/min_sdk="" +custom_build/target_sdk="" +architectures/armeabi-v7a=true +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=false keystore/debug="" keystore/debug_user="" keystore/debug_password="" keystore/release="" keystore/release_user="" keystore/release_password="" +one_click_deploy/clear_previous_install=false +version/code=1 +version/name="1.0" +package/unique_name="au.ufeff.rhythmgame" +package/name="Rhythm Game" +package/signed=true +package/classify_as_game=true +package/retain_data_on_uninstall=false +package/exclude_from_recents=false +launcher_icons/main_192x192="" +launcher_icons/adaptive_foreground_432x432="" +launcher_icons/adaptive_background_432x432="" +graphics/opengl_debug=false +xr_features/xr_mode=0 +xr_features/hand_tracking=0 +xr_features/hand_tracking_frequency=0 +xr_features/passthrough=0 +screen/immersive_mode=true +screen/support_small=true +screen/support_normal=true +screen/support_large=true +screen/support_xlarge=true +user_data_backup/allow=false +command_line/extra_args="" apk_expansion/enable=false apk_expansion/SALT="" apk_expansion/public_key="" -architectures/armeabi-v7a=true -architectures/arm64-v8a=true -architectures/x86=false -architectures/x86_64=false -permissions/custom_permissions=PoolStringArray( ) +permissions/custom_permissions=PoolStringArray( "MANAGE_EXTERNAL_STORAGE" ) permissions/access_checkin_properties=false permissions/access_coarse_location=false permissions/access_fine_location=false @@ -150,6 +154,7 @@ permissions/location_hardware=false permissions/manage_accounts=false permissions/manage_app_tokens=false permissions/manage_documents=false +permissions/manage_external_storage=true permissions/master_clear=false permissions/media_content_control=false permissions/modify_audio_settings=false @@ -158,6 +163,7 @@ permissions/mount_format_filesystems=false permissions/mount_unmount_filesystems=false permissions/nfc=false permissions/persistent_activity=false +permissions/post_notifications=false permissions/process_outgoing_calls=false permissions/read_calendar=false permissions/read_call_log=false @@ -234,15 +240,27 @@ export_filter="all_resources" include_filter="" exclude_filter="" export_path="" -patch_list=PoolStringArray( ) script_export_mode=1 script_encryption_key="" [preset.2.options] +custom_template/debug="" +custom_template/release="" +variant/export_type=0 vram_texture_compression/for_desktop=true vram_texture_compression/for_mobile=false +html/export_icon=true html/custom_html_shell="" html/head_include="" -custom_template/release="" -custom_template/debug="" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +html/experimental_virtual_keyboard=false +progressive_web_app/enabled=false +progressive_web_app/offline_page="" +progressive_web_app/display=1 +progressive_web_app/orientation=0 +progressive_web_app/icon_144x144="" +progressive_web_app/icon_180x180="" +progressive_web_app/icon_512x512="" +progressive_web_app/background_color=Color( 0, 0, 0, 1 ) diff --git a/main.gd b/main.gd index 390ee9e..f96bfd6 100644 --- a/main.gd +++ b/main.gd @@ -1,21 +1,40 @@ extends Control onready var mainMenu := $'%mainMenu' +onready var optionPanel := $'%OptionPanel' const touchGamePath := 'res://scenes/RadialGame.tscn' const stepGamePath := 'res://scenes/StepGame.tscn' -var touchGameScene := preload(touchGamePath) -var stepGameScene := preload(stepGamePath) +const touchGameScene := preload(touchGamePath) +const stepGameScene := preload(stepGamePath) +const SettingsMenu := preload('res://scenes/SettingsMenu.tscn') var activeGame: Node = null +func exit_mode() -> void: + remove_child(activeGame) + activeGame = null + mainMenu.show() + optionPanel.show() + + func _on_MainMenu_start_stepgame() -> void: mainMenu.hide() activeGame = stepGameScene.instance() + activeGame.connect('exit_mode', self, 'exit_mode') add_child_below_node(mainMenu, activeGame) func _on_MainMenu_start_touchgame() -> void: mainMenu.hide() activeGame = touchGameScene.instance() + activeGame.connect('exit_mode', self, 'exit_mode') add_child_below_node(mainMenu, activeGame) activeGame.alignment_horizontal = AspectRatioContainer.ALIGN_BEGIN + + +func _on_mainMenu_open_settings(): + mainMenu.hide() + optionPanel.hide() + activeGame = SettingsMenu.instance() + activeGame.connect('exit_mode', self, 'exit_mode') + add_child_below_node(mainMenu, activeGame) diff --git a/main.tscn b/main.tscn index 139beb2..bd057df 100644 --- a/main.tscn +++ b/main.tscn @@ -19,6 +19,7 @@ __meta__ = { unique_name_in_owner = true [node name="OptionPanel" parent="." instance=ExtResource( 13 )] +unique_name_in_owner = true anchor_left = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 @@ -36,5 +37,6 @@ __meta__ = { "_edit_use_anchors_": false } +[connection signal="open_settings" from="mainMenu" to="." method="_on_mainMenu_open_settings"] [connection signal="start_stepgame" from="mainMenu" to="." method="_on_MainMenu_start_stepgame"] [connection signal="start_touchgame" from="mainMenu" to="." method="_on_MainMenu_start_touchgame"] diff --git a/project.godot b/project.godot index 343a64a..5437016 100644 --- a/project.godot +++ b/project.godot @@ -39,7 +39,6 @@ gdscript/warnings/unused_variable=false gdscript/warnings/shadowed_variable=false gdscript/warnings/unused_argument=false gdscript/warnings/unused_signal=false -gdscript/warnings/return_value_discarded=false gdscript/warnings/integer_division=false [display] diff --git a/scenes/DirectorySettingsEntry.gd b/scenes/DirectorySettingsEntry.gd new file mode 100644 index 0000000..6213687 --- /dev/null +++ b/scenes/DirectorySettingsEntry.gd @@ -0,0 +1,52 @@ +extends HBoxContainer +onready var le_directory := $le_directory +onready var btn_browse := $btn_browse +onready var btn_remove := $btn_remove + +signal path_updated + +export var directory: String = "" setget set_directory, get_directory +func set_directory(value: String): + directory = value + if le_directory: + le_directory.text = directory + emit_signal('path_updated', value) + +func get_directory(): + return le_directory.text + +export var removable: bool = true setget set_removable +func set_removable(value: bool): + removable = value + if btn_remove: + btn_remove.disabled = not removable + +export var readonly: bool = false setget set_readonly +func set_readonly(value: bool): + readonly = value + if le_directory: + le_directory.editable = not readonly + if btn_browse: + btn_browse.disabled = readonly + +func _ready(): + le_directory.text = directory + le_directory.editable = not readonly + btn_browse.disabled = readonly + btn_remove.disabled = not removable + +#func _process(delta): +# pass + + +func _on_btn_browse_pressed(): + var dialog = FileDialog.new() + dialog.access = FileDialog.ACCESS_FILESYSTEM + dialog.mode = FileDialog.MODE_OPEN_DIR + dialog.connect("dir_selected", self, "set_directory") + add_child(dialog) + dialog.popup(Rect2(0, 0, 800, 600)) + + +func _on_btn_remove_pressed(): + set_directory("") diff --git a/scenes/DirectorySettingsEntry.tscn b/scenes/DirectorySettingsEntry.tscn new file mode 100644 index 0000000..02a10f0 --- /dev/null +++ b/scenes/DirectorySettingsEntry.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://scenes/DirectorySettingsEntry.gd" type="Script" id=1] + +[node name="DirectorySettingsEntry" type="HBoxContainer"] +margin_top = 18.0 +margin_right = 1080.0 +margin_bottom = 42.0 +script = ExtResource( 1 ) + +[node name="le_directory" type="LineEdit" parent="."] +margin_right = 937.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 480, 1.36422e-12 ) +size_flags_horizontal = 3 + +[node name="btn_browse" type="Button" parent="."] +margin_left = 941.0 +margin_right = 1012.0 +margin_bottom = 24.0 +text = "Browse..." + +[node name="btn_remove" type="Button" parent="."] +margin_left = 1016.0 +margin_right = 1080.0 +margin_bottom = 24.0 +text = "Remove" + +[connection signal="pressed" from="btn_browse" to="." method="_on_btn_browse_pressed"] +[connection signal="pressed" from="btn_remove" to="." method="_on_btn_remove_pressed"] diff --git a/scenes/MainMenu.tscn b/scenes/MainMenu.tscn index afb8a77..54c5d99 100644 --- a/scenes/MainMenu.tscn +++ b/scenes/MainMenu.tscn @@ -37,7 +37,6 @@ margin_top = 548.0 margin_right = 653.0 margin_bottom = 616.0 size_flags_horizontal = 4 -disabled = true text = "Settings" [node name="btn_quit" type="Button" parent="VBoxContainer"] @@ -56,4 +55,5 @@ align = 1 [connection signal="pressed" from="VBoxContainer/btn_touch" to="." method="_on_btn_touch_pressed"] [connection signal="pressed" from="VBoxContainer/btn_step" to="." method="_on_btn_step_pressed"] +[connection signal="pressed" from="VBoxContainer/btn_settings" to="." method="_on_btn_settings_pressed"] [connection signal="pressed" from="VBoxContainer/btn_quit" to="." method="quit"] diff --git a/scenes/RadialGame.gd b/scenes/RadialGame.gd new file mode 100644 index 0000000..857bbdf --- /dev/null +++ b/scenes/RadialGame.gd @@ -0,0 +1,11 @@ +extends AspectRatioContainer +signal exit_mode + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass diff --git a/scenes/RadialGame.tscn b/scenes/RadialGame.tscn index f8ac32f..56e5bce 100644 --- a/scenes/RadialGame.tscn +++ b/scenes/RadialGame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=2] +[gd_scene load_steps=22 format=2] [ext_resource path="res://scripts/NoteViewport.gd" type="Script" id=1] [ext_resource path="res://assets/text-4k.png" type="Texture" id=2] @@ -13,6 +13,7 @@ [ext_resource path="res://shaders/receptors.shader" type="Shader" id=11] [ext_resource path="res://shaders/notemesh.shader" type="Shader" id=12] [ext_resource path="res://shaders/notelines.shader" type="Shader" id=13] +[ext_resource path="res://scenes/RadialGame.gd" type="Script" id=14] [sub_resource type="ShaderMaterial" id=1] shader = ExtResource( 11 ) @@ -83,6 +84,7 @@ _data = [ Vector2( -1, -1 ), 0.0, 0.0, 0, 0, Vector2( 0, 0 ), 2.0, 2.0, 1, 1, Ve [node name="RadialGame" type="AspectRatioContainer"] anchor_right = 1.0 anchor_bottom = 1.0 +script = ExtResource( 14 ) [node name="square" type="Control" parent="."] unique_name_in_owner = true diff --git a/scenes/SettingsMenu.gd b/scenes/SettingsMenu.gd new file mode 100644 index 0000000..4671039 --- /dev/null +++ b/scenes/SettingsMenu.gd @@ -0,0 +1,58 @@ +extends Control +const DirectorySettingsEntry = preload('res://scenes/DirectorySettingsEntry.tscn') +onready var container_folders = $'%container_folders' + +signal exit_mode + +var paths = [] +var path_entries := [] +# Called when the node enters the scene tree for the first time. +func _ready(): + paths = Array(Settings.get_library_paths()) + generate_entries() + +func generate_entries(): + paths.append("") + for entry in path_entries: + container_folders.remove_child(entry) + path_entries = [] + + for path in paths: + var entry = DirectorySettingsEntry.instance() + entry.connect('path_updated', self, '_entry_updated', [len(path_entries)]) + path_entries.append(entry) + entry.directory = path + container_folders.add_child(entry) + path_entries[0].removable = false + path_entries[0].readonly = true + +func get_paths_from_entries() -> Array: + var paths := [] + for entry in path_entries: + var dir = entry.directory + if dir: + paths.append(entry.directory) + return paths + +func _save_settings(): + var paths = get_paths_from_entries() + var user_path: String = paths[0] + var extra_paths: Array = paths.slice(1, -1) + Settings.set_additional_library_paths(extra_paths) + +func _entry_updated(new_path: String, index: int): + paths = get_paths_from_entries() + if (index == len(path_entries)-1): + if new_path: + generate_entries() + elif not new_path: + generate_entries() + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass + + +func _on_btn_back_pressed(): + _save_settings() + emit_signal('exit_mode') diff --git a/scenes/SettingsMenu.tscn b/scenes/SettingsMenu.tscn new file mode 100644 index 0000000..7a498df --- /dev/null +++ b/scenes/SettingsMenu.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://scenes/SettingsMenu.gd" type="Script" id=2] + +[node name="SettingsMenu" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 2 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="Label" type="Label" parent="VBoxContainer"] +margin_right = 1080.0 +margin_bottom = 14.0 +text = "Resource Folders" +align = 1 + +[node name="container_folders" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 18.0 +margin_right = 1080.0 +margin_bottom = 18.0 + +[node name="btn_back" type="Button" parent="VBoxContainer"] +margin_top = 22.0 +margin_right = 1080.0 +margin_bottom = 42.0 +text = "Back" + +[connection signal="pressed" from="VBoxContainer/btn_back" to="." method="_on_btn_back_pressed"] diff --git a/scenes/StepGame.gd b/scenes/StepGame.gd new file mode 100644 index 0000000..630c8e5 --- /dev/null +++ b/scenes/StepGame.gd @@ -0,0 +1,12 @@ +extends Control +signal exit_mode + + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass diff --git a/scenes/StepGame.tscn b/scenes/StepGame.tscn index 07581cc..a2676b0 100644 --- a/scenes/StepGame.tscn +++ b/scenes/StepGame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=2] +[gd_scene load_steps=17 format=2] [ext_resource path="res://scripts/NoteViewport.gd" type="Script" id=1] [ext_resource path="res://assets/text-4k.png" type="Texture" id=2] @@ -7,6 +7,7 @@ [ext_resource path="res://scenes/StepMenu.tscn" type="PackedScene" id=5] [ext_resource path="res://scripts/NoteHandler.gd" type="Script" id=6] [ext_resource path="res://assets/fonts/Sniglet-Regular.ttf" type="DynamicFontData" id=7] +[ext_resource path="res://scenes/StepGame.gd" type="Script" id=8] [ext_resource path="res://scripts/ScreenFilter.gd" type="Script" id=9] [ext_resource path="res://shaders/notemesh.shader" type="Shader" id=12] [ext_resource path="res://shaders/notelines.shader" type="Shader" id=13] @@ -62,6 +63,7 @@ blend_mode = 4 [node name="StepGame" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 +script = ExtResource( 8 ) [node name="video" type="TextureRect" parent="." groups=["VideoTexRects"]] anchor_right = 1.0 diff --git a/scripts/FileHelpers.gd b/scripts/FileHelpers.gd index f1c7b1e..7671141 100644 --- a/scripts/FileHelpers.gd +++ b/scripts/FileHelpers.gd @@ -92,6 +92,8 @@ static func find_by_extensions(array, extensions=null) -> Dictionary: static func init_directory(directory: String): var dir = Directory.new() var err = dir.make_dir_recursive(directory) + if err == ERR_ALREADY_EXISTS: # API changed? + err = OK if err != OK: print('An error occurred while trying to create the directory: ', directory, err, ERROR_CODES[err]) return err diff --git a/scripts/MainMenu.gd b/scripts/MainMenu.gd index 1a7389b..430575b 100644 --- a/scripts/MainMenu.gd +++ b/scripts/MainMenu.gd @@ -1,6 +1,7 @@ extends Control signal start_touchgame signal start_stepgame +signal open_settings func update_libraries_text() -> void: $lbl_settingspath.text = 'Data directories:\n' + '\n'.join(Settings.get_library_paths()) @@ -17,3 +18,6 @@ func _on_btn_touch_pressed() -> void: func _on_btn_step_pressed() -> void: emit_signal('start_stepgame') + +func _on_btn_settings_pressed(): + emit_signal('open_settings') diff --git a/singletons/FileLoader.gd b/singletons/FileLoader.gd index c8c2fcf..16b57c7 100644 --- a/singletons/FileLoader.gd +++ b/singletons/FileLoader.gd @@ -62,7 +62,7 @@ func scan_library() -> Dictionary: var rootdir = root + 'songs' var dir = Directory.new() var err = dir.make_dir_recursive(rootdir) - if err != OK: + if err != OK and err != ERR_ALREADY_EXISTS: print_debug('An error occurred while trying to create the songs directory: ', err) return err diff --git a/singletons/Settings.gd b/singletons/Settings.gd index f84f84d..e0b9593 100644 --- a/singletons/Settings.gd +++ b/singletons/Settings.gd @@ -5,6 +5,31 @@ extends Node signal config_loaded signal subsampling_changed(xy) +# Newer Android versions require storage access permissions to be requested at runtime. +# There is no way around this, so this code needs to be updated to work. +var android_permissions_granted := false +const REQUIRED_ANDROID_PERMISSIONS := PoolStringArray(['MANAGE_EXTERNAL_FILES', 'READ_EXTERNAL_FILES', 'WRITE_EXTERNAL_FILES']) +func check_android_permissions() -> bool: + if self.android_permissions_granted: + return true + var permissions_granted := OS.get_granted_permissions() + for perm in REQUIRED_ANDROID_PERMISSIONS: + if not (perm in permissions_granted): + return false + self.android_permissions_granted = true + return true +func update_android_permissions() -> void: + if not OS.has_feature('Android'): + return + if self.check_android_permissions(): + return + OS.request_permissions() + +func _on_request_permissions_result(permission: String, granted: bool) -> void: + print('_on_request_permissions_result: %s = %s' % [permission, granted]) + # if granted and (permission in REQUIRED_ANDROID_PERMISSIONS): + # self.call_deferred('update_view') + const ANDROID_USERDIR := '/storage/emulated/0/RhythmGame/' # The following would probably work. One huge caveat is that permission needs to be manually granted by the user in app settings as we can't use OS.request_permission('WRITE_EXTERNAL_STORAGE') # '/storage/emulated/0/Android/data/au.ufeff.rhythmgame/' @@ -44,6 +69,10 @@ func get_library_paths() -> PoolStringArray: paths.append(p.rstrip('/')+'/') return PoolStringArray(paths) +func set_additional_library_paths(entries): + config.set_value('libraries', 'additional_paths', PoolStringArray(entries)) + save_settings() + func load_settings(): config = ConfigFile.new() config.set_value('libraries', 'additional_paths', []) @@ -64,3 +93,5 @@ func save_settings(): func _ready(): load_settings() + get_tree().connect('on_request_permissions_result', self, '_on_request_permissions_result') + self.update_android_permissions()