diff --git a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/Screens/ImportCharacter.gd b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/Screens/ImportCharacter.gd index ea8330f..3e97618 100644 --- a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/Screens/ImportCharacter.gd +++ b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/Screens/ImportCharacter.gd @@ -30,7 +30,7 @@ func _ready() -> void: $But_Play.call_deferred("grab_focus") #This will grab focus on the "Start Game" option by default func _on_Button_pressed(): - $FileDialog.popup() + open_file_dialog() #takes appropriate action when the custom hotkey is pressed func _unhandled_input(event): @@ -40,6 +40,20 @@ func _unhandled_input(event): if Input.is_action_pressed("save_character_file_hotkey"): _save_data_to_singleton() +#FUNCTION open file dialog +#Params: none +#Returns: Nothing; all work done in function +#Notes: decides how to open a file based on whether or not javascript is present (HTML5 build) +# non-HTML5 versions continue in _on_FileDialog_file_selected +# HTML5 continues in _js_populate_preset_character_format +func open_file_dialog(): + if OS.has_feature('JavaScript'): + var file_text = yield(JavaScriptFileManager.open_file_dialog_get_text(), "completed") + _clear_char_sheet() + _js_populate_preset_character_format(file_text) + else: + $FileDialog.popup() + #FUNCTION populate preset character format #Params: file we have opened and are reading #Returns: Nothing; all work done in function @@ -144,6 +158,49 @@ func _populate_preset_character_format(file:File): #$VBoxContainer2/But_OpenFile.focus_previous = previous_control.get_path() previous_control.focus_next = $But_Play.get_path() +#FUNCTION populate preset character format (javascript version) +#Params: string containing the text of the file we read with javascript +#Returns: Nothing; all work done in function +#Notes: functionally versy similar to the function above, but takes a string instead of a file +func _js_populate_preset_character_format(file_text: String): + var file_text_split = file_text.split('\n', false,0) + #print(file_text_split[0]) + cust_cap_count = 0 + + var header_array = file_text_split[0].split(',', true, 0) + var contents_array = file_text_split[1].split(',', true, 0) + + for i in header_array.size(): + #make a new textbox for each header piece + var textLine = Label.new() + $ScrollContainer/VBoxContainer.add_child(textLine) + cust_cap_count = cust_cap_count + 1 + textLine.text = header_array[i].to_upper() + print(header_array[i]) + + #match to content, assuming it exists and aligns + if(contents_array.size()>= i): + var textBox = LineEdit.new() + $ScrollContainer/VBoxContainer.add_child(textBox) + cust_cap_count = cust_cap_count + 1 + textBox.text = contents_array[i] + + if(textLine.text=="QUOTE"): + _add_capability_button() + + # Set focus order for dynamically created LineEdit fields + var previous_control = $VBoxContainer2/Save_Button + for child in $ScrollContainer/VBoxContainer.get_children(): + if child is LineEdit: + child.focus_previous = previous_control.get_path() + previous_control.focus_next = child.get_path() + previous_control = child + + # Connect the last LineEdit back to the first button + if previous_control != $VBoxContainer2/Save_Button: + #previous_control.focus_next = $VBoxContainer2/But_OpenFile.get_path() + #$VBoxContainer2/But_OpenFile.focus_previous = previous_control.get_path() + previous_control.focus_next = $But_Play.get_path() #FUNCTION clear the character sheet #Params: None diff --git a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/_userFiles/game_01.tscn b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/_userFiles/game_01.tscn index 4f977ea..79c3790 100644 --- a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/_userFiles/game_01.tscn +++ b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/_userFiles/game_01.tscn @@ -1,17 +1,16 @@ [gd_scene load_steps=6 format=2] -[ext_resource path="res://gamePlay/But_Option.tscn" type="PackedScene" id=1] -[ext_resource path="res://assets/liberation_serif_30pt.tres" type="DynamicFont" id=2] -[ext_resource path="res://assets/Themes/ui_controlNode_dark_theme.tres" type="Theme" id=3] -[ext_resource path="res://gamePlay/But_MoreOptions.gd" type="Script" id=4] -[ext_resource path="res://gamePlay/Game.gd" type="Script" id=5] - +[ext_resource path="res://gamePlay/Game.gd" type="Script" id=1] +[ext_resource path="res://gamePlay/But_Option.tscn" type="PackedScene" id=2] +[ext_resource path="res://assets/Themes/Dark Theme/ui_controlNode_dark_theme.tres" type="Theme" id=3] +[ext_resource path="res://assets/liberation_serif_30pt.tres" type="DynamicFont" id=4] +[ext_resource path="res://gamePlay/But_MoreOptions.gd" type="Script" id=5] [node name="Game" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 theme = ExtResource( 3 ) -script = ExtResource( 5 ) +script = ExtResource( 1 ) [node name="Background" type="PanelContainer" parent="."] anchor_right = 1.0 @@ -42,12 +41,13 @@ custom_constants/separation = 10 margin_right = 708.0 margin_bottom = 40.0 rect_min_size = Vector2( 0, 40 ) +focus_mode = 0 [node name="But_MoreOptions" type="Button" parent="Background/MarginContainer/Rows/ItemList"] margin_right = 197.0 margin_bottom = 36.0 text = "More Options" -script = ExtResource( 4 ) +script = ExtResource( 5 ) __meta__ = { "_edit_use_anchors_": false } @@ -79,16 +79,17 @@ margin_left = 1.0 margin_top = 1.0 margin_right = 707.0 margin_bottom = 119.0 +follow_focus = true [node name="OptionsContainer" type="VBoxContainer" parent="Background/MarginContainer/Rows/InputArea/ScrollContainer"] margin_right = 108.0 margin_bottom = 36.0 custom_constants/separation = 5 -[node name="option1" type="Button" parent="Background/MarginContainer/Rows/InputArea/ScrollContainer/OptionsContainer" instance=ExtResource( 1 )] +[node name="option1" type="Button" parent="Background/MarginContainer/Rows/InputArea/ScrollContainer/OptionsContainer" instance=ExtResource( 2 )] margin_right = 108.0 margin_bottom = 36.0 -custom_fonts/font = ExtResource( 2 ) +custom_fonts/font = ExtResource( 4 ) text = "Option 1" align = 0 __meta__ = { @@ -100,9 +101,6 @@ anchor_left = 1.0 anchor_right = 1.0 margin_left = -294.0 margin_bottom = 406.0 -__meta__ = { -"_edit_use_anchors_": false -} [node name="MarginContainer" type="MarginContainer" parent="Con_charSheet"] margin_left = 1.0 @@ -118,6 +116,7 @@ margin_bottom = 404.0 margin_right = 292.0 margin_bottom = 40.0 rect_min_size = Vector2( 0, 40 ) +focus_mode = 0 __meta__ = { "_edit_use_anchors_": false } @@ -131,14 +130,12 @@ margin_bottom = 43.0 text = "Character Sheet" align = 1 valign = 1 -__meta__ = { -"_edit_use_anchors_": false -} [node name="CharacterSheet" type="TextEdit" parent="Con_charSheet/MarginContainer/VBoxContainer"] margin_top = 44.0 margin_right = 292.0 margin_bottom = 404.0 +focus_mode = 0 size_flags_vertical = 3 readonly = true wrap_enabled = true @@ -169,6 +166,7 @@ size_flags_vertical = 3 margin_right = 290.0 margin_bottom = 40.0 rect_min_size = Vector2( 0, 40 ) +focus_mode = 0 __meta__ = { "_edit_use_anchors_": false } @@ -190,6 +188,7 @@ __meta__ = { margin_top = 44.0 margin_right = 290.0 margin_bottom = 194.0 +focus_mode = 0 size_flags_vertical = 3 text = "Nothing yet " diff --git a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/globalScripts/JavaScriptFileManager.gd b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/globalScripts/JavaScriptFileManager.gd new file mode 100644 index 0000000..86a9936 --- /dev/null +++ b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/globalScripts/JavaScriptFileManager.gd @@ -0,0 +1,72 @@ +extends Node + +# NOTE: All yield() functions are replaced by await() in Godot 4.x! + +var _text_callback +var file_text +signal file_text_received(file_text) + +func _ready(): + _text_callback = JavaScript.create_callback(self, "_text_callback_func") + +#FUNCTION open file dialog get text +#Params: none +#Returns: String containing opened file text +#Notes: yields to allow asynch javascript file dialog to return read file contents +# When calling, call with yield(JavaScriptFileManager.open_file_dialog_get_text(), "completed") +# That allows the function to actually return something before the code continues +func open_file_dialog_get_text(): + _open_file_dialog_get_text_internal() + return yield(self, "file_text_received") + +#FUNCTION text callback function +#Params: Array of arguments passed by the javascript code +#Returns: nothing, emits signal containing the text of the file read by javascript +#Notes: signal tells yield in open_file_dialog_get_text() to continue +func _text_callback_func(args): + emit_signal("file_text_received", args[0]) + +#FUNCTION open file dialog get text (internal) +#Params: none +#Returns: nothing, calls _text_callback_func and passes the file text read by javascript to it +#Notes: terrible choice of name +func _open_file_dialog_get_text_internal(): + if OS.has_feature('JavaScript'): + print("running javascript") + + var js_window = JavaScript.get_interface("window") + js_window.godot_callback = _text_callback + + JavaScript.eval(""" + console.log("opening file dialog"); + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.csv,text/csv'; + + input.addEventListener("change", on_input_change); + input.click(); + + function on_input_change() { + const file = input.files[0]; + if (!file) { + window.godot_callback(["No file to read"]); + return; + } + + const reader = new FileReader(); + + reader.onerror = function() { + window.godot_callback(["Failed to read file"]); + } + + reader.onloadend = function() { + console.log("reader onloadend start"); + + window.godot_callback(reader.result); + + console.log("reader onloadend end"); + } + + reader.readAsText(file); + }; + """, true) diff --git a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/project.godot b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/project.godot index 042e9f4..099801e 100644 --- a/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/project.godot +++ b/Phase2/MVP/Game/bcirpg_game_mvp_2024_0831A/project.godot @@ -115,22 +115,23 @@ PlayerCharacter="*res://globalScripts/PlayerCharacter.gd" DiceRoller="*res://globalScripts/DiceRoller.gd" GlobalSaveInstance="*res://globalScripts/globalSaveInstance.gd" GameCurrent="*res://globalScripts/gameCurrent.gd" +JavaScriptFileManager="*res://globalScripts/JavaScriptFileManager.gd" [input] open_file_hotkey={ "deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":79,"unicode":0,"echo":false,"script":null) +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":79,"unicode":0,"echo":false,"script":null) ] } save_character_file_hotkey={ "deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":83,"unicode":0,"echo":false,"script":null) +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":83,"unicode":0,"echo":false,"script":null) ] } back_to_main_menu_hotkey={ "deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":77,"unicode":0,"echo":false,"script":null) +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":true,"command":true,"pressed":false,"scancode":0,"physical_scancode":77,"unicode":0,"echo":false,"script":null) ] }