commit 0ab65e1df6235ccb685fffc95d3ccf0950f005e4 Author: Marvin Dalheimer Date: Sat Jan 25 21:40:19 2025 +0100 Init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5e700d2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf +*.ogg filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85f4025 --- /dev/null +++ b/.gitignore @@ -0,0 +1,169 @@ +# Godot 4+ specific ignores +.godot/ +/android/ +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all,macos,linux,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all,macos,linux,windows + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,macos,linux,windows + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..211023d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "godotTools.editorPath.godot4": "/Users/rinma/Library/Application Support/Godot/app_userdata/Godots/versions/Godot_v4_4-beta1_macos_universal/Godot.app" +} \ No newline at end of file diff --git a/addons/ai_assistant_hub/ai_answer_handler.gd b/addons/ai_assistant_hub/ai_answer_handler.gd new file mode 100644 index 0000000..0ca2799 --- /dev/null +++ b/addons/ai_assistant_hub/ai_answer_handler.gd @@ -0,0 +1,73 @@ +@tool +class_name AIAnswerHandler + +signal bot_message_produced(message:String) +signal error_message_produced(message:String) + +const COMMENT_LENGTH := 80 + +var _code_writer: AssistantToolCodeWriter + + +func _init(plugin:EditorPlugin, code_selector:AssistantToolSelection) -> void: + _code_writer = AssistantToolCodeWriter.new(plugin, code_selector) + + +func handle(text_answer:String, quick_prompt:AIQuickPromptResource) -> void: + #Simple chat + if quick_prompt == null: + bot_message_produced.emit(text_answer) + #Response is for a quick prompt + else: + if quick_prompt.format_response_as_comment: + text_answer = _convert_to_comment(text_answer) + bot_message_produced.emit(text_answer) + match quick_prompt.response_target: + AIQuickPromptResource.ResponseTarget.CodeEditor: + _write_to_code_editor(text_answer, quick_prompt.code_placement) + AIQuickPromptResource.ResponseTarget.OnlyCodeToCodeEditor: + var code = _extract_gdscript(text_answer) + if code.length() > 0: + _write_to_code_editor(code, quick_prompt.code_placement) + + +func _write_to_code_editor(text_answer:String, code_placement:AIQuickPromptResource.CodePlacement) -> void: + var succeed = _code_writer.write_to_code_editor(text_answer, code_placement) + if not succeed: + error_message_produced.emit("The selection sent to the assistant was not found, you need to make the changes manually based on the response in the chat.") + + +func _extract_gdscript(text:String) -> String: + var extracted_code:= "" + var start:= text.find("```gdscript") + var end:= text.find("```", start + 11) + while start >= 0 and end >= start: + if extracted_code.length() > 0: + extracted_code += "\n" + extracted_code += text.substr(start+11, end-start-11) + start = text.find("```gdscript", end+3) + end = text.find("```", start + 11) + return extracted_code + + +func _convert_to_comment(text:String) -> String: + text = text.strip_edges(true, true) + if text.begins_with("#"): + #trusting the model returned a comment somewhat formatted + return text + else: + #formatting the comment + var result := "# " + var line_length := COMMENT_LENGTH + var curr_line_length := 0 + for i in range(text.length()): + if curr_line_length >= line_length and text[i] == " ": + result += "\n# " + curr_line_length = 0 + else: + result += text[i] + if text[i] == "\n": + result += "# " + curr_line_length = 0 + curr_line_length += 1 + return result diff --git a/addons/ai_assistant_hub/ai_answer_handler.gd.uid b/addons/ai_assistant_hub/ai_answer_handler.gd.uid new file mode 100644 index 0000000..eb8a28b --- /dev/null +++ b/addons/ai_assistant_hub/ai_answer_handler.gd.uid @@ -0,0 +1 @@ +uid://b4kbwwm0flgbr diff --git a/addons/ai_assistant_hub/ai_assistant_hub.gd b/addons/ai_assistant_hub/ai_assistant_hub.gd new file mode 100644 index 0000000..f2861b2 --- /dev/null +++ b/addons/ai_assistant_hub/ai_assistant_hub.gd @@ -0,0 +1,136 @@ +@tool +class_name AIAssistantHub +extends Control + +signal models_refreshed(models:Array[String]) +signal new_api_loaded() + +const NEW_AI_ASSISTANT_BUTTON = preload("res://addons/ai_assistant_hub/new_ai_assistant_button.tscn") + +@onready var models_http_request: HTTPRequest = %ModelsHTTPRequest +@onready var url_txt: LineEdit = %UrlTxt +@onready var api_class_txt: LineEdit = %ApiClassTxt +@onready var models_list: RichTextLabel = %ModelsList +@onready var no_assistants_guide: Label = %NoAssistantsGuide +@onready var assistant_types_container: HFlowContainer = %AssistantTypesContainer +@onready var tab_container: TabContainer = %TabContainer + +var _plugin:EditorPlugin +var _tab_bar:TabBar +var _model_names:Array[String] = [] +var _models_llm: LLMInterface + + +func _tab_changed(tab_index: int) -> void: + if tab_index > 0: + _tab_bar.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY + else: + _tab_bar.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_NEVER + + +func _close_tab(tab_index: int) -> void: + var chat = tab_container.get_tab_control(tab_index) + models_refreshed.disconnect(chat.refresh_models) + chat.queue_free() + + +func initialize(plugin:EditorPlugin) -> void: + _plugin = plugin + _models_llm = _plugin.new_llm_provider() + + await ready + url_txt.text = ProjectSettings.get_setting(AIHubPlugin.CONFIG_BASE_URL) + api_class_txt.text = ProjectSettings.get_setting(AIHubPlugin.CONFIG_LLM_API) + + _on_assistants_refresh_btn_pressed() + _on_refresh_models_btn_pressed() + + _tab_bar = tab_container.get_tab_bar() + _tab_bar.tab_changed.connect(_tab_changed) + _tab_bar.tab_close_pressed.connect(_close_tab) + + +func _on_settings_changed(_x) -> void: + ProjectSettings.set_setting(AIHubPlugin.CONFIG_BASE_URL, url_txt.text) + ProjectSettings.set_setting(AIHubPlugin.CONFIG_LLM_API, api_class_txt.text) + + +func _on_refresh_models_btn_pressed() -> void: + models_list.text = "" + _models_llm.send_get_models_request(models_http_request) + + +func _on_models_http_request_completed(result: HTTPRequest.Result, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: + if result == 0: + var models_returned: Array = _models_llm.read_models_response(body) + if models_returned.size() == 0: + models_list.text = "No models found. Download at least one model and try again." + else: + if models_returned[0] == LLMInterface.INVALID_RESPONSE: + models_list.text = "Error while trying to get the models list. Response: %s" % _models_llm.get_full_response(body) + else: + _model_names = models_returned + for model in _model_names: + models_list.text += "%s\n" % model + models_refreshed.emit(_model_names) #for existing chats + else: + push_error("HTTP response: Result: %s, Response Code: %d, Headers: %s, Body: %s" % [result, response_code, headers, body]) + models_list.text = "Something went wrong querying for models, is the Server URL correct?" + + +func _on_assistants_refresh_btn_pressed() -> void: + var assistants_path = "%s/assistants" % self.scene_file_path.get_base_dir() + var files = _get_all_resources(assistants_path) + var found:= false + + for child in assistant_types_container.get_children(): + if child != no_assistants_guide: + assistant_types_container.remove_child(child) + + for assistant_file in files: + var assistant = load(assistant_file) + if assistant is AIAssistantResource: + found = true + var new_bot_btn:NewAIAssistantButton= NEW_AI_ASSISTANT_BUTTON.instantiate() + new_bot_btn.initialize(_plugin, assistant) + new_bot_btn.chat_created.connect(_on_new_bot_btn_chat_created) + assistant_types_container.add_child(new_bot_btn) + + if not found: + no_assistants_guide.text = "You have no assistant types! Create a new AIAssistantResource in the assistants folder, then click the refresh button. The folder is at: %s" % assistants_path + no_assistants_guide.visible = true + assistant_types_container.visible = false + else: + no_assistants_guide.visible = false + assistant_types_container.visible = true + + +func _on_new_bot_btn_chat_created(chat:AIChat, assistant_type:AIAssistantResource) -> void: + tab_container.add_child(chat) + tab_container.set_tab_icon(tab_container.get_child_count() - 1, assistant_type.type_icon) + chat.refresh_models(_model_names) + models_refreshed.connect(chat.refresh_models) + new_api_loaded.connect(chat.load_api) + chat.greet() + + +func _get_all_resources(path: String) -> Array[String]: + var file_paths: Array[String] = [] + var dir = DirAccess.open(path) + dir.list_dir_begin() + var file_name = dir.get_next() + while not file_name.is_empty(): + if file_name.ends_with(".tres"): + var file_path = path + "/" + file_name + file_paths.append(file_path) + file_name = dir.get_next() + return file_paths + + +func _on_api_load_btn_pressed() -> void: + var new_llm:LLMInterface = _plugin.new_llm_provider() + if new_llm == null: + push_error("Invalid API class") + return + _models_llm = new_llm + new_api_loaded.emit() diff --git a/addons/ai_assistant_hub/ai_assistant_hub.gd.uid b/addons/ai_assistant_hub/ai_assistant_hub.gd.uid new file mode 100644 index 0000000..697b079 --- /dev/null +++ b/addons/ai_assistant_hub/ai_assistant_hub.gd.uid @@ -0,0 +1 @@ +uid://55itx6djrjvr diff --git a/addons/ai_assistant_hub/ai_assistant_hub.tscn b/addons/ai_assistant_hub/ai_assistant_hub.tscn new file mode 100644 index 0000000..169a0ec --- /dev/null +++ b/addons/ai_assistant_hub/ai_assistant_hub.tscn @@ -0,0 +1,207 @@ +[gd_scene load_steps=2 format=3 uid="uid://w1f4dho35qy2"] + +[ext_resource type="Script" path="res://addons/ai_assistant_hub/ai_assistant_hub.gd" id="1_x668t"] + +[node name="AIAssistantHub" type="Control"] +custom_minimum_size = Vector2(0, 260) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_x668t") + +[node name="ModelsHTTPRequest" type="HTTPRequest" parent="."] +unique_name_in_owner = true +accept_gzip = false +timeout = 10.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TabContainer" type="TabContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +current_tab = 0 + +[node name="AI Hub" type="MarginContainer" parent="VBoxContainer/TabContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/AI Hub"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 10 + +[node name="Label" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer/VBoxContainer"] +layout_mode = 2 +theme_type_variation = &"HeaderMedium" +text = "Summon +assistant!" + +[node name="NoAssistantsGuide" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "You have no assistant types! Create a new AIAssistantResource in the assistants folder, then click the ↻ button. The folder is at: +res://addons/ai_assistant_hub/assistants" +autowrap_mode = 2 + +[node name="AssistantTypesContainer" type="HFlowContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +alignment = 1 + +[node name="AssistantsRefreshBtn" type="Button" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer"] +auto_translate_mode = 1 +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 +text = "↻" +autowrap_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer"] +layout_mode = 2 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer"] +auto_translate_mode = 1 +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/h_separation = 20 +theme_override_constants/v_separation = 10 +columns = 2 + +[node name="Label" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer"] +auto_translate_mode = 1 +layout_mode = 2 +text = "Available models" +horizontal_alignment = 2 + +[node name="VBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer"] +auto_translate_mode = 1 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="PanelContainer" type="ScrollContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="ModelsList" type="RichTextLabel" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer/PanelContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 24) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +focus_mode = 2 +autowrap_mode = 2 +selection_enabled = true + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer"] +layout_mode = 2 + +[node name="RefreshModelsBtn" type="Button" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer/VBoxContainer"] +auto_translate_mode = 1 +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +size_flags_vertical = 0 +text = "Refresh models" + +[node name="Control" type="Control" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Label2" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer"] +auto_translate_mode = 1 +layout_mode = 2 +text = "Server URL" +horizontal_alignment = 2 + +[node name="AdvancedSettings" type="HBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="UrlTxt" type="LineEdit" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +unique_name_in_owner = true +auto_translate_mode = 1 +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "The URL of the host that runs your LLMs. + +The default value is for the local host using Ollama's default port." +placeholder_text = "e.g. http://127.0.0.1:11434" + +[node name="Spacer" type="Control" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +layout_mode = 2 + +[node name="Label3" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +auto_translate_mode = 1 +layout_mode = 2 +text = "API class" +horizontal_alignment = 2 + +[node name="ApiClassTxt" type="LineEdit" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +unique_name_in_owner = true +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +tooltip_text = "This must correspond to a script in `res://addons/ai_assistant_hub/llm_apis/` folder. + +If you create your own scripts that extend from `LLMInterface` class, provide the script name in this property to start using it instead of the Ollama API." +placeholder_text = "ollama_api" + +[node name="APILoadBtn" type="Button" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +auto_translate_mode = 1 +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +tooltip_text = "Load this class." +text = "↻" +alignment = 2 + +[node name="Spacer2" type="Control" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +auto_translate_mode = 1 +layout_mode = 2 + +[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings"] +layout_mode = 2 + +[node name="VersionLabel" type="Label" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings/HBoxContainer"] +layout_mode = 2 +text = "v1.0.0" +horizontal_alignment = 2 + +[node name="SupportBtn" type="LinkButton" parent="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +tooltip_text = "If you like this plugin, see how you can support it." +text = "♥" +underline = 2 +uri = "https://github.com/FlamxGames/godot-ai-assistant-hub/blob/main/support.md" + +[connection signal="request_completed" from="ModelsHTTPRequest" to="." method="_on_models_http_request_completed"] +[connection signal="pressed" from="VBoxContainer/TabContainer/AI Hub/VBoxContainer/HBoxContainer/AssistantsRefreshBtn" to="." method="_on_assistants_refresh_btn_pressed"] +[connection signal="pressed" from="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/VBoxContainer/VBoxContainer/RefreshModelsBtn" to="." method="_on_refresh_models_btn_pressed"] +[connection signal="text_changed" from="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings/UrlTxt" to="." method="_on_settings_changed"] +[connection signal="text_changed" from="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings/ApiClassTxt" to="." method="_on_settings_changed"] +[connection signal="pressed" from="VBoxContainer/TabContainer/AI Hub/VBoxContainer/GridContainer/AdvancedSettings/APILoadBtn" to="." method="_on_api_load_btn_pressed"] diff --git a/addons/ai_assistant_hub/ai_chat.gd b/addons/ai_assistant_hub/ai_chat.gd new file mode 100644 index 0000000..b390e89 --- /dev/null +++ b/addons/ai_assistant_hub/ai_chat.gd @@ -0,0 +1,219 @@ +@tool +class_name AIChat +extends Control + +enum Caller { + You, + Bot, + System +} + +const CHAT_HISTORY_EDITOR = preload("res://addons/ai_assistant_hub/chat_history_editor.tscn") + +@onready var http_request: HTTPRequest = %HTTPRequest +@onready var output_window: RichTextLabel = %OutputWindow +@onready var prompt_txt: TextEdit = %PromptTxt +@onready var bot_portrait: BotPortrait = %BotPortrait +@onready var quick_prompts_panel: Container = %QuickPromptsPanel +@onready var reply_sound: AudioStreamPlayer = %ReplySound +@onready var error_sound: AudioStreamPlayer = %ErrorSound +@onready var model_options_btn: OptionButton = %ModelOptionsBtn +@onready var temperature_slider: HSlider = %TemperatureSlider +@onready var temperature_override_checkbox: CheckBox = %TemperatureOverrideCheckbox +@onready var temperature_slider_container: HBoxContainer = %TemperatureSliderContainer + + +var _plugin:EditorPlugin +var _bot_name: String +var _assistant_settings: AIAssistantResource +var _last_quick_prompt: AIQuickPromptResource +var _code_selector: AssistantToolSelection +var _bot_answer_handler: AIAnswerHandler +var _llm: LLMInterface +var _conversation: AIConversation + + +func initialize(plugin:EditorPlugin, assistant_settings: AIAssistantResource, bot_name:String) -> void: + _plugin = plugin + _assistant_settings = assistant_settings + _bot_name = bot_name + _code_selector = AssistantToolSelection.new(plugin) + _bot_answer_handler = AIAnswerHandler.new(plugin, _code_selector) + _bot_answer_handler.bot_message_produced.connect(func(message): _add_to_chat(message, Caller.Bot) ) + _bot_answer_handler.error_message_produced.connect(func(message): _add_to_chat(message, Caller.System) ) + _conversation = AIConversation.new() + + if _assistant_settings: # We need to check this, otherwise this is called when editing the plugin + load_api() + _conversation.set_system_message(_assistant_settings.ai_description) + + await ready + temperature_slider.value = assistant_settings.custom_temperature + temperature_override_checkbox.button_pressed = assistant_settings.use_custom_temperature + _on_temperature_override_checkbox_toggled(temperature_override_checkbox.button_pressed) + + bot_portrait.set_random() + reply_sound.pitch_scale = randf_range(0.7, 1.2) + + for qp in _assistant_settings.quick_prompts: + var qp_button:= Button.new() + qp_button.text = qp.action_name + qp_button.icon = qp.icon + qp_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + qp_button.pressed.connect(func(): _on_qp_button_pressed(qp)) + quick_prompts_panel.add_child(qp_button) + + +func load_api() -> void: + _llm = _plugin.new_llm_provider() + _llm.model = _assistant_settings.ai_model + _llm.override_temperature = _assistant_settings.use_custom_temperature + _llm.temperature = _assistant_settings.custom_temperature + + +func greet() -> void: + if _assistant_settings.quick_prompts.size() == 0: + _add_to_chat("This assistant type doesn't have Quick Prompts defined. Add them to the assistant's resource configuration to unlock some additional capabilities, like writing in the code editor.", Caller.System) + + var greet_prompt := "Give a short greeting including just your name (which is \"%s\") and how can you help in a concise sentence." % _bot_name + _submit_prompt(greet_prompt) + + +func refresh_models(models: Array[String]) -> void: + model_options_btn.clear() + var selected_found := false + for model in models: + model_options_btn.add_item(model) + if model.contains(_assistant_settings.ai_model): + model_options_btn.select(model_options_btn.item_count - 1) + selected_found = true + if not selected_found: + model_options_btn.add_item(_assistant_settings.ai_model) + model_options_btn.select(model_options_btn.item_count - 1) + + +func _input(event: InputEvent) -> void: + if prompt_txt.has_focus() and event.is_pressed() and event is InputEventKey: + var e:InputEventKey = event + var is_enter_key := e.keycode == KEY_ENTER or e.keycode == KEY_KP_ENTER + var shift_pressed := Input.is_physical_key_pressed(KEY_SHIFT) + if shift_pressed and is_enter_key: + prompt_txt.insert_text_at_caret("\n") + else: + var ctrl_pressed = Input.is_physical_key_pressed(KEY_CTRL) + if not ctrl_pressed: + if not prompt_txt.text.is_empty() and is_enter_key: + if bot_portrait.is_thinking: + _abandon_request() + get_viewport().set_input_as_handled() + var prompt = _engineer_prompt(prompt_txt.text) + prompt_txt.text = "" + _add_to_chat(prompt, Caller.You) + _submit_prompt(prompt) + + +func _on_qp_button_pressed(qp: AIQuickPromptResource) -> void: + _last_quick_prompt = qp + var prompt = qp.action_prompt.replace("{CODE}", _code_selector.get_selection()) + if prompt.contains("{CHAT}"): + prompt = prompt.replace("{CHAT}", prompt_txt.text) + prompt_txt.text = "" + _add_to_chat(prompt, Caller.You) + _submit_prompt(prompt, qp) + + +func _find_code_editor() -> TextEdit: + var script_editor := _plugin.get_editor_interface().get_script_editor().get_current_editor() + return script_editor.get_base_editor() + + +func _engineer_prompt(original:String) -> String: + if original.contains("{CODE}"): + var curr_code:String = _find_code_editor().get_selected_text() + var prompt:String = original.replace("{CODE}", curr_code) + return prompt + else: + return original + + +func _submit_prompt(prompt:String, quick_prompt:AIQuickPromptResource = null) -> void: + if bot_portrait.is_thinking: + _abandon_request() + _last_quick_prompt = quick_prompt + bot_portrait.is_thinking = true + _conversation.add_user_prompt(prompt) + var success := _llm.send_chat_request(http_request, _conversation.build()) + if not success: + _add_to_chat("Something went wrong. Review the details in Godot's Output tab.", Caller.System) + + +func _abandon_request() -> void: + error_sound.play() + http_request.cancel_request() + bot_portrait.is_thinking = false + _add_to_chat("Abandoned previous request.", Caller.System) + _conversation.forget_last_prompt() + + +func _on_http_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: + #print("HTTP response: Result: %d, Response Code: %d, Headers: %s, Body: %s" % [result, response_code, headers, body]) + bot_portrait.is_thinking = false + if result == 0: + var text_answer = _llm.read_response(body) + if text_answer == LLMInterface.INVALID_RESPONSE: + error_sound.play() + push_error("Response: %s" % _llm.get_full_response(body)) + _add_to_chat("An error occurred while processing your last request. Review the details in Godot's Output tab.", Caller.System) + else: + reply_sound.play() + _conversation.add_assistant_response(text_answer) + _bot_answer_handler.handle(text_answer, _last_quick_prompt) + else: + error_sound.play() + push_error("HTTP response: Result: %s, Response Code: %d, Headers: %s, Body: %s" % [result, response_code, headers, body]) + _add_to_chat("An error occurred while communicating with the assistant. Review the details in Godot's Output tab.", Caller.System) + + +func _add_to_chat(text:String, caller:Caller) -> void: + var prefix:String + var suffix:String + match caller: + Caller.You: + prefix = "\n[color=FFFF00]> " + suffix = "[/color]\n" + Caller.Bot: + prefix = "\n[right][color=777777][b]%s[/b][/color]:\n" % _bot_name + var code_found := false + if text.contains("```gdscript"): + code_found = true + text = text.replace("```gdscript","[left][color=33AAFF]") + if text.contains("```glsl"): + code_found = true + text = text.replace("```glsl","[left][color=33AAFF]") + if code_found: + text = text.replace("```","[/color][/left]") + suffix = "[/right]\n" + Caller.System: + prefix = "\n[center][color=FF7700][ " + suffix = " ][/color][/center]\n" + output_window.text += "%s%s%s" % [prefix, text, suffix] + + +func _on_edit_history_pressed() -> void: + var history_editor:ChatHistoryEditor = CHAT_HISTORY_EDITOR.instantiate() + history_editor.initialize(_conversation) + add_child(history_editor) + history_editor.popup() + + +func _on_temperature_override_checkbox_toggled(toggled_on: bool) -> void: + temperature_slider_container.visible = toggled_on + _llm.override_temperature = toggled_on + + +func _on_model_options_btn_item_selected(index: int) -> void: + _llm.model = model_options_btn.text + + +func _on_temperature_slider_value_changed(value: float) -> void: + _llm.temperature = snappedf(temperature_slider.value, 0.001) diff --git a/addons/ai_assistant_hub/ai_chat.gd.uid b/addons/ai_assistant_hub/ai_chat.gd.uid new file mode 100644 index 0000000..9085f09 --- /dev/null +++ b/addons/ai_assistant_hub/ai_chat.gd.uid @@ -0,0 +1 @@ +uid://r5gybxqvu0ye diff --git a/addons/ai_assistant_hub/ai_chat.tscn b/addons/ai_assistant_hub/ai_chat.tscn new file mode 100644 index 0000000..97c166e --- /dev/null +++ b/addons/ai_assistant_hub/ai_chat.tscn @@ -0,0 +1,179 @@ +[gd_scene load_steps=6 format=3 uid="uid://c5d12f133cpv7"] + +[ext_resource type="Script" path="res://addons/ai_assistant_hub/ai_chat.gd" id="1_v0kvm"] +[ext_resource type="Texture2D" uid="uid://beq5nk70uk8v4" path="res://addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png" id="2_8urns"] +[ext_resource type="PackedScene" uid="uid://dxr0f1xqsje6b" path="res://addons/ai_assistant_hub/bot_portrait.tscn" id="3_lcoap"] +[ext_resource type="AudioStream" uid="uid://dk1yumltykf6x" path="res://addons/ai_assistant_hub/sounds/ai_replied.wav" id="4_7y76u"] +[ext_resource type="AudioStream" uid="uid://b2lftlbs848c4" path="res://addons/ai_assistant_hub/sounds/ai_error_cancel.wav" id="5_mmsii"] + +[node name="AIChat" type="Control"] +clip_contents = true +custom_minimum_size = Vector2(0, 180) +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 +script = ExtResource("1_v0kvm") + +[node name="HTTPRequest" type="HTTPRequest" parent="."] +unique_name_in_owner = true + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer"] +layout_mode = 2 +horizontal_scroll_mode = 0 + +[node name="QuickPromptsPanel" type="VBoxContainer" parent="HBoxContainer/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="VSplitContainer" type="VSplitContainer" parent="HBoxContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 7 +theme_override_constants/autohide = 0 + +[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/VSplitContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="OutputWindow" type="RichTextLabel" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +focus_mode = 2 +bbcode_enabled = true +scroll_following = true +selection_enabled = true +deselect_on_focus_loss_enabled = false + +[node name="MarginContainer" type="Control" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="EditHistory" type="Button" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer/MarginContainer"] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -48.0 +offset_bottom = 48.0 +grow_horizontal = 0 +size_flags_vertical = 0 +icon = ExtResource("2_8urns") +icon_alignment = 1 + +[node name="BotPortrait" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer/MarginContainer" instance=ExtResource("3_lcoap")] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -24.0 +offset_top = -48.0 +offset_right = 24.0 +offset_bottom = 0.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_filter = 2 + +[node name="HBoxContainer2" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/VSplitContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer2"] +layout_mode = 2 +theme_type_variation = &"HeaderLarge" +text = ">" + +[node name="PromptTxt" type="TextEdit" parent="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer2"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 40) +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Ask something, press ENTER to send. +(Tip: Use CTRL + ENTER or SHIFT + ENTER for adding a new line)" +wrap_mode = 1 + +[node name="HSeparator" type="HSeparator" parent="HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 4 + +[node name="TemperatureOverrideCheckbox" type="CheckBox" parent="HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Override model's behavior." + +[node name="TemperatureSliderContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="HBoxContainer/VBoxContainer/HBoxContainer/TemperatureSliderContainer"] +layout_mode = 2 +text = "Precise" +vertical_alignment = 1 + +[node name="TemperatureSlider" type="HSlider" parent="HBoxContainer/VBoxContainer/HBoxContainer/TemperatureSliderContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.001 +ticks_on_borders = true + +[node name="Label2" type="Label" parent="HBoxContainer/VBoxContainer/HBoxContainer/TemperatureSliderContainer"] +auto_translate_mode = 1 +layout_mode = 2 +text = "Creative" +vertical_alignment = 1 + +[node name="VSeparator" type="VSeparator" parent="HBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label3" type="Label" parent="HBoxContainer/VBoxContainer/HBoxContainer"] +auto_translate_mode = 1 +layout_mode = 2 +text = "Model" +vertical_alignment = 1 + +[node name="ModelOptionsBtn" type="OptionButton" parent="HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="ReplySound" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("4_7y76u") +pitch_scale = 1.0108 + +[node name="ErrorSound" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("5_mmsii") + +[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_completed"] +[connection signal="pressed" from="HBoxContainer/VBoxContainer/VSplitContainer/HBoxContainer/MarginContainer/EditHistory" to="." method="_on_edit_history_pressed"] +[connection signal="toggled" from="HBoxContainer/VBoxContainer/HBoxContainer/TemperatureOverrideCheckbox" to="." method="_on_temperature_override_checkbox_toggled"] +[connection signal="value_changed" from="HBoxContainer/VBoxContainer/HBoxContainer/TemperatureSliderContainer/TemperatureSlider" to="." method="_on_temperature_slider_value_changed"] +[connection signal="item_selected" from="HBoxContainer/VBoxContainer/HBoxContainer/ModelOptionsBtn" to="." method="_on_model_options_btn_item_selected"] diff --git a/addons/ai_assistant_hub/ai_conversation.gd b/addons/ai_assistant_hub/ai_conversation.gd new file mode 100644 index 0000000..45406ef --- /dev/null +++ b/addons/ai_assistant_hub/ai_conversation.gd @@ -0,0 +1,57 @@ +@tool +class_name AIConversation + +var _chat_history:= [] +var _system_msg: String + + +func set_system_message(message:String) -> void: + _system_msg = message + # If your models don't mark the code with ```gdscript, the plugin won't wort well, + # consider giving it an instruction like the one in the comment below, either in the + # _system_msg or as part of the bot initial request. + # + #_system_msg = "%s. Any code you write you should identify with the programming language, for example for GDScript you must use prefix \"```gdscript\" and suffix \"```\"." % message + # + + +func add_user_prompt(prompt:String) -> void: + _chat_history.append( + { + "role": "user", + "content": prompt + } + ) + + +func add_assistant_response(response:String) -> void: + _chat_history.append( + { + "role": "assistant", + "content": response + } + ) + + +func build() -> Array: + var messages := [] + messages.append( + { + "role": "system", + "content": _system_msg + } + ) + messages.append_array(_chat_history) + return messages + + +func forget_last_prompt() -> void: + _chat_history.pop_back() + + +func clone_chat() -> Array: + return _chat_history.duplicate(true) + + +func overwrite_chat(new_chat:Array) -> void: + _chat_history = new_chat diff --git a/addons/ai_assistant_hub/ai_conversation.gd.uid b/addons/ai_assistant_hub/ai_conversation.gd.uid new file mode 100644 index 0000000..f822ed7 --- /dev/null +++ b/addons/ai_assistant_hub/ai_conversation.gd.uid @@ -0,0 +1 @@ +uid://fnqec17oj0rd diff --git a/addons/ai_assistant_hub/ai_hub_plugin.gd b/addons/ai_assistant_hub/ai_hub_plugin.gd new file mode 100644 index 0000000..3c3b091 --- /dev/null +++ b/addons/ai_assistant_hub/ai_hub_plugin.gd @@ -0,0 +1,31 @@ +@tool +class_name AIHubPlugin +extends EditorPlugin + +const CONFIG_BASE_URL:= "ai_assistant_hub/base_url" +const CONFIG_LLM_API:= "ai_assistant_hub/llm_api" + +var _hub_dock:AIAssistantHub + +func _enter_tree() -> void: + if ProjectSettings.get_setting(CONFIG_BASE_URL, "").is_empty(): + ProjectSettings.set_setting(CONFIG_BASE_URL, "http://127.0.0.1:11434") + if ProjectSettings.get_setting(CONFIG_LLM_API, "").is_empty(): + ProjectSettings.set_setting(CONFIG_LLM_API, "ollama_api") + + _hub_dock = load("res://addons/ai_assistant_hub/ai_assistant_hub.tscn").instantiate() + _hub_dock.initialize(self) + add_control_to_bottom_panel(_hub_dock, "AI Hub") + #add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UL, _hub_dock) + + +func _exit_tree() -> void: + remove_control_from_bottom_panel(_hub_dock) + #remove_control_from_docks(_hub_dock) + _hub_dock.queue_free() + + +## Load the API dinamically based on the script name given in project setting: ai_assistant_hub/llm_api +## By default this is equivalent to: return OllamaAPI.new() +func new_llm_provider() -> LLMInterface: + return load("res://addons/ai_assistant_hub/llm_apis/%s.gd" % ProjectSettings.get_setting(AIHubPlugin.CONFIG_LLM_API)).new() diff --git a/addons/ai_assistant_hub/ai_hub_plugin.gd.uid b/addons/ai_assistant_hub/ai_hub_plugin.gd.uid new file mode 100644 index 0000000..73a9324 --- /dev/null +++ b/addons/ai_assistant_hub/ai_hub_plugin.gd.uid @@ -0,0 +1 @@ +uid://c2nhbu4ivwaja diff --git a/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd b/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd new file mode 100644 index 0000000..e3160f1 --- /dev/null +++ b/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd @@ -0,0 +1,27 @@ +class_name AIAssistantResource +extends Resource + +## Name of the assistant type (e.g., "Writer", "Programmer"). +@export var type_name: String + +## Icon displayed in hub buttons and tabs for this assistant. +@export var type_icon: Texture2D + +## The name of the AI model as listed in the available models section. +@export var ai_model: String + +## Used to give the System message to the chat. +## This gives the overall direction on what the assistant should do. +@export_multiline var ai_description: String = "You are a useful Godot AI assistant." + +## Models have a default temperature recommended for most use cases. +## When checking this, the value of the temperature will be dictated by the CustomTemperature property. +@export var use_custom_temperature: bool = false + +## The temperature indicates to the models how much they can deviate from the most expected patterns, usually having low temperature returns more precise output, and high temperature more creative output. +## This value is ignored if UseCustomTemperature is false. +@export_range(0.0, 1.0) var custom_temperature := 0.5 + +## Quick Prompts available for a model are displayed in the chat window as buttons. +## These allow to create prompt templates, as well as read and write to the code editor. +@export var quick_prompts: Array[AIQuickPromptResource] diff --git a/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd.uid b/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd.uid new file mode 100644 index 0000000..5b19d75 --- /dev/null +++ b/addons/ai_assistant_hub/assistants/ai_assistant_resource.gd.uid @@ -0,0 +1 @@ +uid://cb6snexdvyill diff --git a/addons/ai_assistant_hub/assistants/ollama.tres b/addons/ai_assistant_hub/assistants/ollama.tres new file mode 100644 index 0000000..d15c77c --- /dev/null +++ b/addons/ai_assistant_hub/assistants/ollama.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="AIAssistantResource" load_steps=3 format=3 uid="uid://c6eyb701hpdqd"] + +[ext_resource type="Script" uid="uid://cb6snexdvyill" path="res://addons/ai_assistant_hub/assistants/ai_assistant_resource.gd" id="1_36d27"] +[ext_resource type="Script" uid="uid://gfjw8kfxgsnh" path="res://addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd" id="2_vs81c"] + +[resource] +script = ExtResource("1_36d27") +type_name = "" +ai_model = "llama3.1:8b" +ai_description = "You are a useful Godot AI assistant." +use_custom_temperature = false +custom_temperature = 0.5 +quick_prompts = Array[ExtResource("2_vs81c")]([]) +metadata/_custom_type_script = ExtResource("1_36d27") diff --git a/addons/ai_assistant_hub/bot_portrait.gd b/addons/ai_assistant_hub/bot_portrait.gd new file mode 100644 index 0000000..5c87cc1 --- /dev/null +++ b/addons/ai_assistant_hub/bot_portrait.gd @@ -0,0 +1,59 @@ +@tool +class_name BotPortrait +extends Control + +const PORTRAITS_BASE := preload("res://addons/ai_assistant_hub/graphics/portraits/portraits_base.png") +const PORTRAIT_AMOUNT_X := 3 +const PORTRAIT_AMOUNT_Y := 3 +const SCALE := 3 # given the images are 16px but we are displaying them 48px, this is used to move the face when thinking + +@onready var portrait_base: TextureRect = %PortraitBase +@onready var portrait_mouth: TextureRect = %PortraitMouth +@onready var portrait_eyes: TextureRect = %PortraitEyes +@onready var portrait_thinking: TextureRect = %PortraitThinking + +var _think_tween:Tween + +func set_random() -> void: + var tex_size := PORTRAITS_BASE.get_size() + var portrait_size := Vector2i(tex_size.x / PORTRAIT_AMOUNT_X, tex_size.y / PORTRAIT_AMOUNT_Y) + _select_random_region(portrait_base) + _select_random_region(portrait_mouth) + _select_random_region(portrait_eyes) + + +func _select_random_region(image:TextureRect) -> void: + var x_rand := randi_range(0, PORTRAIT_AMOUNT_X - 1) + var y_rand := randi_range(0, PORTRAIT_AMOUNT_Y - 1) + var base_atlas: AtlasTexture = image.texture.duplicate() + image.texture = base_atlas + base_atlas.region = Rect2(x_rand*16,y_rand*16,16,16) + + +var is_thinking:= false: + set(value): + is_thinking = value + if _think_tween != null and _think_tween.is_running(): + _think_tween.stop() + portrait_thinking.visible = is_thinking + if is_thinking: + portrait_eyes.position.x = SCALE + portrait_eyes.position.y = -SCALE + portrait_mouth.position = portrait_eyes.position + _thinking_anim() + else: + portrait_eyes.position = Vector2.ZERO + portrait_mouth.position = Vector2.ZERO + self.rotation_degrees = 0 + + +func _thinking_anim() -> void: + while is_thinking: + _think_tween = create_tween() + _think_tween.tween_property(self, "rotation_degrees", -12, 1) + _think_tween.tween_property(self, "rotation_degrees", 12, 1) + await _think_tween.finished + self.rotation_degrees = 0 + var complete = create_tween() + complete.tween_property(self, "scale", Vector2(1.2, 1.2), 0.05) + complete.tween_property(self, "scale", Vector2(1, 1), 0.05) diff --git a/addons/ai_assistant_hub/bot_portrait.gd.uid b/addons/ai_assistant_hub/bot_portrait.gd.uid new file mode 100644 index 0000000..91acd9c --- /dev/null +++ b/addons/ai_assistant_hub/bot_portrait.gd.uid @@ -0,0 +1 @@ +uid://cnipuj0a7i4p diff --git a/addons/ai_assistant_hub/bot_portrait.tscn b/addons/ai_assistant_hub/bot_portrait.tscn new file mode 100644 index 0000000..d951705 --- /dev/null +++ b/addons/ai_assistant_hub/bot_portrait.tscn @@ -0,0 +1,71 @@ +[gd_scene load_steps=9 format=3 uid="uid://dxr0f1xqsje6b"] + +[ext_resource type="Texture2D" uid="uid://ba07lvip5fjtx" path="res://addons/ai_assistant_hub/graphics/portraits/portraits_base.png" id="1_6ygms"] +[ext_resource type="Script" path="res://addons/ai_assistant_hub/bot_portrait.gd" id="1_irxow"] +[ext_resource type="Texture2D" uid="uid://bqkxfvi24xl6c" path="res://addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png" id="2_5emjf"] +[ext_resource type="Texture2D" uid="uid://bcfkhh3ljqe6g" path="res://addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png" id="3_opry7"] +[ext_resource type="Texture2D" uid="uid://crtbbm01emvsf" path="res://addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png" id="4_yrvbr"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_684wr"] +atlas = ExtResource("1_6ygms") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_82r0e"] +atlas = ExtResource("2_5emjf") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kykyr"] +atlas = ExtResource("3_opry7") +region = Rect2(0, 0, 16, 16) + +[node name="BotPortrait" type="Control"] +auto_translate_mode = 1 +layout_mode = 3 +anchors_preset = 0 +offset_right = 48.0 +offset_bottom = 48.0 +pivot_offset = Vector2(24, 48) +size_flags_horizontal = 0 +size_flags_vertical = 0 +script = ExtResource("1_irxow") + +[node name="PortraitBase" type="TextureRect" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +texture_filter = 1 +custom_minimum_size = Vector2(48, 48) +offset_right = 48.0 +offset_bottom = 48.0 +texture = SubResource("AtlasTexture_684wr") +expand_mode = 3 + +[node name="PortraitMouth" type="TextureRect" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +texture_filter = 1 +custom_minimum_size = Vector2(48, 48) +offset_right = 48.0 +offset_bottom = 48.0 +texture = SubResource("AtlasTexture_82r0e") +expand_mode = 3 + +[node name="PortraitEyes" type="TextureRect" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +texture_filter = 1 +custom_minimum_size = Vector2(48, 48) +offset_right = 48.0 +offset_bottom = 48.0 +texture = SubResource("AtlasTexture_kykyr") +expand_mode = 3 + +[node name="PortraitThinking" type="TextureRect" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +visible = false +texture_filter = 1 +custom_minimum_size = Vector2(48, 48) +offset_right = 48.0 +offset_bottom = 48.0 +texture = ExtResource("4_yrvbr") +expand_mode = 3 diff --git a/addons/ai_assistant_hub/chat_history_editor.gd b/addons/ai_assistant_hub/chat_history_editor.gd new file mode 100644 index 0000000..0eff28b --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_editor.gd @@ -0,0 +1,45 @@ +@tool +class_name ChatHistoryEditor +extends Window + +const CHAT_HISTORY_ENTRY = preload("res://addons/ai_assistant_hub/chat_history_entry.tscn") + +@onready var entries_container: VBoxContainer = %EntriesContainer +@onready var background: Panel = %Background + +var _converstaion:AIConversation +var _chat_history:Array +var _entries_map:Dictionary # ChatHistoryEntry, Dictionary - maps the UI entries to the array entries + +func initialize(converstaion:AIConversation) -> void: + _converstaion = converstaion + _chat_history = _converstaion.clone_chat() + await ready + + var back_color:= EditorInterface.get_base_control().get_theme_color("base_color", "Editor") + background.get_theme_stylebox("panel").bg_color = back_color + + for section in _chat_history: + var entry:ChatHistoryEntry = CHAT_HISTORY_ENTRY.instantiate() + entry.initialize(section) + entries_container.add_child(entry) + _entries_map[entry] = section + entry.modified.connect(_on_entry_modified) + + +func _on_entry_modified(entry:ChatHistoryEntry) -> void: + var section:Dictionary = _entries_map[entry] + section["role"] = entry.get_role() + section["content"] = entry.get_content() + + +func _on_save_and_close_btn_pressed() -> void: + for entry in _entries_map.keys(): + if entry.should_be_forgotten(): + _chat_history.erase(_entries_map[entry]) + _converstaion.overwrite_chat(_chat_history) + queue_free() + + +func _on_close_requested() -> void: + queue_free() diff --git a/addons/ai_assistant_hub/chat_history_editor.gd.uid b/addons/ai_assistant_hub/chat_history_editor.gd.uid new file mode 100644 index 0000000..ccf98c6 --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_editor.gd.uid @@ -0,0 +1 @@ +uid://b3rfwrjgf1y6l diff --git a/addons/ai_assistant_hub/chat_history_editor.tscn b/addons/ai_assistant_hub/chat_history_editor.tscn new file mode 100644 index 0000000..edb02b4 --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_editor.tscn @@ -0,0 +1,82 @@ +[gd_scene load_steps=3 format=3 uid="uid://dn7xoyvs56oy6"] + +[ext_resource type="Script" path="res://addons/ai_assistant_hub/chat_history_editor.gd" id="1_n0n5m"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dxmwm"] +bg_color = Color(0.21, 0.24, 0.29, 1) + +[node name="ChatHistoryEditor" type="Window"] +title = "Chat hisotry editor" +initial_position = 2 +size = Vector2i(800, 600) +exclusive = true +script = ExtResource("1_n0n5m") + +[node name="Background" type="Panel" parent="."] +unique_name_in_owner = true +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_dxmwm") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer2"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="Label" type="Label" parent="VBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Edit the chat history. This is useful when you want the assistant to forget about parts of the conversation, or simply alter parts of it." +autowrap_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +follow_focus = true +horizontal_scroll_mode = 0 + +[node name="EntriesContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer2/VBoxContainer/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"] +auto_translate_mode = 1 +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="SaveAndCloseBtn" type="Button" parent="VBoxContainer/MarginContainer"] +layout_mode = 2 +text = "Save and Close" + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="pressed" from="VBoxContainer/MarginContainer/SaveAndCloseBtn" to="." method="_on_save_and_close_btn_pressed"] diff --git a/addons/ai_assistant_hub/chat_history_entry.gd b/addons/ai_assistant_hub/chat_history_entry.gd new file mode 100644 index 0000000..21dd0ad --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_entry.gd @@ -0,0 +1,36 @@ +@tool +class_name ChatHistoryEntry +extends HBoxContainer + +signal modified(entry:ChatHistoryEntry) + +@onready var role_option_list: OptionButton = %RoleOptionList +@onready var content_txt: TextEdit = %ContentTxt +@onready var forget_check_box: CheckBox = %ForgetCheckBox + + +func initialize(data:Dictionary) -> void: + await ready + if data["role"] == "assistant": + role_option_list.selected = 1 + content_txt.text = data["content"] + + +func get_role() -> String: + return role_option_list.text + + +func get_content() -> String: + return content_txt.text + + +func should_be_forgotten() -> bool: + return forget_check_box.button_pressed + + +func _on_content_txt_text_changed() -> void: + modified.emit(self) + + +func _on_role_option_list_item_selected(index: int) -> void: + modified.emit(self) diff --git a/addons/ai_assistant_hub/chat_history_entry.gd.uid b/addons/ai_assistant_hub/chat_history_entry.gd.uid new file mode 100644 index 0000000..512cd08 --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_entry.gd.uid @@ -0,0 +1 @@ +uid://ddulo2aqtcguh diff --git a/addons/ai_assistant_hub/chat_history_entry.tscn b/addons/ai_assistant_hub/chat_history_entry.tscn new file mode 100644 index 0000000..11c03ad --- /dev/null +++ b/addons/ai_assistant_hub/chat_history_entry.tscn @@ -0,0 +1,39 @@ +[gd_scene load_steps=2 format=3 uid="uid://b1mpesm8gt63t"] + +[ext_resource type="Script" path="res://addons/ai_assistant_hub/chat_history_entry.gd" id="1_rl68l"] + +[node name="ChatHistoryEntry" type="HBoxContainer"] +auto_translate_mode = 1 +offset_right = 162.0 +offset_bottom = 24.0 +size_flags_horizontal = 3 +script = ExtResource("1_rl68l") + +[node name="RoleOptionList" type="OptionButton" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +layout_mode = 2 +selected = 0 +item_count = 2 +popup/item_0/text = "user" +popup/item_0/id = 0 +popup/item_1/text = "assistant" +popup/item_1/id = 1 + +[node name="ContentTxt" type="TextEdit" parent="."] +unique_name_in_owner = true +auto_translate_mode = 1 +layout_mode = 2 +size_flags_horizontal = 3 +wrap_mode = 1 +autowrap_mode = 2 +scroll_fit_content_height = true + +[node name="ForgetCheckBox" type="CheckBox" parent="."] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "This entry will be deleted on save." +text = "Forget" + +[connection signal="item_selected" from="RoleOptionList" to="." method="_on_role_option_list_item_selected"] +[connection signal="text_changed" from="ContentTxt" to="." method="_on_content_txt_text_changed"] diff --git a/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png b/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png new file mode 100644 index 0000000..759d11e Binary files /dev/null and b/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png differ diff --git a/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png.import b/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png.import new file mode 100644 index 0000000..8a08925 --- /dev/null +++ b/addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://beq5nk70uk8v4" +path="res://.godot/imported/edit_chat_icon.png-c36f527ba8826fe5de54ad4e7caea01d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/icons/edit_chat_icon.png" +dest_files=["res://.godot/imported/edit_chat_icon.png-c36f527ba8826fe5de54ad4e7caea01d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png b/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png new file mode 100644 index 0000000..9a38e54 Binary files /dev/null and b/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png differ diff --git a/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png.import b/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png.import new file mode 100644 index 0000000..62b743e --- /dev/null +++ b/addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qjvopjqelt0m" +path="res://.godot/imported/linear_32_3dmsicons.png-16a4a9a4230f81b95397017f41e2e535.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/icons/linear_32_3dmsicons.png" +dest_files=["res://.godot/imported/linear_32_3dmsicons.png-16a4a9a4230f81b95397017f41e2e535.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png b/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png new file mode 100644 index 0000000..a7a6707 Binary files /dev/null and b/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png differ diff --git a/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png.import b/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png.import new file mode 100644 index 0000000..70ede62 --- /dev/null +++ b/addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://r7njmupyi7w4" +path="res://.godot/imported/linear_32_flatmsicons.png-ba9a79e0c1219861dd36e6b90cf49912.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/icons/linear_32_flatmsicons.png" +dest_files=["res://.godot/imported/linear_32_flatmsicons.png-ba9a79e0c1219861dd36e6b90cf49912.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png b/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png new file mode 100644 index 0000000..ef242eb Binary files /dev/null and b/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png differ diff --git a/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png.import b/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png.import new file mode 100644 index 0000000..3e3906e --- /dev/null +++ b/addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://crtbbm01emvsf" +path="res://.godot/imported/portrait_think_hand.png-9633d5b149332319d9d62072ca39808d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/portraits/portrait_think_hand.png" +dest_files=["res://.godot/imported/portrait_think_hand.png-9633d5b149332319d9d62072ca39808d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_base.png b/addons/ai_assistant_hub/graphics/portraits/portraits_base.png new file mode 100644 index 0000000..700b77b Binary files /dev/null and b/addons/ai_assistant_hub/graphics/portraits/portraits_base.png differ diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_base.png.import b/addons/ai_assistant_hub/graphics/portraits/portraits_base.png.import new file mode 100644 index 0000000..10b63e2 --- /dev/null +++ b/addons/ai_assistant_hub/graphics/portraits/portraits_base.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ba07lvip5fjtx" +path="res://.godot/imported/portraits_base.png-bb6417d0f01b1dd60bd0778d72539dc4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/portraits/portraits_base.png" +dest_files=["res://.godot/imported/portraits_base.png-bb6417d0f01b1dd60bd0778d72539dc4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png b/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png new file mode 100644 index 0000000..6ef6c4c Binary files /dev/null and b/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png differ diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png.import b/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png.import new file mode 100644 index 0000000..382bc80 --- /dev/null +++ b/addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bcfkhh3ljqe6g" +path="res://.godot/imported/portraits_eyes.png-85a136d9d6222fce82a8393caf9c6ace.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/portraits/portraits_eyes.png" +dest_files=["res://.godot/imported/portraits_eyes.png-85a136d9d6222fce82a8393caf9c6ace.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png b/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png new file mode 100644 index 0000000..4f47267 Binary files /dev/null and b/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png differ diff --git a/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png.import b/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png.import new file mode 100644 index 0000000..51d2072 --- /dev/null +++ b/addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqkxfvi24xl6c" +path="res://.godot/imported/portraits_mouth.png-ce83a7fccf995934a71e41916fa17ab7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ai_assistant_hub/graphics/portraits/portraits_mouth.png" +dest_files=["res://.godot/imported/portraits_mouth.png-ce83a7fccf995934a71e41916fa17ab7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ai_assistant_hub/llm_apis/llm_interface.gd b/addons/ai_assistant_hub/llm_apis/llm_interface.gd new file mode 100644 index 0000000..8b6a375 --- /dev/null +++ b/addons/ai_assistant_hub/llm_apis/llm_interface.gd @@ -0,0 +1,35 @@ +@tool +class_name LLMInterface +# The intention of this class is to serve as a base class for any LLM API +# to be implemented in this plugin. It is mainly to have a clear definition +# of what properties or functions should be used by other classes. + +const INVALID_RESPONSE := "[INVALID_RESPONSE]" + +var model: String +var override_temperature: bool +var temperature: float + + +func get_full_response(body:PackedByteArray) -> Dictionary: + var json := JSON.new() + json.parse(body.get_string_from_utf8()) + return json.get_data() + + +## All methods below should be overriden by child classes, see for example OllamaAPI + +func send_get_models_request(http_request:HTTPRequest) -> bool: + return false + + +func read_models_response(body:PackedByteArray) -> Array[String]: + return [INVALID_RESPONSE] + + +func send_chat_request(http_request:HTTPRequest, content:Array) -> bool: + return false + + +func read_response(body:PackedByteArray) -> String: + return INVALID_RESPONSE diff --git a/addons/ai_assistant_hub/llm_apis/llm_interface.gd.uid b/addons/ai_assistant_hub/llm_apis/llm_interface.gd.uid new file mode 100644 index 0000000..9b78083 --- /dev/null +++ b/addons/ai_assistant_hub/llm_apis/llm_interface.gd.uid @@ -0,0 +1 @@ +uid://dcapnr8anqxr1 diff --git a/addons/ai_assistant_hub/llm_apis/ollama_api.gd b/addons/ai_assistant_hub/llm_apis/ollama_api.gd new file mode 100644 index 0000000..e5b2540 --- /dev/null +++ b/addons/ai_assistant_hub/llm_apis/ollama_api.gd @@ -0,0 +1,67 @@ +@tool +class_name OllamaAPI +extends LLMInterface + +const HEADERS := ["Content-Type: application/json"] + +func send_get_models_request(http_request:HTTPRequest) -> bool: + var url:String = "%s/api/tags" % ProjectSettings.get_setting(AIHubPlugin.CONFIG_BASE_URL) + #print("Calling: %s" % url) + var error = http_request.request(url, HEADERS, HTTPClient.METHOD_GET) + if error != OK: + push_error("Something when wrong with last AI API call: %s" % url) + return false + return true + + +func read_models_response(body:PackedByteArray) -> Array[String]: + var json := JSON.new() + json.parse(body.get_string_from_utf8()) + var response := json.get_data() + if response.has("models"): + var model_names:Array[String] = [] + for entry in response.models: + model_names.append(entry.model) + model_names.sort() + return model_names + else: + return [INVALID_RESPONSE] + + +func send_chat_request(http_request:HTTPRequest, content:Array) -> bool: + if model.is_empty(): + push_error("ERROR: You need to set an AI model for this assistant type.") + return false + + var body_dict := { + "messages": content, + "stream": false, + "model": model + } + + if override_temperature: + body_dict["options"] = { "temperature": temperature } + + var body := JSON.new().stringify(body_dict) + + var url = _get_chat_url() + #print("calling %s with body: %s" % [url, body]) + var error = http_request.request(url, HEADERS, HTTPClient.METHOD_POST, body) + if error != OK: + push_error("Something when wrong with last AI API call.\nURL: %s\nBody:\n%s" % [url, body]) + return false + return true + + +func read_response(body) -> String: + var json := JSON.new() + json.parse(body.get_string_from_utf8()) + var response := json.get_data() + if response.has("message"): + return response.message.content + else: + return LLMInterface.INVALID_RESPONSE + + +func _get_chat_url() -> String: + return "%s/api/chat" % ProjectSettings.get_setting(AIHubPlugin.CONFIG_BASE_URL) diff --git a/addons/ai_assistant_hub/llm_apis/ollama_api.gd.uid b/addons/ai_assistant_hub/llm_apis/ollama_api.gd.uid new file mode 100644 index 0000000..7884038 --- /dev/null +++ b/addons/ai_assistant_hub/llm_apis/ollama_api.gd.uid @@ -0,0 +1 @@ +uid://bqxt4y1tg8a1p diff --git a/addons/ai_assistant_hub/new_ai_assistant_button.gd b/addons/ai_assistant_hub/new_ai_assistant_button.gd new file mode 100644 index 0000000..6d39eff --- /dev/null +++ b/addons/ai_assistant_hub/new_ai_assistant_button.gd @@ -0,0 +1,50 @@ +@tool +class_name NewAIAssistantButton +extends Button + +signal chat_created(chat: AIChat, assistant_type:AIAssistantResource) + +const AI_CHAT = preload("res://addons/ai_assistant_hub/ai_chat.tscn") +const NAMES: Array[String] = ["Ace", "Bean", "Boss", "Bubs", "Bugger", "Shushi", "Chicky", "Crash", +"Cub", "Daisy", "Dixie", "Doofus", "Doozy", "Dudedorf", "Fuzz", "Gabby", "Gizmo", "Goose", "Hiccup", +"Hobo", "Jinx", "Kix", "Lulu", "Munch", "Nuppy", "Ollie", "Ookie", "Pud", "Punchme", "Pup", +"Rascal", "Rusty", "Sausy", "Sparky", "Squirro", "Stubby", "Sugar", "Taco", "Tank", "Tater", "Ted", +"Titus", "Toady", "Tweedle", "Winky", "Zippy", "Luffy", "Zoro", "Chopper", "Usop", "Nami", "Robin", +"Juan", "Paco", "Pedro", "Goku", "Vegeta", "Trunks", "Piccolo", "Gohan", "Krillin", "Tenshinhan", +"Bulma", "Oolong", "Yamcha", "Pika", "Buu", "Freezer", "Cell", "L", "Light", "Ryuk", "Misa", "Near", +"Mello", "Rem", "Eren", "Mike", "Armin", "Hange", "Levi", "Eva", "Erwin", "Conny", "Mikasa", +"Naruto", "Sasuke", "Kakashi", "Tsunade", "Iruka", "Sakura", "Shikamaru", "Obito", "Itadori", +"Fushiguro", "Nobara", "Gojo", "Geto", "Sukuna", "Spike", "Jet", "Faye", "Ed", "Ein", "Julia", +"Jotaro", "Joestar", "Jolyne", "Jonathan", "Giorno", "Dio", "Polnareff", "Kakyoin", "Saitama", +"Genos", "Tenma", "Shinji", "Asuka", "Rei", "Misato", "Tanjiro", "Nezuko", "Inosuke", "Zenitsu" ] + +static var available_names: Array[String] + +var _plugin:EditorPlugin +var _data: AIAssistantResource +var _chat: AIChat +var _name: String + + +func initialize(plugin:EditorPlugin, assistant_resource: AIAssistantResource) -> void: + _plugin = plugin + _data = assistant_resource + text = _data.type_name + icon = _data.type_icon + if text.is_empty() and icon == null: + text = _data.resource_path.get_file().trim_suffix(".tres") + + +func _on_pressed() -> void: + if available_names == null or available_names.size() == 0: + available_names = NAMES.duplicate() + available_names.shuffle() + _name = available_names.pop_back() + + _chat = AI_CHAT.instantiate() + _chat.initialize(_plugin, _data, _name) + if _data.type_icon == null: + _chat.name = "%s [%s]" % [text, _name] + else: + _chat.name = "%s" % [_name] + chat_created.emit(_chat, _data) diff --git a/addons/ai_assistant_hub/new_ai_assistant_button.gd.uid b/addons/ai_assistant_hub/new_ai_assistant_button.gd.uid new file mode 100644 index 0000000..7a87140 --- /dev/null +++ b/addons/ai_assistant_hub/new_ai_assistant_button.gd.uid @@ -0,0 +1 @@ +uid://n201ldaaytxq diff --git a/addons/ai_assistant_hub/new_ai_assistant_button.tscn b/addons/ai_assistant_hub/new_ai_assistant_button.tscn new file mode 100644 index 0000000..f2f44d9 --- /dev/null +++ b/addons/ai_assistant_hub/new_ai_assistant_button.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://dgrssjr4dn6kx"] + +[ext_resource type="Script" path="res://addons/ai_assistant_hub/new_ai_assistant_button.gd" id="1_i6vt6"] + +[node name="NewAIAssistantButton" type="Button"] +auto_translate_mode = 1 +custom_minimum_size = Vector2(48, 32) +text = "Coder" +script = ExtResource("1_i6vt6") + +[connection signal="pressed" from="." to="." method="_on_pressed"] diff --git a/addons/ai_assistant_hub/plugin.cfg b/addons/ai_assistant_hub/plugin.cfg new file mode 100644 index 0000000..c2ef98c --- /dev/null +++ b/addons/ai_assistant_hub/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="AI Assistant Hub" +description="" +author="Flamx Games" +version="1.0.0" +script="ai_hub_plugin.gd" diff --git a/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd b/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd new file mode 100644 index 0000000..32d1393 --- /dev/null +++ b/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd @@ -0,0 +1,28 @@ +class_name AIQuickPromptResource +extends Resource + +enum ResponseTarget { Chat, CodeEditor, OnlyCodeToCodeEditor } +enum CodePlacement { BeforeSelection, AfterSelection, ReplaceSelection } + +## This name will be used in the Quick Prompt button. +## Leave it blank for an icon-only display. +@export var action_name: String + +## Tell the assistant what you want it to do. +## Use `{CODE}` to insert the code currently selected in the editor. +## Use `{CHAT}` to include the current content of the text prompt. +@export_multiline var action_prompt: String + +## Optional icon for the button displayed in the chat window for this Quick Prompt. +@export var icon: Texture2D + +## Indicates if the answer should be written in the chat or in the code editor. +@export var response_target: ResponseTarget + +## Indicates in what part of the Code Editor you want to put the answer (ignored when not writing to the Code Editor). +@export var code_placement: CodePlacement + +## Ensures the assistant's response is returned as a GDScript comment. +## If required, adds a # to each line and keeps lines around 80 characters long. +## This is useful to request the generation of inline documentation. +@export var format_response_as_comment: bool diff --git a/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd.uid b/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd.uid new file mode 100644 index 0000000..fdba5e8 --- /dev/null +++ b/addons/ai_assistant_hub/quick_prompts/ai_quick_prompt_resource.gd.uid @@ -0,0 +1 @@ +uid://gfjw8kfxgsnh diff --git a/addons/ai_assistant_hub/sounds/ai_error_cancel.wav b/addons/ai_assistant_hub/sounds/ai_error_cancel.wav new file mode 100644 index 0000000..14909f9 Binary files /dev/null and b/addons/ai_assistant_hub/sounds/ai_error_cancel.wav differ diff --git a/addons/ai_assistant_hub/sounds/ai_error_cancel.wav.import b/addons/ai_assistant_hub/sounds/ai_error_cancel.wav.import new file mode 100644 index 0000000..9bd3f05 --- /dev/null +++ b/addons/ai_assistant_hub/sounds/ai_error_cancel.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b2lftlbs848c4" +path="res://.godot/imported/ai_error_cancel.wav-f939aafd44dd51184a77c7724bab89a2.sample" + +[deps] + +source_file="res://addons/ai_assistant_hub/sounds/ai_error_cancel.wav" +dest_files=["res://.godot/imported/ai_error_cancel.wav-f939aafd44dd51184a77c7724bab89a2.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/ai_assistant_hub/sounds/ai_replied.wav b/addons/ai_assistant_hub/sounds/ai_replied.wav new file mode 100644 index 0000000..58734a0 Binary files /dev/null and b/addons/ai_assistant_hub/sounds/ai_replied.wav differ diff --git a/addons/ai_assistant_hub/sounds/ai_replied.wav.import b/addons/ai_assistant_hub/sounds/ai_replied.wav.import new file mode 100644 index 0000000..98e7cf5 --- /dev/null +++ b/addons/ai_assistant_hub/sounds/ai_replied.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dk1yumltykf6x" +path="res://.godot/imported/ai_replied.wav-ab3f7a4b5ea5f78d32ee362c9089f547.sample" + +[deps] + +source_file="res://addons/ai_assistant_hub/sounds/ai_replied.wav" +dest_files=["res://.godot/imported/ai_replied.wav-ab3f7a4b5ea5f78d32ee362c9089f547.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd b/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd new file mode 100644 index 0000000..6cf8dc9 --- /dev/null +++ b/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd @@ -0,0 +1,38 @@ +@tool +class_name AssistantToolCodeWriter + +var _plugin:EditorPlugin +var _code_selector:AssistantToolSelection + + +func _init(plugin:EditorPlugin, code_selector:AssistantToolSelection) -> void: + _plugin = plugin + _code_selector = code_selector + + +func write_to_code_editor(text_answer:String, code_placement:AIQuickPromptResource.CodePlacement) -> bool: + var select_success := _code_selector.back_to_selection() + if select_success: + var script_editor := _plugin.get_editor_interface().get_script_editor().get_current_editor() + var code_editor = script_editor.get_base_editor() + var start_line:int = code_editor.get_selection_from_line() + var end_line:int = code_editor.get_selection_to_line() + code_editor.set_caret_line(start_line) + match code_placement: + AIQuickPromptResource.CodePlacement.BeforeSelection: + code_editor.insert_line_at(start_line, text_answer) + AIQuickPromptResource.CodePlacement.AfterSelection: + if end_line == code_editor.get_line_count() - 1: #it is at the end of the editor + code_editor.text += "\n%s" % text_answer + else: + code_editor.insert_line_at(end_line + 1, text_answer) + AIQuickPromptResource.CodePlacement.ReplaceSelection: + code_editor.delete_selection() + text_answer = text_answer.trim_suffix("\n") + code_editor.insert_text_at_caret(text_answer) + _: + push_error("Unexpected Quick Prompt code placement value: %s" % code_placement) + code_editor.scroll_vertical = code_editor.get_scroll_pos_for_line(start_line) - 10 + code_editor.select(start_line, 0, start_line, 0) + return true + return false diff --git a/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd.uid b/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd.uid new file mode 100644 index 0000000..be989d4 --- /dev/null +++ b/addons/ai_assistant_hub/tools/assistant_tool_code_writer.gd.uid @@ -0,0 +1 @@ +uid://c224dpt2c7cc diff --git a/addons/ai_assistant_hub/tools/assistant_tool_selection.gd b/addons/ai_assistant_hub/tools/assistant_tool_selection.gd new file mode 100644 index 0000000..95b8e46 --- /dev/null +++ b/addons/ai_assistant_hub/tools/assistant_tool_selection.gd @@ -0,0 +1,113 @@ +@tool +class_name AssistantToolSelection + +var _plugin:EditorPlugin +var _code_editor:TextEdit +var _selected_script: Script +var _selected_code: String +var _selected_code_first_line: String +var _selected_code_last_line: String +var _selected_code_line_start: int +var _selected_code_line_start_column: int +var _selected_code_line_end: int +var _selected_code_line_end_column: int + + +func _init(plugin:EditorPlugin) -> void: + _plugin = plugin + + +func get_selection() -> String: + var script_editor:= _plugin.get_editor_interface().get_script_editor() + _code_editor = script_editor.get_current_editor().get_base_editor() + + _selected_script = script_editor.get_current_script() + _selected_code = _code_editor.get_selected_text() + if _selected_code.strip_edges(true, true).length() == 0: + var curr_line = _code_editor.get_caret_line() + _code_editor.select(curr_line, 0, curr_line, line(curr_line).length()) + _selected_code = _code_editor.get_selected_text().strip_edges(true, true) + + if not _selected_code.is_empty(): + #Make sure we don't start or end with empty lines, as that makes difficult to find the code again + var first_not_empty = line(first_line()).strip_edges(true, false) + while first_not_empty.is_empty() and first_line() + 1 <= last_line(): + _code_editor.select(first_line() + 1, 0, last_line(), last_column()) + first_not_empty = line(first_line()).strip_edges(true, false) + + var last_not_empty = line(last_line()).strip_edges(false, true) + while last_not_empty.is_empty() and last_line() - 1 >= first_line(): + _code_editor.select(first_line(), first_column(), last_line() - 1, line(last_line()-1).length()) + last_not_empty = line(last_line()).strip_edges(false, true) + + _selected_code = _code_editor.get_selected_text() + _selected_code_line_start = first_line() + _selected_code_line_start_column = first_column() + _selected_code_line_end = last_line() + _selected_code_line_end_column = last_column() + _selected_code_first_line = line(_selected_code_line_start) + _selected_code_last_line = line(_selected_code_line_end) + return _selected_code + + +func line(i:int) -> String: + return _code_editor.get_line(i) + + +func first_line() -> int: + return _code_editor.get_selection_from_line() + + +func first_column() -> int: + return _code_editor.get_selection_from_column() + + +func last_line() -> int: + return _code_editor.get_selection_to_line() + + +func last_column() -> int: + return _code_editor.get_selection_to_column() + + +func forget_selection() -> void: + _selected_script = null + + +# Attempts to select the original line range previously used and returns true on success. +func back_to_selection() -> bool: + if _selected_code.is_empty(): + return false + + #double check the script to edit is still open, if it's not open it + var editor_interface:EditorInterface = _plugin.get_editor_interface() + var curr_script:Script = editor_interface.get_script_editor().get_current_script() + if curr_script != _selected_script: + #print("The script for the original request was: %s" % _selected_script.resource_path) + #print("The script currently opened is: %s" % curr_script.resource_path) + print("Opening %s" % _selected_script.resource_path) + editor_interface.edit_script(_selected_script) + forget_selection() + + var script_editor:= _plugin.get_editor_interface().get_script_editor() + var code_editor:TextEdit = script_editor.get_current_editor().get_base_editor() + var curr_selection: String = code_editor.get_selected_text() + if _selected_code != curr_selection: + print("The selection changed. Finding: %s" % _selected_code_first_line) + var search_start:Vector2i = code_editor.search(_selected_code_first_line, TextEdit.SearchFlags.SEARCH_MATCH_CASE, 0, 0) + if search_start.x == -1: + return false + else: + #print("First line found. Finding: %s" % _selected_code_last_line) + var original_line_diff = _selected_code_line_end - _selected_code_line_start + var search_end:Vector2i = code_editor.search(_selected_code_last_line, TextEdit.SearchFlags.SEARCH_MATCH_CASE, search_start.y + original_line_diff, 0) + if search_end.x == -1: + return false + else: + #print("Last line found.") + var line_diff = search_end.y - search_start.y + if original_line_diff == line_diff: + code_editor.select(search_start.y, search_start.x, search_end.y, _selected_code_line_end_column) + else: + return false + return true diff --git a/addons/ai_assistant_hub/tools/assistant_tool_selection.gd.uid b/addons/ai_assistant_hub/tools/assistant_tool_selection.gd.uid new file mode 100644 index 0000000..1dc890e --- /dev/null +++ b/addons/ai_assistant_hub/tools/assistant_tool_selection.gd.uid @@ -0,0 +1 @@ +uid://c1nam6n2xd5as diff --git a/addons/code_time/plugin.cfg b/addons/code_time/plugin.cfg new file mode 100644 index 0000000..c57aab5 --- /dev/null +++ b/addons/code_time/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="CodeTime" +description="This plugin adds a timer to the script editor so you can see how many hours you spend on coding." +author="Dexter" +version="0.1" +script="plugin.gd" diff --git a/addons/code_time/plugin.gd b/addons/code_time/plugin.gd new file mode 100644 index 0000000..f1fd27f --- /dev/null +++ b/addons/code_time/plugin.gd @@ -0,0 +1,53 @@ +@tool +extends EditorPlugin + +const TimerPanel = preload("res://addons/code_time/timer_panel.gd") +const TIMER_PANEL = preload("res://addons/code_time/timer_panel.tscn") + +var timer_panel: TimerPanel +var menu_bar: Control +var item_list: ItemList +var base_editor: CodeEdit: + set(value): + if is_instance_valid(base_editor): + if base_editor.is_connected("text_changed", _on_base_editor_text_changed): + base_editor.disconnect("text_changed", _on_base_editor_text_changed) + + base_editor = value + if is_instance_valid(base_editor): + base_editor.connect("text_changed", _on_base_editor_text_changed) + +func _enter_tree() -> void: + main_screen_changed.connect(_on_main_screen_changed) + +func _ready() -> void: + timer_panel = TIMER_PANEL.instantiate() + menu_bar = EditorInterface.get_script_editor().get_children()[0].get_children()[0] + menu_bar.add_child(timer_panel) + menu_bar.move_child(timer_panel, 7) + item_list = EditorInterface.get_script_editor().get_children()[0].get_children()[1].get_children()[0].get_children()[0].get_children()[1] + item_list.connect("item_selected", _on_item_list_selected) + +func _exit_tree() -> void: + menu_bar.remove_child(timer_panel) + timer_panel = null + menu_bar = null + item_list = null + base_editor = null + +func get_base_editor() -> CodeEdit: + return EditorInterface.get_script_editor().get_current_editor().get_base_editor() + +func _on_main_screen_changed(screen_name: String) -> void: + if screen_name == "Script": + base_editor = get_base_editor() + +func _on_base_editor_text_changed() -> void: + if timer_panel.timer.is_stopped(): + timer_panel.timer.start() + + if timer_panel.timer.paused: + timer_panel.timer.paused = false + +func _on_item_list_selected(index: int) -> void: + base_editor = get_base_editor() diff --git a/addons/code_time/plugin.gd.uid b/addons/code_time/plugin.gd.uid new file mode 100644 index 0000000..22916f1 --- /dev/null +++ b/addons/code_time/plugin.gd.uid @@ -0,0 +1 @@ +uid://cbpkqlo4v57ty diff --git a/addons/code_time/timer_panel.gd b/addons/code_time/timer_panel.gd new file mode 100644 index 0000000..e3f3940 --- /dev/null +++ b/addons/code_time/timer_panel.gd @@ -0,0 +1,19 @@ +@tool +extends Control + +var time: float = 0 + +@onready var time_label: Label = %TimeLabel +@onready var timer: Timer = %Timer + +func _process(delta: float) -> void: + timer.time_left + +func _on_timer_timeout() -> void: + time += 0.1 + var _time: int = time + var h: int = _time / 3_600 + var m: int = (_time % 3_600) / 60 + var s: int = (_time % 3_600) % 60 + time_label.text = "%02d:%02d:%02d" % [h, m, s] + timer.paused = true diff --git a/addons/code_time/timer_panel.gd.uid b/addons/code_time/timer_panel.gd.uid new file mode 100644 index 0000000..9d8f963 --- /dev/null +++ b/addons/code_time/timer_panel.gd.uid @@ -0,0 +1 @@ +uid://cuxddsdq6yapf diff --git a/addons/code_time/timer_panel.tscn b/addons/code_time/timer_panel.tscn new file mode 100644 index 0000000..f845b15 --- /dev/null +++ b/addons/code_time/timer_panel.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=2 format=3 uid="uid://bisjbnqhm460p"] + +[ext_resource type="Script" path="res://addons/code_time/timer_panel.gd" id="1_ecqis"] + +[node name="TimerPanel" type="PanelContainer"] +custom_minimum_size = Vector2(250, 0) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ecqis") + +[node name="TimeLabel" type="Label" parent="."] +unique_name_in_owner = true +layout_mode = 2 +text = "00:00:00" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Timer" type="Timer" parent="."] +unique_name_in_owner = true +wait_time = 0.1 + +[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] diff --git a/addons/ridiculous_coding/GravityBold8.ttf b/addons/ridiculous_coding/GravityBold8.ttf new file mode 100644 index 0000000..5e119db Binary files /dev/null and b/addons/ridiculous_coding/GravityBold8.ttf differ diff --git a/addons/ridiculous_coding/GravityBold8.ttf.import b/addons/ridiculous_coding/GravityBold8.ttf.import new file mode 100644 index 0000000..8b037e9 --- /dev/null +++ b/addons/ridiculous_coding/GravityBold8.ttf.import @@ -0,0 +1,40 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://coiaqb8xwuaic" +path="res://.godot/imported/GravityBold8.ttf-e67aa3e4866d0a81519c9b866a8f924b.fontdata" + +[deps] + +source_file="res://addons/ridiculous_coding/GravityBold8.ttf" +dest_files=["res://.godot/imported/GravityBold8.ttf-e67aa3e4866d0a81519c9b866a8f924b.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0) +}] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/ridiculous_coding/blip.gd b/addons/ridiculous_coding/blip.gd new file mode 100644 index 0000000..dde337d --- /dev/null +++ b/addons/ridiculous_coding/blip.gd @@ -0,0 +1,36 @@ +@tool +extends Node2D + +var destroy: bool = false +var last_key: String = "" +var pitch_increase: float = 0.0 +var sound: bool = true +var blips: bool = true + +@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer +@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D +@onready var animated_player: AnimationPlayer = $AnimationPlayer +@onready var gpu_particle_2d: GPUParticles2D = $GPUParticles2D +@onready var timer: Timer = $Timer +@onready var label: Label = $Label + + +func _ready(): + if sound: + audio_stream_player.pitch_scale = 1.0 + pitch_increase * 0.01 + audio_stream_player.play() + + if blips: + animated_sprite_2d.frame = 0 + animated_sprite_2d.play("default") + animated_player.play("default") + gpu_particle_2d.emitting = true + + timer.start() + label.text = last_key + label.modulate = Color(randf_range(0,2), randf_range(0,2), randf_range(0,2)) + + +func _on_Timer_timeout(): + if destroy: + queue_free() diff --git a/addons/ridiculous_coding/blip.gd.uid b/addons/ridiculous_coding/blip.gd.uid new file mode 100644 index 0000000..63ae383 --- /dev/null +++ b/addons/ridiculous_coding/blip.gd.uid @@ -0,0 +1 @@ +uid://b1hwt8t7jrjq2 diff --git a/addons/ridiculous_coding/blip.png b/addons/ridiculous_coding/blip.png new file mode 100644 index 0000000..9030639 Binary files /dev/null and b/addons/ridiculous_coding/blip.png differ diff --git a/addons/ridiculous_coding/blip.png.import b/addons/ridiculous_coding/blip.png.import new file mode 100644 index 0000000..ac14267 --- /dev/null +++ b/addons/ridiculous_coding/blip.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d1tio2ceqgm7m" +path="res://.godot/imported/blip.png-7449238b2e7dcd337fbb9eec8c7b957e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ridiculous_coding/blip.png" +dest_files=["res://.godot/imported/blip.png-7449238b2e7dcd337fbb9eec8c7b957e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ridiculous_coding/blip.tscn b/addons/ridiculous_coding/blip.tscn new file mode 100644 index 0000000..1c98cf2 --- /dev/null +++ b/addons/ridiculous_coding/blip.tscn @@ -0,0 +1,174 @@ +[gd_scene load_steps=19 format=3 uid="uid://c0fhho0dp1svt"] + +[ext_resource type="Script" path="res://addons/ridiculous_coding/blip.gd" id="1_tp8nq"] +[ext_resource type="Texture2D" uid="uid://d1tio2ceqgm7m" path="res://addons/ridiculous_coding/blip.png" id="2_kj7um"] +[ext_resource type="AudioStream" uid="uid://b2ood3lkcgpqb" path="res://addons/ridiculous_coding/blip.wav" id="3_xg6qd"] +[ext_resource type="FontFile" uid="uid://bvwnnnja1ur2i" path="res://addons/ridiculous_coding/font.tres" id="4_ullf3"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_hp4ed"] +atlas = ExtResource("2_kj7um") +region = Rect2(192, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_hcxxe"] +atlas = ExtResource("2_kj7um") +region = Rect2(160, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3w7u8"] +atlas = ExtResource("2_kj7um") +region = Rect2(128, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qo2pv"] +atlas = ExtResource("2_kj7um") +region = Rect2(96, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_55mlh"] +atlas = ExtResource("2_kj7um") +region = Rect2(64, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3eube"] +atlas = ExtResource("2_kj7um") +region = Rect2(32, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jwwsh"] +atlas = ExtResource("2_kj7um") +region = Rect2(0, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ye4cv"] +atlas = ExtResource("2_kj7um") +region = Rect2(224, 0, 32, 32) + +[sub_resource type="SpriteFrames" id="SpriteFrames_g4ki7"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_hp4ed") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_hcxxe") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3w7u8") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qo2pv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_55mlh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3eube") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jwwsh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ye4cv") +}], +"loop": false, +"name": &"default", +"speed": 24.0 +}] + +[sub_resource type="Animation" id="Animation_u2m4c"] +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("AnimatedSprite2D:scale") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5), +"transitions": PackedFloat32Array(-2, 1), +"update": 0, +"values": [Vector2(1, 1), Vector2(5, 5)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Label:scale") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(-2, 1), +"update": 0, +"values": [Vector2(1, 1), Vector2(2, 2)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Label:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(-2, 1), +"update": 0, +"values": [Vector2(-35, -32), Vector2(-35, -70)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_8epm6"] +_data = { +"default": SubResource("Animation_u2m4c") +} + +[sub_resource type="Gradient" id="Gradient_mplh3"] +offsets = PackedFloat32Array(0, 0.350746, 1) +colors = PackedColorArray(0.160156, 0.783478, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_gu7qo"] +gradient = SubResource("Gradient_mplh3") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_5k50n"] +lifetime_randomness = 0.5 +spread = 180.0 +gravity = Vector3(0, 0, 0) +color_ramp = SubResource("GradientTexture2D_gu7qo") + +[node name="Node2D" type="Node2D"] +texture_filter = 1 +script = ExtResource("1_tp8nq") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +scale = Vector2(5, 5) +sprite_frames = SubResource("SpriteFrames_g4ki7") +frame = 7 +frame_progress = 1.0 + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("3_xg6qd") +volume_db = -12.0 +autoplay = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_8epm6") +} +autoplay = "default" + +[node name="GPUParticles2D" type="GPUParticles2D" parent="."] +emitting = false +amount = 50 +process_material = SubResource("ParticleProcessMaterial_5k50n") +lifetime = 0.5 +one_shot = true +explosiveness = 1.0 + +[node name="Timer" type="Timer" parent="."] +one_shot = true + +[node name="Label" type="Label" parent="."] +modulate = Color(1.88557, 1.35563, 0.609976, 1) +texture_filter = 1 +offset_left = -35.0 +offset_top = -70.0 +offset_right = 35.0 +offset_bottom = -47.0 +scale = Vector2(2, 2) +pivot_offset = Vector2(35, 8) +theme_override_fonts/font = ExtResource("4_ullf3") +theme_override_font_sizes/font_size = 16 +uppercase = true + +[connection signal="animation_finished" from="AnimatedSprite2D" to="." method="_on_AnimatedSprite1_animation_finished"] +[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] diff --git a/addons/ridiculous_coding/blip.wav b/addons/ridiculous_coding/blip.wav new file mode 100644 index 0000000..b586dde Binary files /dev/null and b/addons/ridiculous_coding/blip.wav differ diff --git a/addons/ridiculous_coding/blip.wav.import b/addons/ridiculous_coding/blip.wav.import new file mode 100644 index 0000000..d906f85 --- /dev/null +++ b/addons/ridiculous_coding/blip.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b2ood3lkcgpqb" +path="res://.godot/imported/blip.wav-b30aed86048a8d09a6b99458d730bd48.sample" + +[deps] + +source_file="res://addons/ridiculous_coding/blip.wav" +dest_files=["res://.godot/imported/blip.wav-b30aed86048a8d09a6b99458d730bd48.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/ridiculous_coding/boom.gd b/addons/ridiculous_coding/boom.gd new file mode 100644 index 0000000..dcb0236 --- /dev/null +++ b/addons/ridiculous_coding/boom.gd @@ -0,0 +1,29 @@ +@tool +extends Node2D + +var destroy = false +var last_key = "" +var sound = true + +@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer +@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D +@onready var animation_player: AnimationPlayer = $AnimationPlayer +@onready var timer: Timer = $Timer +@onready var label: Label = $Label + + +func _ready(): + if sound: + audio_stream_player.play() + + animated_sprite_2d.frame = 0 + animated_sprite_2d.play("default") + animation_player.play("default") + timer.start() + label.text = last_key + label.modulate = Color(randf_range(0,2), randf_range(0,2), randf_range(0,2)) + + +func _on_Timer_timeout(): + if destroy: + queue_free() diff --git a/addons/ridiculous_coding/boom.gd.uid b/addons/ridiculous_coding/boom.gd.uid new file mode 100644 index 0000000..c48a0b1 --- /dev/null +++ b/addons/ridiculous_coding/boom.gd.uid @@ -0,0 +1 @@ +uid://dnudpbbop16aj diff --git a/addons/ridiculous_coding/boom.png b/addons/ridiculous_coding/boom.png new file mode 100644 index 0000000..7e0ab90 Binary files /dev/null and b/addons/ridiculous_coding/boom.png differ diff --git a/addons/ridiculous_coding/boom.png.import b/addons/ridiculous_coding/boom.png.import new file mode 100644 index 0000000..86cbdfc --- /dev/null +++ b/addons/ridiculous_coding/boom.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl75e74oom1i3" +path="res://.godot/imported/boom.png-8824a5a1f762eb053b72081ff54ed1ba.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ridiculous_coding/boom.png" +dest_files=["res://.godot/imported/boom.png-8824a5a1f762eb053b72081ff54ed1ba.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ridiculous_coding/boom.tscn b/addons/ridiculous_coding/boom.tscn new file mode 100644 index 0000000..4a87c10 --- /dev/null +++ b/addons/ridiculous_coding/boom.tscn @@ -0,0 +1,135 @@ +[gd_scene load_steps=15 format=3 uid="uid://c4rdv4wkukc5g"] + +[ext_resource type="Texture2D" uid="uid://dl75e74oom1i3" path="res://addons/ridiculous_coding/boom.png" id="1"] +[ext_resource type="AudioStream" uid="uid://b6841f7osi4rh" path="res://addons/ridiculous_coding/boom.wav" id="2"] +[ext_resource type="Script" path="res://addons/ridiculous_coding/boom.gd" id="4"] +[ext_resource type="FontFile" uid="uid://bvwnnnja1ur2i" path="res://addons/ridiculous_coding/font.tres" id="5"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_g3t5t"] +atlas = ExtResource("1") +region = Rect2(0, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_4skxo"] +atlas = ExtResource("1") +region = Rect2(128, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ccvbr"] +atlas = ExtResource("1") +region = Rect2(256, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0ojec"] +atlas = ExtResource("1") +region = Rect2(384, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1koxs"] +atlas = ExtResource("1") +region = Rect2(512, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_g5t7n"] +atlas = ExtResource("1") +region = Rect2(640, 0, 128, 128) + +[sub_resource type="AtlasTexture" id="AtlasTexture_27xno"] +atlas = ExtResource("1") +region = Rect2(0, 128, 128, 128) + +[sub_resource type="SpriteFrames" id="SpriteFrames_ld5tu"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_g3t5t") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_4skxo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ccvbr") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0ojec") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1koxs") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_g5t7n") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_27xno") +}], +"loop": false, +"name": &"default", +"speed": 24.0 +}] + +[sub_resource type="Animation" id="Animation_pxf6h"] +resource_name = "default" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Label:scale") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(-2, 1), +"update": 0, +"values": [Vector2(1, 1), Vector2(2, 2)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Label:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(-2, 1), +"update": 0, +"values": [Vector2(-35, -32), Vector2(-35, -70)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_ocb2c"] +_data = { +"default": SubResource("Animation_pxf6h") +} + +[node name="Node2D" type="Node2D"] +texture_filter = 1 +script = ExtResource("4") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +scale = Vector2(0.5, 0.5) +sprite_frames = SubResource("SpriteFrames_ld5tu") +frame = 6 +frame_progress = 1.0 + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("2") +volume_db = -26.0 +autoplay = true + +[node name="Timer" type="Timer" parent="."] +one_shot = true + +[node name="Label" type="Label" parent="."] +modulate = Color(0.852828, 1.64201, 1.90577, 1) +texture_filter = 1 +offset_left = -35.0 +offset_top = -70.0 +offset_right = 35.0 +offset_bottom = -47.0 +scale = Vector2(2, 2) +pivot_offset = Vector2(35, 8) +theme_override_fonts/font = ExtResource("5") +theme_override_font_sizes/font_size = 16 +uppercase = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_ocb2c") +} +autoplay = "default" + +[connection signal="animation_finished" from="AnimatedSprite2D" to="." method="_on_AnimatedSprite_animation_finished"] +[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] diff --git a/addons/ridiculous_coding/boom.wav b/addons/ridiculous_coding/boom.wav new file mode 100644 index 0000000..bd554d3 Binary files /dev/null and b/addons/ridiculous_coding/boom.wav differ diff --git a/addons/ridiculous_coding/boom.wav.import b/addons/ridiculous_coding/boom.wav.import new file mode 100644 index 0000000..b6788a2 --- /dev/null +++ b/addons/ridiculous_coding/boom.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b6841f7osi4rh" +path="res://.godot/imported/boom.wav-8be8413d6a593a69a88c5aabb324258d.sample" + +[deps] + +source_file="res://addons/ridiculous_coding/boom.wav" +dest_files=["res://.godot/imported/boom.wav-8be8413d6a593a69a88c5aabb324258d.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/ridiculous_coding/dock.gd b/addons/ridiculous_coding/dock.gd new file mode 100644 index 0000000..30cb1d4 --- /dev/null +++ b/addons/ridiculous_coding/dock.gd @@ -0,0 +1,168 @@ +@tool +extends Control + +const BASE_XP: int = 50 +const STATS_FILE: String = "user://ridiculous_xp.ini" + +var explosions: bool = true +var blips: bool = true +var chars: bool = true +var shake: bool = true +var sound: bool = true +var fireworks: bool = true +var xp: int = 0 +var xp_next: int = 2*BASE_XP +var level: int = 1 +var stats: ConfigFile = ConfigFile.new() + +@onready var explosion_checkbox: CheckButton = $VBoxContainer/GridContainer/explosionCheckbox +@onready var blip_checkbox: CheckButton = $VBoxContainer/GridContainer/blipCheckbox +@onready var chars_checkbox: CheckButton = $VBoxContainer/GridContainer/charsCheckbox +@onready var shake_checkbox: CheckButton = $VBoxContainer/GridContainer/shakeCheckbox +@onready var sound_checkbox: CheckButton = $VBoxContainer/GridContainer/soundCheckbox +@onready var fireworks_checkbox: CheckButton = $VBoxContainer/GridContainer/fireworksCheckbox +@onready var progress: TextureProgressBar = $VBoxContainer/XP/ProgressBar +@onready var sfx_fireworks: AudioStreamPlayer = $VBoxContainer/XP/ProgressBar/sfxFireworks +@onready var fireworks_timer: Timer = $VBoxContainer/XP/ProgressBar/fireworksTimer +@onready var fire_particles_one: GPUParticles2D = $VBoxContainer/XP/ProgressBar/fire1/GPUParticles2D +@onready var fire_particles_two: GPUParticles2D = $VBoxContainer/XP/ProgressBar/fire2/GPUParticles2D +@onready var xp_label: Label = $VBoxContainer/XP/HBoxContainer/xpLabel +@onready var level_label: Label = $VBoxContainer/XP/HBoxContainer/levelLabel +@onready var reset_button: Button = $VBoxContainer/CenterContainer/resetButton + + +func _ready(): + reset_button.pressed.connect(on_reset_button_pressed) + + load_checkbox_state() + connect_checkboxes() + fireworks_timer.timeout.connect(stop_fireworks) + load_experience_progress() + update_progress() + stop_fireworks() + + +func load_experience_progress(): + if stats.load(STATS_FILE) == OK: + level = stats.get_value("xp", "level", 1) + xp = stats.get_value("xp", "xp", 0) + else: + level = 1 + xp = 0 + + xp_next = 2*BASE_XP + progress.max_value = xp_next + + for i in range(2,level+1): + xp_next += round(BASE_XP * i / 10.0) * 10 + progress.max_value = round(BASE_XP * level / 10.0) * 10 + + progress.value = xp - (xp_next - progress.max_value) + + +func save_experioence_progress(): + stats.set_value("xp", "level", level) + stats.set_value("xp", "xp", xp) + stats.save(STATS_FILE) + + +func _on_typing(): + xp += 1 + progress.value += 1 + + if progress.value >= progress.max_value: + level += 1 + xp_next = xp + round(BASE_XP * level / 10.0) * 10 + progress.value = 0 + progress.max_value = xp_next - xp + + if fireworks: + start_fireworks() + + save_experioence_progress() + update_progress() + + +func start_fireworks(): + sfx_fireworks.play() + fireworks_timer.start() + + fire_particles_one.emitting = true + fire_particles_two.emitting = true + + +func stop_fireworks(): + fire_particles_one.emitting = false + fire_particles_two.emitting = false + + +func update_progress(): + xp_label.text = "XP: %d / %d" % [ xp, xp_next ] + level_label.text = "Level: %d" % level + + +func connect_checkboxes(): + explosion_checkbox.toggled.connect(func(toggled): + explosions = toggled + save_checkbox_state() + ) + + blip_checkbox.toggled.connect(func(toggled): + blips = toggled + save_checkbox_state() + ) + + chars_checkbox.toggled.connect(func(toggled): + chars = toggled + save_checkbox_state() + ) + + shake_checkbox.toggled.connect(func(toggled): + shake = toggled + save_checkbox_state() + ) + + sound_checkbox.toggled.connect(func(toggled): + sound = toggled + save_checkbox_state() + ) + + fireworks_checkbox.toggled.connect(func(toggled): + fireworks = toggled + save_checkbox_state() + ) + + +func save_checkbox_state(): + stats.set_value("settings", "explosion", explosions) + stats.set_value("settings", "blips", blips) + stats.set_value("settings", "chars", chars) + stats.set_value("settings", "shake", shake) + stats.set_value("settings", "sound", sound) + stats.set_value("settings", "fireworks", fireworks) + stats.save(STATS_FILE) + + +func load_checkbox_state(): + if stats.load(STATS_FILE) == OK: + explosions = stats.get_value("settings", "explosion", true) + blips = stats.get_value("settings", "blips", true) + chars = stats.get_value("settings", "chars", true) + shake = stats.get_value("settings", "shake", true) + sound = stats.get_value("settings", "sound", true) + fireworks = stats.get_value("settings", "fireworks", true) + explosion_checkbox.set_pressed_no_signal(explosions) + blip_checkbox.set_pressed_no_signal(blips) + chars_checkbox.set_pressed_no_signal(chars) + shake_checkbox.set_pressed_no_signal(shake) + sound_checkbox.set_pressed_no_signal(sound) + fireworks_checkbox.set_pressed_no_signal(fireworks) + + +func on_reset_button_pressed(): + level = 1 + xp = 0 + xp_next = 2*BASE_XP + progress.value = 0 + progress.max_value = xp_next + update_progress() diff --git a/addons/ridiculous_coding/dock.gd.uid b/addons/ridiculous_coding/dock.gd.uid new file mode 100644 index 0000000..75574de --- /dev/null +++ b/addons/ridiculous_coding/dock.gd.uid @@ -0,0 +1 @@ +uid://yqhhxh7vjfs diff --git a/addons/ridiculous_coding/dock.tscn b/addons/ridiculous_coding/dock.tscn new file mode 100644 index 0000000..b4a5dc0 --- /dev/null +++ b/addons/ridiculous_coding/dock.tscn @@ -0,0 +1,149 @@ +[gd_scene load_steps=8 format=3 uid="uid://b76vnt4rv4p0q"] + +[ext_resource type="Script" path="res://addons/ridiculous_coding/dock.gd" id="1_bwupq"] +[ext_resource type="Texture2D" uid="uid://c3ltnrdrb2qmg" path="res://addons/ridiculous_coding/under.png" id="3_vdb3k"] +[ext_resource type="AudioStream" uid="uid://g6lh6kjt0ynq" path="res://addons/ridiculous_coding/fireworks.wav" id="4_1o4lv"] +[ext_resource type="Texture2D" uid="uid://dmvuvaqf5uhwi" path="res://addons/ridiculous_coding/progress.png" id="4_y2kl4"] + +[sub_resource type="Gradient" id="Gradient_v1eyn"] +offsets = PackedFloat32Array(0, 0.419689, 0.715026, 1) +colors = PackedColorArray(1, 1, 1, 1, 0.979167, 1, 0.333333, 1, 1, 0, 0, 1, 1, 1, 1, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_6lmfn"] +gradient = SubResource("Gradient_v1eyn") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_x4b51"] +lifetime_randomness = 0.29 +spread = 20.0 +gravity = Vector3(0, 300, 0) +initial_velocity_min = 400.0 +initial_velocity_max = 400.0 +scale_min = 5.0 +scale_max = 6.0 +color_ramp = SubResource("GradientTexture2D_6lmfn") + +[node name="Ridiculous Coding Dock" type="Control"] +custom_minimum_size = Vector2(300, 175) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_bwupq") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="XP" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/XP"] +layout_mode = 2 + +[node name="xpLabel" type="Label" parent="VBoxContainer/XP/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "XP: 201 / 350" + +[node name="levelLabel" type="Label" parent="VBoxContainer/XP/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Level: 3" +horizontal_alignment = 2 + +[node name="ProgressBar" type="TextureProgressBar" parent="VBoxContainer/XP"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 +max_value = 150.0 +value = 1.0 +nine_patch_stretch = true +texture_under = ExtResource("3_vdb3k") +texture_progress = ExtResource("4_y2kl4") + +[node name="fire1" type="Control" parent="VBoxContainer/XP/ProgressBar"] +layout_mode = 1 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="GPUParticles2D" type="GPUParticles2D" parent="VBoxContainer/XP/ProgressBar/fire1"] +rotation = -0.785397 +emitting = false +amount = 200 +process_material = SubResource("ParticleProcessMaterial_x4b51") + +[node name="fire2" type="Control" parent="VBoxContainer/XP/ProgressBar"] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_top = 8.0 +offset_right = 40.0 +offset_bottom = 48.0 +grow_horizontal = 0 + +[node name="GPUParticles2D" type="GPUParticles2D" parent="VBoxContainer/XP/ProgressBar/fire2"] +rotation = -2.35619 +emitting = false +amount = 200 +process_material = SubResource("ParticleProcessMaterial_x4b51") + +[node name="fireworksTimer" type="Timer" parent="VBoxContainer/XP/ProgressBar"] +wait_time = 3.0 + +[node name="sfxFireworks" type="AudioStreamPlayer" parent="VBoxContainer/XP/ProgressBar"] +stream = ExtResource("4_1o4lv") +volume_db = -12.0 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer"] +layout_mode = 2 +columns = 2 + +[node name="explosionCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Explosions" + +[node name="shakeCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Shake" + +[node name="blipCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Blips" + +[node name="charsCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Keys" + +[node name="soundCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Sound" + +[node name="fireworksCheckbox" type="CheckButton" parent="VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 11 +button_pressed = true +text = "Fireworks" + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="resetButton" type="Button" parent="VBoxContainer/CenterContainer"] +layout_mode = 2 +text = "Reset" diff --git a/addons/ridiculous_coding/fireworks.wav b/addons/ridiculous_coding/fireworks.wav new file mode 100644 index 0000000..a2763d7 Binary files /dev/null and b/addons/ridiculous_coding/fireworks.wav differ diff --git a/addons/ridiculous_coding/fireworks.wav.import b/addons/ridiculous_coding/fireworks.wav.import new file mode 100644 index 0000000..0b01a13 --- /dev/null +++ b/addons/ridiculous_coding/fireworks.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dyi5lstxrfkdt" +path="res://.godot/imported/fireworks.wav-f51025571a3b9e32e6113159ed26c3be.sample" + +[deps] + +source_file="res://addons/ridiculous_coding/fireworks.wav" +dest_files=["res://.godot/imported/fireworks.wav-f51025571a3b9e32e6113159ed26c3be.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/ridiculous_coding/font.tres b/addons/ridiculous_coding/font.tres new file mode 100644 index 0000000..35e2b1d --- /dev/null +++ b/addons/ridiculous_coding/font.tres @@ -0,0 +1,42 @@ +[gd_resource type="FontFile" load_steps=2 format=3 uid="uid://bvwnnnja1ur2i"] + +[ext_resource type="FontFile" uid="uid://coiaqb8xwuaic" path="res://addons/ridiculous_coding/GravityBold8.ttf" id="1"] + +[resource] +fallbacks = Array[Font]([ExtResource("1")]) +cache/0/16/0/ascent = 0.0 +cache/0/16/0/descent = 0.0 +cache/0/16/0/underline_position = 0.0 +cache/0/16/0/underline_thickness = 0.0 +cache/0/16/0/scale = 1.0 +cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0) +cache/0/16/0/kerning_overrides/50/0 = Vector2(0, 0) +cache/0/16/0/kerning_overrides/28/0 = Vector2(0, 0) +cache/0/16/0/kerning_overrides/14/0 = Vector2(0, 0) +cache/0/50/0/ascent = 0.0 +cache/0/50/0/descent = 0.0 +cache/0/50/0/underline_position = 0.0 +cache/0/50/0/underline_thickness = 0.0 +cache/0/50/0/scale = 1.0 +cache/0/50/0/kerning_overrides/16/0 = Vector2(0, 0) +cache/0/50/0/kerning_overrides/50/0 = Vector2(0, 0) +cache/0/50/0/kerning_overrides/28/0 = Vector2(0, 0) +cache/0/50/0/kerning_overrides/14/0 = Vector2(0, 0) +cache/0/28/0/ascent = 0.0 +cache/0/28/0/descent = 0.0 +cache/0/28/0/underline_position = 0.0 +cache/0/28/0/underline_thickness = 0.0 +cache/0/28/0/scale = 1.0 +cache/0/28/0/kerning_overrides/16/0 = Vector2(0, 0) +cache/0/28/0/kerning_overrides/50/0 = Vector2(0, 0) +cache/0/28/0/kerning_overrides/28/0 = Vector2(0, 0) +cache/0/28/0/kerning_overrides/14/0 = Vector2(0, 0) +cache/0/14/0/ascent = 0.0 +cache/0/14/0/descent = 0.0 +cache/0/14/0/underline_position = 0.0 +cache/0/14/0/underline_thickness = 0.0 +cache/0/14/0/scale = 1.0 +cache/0/14/0/kerning_overrides/16/0 = Vector2(0, 0) +cache/0/14/0/kerning_overrides/50/0 = Vector2(0, 0) +cache/0/14/0/kerning_overrides/28/0 = Vector2(0, 0) +cache/0/14/0/kerning_overrides/14/0 = Vector2(0, 0) diff --git a/addons/ridiculous_coding/newline.gd b/addons/ridiculous_coding/newline.gd new file mode 100644 index 0000000..4e910aa --- /dev/null +++ b/addons/ridiculous_coding/newline.gd @@ -0,0 +1,24 @@ +@tool +extends Node2D + +var destroy = false +var blips = true + +@onready var animation_player: AnimationPlayer = $AnimationPlayer +@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D +@onready var timer: Timer = $Timer + + +func _ready(): + if blips: + animation_player.stop() + animation_player.play("default") + animated_sprite_2d.frame = 0 + animated_sprite_2d.play("default") + + timer.start() + + +func _on_Timer_timeout(): + if destroy: + queue_free() diff --git a/addons/ridiculous_coding/newline.gd.uid b/addons/ridiculous_coding/newline.gd.uid new file mode 100644 index 0000000..beddd56 --- /dev/null +++ b/addons/ridiculous_coding/newline.gd.uid @@ -0,0 +1 @@ +uid://dulvfmb6n6ot8 diff --git a/addons/ridiculous_coding/newline.png b/addons/ridiculous_coding/newline.png new file mode 100644 index 0000000..95cae82 Binary files /dev/null and b/addons/ridiculous_coding/newline.png differ diff --git a/addons/ridiculous_coding/newline.png.import b/addons/ridiculous_coding/newline.png.import new file mode 100644 index 0000000..c335303 --- /dev/null +++ b/addons/ridiculous_coding/newline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1vn1823wqae2" +path="res://.godot/imported/newline.png-03ec198c1b51eb116216995dcb893beb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ridiculous_coding/newline.png" +dest_files=["res://.godot/imported/newline.png-03ec198c1b51eb116216995dcb893beb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ridiculous_coding/newline.tscn b/addons/ridiculous_coding/newline.tscn new file mode 100644 index 0000000..87fd614 --- /dev/null +++ b/addons/ridiculous_coding/newline.tscn @@ -0,0 +1,89 @@ +[gd_scene load_steps=11 format=3 uid="uid://wd4tkg0uxd18"] + +[ext_resource type="Script" path="res://addons/ridiculous_coding/newline.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://b1vn1823wqae2" path="res://addons/ridiculous_coding/newline.png" id="2"] + +[sub_resource type="AtlasTexture" id="1"] +atlas = ExtResource("2") +region = Rect2(0, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="2"] +atlas = ExtResource("2") +region = Rect2(64, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="3"] +atlas = ExtResource("2") +region = Rect2(128, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="4"] +atlas = ExtResource("2") +region = Rect2(192, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="5"] +atlas = ExtResource("2") +region = Rect2(256, 0, 64, 64) + +[sub_resource type="SpriteFrames" id="6"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("1") +}, { +"duration": 1.0, +"texture": SubResource("2") +}, { +"duration": 1.0, +"texture": SubResource("3") +}, { +"duration": 1.0, +"texture": SubResource("4") +}, { +"duration": 1.0, +"texture": SubResource("5") +}], +"loop": true, +"name": &"default", +"speed": 12.0 +}] + +[sub_resource type="Animation" id="7"] +resource_name = "default" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("AnimatedSprite2D:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.1), +"transitions": PackedFloat32Array(0.233258, 1), +"update": 0, +"values": [Vector2(-200, 0), Vector2(-50, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_gwr1r"] +_data = { +"default": SubResource("7") +} + +[node name="Node2D" type="Node2D"] +texture_filter = 1 +script = ExtResource("1") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +position = Vector2(-50, 0) +rotation = -1.57079 +scale = Vector2(2, 2) +sprite_frames = SubResource("6") +frame_progress = 0.432846 + +[node name="Timer" type="Timer" parent="."] +one_shot = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_gwr1r") +} +autoplay = "default" + +[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] diff --git a/addons/ridiculous_coding/plugin.cfg b/addons/ridiculous_coding/plugin.cfg new file mode 100644 index 0000000..6638258 --- /dev/null +++ b/addons/ridiculous_coding/plugin.cfg @@ -0,0 +1,8 @@ +[plugin] + +name="Ridiculous Coding - Godot 4" +description="Ridiculous screen-shakey coding inspired by Textreme https://ash-k.itch.io/textreme-2" +author="John Watson" +version="2021.11.28" +script="plugin.gd" +constributors="Jeferson 'Shin' Leite Borges" diff --git a/addons/ridiculous_coding/plugin.gd b/addons/ridiculous_coding/plugin.gd new file mode 100644 index 0000000..22ce636 --- /dev/null +++ b/addons/ridiculous_coding/plugin.gd @@ -0,0 +1,170 @@ +@tool +extends EditorPlugin + +signal typing + +# Scenes preloaded +const Boom: PackedScene = preload("res://addons/ridiculous_coding/boom.tscn") +const Blip: PackedScene = preload("res://addons/ridiculous_coding/blip.tscn") +const Newline: PackedScene = preload("res://addons/ridiculous_coding/newline.tscn") +const Dock: PackedScene = preload("res://addons/ridiculous_coding/dock.tscn") + +# Inner Variables +const PITCH_DECREMENT := 2.0 + +var shake: float = 0.0 +var shake_intensity:float = 0.0 +var timer: float = 0.0 +var last_key: String = "" +var pitch_increase: float = 0.0 +var editors = {} +var dock + + +func _enter_tree(): + var editor: EditorInterface = get_editor_interface() + var script_editor: ScriptEditor = editor.get_script_editor() + script_editor.editor_script_changed.connect(editor_script_changed) + + # Add the main panel + dock = Dock.instantiate() + typing.connect(Callable(dock,"_on_typing")) + add_control_to_dock(DOCK_SLOT_RIGHT_BL, dock) + + +func _exit_tree(): + if dock: + remove_control_from_docks(dock) + dock.free() + + +func get_all_text_editors(parent : Node): + for child in parent.get_children(): + if child.get_child_count(): + get_all_text_editors(child) + + if child is TextEdit: + editors[child] = { + "text": child.text, + "line": child.get_caret_line() + } + + if child.caret_changed.is_connected(caret_changed): + child.caret_changed.disconnect(caret_changed) + child.caret_changed.connect(caret_changed.bind(child)) + + if child.text_changed.is_connected(text_changed): + child.text_changed.disconnect(text_changed) + child.text_changed.connect(text_changed.bind(child)) + + if child.gui_input.is_connected(gui_input): + child.gui_input.disconnect(gui_input) + child.gui_input.connect(gui_input) + + +func gui_input(event): + # Get last key typed + if event is InputEventKey and event.pressed: + event = event as InputEventKey + last_key = OS.get_keycode_string(event.get_keycode_with_modifiers()) + + +func editor_script_changed(script): + var editor = get_editor_interface() + var script_editor = editor.get_script_editor() + + editors.clear() + get_all_text_editors(script_editor) + + +func _process(delta): + var editor = get_editor_interface() + + if shake > 0: + shake -= delta + editor.get_base_control().position = Vector2(randf_range(-shake_intensity,shake_intensity), randf_range(-shake_intensity,shake_intensity)) + else: + editor.get_base_control().position = Vector2.ZERO + + timer += delta + if (pitch_increase > 0.0): + pitch_increase -= delta * PITCH_DECREMENT + + +func shake_screen(duration, intensity): + if shake > 0: + return + + shake = duration + shake_intensity = intensity + + +func caret_changed(textedit): + var editor = get_editor_interface() + + if not editors.has(textedit): + # For some reason the editor instances all change + # when the file is saved so you need to reload them + editors.clear() + get_all_text_editors(editor.get_script_editor()) + + editors[textedit]["line"] = textedit.get_caret_line() + + +func text_changed(textedit : TextEdit): + var line_height = textedit.get_line_height() + var pos = textedit.get_caret_draw_pos() + Vector2(0,-line_height/2.0) + emit_signal("typing") + + if editors.has(textedit): + # Deleting + if timer > 0.1 and len(textedit.text) < len(editors[textedit]["text"]): + timer = 0.0 + + if dock.explosions: + # Draw the thing + var thing = Boom.instantiate() + thing.position = pos + thing.destroy = true + if dock.chars: thing.last_key = last_key + thing.sound = dock.sound + textedit.add_child(thing) + + if dock.shake: + # Shake + shake_screen(0.2, 10) + + # Typing + if timer > 0.02 and len(textedit.text) >= len(editors[textedit]["text"]): + timer = 0.0 + + # Draw the thing + var thing = Blip.instantiate() + thing.pitch_increase = pitch_increase + pitch_increase += 1.0 + thing.position = pos + thing.destroy = true + thing.blips = dock.blips + if dock.chars: thing.last_key = last_key + thing.sound = dock.sound + textedit.add_child(thing) + + if dock.shake: + # Shake + shake_screen(0.05, 5) + + # Newline + if textedit.get_caret_line() != editors[textedit]["line"]: + # Draw the thing + var thing = Newline.instantiate() + thing.position = pos + thing.destroy = true + thing.blips = dock.blips + textedit.add_child(thing) + + if dock.shake: + # Shake + shake_screen(0.05, 5) + + editors[textedit]["text"] = textedit.text + editors[textedit]["line"] = textedit.get_caret_line() diff --git a/addons/ridiculous_coding/plugin.gd.uid b/addons/ridiculous_coding/plugin.gd.uid new file mode 100644 index 0000000..7e520a8 --- /dev/null +++ b/addons/ridiculous_coding/plugin.gd.uid @@ -0,0 +1 @@ +uid://dj0kkddglgu2g diff --git a/addons/ridiculous_coding/progress.png b/addons/ridiculous_coding/progress.png new file mode 100644 index 0000000..04dc8a0 Binary files /dev/null and b/addons/ridiculous_coding/progress.png differ diff --git a/addons/ridiculous_coding/progress.png.import b/addons/ridiculous_coding/progress.png.import new file mode 100644 index 0000000..be70f51 --- /dev/null +++ b/addons/ridiculous_coding/progress.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqpxh1bccjaae" +path="res://.godot/imported/progress.png-7f475b7e84ff0c0c87db61c1f97b4978.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ridiculous_coding/progress.png" +dest_files=["res://.godot/imported/progress.png-7f475b7e84ff0c0c87db61c1f97b4978.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/addons/ridiculous_coding/under.png b/addons/ridiculous_coding/under.png new file mode 100644 index 0000000..eecb866 Binary files /dev/null and b/addons/ridiculous_coding/under.png differ diff --git a/addons/ridiculous_coding/under.png.import b/addons/ridiculous_coding/under.png.import new file mode 100644 index 0000000..85a494a --- /dev/null +++ b/addons/ridiculous_coding/under.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dgda8elabipl5" +path="res://.godot/imported/under.png-b737211ee1ab1b3c04a4062a02353851.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/ridiculous_coding/under.png" +dest_files=["res://.godot/imported/under.png-b737211ee1ab1b3c04a4062a02353851.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/Blower.svg b/assets/Blower.svg new file mode 100644 index 0000000..b7a87a6 --- /dev/null +++ b/assets/Blower.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/Blower.svg.import b/assets/Blower.svg.import new file mode 100644 index 0000000..b31a5c8 --- /dev/null +++ b/assets/Blower.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dg6g5gxw8s4ll" +path="res://.godot/imported/Blower.svg-1364c54f1ef1b83f9c023d634738b6c9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/Blower.svg" +dest_files=["res://.godot/imported/Blower.svg-1364c54f1ef1b83f9c023d634738b6c9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/Cheerful Annoyance.ogg b/assets/Cheerful Annoyance.ogg new file mode 100644 index 0000000..ebf31fb --- /dev/null +++ b/assets/Cheerful Annoyance.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e262e050e53eec55ebd1702981fa3944cd558d482580b6e34eea5a01ef54b05 +size 185828 diff --git a/assets/Cheerful Annoyance.ogg.import b/assets/Cheerful Annoyance.ogg.import new file mode 100644 index 0000000..dd57e87 --- /dev/null +++ b/assets/Cheerful Annoyance.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://c7dphq7kgyqki" +path="res://.godot/imported/Cheerful Annoyance.ogg-e6b7e427264a061dd8ed62f3b50e4978.oggvorbisstr" + +[deps] + +source_file="res://assets/Cheerful Annoyance.ogg" +dest_files=["res://.godot/imported/Cheerful Annoyance.ogg-e6b7e427264a061dd8ed62f3b50e4978.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/background.svg b/assets/background.svg new file mode 100644 index 0000000..ddc332d --- /dev/null +++ b/assets/background.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/background.svg.import b/assets/background.svg.import new file mode 100644 index 0000000..c198bbf --- /dev/null +++ b/assets/background.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1memr7pxwyl5" +path="res://.godot/imported/background.svg-ef40ca42a4262420673651b4c6a8a059.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/background.svg" +dest_files=["res://.godot/imported/background.svg-ef40ca42a4262420673651b4c6a8a059.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/soap.svg b/assets/soap.svg new file mode 100644 index 0000000..8e2b952 --- /dev/null +++ b/assets/soap.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/assets/soap.svg.import b/assets/soap.svg.import new file mode 100644 index 0000000..5b2773f --- /dev/null +++ b/assets/soap.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d0iedh3qhku3k" +path="res://.godot/imported/soap.svg-9bc9ffc7c6a7546990909fdaddec32d9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/soap.svg" +dest_files=["res://.godot/imported/soap.svg-9bc9ffc7c6a7546990909fdaddec32d9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/builds/ios/GGJ - Bubbles.pck b/builds/ios/GGJ - Bubbles.pck new file mode 100644 index 0000000..48930f0 Binary files /dev/null and b/builds/ios/GGJ - Bubbles.pck differ diff --git a/builds/ios/GGJ - Bubbles.xcframework/Info.plist b/builds/ios/GGJ - Bubbles.xcframework/Info.plist new file mode 100755 index 0000000..8465335 --- /dev/null +++ b/builds/ios/GGJ - Bubbles.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libgodot.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libgodot.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/builds/ios/GGJ - Bubbles.xcframework/ios-arm64/libgodot.a b/builds/ios/GGJ - Bubbles.xcframework/ios-arm64/libgodot.a new file mode 100755 index 0000000..b220fe9 Binary files /dev/null and b/builds/ios/GGJ - Bubbles.xcframework/ios-arm64/libgodot.a differ diff --git a/builds/ios/GGJ - Bubbles.xcframework/ios-arm64_x86_64-simulator/libgodot.a b/builds/ios/GGJ - Bubbles.xcframework/ios-arm64_x86_64-simulator/libgodot.a new file mode 100644 index 0000000..ab34216 Binary files /dev/null and b/builds/ios/GGJ - Bubbles.xcframework/ios-arm64_x86_64-simulator/libgodot.a differ diff --git a/builds/ios/GGJ - Bubbles.xcodeproj/project.pbxproj b/builds/ios/GGJ - Bubbles.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1647fdb --- /dev/null +++ b/builds/ios/GGJ - Bubbles.xcodeproj/project.pbxproj @@ -0,0 +1,412 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 054F8BE52D38852F00B81423 /* MetalFX.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */; }; + 9039D3BE24C093AC0020482C /* MoltenVK in Frameworks */ = {isa = PBXBuildFile; fileRef = 9039D3BD24C093AC0020482C /* MoltenVK */; }; + 90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; }; + D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; }; + D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */; }; + D0BCFE7818AEBFEB004A7AAE /* GGJ - Bubbles.pck in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE7718AEBFEB004A7AAE /* GGJ - Bubbles.pck */; }; + DEADBEEF2F582BE20003B888 /* godot in Frameworks */ = {isa = PBXBuildFile; fileRef = DEADBEEF1F582BE20003B888 /* godot */; }; + F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 90A13CD024AA68E500E8464F /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 054F8BE52D38852F00B81423 /* MetalFX.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalFX.framework; path = System/Library/Frameworks/MetalFX.framework; sourceTree = SDKROOT; }; + 1F1575711F582BE20003B888 /* dylibs */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dylibs; path = "GGJ - Bubbles/dylibs"; sourceTree = ""; }; + 1FF4C1881F584E6300A41E41 /* GGJ - Bubbles.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "GGJ - Bubbles.entitlements"; sourceTree = ""; }; + 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = ""; }; + 9039D3BD24C093AC0020482C /* MoltenVK */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MoltenVK; path = MoltenVK.xcframework; sourceTree = ""; }; + 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; + D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D0BCFE3418AEBDA2004A7AAE /* GGJ - Bubbles.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GGJ - Bubbles.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + D0BCFE4318AEBDA2004A7AAE /* GGJ - Bubbles-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GGJ - Bubbles-Info.plist"; sourceTree = ""; }; + D0BCFE4518AEBDA2004A7AAE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D0BCFE7718AEBFEB004A7AAE /* GGJ - Bubbles.pck */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GGJ - Bubbles.pck"; sourceTree = ""; }; + DEADBEEF1F582BE20003B888 /* godot */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = godot; path = "GGJ - Bubbles.xcframework"; sourceTree = ""; }; + F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D0BCFE3118AEBDA2004A7AAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9039D3BE24C093AC0020482C /* MoltenVK in Frameworks */, + 054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */, + DEADBEEF2F582BE20003B888 /* godot in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D0BCFE2B18AEBDA2004A7AAE = { + isa = PBXGroup; + children = ( + 1F1575711F582BE20003B888 /* dylibs */, + D0BCFE7718AEBFEB004A7AAE /* GGJ - Bubbles.pck */, + D0BCFE4118AEBDA2004A7AAE /* GGJ - Bubbles */, + D0BCFE3618AEBDA2004A7AAE /* Frameworks */, + D0BCFE3518AEBDA2004A7AAE /* Products */, + F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */, + ); + sourceTree = ""; + }; + D0BCFE3518AEBDA2004A7AAE /* Products */ = { + isa = PBXGroup; + children = ( + D0BCFE3418AEBDA2004A7AAE /* GGJ - Bubbles.app */, + ); + name = Products; + sourceTree = ""; + }; + D0BCFE3618AEBDA2004A7AAE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9039D3BD24C093AC0020482C /* MoltenVK */, + 054F8BE52D38852F00B81423 /* MetalFX.framework */, + DEADBEEF1F582BE20003B888 /* godot */, + ); + name = Frameworks; + sourceTree = ""; + }; + D0BCFE4118AEBDA2004A7AAE /* GGJ - Bubbles */ = { + isa = PBXGroup; + children = ( + 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */, + 1FF4C1881F584E6300A41E41 /* GGJ - Bubbles.entitlements */, + D07CD44D1C5D589C00B7FB28 /* Images.xcassets */, + D0BCFE4218AEBDA2004A7AAE /* Supporting Files */, + 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */, + ); + path = "GGJ - Bubbles"; + sourceTree = ""; + }; + D0BCFE4218AEBDA2004A7AAE /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D0BCFE4318AEBDA2004A7AAE /* GGJ - Bubbles-Info.plist */, + D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D0BCFE3318AEBDA2004A7AAE /* GGJ - Bubbles */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0BCFE7118AEBDA3004A7AAE /* Build configuration list for PBXNativeTarget "GGJ - Bubbles" */; + buildPhases = ( + D0BCFE3018AEBDA2004A7AAE /* Sources */, + D0BCFE3118AEBDA2004A7AAE /* Frameworks */, + D0BCFE3218AEBDA2004A7AAE /* Resources */, + 90A13CD024AA68E500E8464F /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "GGJ - Bubbles"; + productName = "GGJ - Bubbles"; + productReference = D0BCFE3418AEBDA2004A7AAE /* GGJ - Bubbles.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D0BCFE2C18AEBDA2004A7AAE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1620; + ORGANIZATIONNAME = GodotEngine; + TargetAttributes = { + D0BCFE3318AEBDA2004A7AAE = { + SystemCapabilities = { + }; + }; + }; + }; + buildConfigurationList = D0BCFE2F18AEBDA2004A7AAE /* Build configuration list for PBXProject "GGJ - Bubbles" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D0BCFE2B18AEBDA2004A7AAE; + productRefGroup = D0BCFE3518AEBDA2004A7AAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D0BCFE3318AEBDA2004A7AAE /* GGJ - Bubbles */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D0BCFE3218AEBDA2004A7AAE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */, + D0BCFE7818AEBFEB004A7AAE /* GGJ - Bubbles.pck in Resources */, + 90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */, + D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */, + F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D0BCFE3018AEBDA2004A7AAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D0BCFE4518AEBDA2004A7AAE /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + D0BCFE6F18AEBDA3004A7AAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$(PROJECT_DIR)/**"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_CLASSIC_1500 = "-ld_classic"; + LD_CLASSIC_1501 = "-ld_classic"; + LD_CLASSIC_1510 = "-ld_classic"; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "$(LD_CLASSIC_$(XCODE_VERSION_ACTUAL)) "; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + D0BCFE7018AEBDA3004A7AAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$(PROJECT_DIR)/**"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_CLASSIC_1500 = "-ld_classic"; + LD_CLASSIC_1501 = "-ld_classic"; + LD_CLASSIC_1510 = "-ld_classic"; + OTHER_LDFLAGS = "$(LD_CLASSIC_$(XCODE_VERSION_ACTUAL)) "; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = 1; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + D0BCFE7218AEBDA3004A7AAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "GGJ - Bubbles/GGJ - Bubbles.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + CURRENT_PROJECT_VERSION = 1.0.0; + DEVELOPMENT_TEAM = 75R6R6S3K5; + EXECUTABLE_NAME = "GGJ - Bubbles"; + INFOPLIST_FILE = "GGJ - Bubbles/GGJ - Bubbles-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "GGJ - Bubbles"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/**", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.rinma.bubbles; + PRODUCT_NAME = "GGJ - Bubbles"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + TARGETED_DEVICE_FAMILY = 1; + VALID_ARCHS = "arm64 x86_64"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + D0BCFE7318AEBDA3004A7AAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "GGJ - Bubbles/GGJ - Bubbles.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + CURRENT_PROJECT_VERSION = 1.0.0; + DEVELOPMENT_TEAM = 75R6R6S3K5; + EXECUTABLE_NAME = "GGJ - Bubbles"; + INFOPLIST_FILE = "GGJ - Bubbles/GGJ - Bubbles-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "GGJ - Bubbles"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/**", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.rinma.bubbles; + PRODUCT_NAME = "GGJ - Bubbles"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + TARGETED_DEVICE_FAMILY = 1; + VALID_ARCHS = "arm64 x86_64"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D0BCFE2F18AEBDA2004A7AAE /* Build configuration list for PBXProject "GGJ - Bubbles" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0BCFE6F18AEBDA3004A7AAE /* Debug */, + D0BCFE7018AEBDA3004A7AAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + D0BCFE7118AEBDA3004A7AAE /* Build configuration list for PBXNativeTarget "GGJ - Bubbles" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0BCFE7218AEBDA3004A7AAE /* Debug */, + D0BCFE7318AEBDA3004A7AAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = D0BCFE2C18AEBDA2004A7AAE /* Project object */; +} diff --git a/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..37b62f8 --- /dev/null +++ b/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,8 @@ + + + + + + diff --git a/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/xcuserdata/rinma.xcuserdatad/UserInterfaceState.xcuserstate b/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/xcuserdata/rinma.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..1977a5f Binary files /dev/null and b/builds/ios/GGJ - Bubbles.xcodeproj/project.xcworkspace/xcuserdata/rinma.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/builds/ios/GGJ - Bubbles.xcodeproj/xcshareddata/xcschemes/GGJ - Bubbles.xcscheme b/builds/ios/GGJ - Bubbles.xcodeproj/xcshareddata/xcschemes/GGJ - Bubbles.xcscheme new file mode 100644 index 0000000..af4b8e5 --- /dev/null +++ b/builds/ios/GGJ - Bubbles.xcodeproj/xcshareddata/xcschemes/GGJ - Bubbles.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builds/ios/GGJ - Bubbles/GGJ - Bubbles-Info.plist b/builds/ios/GGJ - Bubbles/GGJ - Bubbles-Info.plist new file mode 100644 index 0000000..1967bf9 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/GGJ - Bubbles-Info.plist @@ -0,0 +1,65 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + $(INFOPLIST_KEY_CFBundleDisplayName) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + UIRequiredDeviceCapabilities + + + NSCameraUsageDescription + + NSPhotoLibraryUsageDescription + + NSMicrophoneUsageDescription + + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + + + + UILaunchStoryboardName +Launch Screen + CADisableMinimumFrameDurationOnPhone + + + diff --git a/builds/ios/GGJ - Bubbles/GGJ - Bubbles.entitlements b/builds/ios/GGJ - Bubbles/GGJ - Bubbles.entitlements new file mode 100644 index 0000000..c74150d --- /dev/null +++ b/builds/ios/GGJ - Bubbles/GGJ - Bubbles.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.game-center + + + diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Contents.json b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..69a1206 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"idiom":"universal","platform":"ios","size":"29x29","scale":"2x","filename":"Icon-58.png"},{"idiom":"universal","platform":"ios","size":"29x29","scale":"3x","filename":"Icon-87.png"},{"idiom":"universal","platform":"ios","size":"20x20","scale":"2x","filename":"Icon-40.png"},{"idiom":"universal","platform":"ios","size":"20x20","scale":"3x","filename":"Icon-60.png"},{"idiom":"universal","platform":"ios","size":"38x38","scale":"2x","filename":"Icon-76.png"},{"idiom":"universal","platform":"ios","size":"38x38","scale":"3x","filename":"Icon-114.png"},{"idiom":"universal","platform":"ios","size":"40x40","scale":"2x","filename":"Icon-80.png"},{"idiom":"universal","platform":"ios","size":"40x40","scale":"3x","filename":"Icon-120.png"},{"idiom":"universal","platform":"ios","size":"60x60","scale":"2x","filename":"Icon-120-1.png"},{"idiom":"universal","platform":"ios","size":"60x60","scale":"3x","filename":"Icon-180.png"},{"idiom":"universal","platform":"ios","size":"83.5x83.5","scale":"2x","filename":"Icon-167.png"},{"idiom":"universal","platform":"ios","size":"76x76","scale":"2x","filename":"Icon-152.png"},{"idiom":"universal","platform":"ios","size":"64x64","scale":"2x","filename":"Icon-128.png"},{"idiom":"universal","platform":"ios","size":"64x64","scale":"3x","filename":"Icon-192.png"},{"idiom":"universal","platform":"ios","size":"68x68","scale":"2x","filename":"Icon-136.png"},{"idiom":"universal","platform":"ios","size":"1024x1024","filename":"Icon-1024.png"}],"info":{"author":"xcode","version":1}} \ No newline at end of file diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png new file mode 100644 index 0000000..4e9eb66 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png.import new file mode 100644 index 0000000..78fe0e9 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cx6up48f4tfe1" +path="res://.godot/imported/Icon-1024.png-776b8092069efe29b17110975fa122d0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-1024.png" +dest_files=["res://.godot/imported/Icon-1024.png-776b8092069efe29b17110975fa122d0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png new file mode 100644 index 0000000..670d55c Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png.import new file mode 100644 index 0000000..01dc7ad --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dga81gno8p5x1" +path="res://.godot/imported/Icon-114.png-a67d5061fb44921db9a442381951548b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-114.png" +dest_files=["res://.godot/imported/Icon-114.png-a67d5061fb44921db9a442381951548b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png new file mode 100644 index 0000000..3b23d9d Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png.import new file mode 100644 index 0000000..9240c09 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8wvyp423yjj1" +path="res://.godot/imported/Icon-120-1.png-797105a3c605edc0ac65b4e231ae0473.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120-1.png" +dest_files=["res://.godot/imported/Icon-120-1.png-797105a3c605edc0ac65b4e231ae0473.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png new file mode 100644 index 0000000..3b23d9d Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png.import new file mode 100644 index 0000000..a9ba3f3 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bjci6halnijm2" +path="res://.godot/imported/Icon-120.png-e6832d0852284a8d019e23d0497b96a8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-120.png" +dest_files=["res://.godot/imported/Icon-120.png-e6832d0852284a8d019e23d0497b96a8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 0000000..83d9bdf Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png.import new file mode 100644 index 0000000..828af28 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnthapcywrm1p" +path="res://.godot/imported/Icon-128.png-841708941e0ab596dfd303ffce80bc13.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-128.png" +dest_files=["res://.godot/imported/Icon-128.png-841708941e0ab596dfd303ffce80bc13.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png new file mode 100644 index 0000000..392b703 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png.import new file mode 100644 index 0000000..33adbb7 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://e38y1gmthll0" +path="res://.godot/imported/Icon-136.png-dcb9095dd2a598a479063d22e43eae35.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-136.png" +dest_files=["res://.godot/imported/Icon-136.png-dcb9095dd2a598a479063d22e43eae35.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png new file mode 100644 index 0000000..69b1382 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png.import new file mode 100644 index 0000000..f1d3bf2 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cl467hw2s2mph" +path="res://.godot/imported/Icon-152.png-e534afc9196437d7c898504e5796f1a9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-152.png" +dest_files=["res://.godot/imported/Icon-152.png-e534afc9196437d7c898504e5796f1a9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png new file mode 100644 index 0000000..d17babe Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png.import new file mode 100644 index 0000000..baba4b6 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drqnjk3f50nti" +path="res://.godot/imported/Icon-167.png-a9feaa532cad45d765803606cb6f8309.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-167.png" +dest_files=["res://.godot/imported/Icon-167.png-a9feaa532cad45d765803606cb6f8309.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png new file mode 100644 index 0000000..20204a0 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png.import new file mode 100644 index 0000000..1328617 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wqgb4uj3mx1j" +path="res://.godot/imported/Icon-180.png-6a9b9ac355e3128afbfcf9927ac2afc0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-180.png" +dest_files=["res://.godot/imported/Icon-180.png-6a9b9ac355e3128afbfcf9927ac2afc0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png new file mode 100644 index 0000000..ce753cc Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png.import new file mode 100644 index 0000000..314fcac --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cugscbnw0mal0" +path="res://.godot/imported/Icon-192.png-93153b665a364ebc9f210a33428f34f7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-192.png" +dest_files=["res://.godot/imported/Icon-192.png-93153b665a364ebc9f210a33428f34f7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 0000000..b472e7c Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png.import new file mode 100644 index 0000000..d37117a --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://w8a31nvuov3i" +path="res://.godot/imported/Icon-40.png-71a1e640e61324d74d5e65a0fdc32a31.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-40.png" +dest_files=["res://.godot/imported/Icon-40.png-71a1e640e61324d74d5e65a0fdc32a31.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png new file mode 100644 index 0000000..74201d1 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png.import new file mode 100644 index 0000000..1f57712 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bo8fbett8ckg" +path="res://.godot/imported/Icon-58.png-ddbddfdc6d7126affd65ac070686f1b3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-58.png" +dest_files=["res://.godot/imported/Icon-58.png-ddbddfdc6d7126affd65ac070686f1b3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png new file mode 100644 index 0000000..700e7c3 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png.import new file mode 100644 index 0000000..2bc3bc9 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7cfxyd1ke0yq" +path="res://.godot/imported/Icon-60.png-1c1f3b8cd7070bb84cbb3091d82a5f5d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-60.png" +dest_files=["res://.godot/imported/Icon-60.png-1c1f3b8cd7070bb84cbb3091d82a5f5d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..015c05b Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png.import new file mode 100644 index 0000000..bdb9ff3 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bscf7bh2uu72a" +path="res://.godot/imported/Icon-76.png-f2e7f80cc8132cfd6496edc07bbffdda.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-76.png" +dest_files=["res://.godot/imported/Icon-76.png-f2e7f80cc8132cfd6496edc07bbffdda.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png new file mode 100644 index 0000000..2190cad Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png.import new file mode 100644 index 0000000..47bd75c --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b520q7k30lbga" +path="res://.godot/imported/Icon-80.png-f7da298ea23d870b204fc42d9e1b785d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-80.png" +dest_files=["res://.godot/imported/Icon-80.png-f7da298ea23d870b204fc42d9e1b785d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png new file mode 100644 index 0000000..8684112 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png.import new file mode 100644 index 0000000..c04b0f7 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2cnetctbyurm" +path="res://.godot/imported/Icon-87.png-6c05add36712c35b33de17fa84215bd0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/Icon-87.png" +dest_files=["res://.godot/imported/Icon-87.png-6c05add36712c35b33de17fa84215bd0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/sizes b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/sizes new file mode 100644 index 0000000..8850e4c --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/AppIcon.appiconset/sizes @@ -0,0 +1,16 @@ +58 +87 +40 +60 +76 +114 +80 +120 +120 +180 +167 +152 +128 +192 +136 +1024 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/Contents.json b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/Contents.json new file mode 100644 index 0000000..1e63e78 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "splash@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "splash@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png new file mode 100644 index 0000000..766b0b6 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png.import new file mode 100644 index 0000000..89a9034 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dltrfx6yo0rwm" +path="res://.godot/imported/splash@2x.png-3d95849e4e072bde8c635f05ad9600fb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@2x.png" +dest_files=["res://.godot/imported/splash@2x.png-3d95849e4e072bde8c635f05ad9600fb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png new file mode 100644 index 0000000..766b0b6 Binary files /dev/null and b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png differ diff --git a/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png.import b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png.import new file mode 100644 index 0000000..622f45b --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5w7vofbyjsru" +path="res://.godot/imported/splash@3x.png-3e92e5fb93b70c86f73e6eb8938389b7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/ios/GGJ - Bubbles/Images.xcassets/SplashImage.imageset/splash@3x.png" +dest_files=["res://.godot/imported/splash@3x.png-3e92e5fb93b70c86f73e6eb8938389b7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/ios/GGJ - Bubbles/Launch Screen.storyboard b/builds/ios/GGJ - Bubbles/Launch Screen.storyboard new file mode 100644 index 0000000..9d27f16 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/Launch Screen.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builds/ios/GGJ - Bubbles/dummy.cpp b/builds/ios/GGJ - Bubbles/dummy.cpp new file mode 100644 index 0000000..21e65be --- /dev/null +++ b/builds/ios/GGJ - Bubbles/dummy.cpp @@ -0,0 +1,44 @@ +/**************************************************************************/ +/* dummy.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + + +// Godot Plugins +void godot_ios_plugins_initialize(); +void godot_ios_plugins_deinitialize(); +// Exported Plugins + +// Use Plugins +void godot_ios_plugins_initialize() { +} + +void godot_ios_plugins_deinitialize() { +} + + diff --git a/builds/ios/GGJ - Bubbles/dummy.h b/builds/ios/GGJ - Bubbles/dummy.h new file mode 100644 index 0000000..23f1795 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/dummy.h @@ -0,0 +1,31 @@ +/**************************************************************************/ +/* dummy.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +// #import diff --git a/builds/ios/GGJ - Bubbles/dummy.swift b/builds/ios/GGJ - Bubbles/dummy.swift new file mode 100644 index 0000000..495b5c9 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/dummy.swift @@ -0,0 +1,31 @@ +/**************************************************************************/ +/* dummy.swift */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +import Foundation diff --git a/builds/ios/GGJ - Bubbles/dylibs/empty b/builds/ios/GGJ - Bubbles/dylibs/empty new file mode 100644 index 0000000..bd3e894 --- /dev/null +++ b/builds/ios/GGJ - Bubbles/dylibs/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/builds/ios/GGJ - Bubbles/en.lproj/InfoPlist.strings b/builds/ios/GGJ - Bubbles/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..b92732c --- /dev/null +++ b/builds/ios/GGJ - Bubbles/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/builds/ios/GGJ - Bubbles/export_options.plist b/builds/ios/GGJ - Bubbles/export_options.plist new file mode 100644 index 0000000..bbe42fb --- /dev/null +++ b/builds/ios/GGJ - Bubbles/export_options.plist @@ -0,0 +1,21 @@ + + + + + method + development + + teamID + 46G677SS3Y + + provisioningProfiles + + dev.rinma.bubbles + + + + compileBitcode + + + + diff --git a/builds/ios/MoltenVK.xcframework/Info.plist b/builds/ios/MoltenVK.xcframework/Info.plist new file mode 100644 index 0000000..b730fa8 --- /dev/null +++ b/builds/ios/MoltenVK.xcframework/Info.plist @@ -0,0 +1,108 @@ + + + + + AvailableLibraries + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + ios-arm64 + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + macos-arm64_x86_64 + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + macos + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + tvos-arm64_x86_64-simulator + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + tvos + SupportedPlatformVariant + simulator + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + tvos-arm64_arm64e + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + arm64e + + SupportedPlatform + tvos + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + BinaryPath + libMoltenVK.a + LibraryIdentifier + ios-arm64_x86_64-maccatalyst + LibraryPath + libMoltenVK.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + maccatalyst + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/builds/ios/MoltenVK.xcframework/ios-arm64/libMoltenVK.a b/builds/ios/MoltenVK.xcframework/ios-arm64/libMoltenVK.a new file mode 100644 index 0000000..2e5742e Binary files /dev/null and b/builds/ios/MoltenVK.xcframework/ios-arm64/libMoltenVK.a differ diff --git a/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-maccatalyst/libMoltenVK.a b/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-maccatalyst/libMoltenVK.a new file mode 100644 index 0000000..09b9de1 Binary files /dev/null and b/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-maccatalyst/libMoltenVK.a differ diff --git a/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-simulator/libMoltenVK.a b/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-simulator/libMoltenVK.a new file mode 100644 index 0000000..d6b6490 Binary files /dev/null and b/builds/ios/MoltenVK.xcframework/ios-arm64_x86_64-simulator/libMoltenVK.a differ diff --git a/builds/ios/PrivacyInfo.xcprivacy b/builds/ios/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..2b68e58 --- /dev/null +++ b/builds/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,38 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPITypeReasons + + DDA9.1 + C617.1 + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + + + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + + + NSPrivacyAccessedAPITypeReasons + + E174.1 + 85F4.1 + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + + + NSPrivacyTracking + + + + diff --git a/builds/web/GGJ - Bubbles.144x144.png b/builds/web/GGJ - Bubbles.144x144.png new file mode 100644 index 0000000..32812a6 Binary files /dev/null and b/builds/web/GGJ - Bubbles.144x144.png differ diff --git a/builds/web/GGJ - Bubbles.144x144.png.import b/builds/web/GGJ - Bubbles.144x144.png.import new file mode 100644 index 0000000..57eccb2 --- /dev/null +++ b/builds/web/GGJ - Bubbles.144x144.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cd311embmrv4p" +path="res://.godot/imported/GGJ - Bubbles.144x144.png-759bdb6c2f549086c9f3c4db624fdf64.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.144x144.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.144x144.png-759bdb6c2f549086c9f3c4db624fdf64.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.180x180.png b/builds/web/GGJ - Bubbles.180x180.png new file mode 100644 index 0000000..e565555 Binary files /dev/null and b/builds/web/GGJ - Bubbles.180x180.png differ diff --git a/builds/web/GGJ - Bubbles.180x180.png.import b/builds/web/GGJ - Bubbles.180x180.png.import new file mode 100644 index 0000000..cc584ee --- /dev/null +++ b/builds/web/GGJ - Bubbles.180x180.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ocebisaitg35" +path="res://.godot/imported/GGJ - Bubbles.180x180.png-cd10d9edcb066f3113e62c984d9d2abc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.180x180.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.180x180.png-cd10d9edcb066f3113e62c984d9d2abc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.512x512.png b/builds/web/GGJ - Bubbles.512x512.png new file mode 100644 index 0000000..7f22ccb Binary files /dev/null and b/builds/web/GGJ - Bubbles.512x512.png differ diff --git a/builds/web/GGJ - Bubbles.512x512.png.import b/builds/web/GGJ - Bubbles.512x512.png.import new file mode 100644 index 0000000..9dedc1e --- /dev/null +++ b/builds/web/GGJ - Bubbles.512x512.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxx4rg4oem6p6" +path="res://.godot/imported/GGJ - Bubbles.512x512.png-ec02c84f91e4c8d3c49c7fb617fc14b7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.512x512.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.512x512.png-ec02c84f91e4c8d3c49c7fb617fc14b7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.apple-touch-icon.png b/builds/web/GGJ - Bubbles.apple-touch-icon.png new file mode 100644 index 0000000..e565555 Binary files /dev/null and b/builds/web/GGJ - Bubbles.apple-touch-icon.png differ diff --git a/builds/web/GGJ - Bubbles.apple-touch-icon.png.import b/builds/web/GGJ - Bubbles.apple-touch-icon.png.import new file mode 100644 index 0000000..33897c4 --- /dev/null +++ b/builds/web/GGJ - Bubbles.apple-touch-icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c41i6q5rd4tss" +path="res://.godot/imported/GGJ - Bubbles.apple-touch-icon.png-0d831b22fac2eac04acb39c2fea6bf6f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.apple-touch-icon.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.apple-touch-icon.png-0d831b22fac2eac04acb39c2fea6bf6f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.audio.position.worklet.js b/builds/web/GGJ - Bubbles.audio.position.worklet.js new file mode 100644 index 0000000..bf3ac4a --- /dev/null +++ b/builds/web/GGJ - Bubbles.audio.position.worklet.js @@ -0,0 +1,50 @@ +/**************************************************************************/ +/* godot.audio.position.worklet.js */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +class GodotPositionReportingProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.position = 0; + } + + process(inputs, _outputs, _parameters) { + if (inputs.length > 0) { + const input = inputs[0]; + if (input.length > 0) { + this.position += input[0].length; + this.port.postMessage({ 'type': 'position', 'data': this.position }); + return true; + } + } + return true; + } +} + +registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor); diff --git a/builds/web/GGJ - Bubbles.audio.worklet.js b/builds/web/GGJ - Bubbles.audio.worklet.js new file mode 100644 index 0000000..3b94cab --- /dev/null +++ b/builds/web/GGJ - Bubbles.audio.worklet.js @@ -0,0 +1,213 @@ +/**************************************************************************/ +/* audio.worklet.js */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +class RingBuffer { + constructor(p_buffer, p_state, p_threads) { + this.buffer = p_buffer; + this.avail = p_state; + this.threads = p_threads; + this.rpos = 0; + this.wpos = 0; + } + + data_left() { + return this.threads ? Atomics.load(this.avail, 0) : this.avail; + } + + space_left() { + return this.buffer.length - this.data_left(); + } + + read(output) { + const size = this.buffer.length; + let from = 0; + let to_write = output.length; + if (this.rpos + to_write > size) { + const high = size - this.rpos; + output.set(this.buffer.subarray(this.rpos, size)); + from = high; + to_write -= high; + this.rpos = 0; + } + if (to_write) { + output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from); + } + this.rpos += to_write; + if (this.threads) { + Atomics.add(this.avail, 0, -output.length); + Atomics.notify(this.avail, 0); + } else { + this.avail -= output.length; + } + } + + write(p_buffer) { + const to_write = p_buffer.length; + const mw = this.buffer.length - this.wpos; + if (mw >= to_write) { + this.buffer.set(p_buffer, this.wpos); + this.wpos += to_write; + if (mw === to_write) { + this.wpos = 0; + } + } else { + const high = p_buffer.subarray(0, mw); + const low = p_buffer.subarray(mw); + this.buffer.set(high, this.wpos); + this.buffer.set(low); + this.wpos = low.length; + } + if (this.threads) { + Atomics.add(this.avail, 0, to_write); + Atomics.notify(this.avail, 0); + } else { + this.avail += to_write; + } + } +} + +class GodotProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.threads = false; + this.running = true; + this.lock = null; + this.notifier = null; + this.output = null; + this.output_buffer = new Float32Array(); + this.input = null; + this.input_buffer = new Float32Array(); + this.port.onmessage = (event) => { + const cmd = event.data['cmd']; + const data = event.data['data']; + this.parse_message(cmd, data); + }; + } + + process_notify() { + if (this.notifier) { + Atomics.add(this.notifier, 0, 1); + Atomics.notify(this.notifier, 0); + } + } + + parse_message(p_cmd, p_data) { + if (p_cmd === 'start' && p_data) { + const state = p_data[0]; + let idx = 0; + this.threads = true; + this.lock = state.subarray(idx, ++idx); + this.notifier = state.subarray(idx, ++idx); + const avail_in = state.subarray(idx, ++idx); + const avail_out = state.subarray(idx, ++idx); + this.input = new RingBuffer(p_data[1], avail_in, true); + this.output = new RingBuffer(p_data[2], avail_out, true); + } else if (p_cmd === 'stop') { + this.running = false; + this.output = null; + this.input = null; + this.lock = null; + this.notifier = null; + } else if (p_cmd === 'start_nothreads') { + this.output = new RingBuffer(p_data[0], p_data[0].length, false); + } else if (p_cmd === 'chunk') { + this.output.write(p_data); + } + } + + static array_has_data(arr) { + return arr.length && arr[0].length && arr[0][0].length; + } + + process(inputs, outputs, parameters) { + if (!this.running) { + return false; // Stop processing. + } + if (this.output === null) { + return true; // Not ready yet, keep processing. + } + const process_input = GodotProcessor.array_has_data(inputs); + if (process_input) { + const input = inputs[0]; + const chunk = input[0].length * input.length; + if (this.input_buffer.length !== chunk) { + this.input_buffer = new Float32Array(chunk); + } + if (!this.threads) { + GodotProcessor.write_input(this.input_buffer, input); + this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer }); + } else if (this.input.space_left() >= chunk) { + GodotProcessor.write_input(this.input_buffer, input); + this.input.write(this.input_buffer); + } else { + // this.port.postMessage('Input buffer is full! Skipping input frame.'); // Uncomment this line to debug input buffer. + } + } + const process_output = GodotProcessor.array_has_data(outputs); + if (process_output) { + const output = outputs[0]; + const chunk = output[0].length * output.length; + if (this.output_buffer.length !== chunk) { + this.output_buffer = new Float32Array(chunk); + } + if (this.output.data_left() >= chunk) { + this.output.read(this.output_buffer); + GodotProcessor.write_output(output, this.output_buffer); + if (!this.threads) { + this.port.postMessage({ 'cmd': 'read', 'data': chunk }); + } + } else { + // this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); // Uncomment this line to debug output buffer. + } + } + this.process_notify(); + return true; + } + + static write_output(dest, source) { + const channels = dest.length; + for (let ch = 0; ch < channels; ch++) { + for (let sample = 0; sample < dest[ch].length; sample++) { + dest[ch][sample] = source[sample * channels + ch]; + } + } + } + + static write_input(dest, source) { + const channels = source.length; + for (let ch = 0; ch < channels; ch++) { + for (let sample = 0; sample < source[ch].length; sample++) { + dest[sample * channels + ch] = source[ch][sample]; + } + } + } +} + +registerProcessor('godot-processor', GodotProcessor); diff --git a/builds/web/GGJ - Bubbles.html b/builds/web/GGJ - Bubbles.html new file mode 100644 index 0000000..5830950 --- /dev/null +++ b/builds/web/GGJ - Bubbles.html @@ -0,0 +1,221 @@ + + + + + + GGJ - Bubbles + + + + + + + + + Your browser does not support the canvas tag. + + + + +
+ + +
+
+ + + + + + diff --git a/builds/web/GGJ - Bubbles.icon.png b/builds/web/GGJ - Bubbles.icon.png new file mode 100644 index 0000000..83d9bdf Binary files /dev/null and b/builds/web/GGJ - Bubbles.icon.png differ diff --git a/builds/web/GGJ - Bubbles.icon.png.import b/builds/web/GGJ - Bubbles.icon.png.import new file mode 100644 index 0000000..ffc1e71 --- /dev/null +++ b/builds/web/GGJ - Bubbles.icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://yxban28bmjf1" +path="res://.godot/imported/GGJ - Bubbles.icon.png-c284c86c37a2b506e44f68150fb66078.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.icon.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.icon.png-c284c86c37a2b506e44f68150fb66078.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.js b/builds/web/GGJ - Bubbles.js new file mode 100644 index 0000000..b1d81be --- /dev/null +++ b/builds/web/GGJ - Bubbles.js @@ -0,0 +1,912 @@ + +var Godot = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){___funcs_on_exit();callRuntimeCallbacks(__ATEXIT__);FS.quit();TTY.shutdown();IDBFS.quit();runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){var f="godot.web.template_debug.wasm32.nothreads.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["hf"];updateMemoryViews();wasmTable=wasmExports["rf"];addOnInit(wasmExports["jf"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=Module["noExitRuntime"]||false;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var wasmTable;var getWasmTableEntry=funcPtr=>wasmTable.get(funcPtr);var ___call_sighandler=(fp,sig)=>getWasmTableEntry(fp)(sig);var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else abort("initRandomDevice")};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url).then(arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},err=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var IDBFS={dbs:{},indexedDB:()=>{if(typeof indexedDB!="undefined")return indexedDB;var ret=null;if(typeof window=="object")ret=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;return ret},DB_VERSION:21,DB_STORE_NAME:"FILE_DATA",queuePersist:mount=>{function onPersistComplete(){if(mount.idbPersistState==="again")startPersist();else mount.idbPersistState=0}function startPersist(){mount.idbPersistState="idb";IDBFS.syncfs(mount,false,onPersistComplete)}if(!mount.idbPersistState){mount.idbPersistState=setTimeout(startPersist,0)}else if(mount.idbPersistState==="idb"){mount.idbPersistState="again"}},mount:mount=>{var mnt=MEMFS.mount(mount);if(mount?.opts?.autoPersist){mnt.idbPersistState=0;var memfs_node_ops=mnt.node_ops;mnt.node_ops=Object.assign({},mnt.node_ops);mnt.node_ops.mknod=(parent,name,mode,dev)=>{var node=memfs_node_ops.mknod(parent,name,mode,dev);node.node_ops=mnt.node_ops;node.idbfs_mount=mnt.mount;node.memfs_stream_ops=node.stream_ops;node.stream_ops=Object.assign({},node.stream_ops);node.stream_ops.write=(stream,buffer,offset,length,position,canOwn)=>{stream.node.isModified=true;return node.memfs_stream_ops.write(stream,buffer,offset,length,position,canOwn)};node.stream_ops.close=stream=>{var n=stream.node;if(n.isModified){IDBFS.queuePersist(n.idbfs_mount);n.isModified=false}if(n.memfs_stream_ops.close)return n.memfs_stream_ops.close(stream)};return node};mnt.node_ops.mkdir=(...args)=>(IDBFS.queuePersist(mnt.mount),memfs_node_ops.mkdir(...args));mnt.node_ops.rmdir=(...args)=>(IDBFS.queuePersist(mnt.mount),memfs_node_ops.rmdir(...args));mnt.node_ops.symlink=(...args)=>(IDBFS.queuePersist(mnt.mount),memfs_node_ops.symlink(...args));mnt.node_ops.unlink=(...args)=>(IDBFS.queuePersist(mnt.mount),memfs_node_ops.unlink(...args));mnt.node_ops.rename=(...args)=>(IDBFS.queuePersist(mnt.mount),memfs_node_ops.rename(...args))}return mnt},syncfs:(mount,populate,callback)=>{IDBFS.getLocalSet(mount,(err,local)=>{if(err)return callback(err);IDBFS.getRemoteSet(mount,(err,remote)=>{if(err)return callback(err);var src=populate?remote:local;var dst=populate?local:remote;IDBFS.reconcile(src,dst,callback)})})},quit:()=>{Object.values(IDBFS.dbs).forEach(value=>value.close());IDBFS.dbs={}},getDB:(name,callback)=>{var db=IDBFS.dbs[name];if(db){return callback(null,db)}var req;try{req=IDBFS.indexedDB().open(name,IDBFS.DB_VERSION)}catch(e){return callback(e)}if(!req){return callback("Unable to connect to IndexedDB")}req.onupgradeneeded=e=>{var db=e.target.result;var transaction=e.target.transaction;var fileStore;if(db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)){fileStore=transaction.objectStore(IDBFS.DB_STORE_NAME)}else{fileStore=db.createObjectStore(IDBFS.DB_STORE_NAME)}if(!fileStore.indexNames.contains("timestamp")){fileStore.createIndex("timestamp","timestamp",{unique:false})}};req.onsuccess=()=>{db=req.result;IDBFS.dbs[name]=db;callback(null,db)};req.onerror=e=>{callback(e.target.error);e.preventDefault()}},getLocalSet:(mount,callback)=>{var entries={};function isRealDir(p){return p!=="."&&p!==".."}function toAbsolute(root){return p=>PATH.join2(root,p)}var check=FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint));while(check.length){var path=check.pop();var stat;try{stat=FS.stat(path)}catch(e){return callback(e)}if(FS.isDir(stat.mode)){check.push(...FS.readdir(path).filter(isRealDir).map(toAbsolute(path)))}entries[path]={timestamp:stat.mtime}}return callback(null,{type:"local",entries:entries})},getRemoteSet:(mount,callback)=>{var entries={};IDBFS.getDB(mount.mountpoint,(err,db)=>{if(err)return callback(err);try{var transaction=db.transaction([IDBFS.DB_STORE_NAME],"readonly");transaction.onerror=e=>{callback(e.target.error);e.preventDefault()};var store=transaction.objectStore(IDBFS.DB_STORE_NAME);var index=store.index("timestamp");index.openKeyCursor().onsuccess=event=>{var cursor=event.target.result;if(!cursor){return callback(null,{type:"remote",db:db,entries:entries})}entries[cursor.primaryKey]={timestamp:cursor.key};cursor.continue()}}catch(e){return callback(e)}})},loadLocalEntry:(path,callback)=>{var stat,node;try{var lookup=FS.lookupPath(path);node=lookup.node;stat=FS.stat(path)}catch(e){return callback(e)}if(FS.isDir(stat.mode)){return callback(null,{timestamp:stat.mtime,mode:stat.mode})}else if(FS.isFile(stat.mode)){node.contents=MEMFS.getFileDataAsTypedArray(node);return callback(null,{timestamp:stat.mtime,mode:stat.mode,contents:node.contents})}else{return callback(new Error("node type not supported"))}},storeLocalEntry:(path,entry,callback)=>{try{if(FS.isDir(entry["mode"])){FS.mkdirTree(path,entry["mode"])}else if(FS.isFile(entry["mode"])){FS.writeFile(path,entry["contents"],{canOwn:true})}else{return callback(new Error("node type not supported"))}FS.chmod(path,entry["mode"]);FS.utime(path,entry["timestamp"],entry["timestamp"])}catch(e){return callback(e)}callback(null)},removeLocalEntry:(path,callback)=>{try{var stat=FS.stat(path);if(FS.isDir(stat.mode)){FS.rmdir(path)}else if(FS.isFile(stat.mode)){FS.unlink(path)}}catch(e){return callback(e)}callback(null)},loadRemoteEntry:(store,path,callback)=>{var req=store.get(path);req.onsuccess=event=>callback(null,event.target.result);req.onerror=e=>{callback(e.target.error);e.preventDefault()}},storeRemoteEntry:(store,path,entry,callback)=>{try{var req=store.put(entry,path)}catch(e){callback(e);return}req.onsuccess=event=>callback();req.onerror=e=>{callback(e.target.error);e.preventDefault()}},removeRemoteEntry:(store,path,callback)=>{var req=store.delete(path);req.onsuccess=event=>callback();req.onerror=e=>{callback(e.target.error);e.preventDefault()}},reconcile:(src,dst,callback)=>{var total=0;var create=[];Object.keys(src.entries).forEach(function(key){var e=src.entries[key];var e2=dst.entries[key];if(!e2||e["timestamp"].getTime()!=e2["timestamp"].getTime()){create.push(key);total++}});var remove=[];Object.keys(dst.entries).forEach(function(key){if(!src.entries[key]){remove.push(key);total++}});if(!total){return callback(null)}var errored=false;var db=src.type==="remote"?src.db:dst.db;var transaction=db.transaction([IDBFS.DB_STORE_NAME],"readwrite");var store=transaction.objectStore(IDBFS.DB_STORE_NAME);function done(err){if(err&&!errored){errored=true;return callback(err)}}transaction.onerror=transaction.onabort=e=>{done(e.target.error);e.preventDefault()};transaction.oncomplete=e=>{if(!errored){callback(null)}};create.sort().forEach(path=>{if(dst.type==="local"){IDBFS.loadRemoteEntry(store,path,(err,entry)=>{if(err)return done(err);IDBFS.storeLocalEntry(path,entry,done)})}else{IDBFS.loadLocalEntry(path,(err,entry)=>{if(err)return done(err);IDBFS.storeRemoteEntry(store,path,entry,done)})}});remove.sort().reverse().forEach(path=>{if(dst.type==="local"){IDBFS.removeLocalEntry(path,done)}else{IDBFS.removeRemoteEntry(store,path,done)}})}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS:MEMFS,IDBFS:IDBFS}},init(input,output,error){FS.init.initialized=true;Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit(){FS.init.initialized=false;_fflush(0);for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_chdir(path){try{path=SYSCALLS.getStr(path);FS.chdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_chmod(path,mode){try{path=SYSCALLS.getStr(path);FS.chmod(path,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fchmod(fd,mode){try{FS.fchmod(fd,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function syscallGetVarargI(){var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret}var syscallGetVarargP=syscallGetVarargI;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function ___syscall_ftruncate64(fd,length){length=bigintToI53Checked(length);try{if(isNaN(length))return 61;FS.ftruncate(fd,length);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(size>3]=BigInt(id);HEAP64[dirp+pos+8>>3]=BigInt((idx+1)*struct_size);HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag:c_iflag,c_oflag:c_oflag,c_cflag:c_cflag,c_lflag:c_lflag,c_cc:c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.lstat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_mkdirat(dirfd,path,mode){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_mknodat(dirfd,path,mode,dev){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.doStat(nofollow?FS.lstat:FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_readlinkat(dirfd,path,buf,bufsize){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_renameat(olddirfd,oldpath,newdirfd,newpath){try{oldpath=SYSCALLS.getStr(oldpath);newpath=SYSCALLS.getStr(newpath);oldpath=SYSCALLS.calculateAt(olddirfd,oldpath);newpath=SYSCALLS.calculateAt(newdirfd,newpath);FS.rename(oldpath,newpath);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_statfs64(path,size,buf){try{path=SYSCALLS.getStr(path);HEAP32[buf+4>>2]=4096;HEAP32[buf+40>>2]=4096;HEAP32[buf+8>>2]=1e6;HEAP32[buf+12>>2]=5e5;HEAP32[buf+16>>2]=5e5;HEAP32[buf+20>>2]=FS.nextInode;HEAP32[buf+24>>2]=1e6;HEAP32[buf+28>>2]=42;HEAP32[buf+44>>2]=2;HEAP32[buf+36>>2]=255;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_symlink(target,linkpath){try{target=SYSCALLS.getStr(target);linkpath=SYSCALLS.getStr(linkpath);FS.symlink(target,linkpath);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(flags===0){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{abort("Invalid flags passed to unlinkat")}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>{abort("")};var nowIsMonotonic=1;var __emscripten_get_now_is_monotonic=()=>nowIsMonotonic;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset{runtimeKeepaliveCounter+=1};var _emscripten_set_main_loop_timing=(mode,value)=>{Browser.mainLoop.timingMode=mode;Browser.mainLoop.timingValue=value;if(!Browser.mainLoop.func){return 1}if(!Browser.mainLoop.running){runtimeKeepalivePush();Browser.mainLoop.running=true}if(mode==0){Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_setTimeout(){var timeUntilNextTick=Math.max(0,Browser.mainLoop.tickStartTime+value-_emscripten_get_now())|0;setTimeout(Browser.mainLoop.runner,timeUntilNextTick)};Browser.mainLoop.method="timeout"}else if(mode==1){Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_rAF(){Browser.requestAnimationFrame(Browser.mainLoop.runner)};Browser.mainLoop.method="rAF"}else if(mode==2){if(typeof Browser.setImmediate=="undefined"){if(typeof setImmediate=="undefined"){var setImmediates=[];var emscriptenMainLoopMessageId="setimmediate";var Browser_setImmediate_messageHandler=event=>{if(event.data===emscriptenMainLoopMessageId||event.data.target===emscriptenMainLoopMessageId){event.stopPropagation();setImmediates.shift()()}};addEventListener("message",Browser_setImmediate_messageHandler,true);Browser.setImmediate=function Browser_emulated_setImmediate(func){setImmediates.push(func);if(ENVIRONMENT_IS_WORKER){Module["setImmediates"]??=[];Module["setImmediates"].push(func);postMessage({target:emscriptenMainLoopMessageId})}else postMessage(emscriptenMainLoopMessageId,"*")}}else{Browser.setImmediate=setImmediate}}Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_setImmediate(){Browser.setImmediate(Browser.mainLoop.runner)};Browser.mainLoop.method="immediate"}return 0};var _emscripten_get_now;_emscripten_get_now=()=>performance.now();var webgl_enable_ANGLE_instanced_arrays=ctx=>{var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=(index,divisor)=>ext["vertexAttribDivisorANGLE"](index,divisor);ctx["drawArraysInstanced"]=(mode,first,count,primcount)=>ext["drawArraysInstancedANGLE"](mode,first,count,primcount);ctx["drawElementsInstanced"]=(mode,count,type,indices,primcount)=>ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount);return 1}};var webgl_enable_OES_vertex_array_object=ctx=>{var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=()=>ext["createVertexArrayOES"]();ctx["deleteVertexArray"]=vao=>ext["deleteVertexArrayOES"](vao);ctx["bindVertexArray"]=vao=>ext["bindVertexArrayOES"](vao);ctx["isVertexArray"]=vao=>ext["isVertexArrayOES"](vao);return 1}};var webgl_enable_WEBGL_draw_buffers=ctx=>{var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=(n,bufs)=>ext["drawBuffersWEBGL"](n,bufs);return 1}};var webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"));var webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"));var webgl_enable_WEBGL_multi_draw=ctx=>!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"));var getEmscriptenSupportedExtensions=ctx=>{var supportedExtensions=["ANGLE_instanced_arrays","EXT_blend_minmax","EXT_disjoint_timer_query","EXT_frag_depth","EXT_shader_texture_lod","EXT_sRGB","OES_element_index_uint","OES_fbo_render_mipmap","OES_standard_derivatives","OES_texture_float","OES_texture_half_float","OES_texture_half_float_linear","OES_vertex_array_object","WEBGL_color_buffer_float","WEBGL_depth_texture","WEBGL_draw_buffers","EXT_color_buffer_float","EXT_conservative_depth","EXT_disjoint_timer_query_webgl2","EXT_texture_norm16","NV_shader_noperspective_interpolation","WEBGL_clip_cull_distance","EXT_color_buffer_half_float","EXT_depth_clamp","EXT_float_blend","EXT_texture_compression_bptc","EXT_texture_compression_rgtc","EXT_texture_filter_anisotropic","KHR_parallel_shader_compile","OES_texture_float_linear","WEBGL_blend_func_extended","WEBGL_compressed_texture_astc","WEBGL_compressed_texture_etc","WEBGL_compressed_texture_etc1","WEBGL_compressed_texture_s3tc","WEBGL_compressed_texture_s3tc_srgb","WEBGL_debug_renderer_info","WEBGL_debug_shaders","WEBGL_lose_context","WEBGL_multi_draw"];return(ctx.getSupportedExtensions()||[]).filter(ext=>supportedExtensions.includes(ext))};var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],stringCache:{},stringiCache:{},unpackAlignment:4,unpackRowLength:0,recordError:errorCode=>{if(!GL.lastError){GL.lastError=errorCode}},getNewId:table=>{var ret=GL.counter++;for(var i=table.length;i{for(var i=0;i>2]=id}},getSource:(shader,count,string,length)=>{var source="";for(var i=0;i>2]:undefined;source+=UTF8ToString(HEAPU32[string+i*4>>2],len)}return source},createContext:(canvas,webGLContextAttributes)=>{if(webGLContextAttributes.renderViaOffscreenBackBuffer)webGLContextAttributes["preserveDrawingBuffer"]=true;var ctx=webGLContextAttributes.majorVersion>1?canvas.getContext("webgl2",webGLContextAttributes):canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},enableOffscreenFramebufferAttributes:webGLContextAttributes=>{webGLContextAttributes.renderViaOffscreenBackBuffer=true;webGLContextAttributes.preserveDrawingBuffer=true},createOffscreenFramebuffer:context=>{var gl=context.GLctx;var fbo=gl.createFramebuffer();gl.bindFramebuffer(36160,fbo);context.defaultFbo=fbo;context.defaultFboForbidBlitFramebuffer=false;if(gl.getContextAttributes().antialias){context.defaultFboForbidBlitFramebuffer=true}context.defaultColorTarget=gl.createTexture();context.defaultDepthTarget=gl.createRenderbuffer();GL.resizeOffscreenFramebuffer(context);gl.bindTexture(3553,context.defaultColorTarget);gl.texParameteri(3553,10241,9728);gl.texParameteri(3553,10240,9728);gl.texParameteri(3553,10242,33071);gl.texParameteri(3553,10243,33071);gl.texImage2D(3553,0,6408,gl.canvas.width,gl.canvas.height,0,6408,5121,null);gl.framebufferTexture2D(36160,36064,3553,context.defaultColorTarget,0);gl.bindTexture(3553,null);var depthTarget=gl.createRenderbuffer();gl.bindRenderbuffer(36161,context.defaultDepthTarget);gl.renderbufferStorage(36161,33189,gl.canvas.width,gl.canvas.height);gl.framebufferRenderbuffer(36160,36096,36161,context.defaultDepthTarget);gl.bindRenderbuffer(36161,null);var vertices=[-1,-1,-1,1,1,-1,1,1];var vb=gl.createBuffer();gl.bindBuffer(34962,vb);gl.bufferData(34962,new Float32Array(vertices),35044);gl.bindBuffer(34962,null);context.blitVB=vb;var vsCode="attribute vec2 pos;"+"varying lowp vec2 tex;"+"void main() { tex = pos * 0.5 + vec2(0.5,0.5); gl_Position = vec4(pos, 0.0, 1.0); }";var vs=gl.createShader(35633);gl.shaderSource(vs,vsCode);gl.compileShader(vs);var fsCode="varying lowp vec2 tex;"+"uniform sampler2D sampler;"+"void main() { gl_FragColor = texture2D(sampler, tex); }";var fs=gl.createShader(35632);gl.shaderSource(fs,fsCode);gl.compileShader(fs);var blitProgram=gl.createProgram();gl.attachShader(blitProgram,vs);gl.attachShader(blitProgram,fs);gl.linkProgram(blitProgram);context.blitProgram=blitProgram;context.blitPosLoc=gl.getAttribLocation(blitProgram,"pos");gl.useProgram(blitProgram);gl.uniform1i(gl.getUniformLocation(blitProgram,"sampler"),0);gl.useProgram(null);context.defaultVao=undefined;if(gl.createVertexArray){context.defaultVao=gl.createVertexArray();gl.bindVertexArray(context.defaultVao);gl.enableVertexAttribArray(context.blitPosLoc);gl.bindVertexArray(null)}},resizeOffscreenFramebuffer:context=>{var gl=context.GLctx;if(context.defaultColorTarget){var prevTextureBinding=gl.getParameter(32873);gl.bindTexture(3553,context.defaultColorTarget);gl.texImage2D(3553,0,6408,gl.drawingBufferWidth,gl.drawingBufferHeight,0,6408,5121,null);gl.bindTexture(3553,prevTextureBinding)}if(context.defaultDepthTarget){var prevRenderBufferBinding=gl.getParameter(36007);gl.bindRenderbuffer(36161,context.defaultDepthTarget);gl.renderbufferStorage(36161,33189,gl.drawingBufferWidth,gl.drawingBufferHeight);gl.bindRenderbuffer(36161,prevRenderBufferBinding)}},blitOffscreenFramebuffer:context=>{var gl=context.GLctx;var prevScissorTest=gl.getParameter(3089);if(prevScissorTest)gl.disable(3089);var prevFbo=gl.getParameter(36006);if(gl.blitFramebuffer&&!context.defaultFboForbidBlitFramebuffer){gl.bindFramebuffer(36008,context.defaultFbo);gl.bindFramebuffer(36009,null);gl.blitFramebuffer(0,0,gl.canvas.width,gl.canvas.height,0,0,gl.canvas.width,gl.canvas.height,16384,9728)}else{gl.bindFramebuffer(36160,null);var prevProgram=gl.getParameter(35725);gl.useProgram(context.blitProgram);var prevVB=gl.getParameter(34964);gl.bindBuffer(34962,context.blitVB);var prevActiveTexture=gl.getParameter(34016);gl.activeTexture(33984);var prevTextureBinding=gl.getParameter(32873);gl.bindTexture(3553,context.defaultColorTarget);var prevBlend=gl.getParameter(3042);if(prevBlend)gl.disable(3042);var prevCullFace=gl.getParameter(2884);if(prevCullFace)gl.disable(2884);var prevDepthTest=gl.getParameter(2929);if(prevDepthTest)gl.disable(2929);var prevStencilTest=gl.getParameter(2960);if(prevStencilTest)gl.disable(2960);function draw(){gl.vertexAttribPointer(context.blitPosLoc,2,5126,false,0,0);gl.drawArrays(5,0,4)}if(context.defaultVao){var prevVAO=gl.getParameter(34229);gl.bindVertexArray(context.defaultVao);draw();gl.bindVertexArray(prevVAO)}else{var prevVertexAttribPointer={buffer:gl.getVertexAttrib(context.blitPosLoc,34975),size:gl.getVertexAttrib(context.blitPosLoc,34339),stride:gl.getVertexAttrib(context.blitPosLoc,34340),type:gl.getVertexAttrib(context.blitPosLoc,34341),normalized:gl.getVertexAttrib(context.blitPosLoc,34922),pointer:gl.getVertexAttribOffset(context.blitPosLoc,34373)};var maxVertexAttribs=gl.getParameter(34921);var prevVertexAttribEnables=[];for(var i=0;i{var handle=GL.getNewId(GL.contexts);var context={handle:handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault=="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}if(webGLContextAttributes.renderViaOffscreenBackBuffer)GL.createOffscreenFramebuffer(context);return handle},makeContextCurrent:contextHandle=>{GL.currentContext=GL.contexts[contextHandle];Module.ctx=GLctx=GL.currentContext?.GLctx;return!(contextHandle&&!GLctx)},getContext:contextHandle=>GL.contexts[contextHandle],deleteContext:contextHandle=>{if(GL.currentContext===GL.contexts[contextHandle]){GL.currentContext=null}if(typeof JSEvents=="object"){JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas)}if(GL.contexts[contextHandle]&&GL.contexts[contextHandle].GLctx.canvas){GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined}GL.contexts[contextHandle]=null},initExtensions:context=>{context||=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;webgl_enable_ANGLE_instanced_arrays(GLctx);webgl_enable_OES_vertex_array_object(GLctx);webgl_enable_WEBGL_draw_buffers(GLctx);webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);if(context.version>=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}webgl_enable_WEBGL_multi_draw(GLctx);getEmscriptenSupportedExtensions(GLctx).forEach(ext=>{if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}})}};var _emscripten_webgl_do_commit_frame=()=>{if(!GL.currentContext||!GL.currentContext.GLctx){return-3}if(GL.currentContext.defaultFbo){GL.blitOffscreenFramebuffer(GL.currentContext);return 0}if(!GL.currentContext.attributes.explicitSwapControl){return-3}return 0};var _emscripten_webgl_commit_frame=_emscripten_webgl_do_commit_frame;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;if(!keepRuntimeAlive()){exitRuntime()}_proc_exit(status)};var _exit=exitJS;var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var maybeExit=()=>{if(runtimeExited){return}if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var runtimeKeepalivePop=()=>{runtimeKeepaliveCounter-=1};var setMainLoop=(browserIterationFunc,fps,simulateInfiniteLoop,arg,noSetTiming)=>{Browser.mainLoop.func=browserIterationFunc;Browser.mainLoop.arg=arg;var thisMainLoopId=Browser.mainLoop.currentlyRunningMainloop;function checkIsRunning(){if(thisMainLoopId0){var start=Date.now();var blocker=Browser.mainLoop.queue.shift();blocker.func(blocker.arg);if(Browser.mainLoop.remainingBlockers){var remaining=Browser.mainLoop.remainingBlockers;var next=remaining%1==0?remaining-1:Math.floor(remaining);if(blocker.counted){Browser.mainLoop.remainingBlockers=next}else{next=next+.5;Browser.mainLoop.remainingBlockers=(8*remaining+next)/9}}Browser.mainLoop.updateStatus();if(!checkIsRunning())return;setTimeout(Browser.mainLoop.runner,0);return}if(!checkIsRunning())return;Browser.mainLoop.currentFrameNumber=Browser.mainLoop.currentFrameNumber+1|0;if(Browser.mainLoop.timingMode==1&&Browser.mainLoop.timingValue>1&&Browser.mainLoop.currentFrameNumber%Browser.mainLoop.timingValue!=0){Browser.mainLoop.scheduler();return}else if(Browser.mainLoop.timingMode==0){Browser.mainLoop.tickStartTime=_emscripten_get_now()}Browser.mainLoop.runIter(browserIterationFunc);if(!checkIsRunning())return;if(typeof SDL=="object")SDL.audio?.queueNewAudioData?.();Browser.mainLoop.scheduler()};if(!noSetTiming){if(fps&&fps>0){_emscripten_set_main_loop_timing(0,1e3/fps)}else{_emscripten_set_main_loop_timing(1,1)}Browser.mainLoop.scheduler()}if(simulateInfiniteLoop){throw"unwind"}};var callUserCallback=func=>{if(runtimeExited||ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var safeSetTimeout=(func,timeout)=>{runtimeKeepalivePush();return setTimeout(()=>{runtimeKeepalivePop();callUserCallback(func)},timeout)};var warnOnce=text=>{warnOnce.shown||={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}};var Browser={mainLoop:{running:false,scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause(){Browser.mainLoop.scheduler=null;Browser.mainLoop.currentlyRunningMainloop++},resume(){Browser.mainLoop.currentlyRunningMainloop++;var timingMode=Browser.mainLoop.timingMode;var timingValue=Browser.mainLoop.timingValue;var func=Browser.mainLoop.func;Browser.mainLoop.func=null;setMainLoop(func,0,false,Browser.mainLoop.arg,true);_emscripten_set_main_loop_timing(timingMode,timingValue);Browser.mainLoop.scheduler()},updateStatus(){if(Module["setStatus"]){var message=Module["statusMessage"]||"Please wait...";var remaining=Browser.mainLoop.remainingBlockers;var expected=Browser.mainLoop.expectedBlockers;if(remaining){if(remaining{var canvas=document.createElement("canvas");canvas.width=img.width;canvas.height=img.height;var ctx=canvas.getContext("2d");ctx.drawImage(img,0,0);preloadedImages[name]=canvas;URL.revokeObjectURL(url);onload?.(byteArray)};img.onerror=event=>{err(`Image ${url} could not be decoded`);onerror?.()};img.src=url};preloadPlugins.push(imagePlugin);var audioPlugin={};audioPlugin["canHandle"]=function audioPlugin_canHandle(name){return!Module.noAudioDecoding&&name.substr(-4)in{".ogg":1,".wav":1,".mp3":1}};audioPlugin["handle"]=function audioPlugin_handle(byteArray,name,onload,onerror){var done=false;function finish(audio){if(done)return;done=true;preloadedAudios[name]=audio;onload?.(byteArray)}var b=new Blob([byteArray],{type:Browser.getMimetype(name)});var url=URL.createObjectURL(b);var audio=new Audio;audio.addEventListener("canplaythrough",()=>finish(audio),false);audio.onerror=function audio_onerror(event){if(done)return;err(`warning: browser could not fully decode audio ${name}, trying slower base64 approach`);function encode64(data){var BASE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var PAD="=";var ret="";var leftchar=0;var leftbits=0;for(var i=0;i=6){var curr=leftchar>>leftbits-6&63;leftbits-=6;ret+=BASE[curr]}}if(leftbits==2){ret+=BASE[(leftchar&3)<<4];ret+=PAD+PAD}else if(leftbits==4){ret+=BASE[(leftchar&15)<<2];ret+=PAD}return ret}audio.src="data:audio/x-"+name.substr(-3)+";base64,"+encode64(byteArray);finish(audio)};audio.src=url;safeSetTimeout(()=>{finish(audio)},1e4)};preloadPlugins.push(audioPlugin);function pointerLockChange(){Browser.pointerLock=document["pointerLockElement"]===Module["canvas"]||document["mozPointerLockElement"]===Module["canvas"]||document["webkitPointerLockElement"]===Module["canvas"]||document["msPointerLockElement"]===Module["canvas"]}var canvas=Module["canvas"];if(canvas){canvas.requestPointerLock=canvas["requestPointerLock"]||canvas["mozRequestPointerLock"]||canvas["webkitRequestPointerLock"]||canvas["msRequestPointerLock"]||(()=>{});canvas.exitPointerLock=document["exitPointerLock"]||document["mozExitPointerLock"]||document["webkitExitPointerLock"]||document["msExitPointerLock"]||(()=>{});canvas.exitPointerLock=canvas.exitPointerLock.bind(document);document.addEventListener("pointerlockchange",pointerLockChange,false);document.addEventListener("mozpointerlockchange",pointerLockChange,false);document.addEventListener("webkitpointerlockchange",pointerLockChange,false);document.addEventListener("mspointerlockchange",pointerLockChange,false);if(Module["elementPointerLock"]){canvas.addEventListener("click",ev=>{if(!Browser.pointerLock&&Module["canvas"].requestPointerLock){Module["canvas"].requestPointerLock();ev.preventDefault()}},false)}}},createContext(canvas,useWebGL,setInModule,webGLContextAttributes){if(useWebGL&&Module.ctx&&canvas==Module.canvas)return Module.ctx;var ctx;var contextHandle;if(useWebGL){var contextAttributes={antialias:false,alpha:false,majorVersion:typeof WebGL2RenderingContext!="undefined"?2:1};if(webGLContextAttributes){for(var attribute in webGLContextAttributes){contextAttributes[attribute]=webGLContextAttributes[attribute]}}if(typeof GL!="undefined"){contextHandle=GL.createContext(canvas,contextAttributes);if(contextHandle){ctx=GL.getContext(contextHandle).GLctx}}}else{ctx=canvas.getContext("2d")}if(!ctx)return null;if(setInModule){Module.ctx=ctx;if(useWebGL)GL.makeContextCurrent(contextHandle);Module.useWebGL=useWebGL;Browser.moduleContextCreatedCallbacks.forEach(callback=>callback());Browser.init()}return ctx},destroyContext(canvas,useWebGL,setInModule){},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen(lockPointer,resizeCanvas){Browser.lockPointer=lockPointer;Browser.resizeCanvas=resizeCanvas;if(typeof Browser.lockPointer=="undefined")Browser.lockPointer=true;if(typeof Browser.resizeCanvas=="undefined")Browser.resizeCanvas=false;var canvas=Module["canvas"];function fullscreenChange(){Browser.isFullscreen=false;var canvasContainer=canvas.parentNode;if((document["fullscreenElement"]||document["mozFullScreenElement"]||document["msFullscreenElement"]||document["webkitFullscreenElement"]||document["webkitCurrentFullScreenElement"])===canvasContainer){canvas.exitFullscreen=Browser.exitFullscreen;if(Browser.lockPointer)canvas.requestPointerLock();Browser.isFullscreen=true;if(Browser.resizeCanvas){Browser.setFullscreenCanvasSize()}else{Browser.updateCanvasDimensions(canvas)}}else{canvasContainer.parentNode.insertBefore(canvas,canvasContainer);canvasContainer.parentNode.removeChild(canvasContainer);if(Browser.resizeCanvas){Browser.setWindowedCanvasSize()}else{Browser.updateCanvasDimensions(canvas)}}Module["onFullScreen"]?.(Browser.isFullscreen);Module["onFullscreen"]?.(Browser.isFullscreen)}if(!Browser.fullscreenHandlersInstalled){Browser.fullscreenHandlersInstalled=true;document.addEventListener("fullscreenchange",fullscreenChange,false);document.addEventListener("mozfullscreenchange",fullscreenChange,false);document.addEventListener("webkitfullscreenchange",fullscreenChange,false);document.addEventListener("MSFullscreenChange",fullscreenChange,false)}var canvasContainer=document.createElement("div");canvas.parentNode.insertBefore(canvasContainer,canvas);canvasContainer.appendChild(canvas);canvasContainer.requestFullscreen=canvasContainer["requestFullscreen"]||canvasContainer["mozRequestFullScreen"]||canvasContainer["msRequestFullscreen"]||(canvasContainer["webkitRequestFullscreen"]?()=>canvasContainer["webkitRequestFullscreen"](Element["ALLOW_KEYBOARD_INPUT"]):null)||(canvasContainer["webkitRequestFullScreen"]?()=>canvasContainer["webkitRequestFullScreen"](Element["ALLOW_KEYBOARD_INPUT"]):null);canvasContainer.requestFullscreen()},exitFullscreen(){if(!Browser.isFullscreen){return false}var CFS=document["exitFullscreen"]||document["cancelFullScreen"]||document["mozCancelFullScreen"]||document["msExitFullscreen"]||document["webkitCancelFullScreen"]||(()=>{});CFS.apply(document,[]);return true},nextRAF:0,fakeRequestAnimationFrame(func){var now=Date.now();if(Browser.nextRAF===0){Browser.nextRAF=now+1e3/60}else{while(now+2>=Browser.nextRAF){Browser.nextRAF+=1e3/60}}var delay=Math.max(Browser.nextRAF-now,0);setTimeout(func,delay)},requestAnimationFrame(func){if(typeof requestAnimationFrame=="function"){requestAnimationFrame(func);return}var RAF=Browser.fakeRequestAnimationFrame;RAF(func)},safeSetTimeout(func,timeout){return safeSetTimeout(func,timeout)},safeRequestAnimationFrame(func){runtimeKeepalivePush();return Browser.requestAnimationFrame(()=>{runtimeKeepalivePop();callUserCallback(func)})},getMimetype(name){return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",bmp:"image/bmp",ogg:"audio/ogg",wav:"audio/wav",mp3:"audio/mpeg"}[name.substr(name.lastIndexOf(".")+1)]},getUserMedia(func){window.getUserMedia||=navigator["getUserMedia"]||navigator["mozGetUserMedia"];window.getUserMedia(func)},getMovementX(event){return event["movementX"]||event["mozMovementX"]||event["webkitMovementX"]||0},getMovementY(event){return event["movementY"]||event["mozMovementY"]||event["webkitMovementY"]||0},getMouseWheelDelta(event){var delta=0;switch(event.type){case"DOMMouseScroll":delta=event.detail/3;break;case"mousewheel":delta=event.wheelDelta/120;break;case"wheel":delta=event.deltaY;switch(event.deltaMode){case 0:delta/=100;break;case 1:delta/=3;break;case 2:delta*=80;break;default:throw"unrecognized mouse wheel delta mode: "+event.deltaMode}break;default:throw"unrecognized mouse wheel event: "+event.type}return delta},mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseCoords(pageX,pageY){var rect=Module["canvas"].getBoundingClientRect();var cw=Module["canvas"].width;var ch=Module["canvas"].height;var scrollX=typeof window.scrollX!="undefined"?window.scrollX:window.pageXOffset;var scrollY=typeof window.scrollY!="undefined"?window.scrollY:window.pageYOffset;var adjustedX=pageX-(scrollX+rect.left);var adjustedY=pageY-(scrollY+rect.top);adjustedX=adjustedX*(cw/rect.width);adjustedY=adjustedY*(ch/rect.height);return{x:adjustedX,y:adjustedY}},setMouseCoords(pageX,pageY){const{x:x,y:y}=Browser.calculateMouseCoords(pageX,pageY);Browser.mouseMovementX=x-Browser.mouseX;Browser.mouseMovementY=y-Browser.mouseY;Browser.mouseX=x;Browser.mouseY=y},calculateMouseEvent(event){if(Browser.pointerLock){if(event.type!="mousemove"&&"mozMovementX"in event){Browser.mouseMovementX=Browser.mouseMovementY=0}else{Browser.mouseMovementX=Browser.getMovementX(event);Browser.mouseMovementY=Browser.getMovementY(event)}Browser.mouseX+=Browser.mouseMovementX;Browser.mouseY+=Browser.mouseMovementY}else{if(event.type==="touchstart"||event.type==="touchend"||event.type==="touchmove"){var touch=event.touch;if(touch===undefined){return}var coords=Browser.calculateMouseCoords(touch.pageX,touch.pageY);if(event.type==="touchstart"){Browser.lastTouches[touch.identifier]=coords;Browser.touches[touch.identifier]=coords}else if(event.type==="touchend"||event.type==="touchmove"){var last=Browser.touches[touch.identifier];last||=coords;Browser.lastTouches[touch.identifier]=last;Browser.touches[touch.identifier]=coords}return}Browser.setMouseCoords(event.pageX,event.pageY)}},resizeListeners:[],updateResizeListeners(){var canvas=Module["canvas"];Browser.resizeListeners.forEach(listener=>listener(canvas.width,canvas.height))},setCanvasSize(width,height,noUpdates){var canvas=Module["canvas"];Browser.updateCanvasDimensions(canvas,width,height);if(!noUpdates)Browser.updateResizeListeners()},windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize(){if(typeof SDL!="undefined"){var flags=HEAPU32[SDL.screen>>2];flags=flags|8388608;HEAP32[SDL.screen>>2]=flags}Browser.updateCanvasDimensions(Module["canvas"]);Browser.updateResizeListeners()},setWindowedCanvasSize(){if(typeof SDL!="undefined"){var flags=HEAPU32[SDL.screen>>2];flags=flags&~8388608;HEAP32[SDL.screen>>2]=flags}Browser.updateCanvasDimensions(Module["canvas"]);Browser.updateResizeListeners()},updateCanvasDimensions(canvas,wNative,hNative){if(wNative&&hNative){canvas.widthNative=wNative;canvas.heightNative=hNative}else{wNative=canvas.widthNative;hNative=canvas.heightNative}var w=wNative;var h=hNative;if(Module["forcedAspectRatio"]&&Module["forcedAspectRatio"]>0){if(w/h{Browser.mainLoop.pause();Browser.mainLoop.func=null};var _emscripten_date_now=()=>Date.now();var _emscripten_force_exit=status=>{__emscripten_runtime_keepalive_clear();_exit(status)};var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var JSEvents={removeAllEventListeners(){while(JSEvents.eventHandlers.length){JSEvents._removeHandler(JSEvents.eventHandlers.length-1)}JSEvents.deferredCalls=[]},registerRemoveEventListeners(){if(!JSEvents.removeEventListenersRegistered){__ATEXIT__.push(JSEvents.removeAllEventListeners);JSEvents.removeEventListenersRegistered=true}},inEventHandler:0,deferredCalls:[],deferCall(targetFunction,precedence,argsList){function arraysHaveEqualContent(arrA,arrB){if(arrA.length!=arrB.length)return false;for(var i in arrA){if(arrA[i]!=arrB[i])return false}return true}for(var call of JSEvents.deferredCalls){if(call.targetFunction==targetFunction&&arraysHaveEqualContent(call.argsList,argsList)){return}}JSEvents.deferredCalls.push({targetFunction:targetFunction,precedence:precedence,argsList:argsList});JSEvents.deferredCalls.sort((x,y)=>x.precedencecall.targetFunction!=targetFunction)},canPerformEventHandlerRequests(){if(navigator.userActivation){return navigator.userActivation.isActive}return JSEvents.inEventHandler&&JSEvents.currentEventHandler.allowsDeferredCalls},runDeferredCalls(){if(!JSEvents.canPerformEventHandlerRequests()){return}var deferredCalls=JSEvents.deferredCalls;JSEvents.deferredCalls=[];for(var call of deferredCalls){call.targetFunction(...call.argsList)}},eventHandlers:[],removeAllHandlersOnTarget:(target,eventTypeString)=>{for(var i=0;icString>2?UTF8ToString(cString):cString;var specialHTMLTargets=[0,typeof document!="undefined"?document:0,typeof window!="undefined"?window:0];var findEventTarget=target=>{target=maybeCStringToJsString(target);var domElement=specialHTMLTargets[target]||(typeof document!="undefined"?document.querySelector(target):undefined);return domElement};var findCanvasEventTarget=findEventTarget;var _emscripten_set_canvas_element_size=(target,width,height)=>{var canvas=findCanvasEventTarget(target);if(!canvas)return-4;canvas.width=width;canvas.height=height;if(canvas.GLctxObject)GL.resizeOffscreenFramebuffer(canvas.GLctxObject);return 0};var _emscripten_set_main_loop=(func,fps,simulateInfiniteLoop)=>{var browserIterationFunc=getWasmTableEntry(func);setMainLoop(browserIterationFunc,fps,simulateInfiniteLoop)};var webglPowerPreferences=["default","low-power","high-performance"];var _emscripten_webgl_do_create_context=(target,attributes)=>{var attr32=attributes>>2;var powerPreference=HEAP32[attr32+(8>>2)];var contextAttributes={alpha:!!HEAP8[attributes+0],depth:!!HEAP8[attributes+1],stencil:!!HEAP8[attributes+2],antialias:!!HEAP8[attributes+3],premultipliedAlpha:!!HEAP8[attributes+4],preserveDrawingBuffer:!!HEAP8[attributes+5],powerPreference:webglPowerPreferences[powerPreference],failIfMajorPerformanceCaveat:!!HEAP8[attributes+12],majorVersion:HEAP32[attr32+(16>>2)],minorVersion:HEAP32[attr32+(20>>2)],enableExtensionsByDefault:HEAP8[attributes+24],explicitSwapControl:HEAP8[attributes+25],proxyContextToMainThread:HEAP32[attr32+(28>>2)],renderViaOffscreenBackBuffer:HEAP8[attributes+32]};var canvas=findCanvasEventTarget(target);if(!canvas){return 0}if(contextAttributes.explicitSwapControl&&!contextAttributes.renderViaOffscreenBackBuffer){contextAttributes.renderViaOffscreenBackBuffer=true}var contextHandle=GL.createContext(canvas,contextAttributes);return contextHandle};var _emscripten_webgl_create_context=_emscripten_webgl_do_create_context;var _emscripten_webgl_destroy_context=contextHandle=>{if(GL.currentContext==contextHandle)GL.currentContext=0;GL.deleteContext(contextHandle)};var _emscripten_webgl_enable_extension=(contextHandle,extension)=>{var context=GL.getContext(contextHandle);var extString=UTF8ToString(extension);if(extString.startsWith("GL_"))extString=extString.substr(3);if(extString=="ANGLE_instanced_arrays")webgl_enable_ANGLE_instanced_arrays(GLctx);if(extString=="OES_vertex_array_object")webgl_enable_OES_vertex_array_object(GLctx);if(extString=="WEBGL_draw_buffers")webgl_enable_WEBGL_draw_buffers(GLctx);if(extString=="WEBGL_draw_instanced_base_vertex_base_instance")webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);if(extString=="WEBGL_multi_draw_instanced_base_vertex_base_instance")webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);if(extString=="WEBGL_multi_draw")webgl_enable_WEBGL_multi_draw(GLctx);var ext=context.GLctx.getExtension(extString);return!!ext};var stringToNewUTF8=str=>{var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret};var _emscripten_webgl_get_supported_extensions=()=>stringToNewUTF8(GLctx.getSupportedExtensions().join(" "));var _emscripten_webgl_make_context_current=contextHandle=>{var success=GL.makeContextCurrent(contextHandle);return success?0:-5};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4}HEAP8[pbuf]=type;HEAP16[pbuf+2>>1]=flags;HEAP64[pbuf+8>>3]=BigInt(rightsBase);HEAP64[pbuf+16>>3]=BigInt(rightsInheriting);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var _glActiveTexture=x0=>GLctx.activeTexture(x0);var _glAttachShader=(program,shader)=>{GLctx.attachShader(GL.programs[program],GL.shaders[shader])};var _glBeginTransformFeedback=x0=>GLctx.beginTransformFeedback(x0);var _glBindBuffer=(target,buffer)=>{if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])};var _glBindBufferBase=(target,index,buffer)=>{GLctx.bindBufferBase(target,index,GL.buffers[buffer])};var _glBindBufferRange=(target,index,buffer,offset,ptrsize)=>{GLctx.bindBufferRange(target,index,GL.buffers[buffer],offset,ptrsize)};var _glBindFramebuffer=(target,framebuffer)=>{GLctx.bindFramebuffer(target,framebuffer?GL.framebuffers[framebuffer]:GL.currentContext.defaultFbo)};var _glBindRenderbuffer=(target,renderbuffer)=>{GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])};var _glBindTexture=(target,texture)=>{GLctx.bindTexture(target,GL.textures[texture])};var _glBindVertexArray=vao=>{GLctx.bindVertexArray(GL.vaos[vao])};var _glBlendColor=(x0,x1,x2,x3)=>GLctx.blendColor(x0,x1,x2,x3);var _glBlendEquation=x0=>GLctx.blendEquation(x0);var _glBlendFunc=(x0,x1)=>GLctx.blendFunc(x0,x1);var _glBlendFuncSeparate=(x0,x1,x2,x3)=>GLctx.blendFuncSeparate(x0,x1,x2,x3);var _glBlitFramebuffer=(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)=>GLctx.blitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);var _glBufferData=(target,size,data,usage)=>{if(GL.currentContext.version>=2){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}return}GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)};var _glBufferSubData=(target,offset,size,data)=>{if(GL.currentContext.version>=2){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))};var _glCheckFramebufferStatus=x0=>GLctx.checkFramebufferStatus(x0);var _glClear=x0=>GLctx.clear(x0);var _glClearBufferfv=(buffer,drawbuffer,value)=>{GLctx.clearBufferfv(buffer,drawbuffer,HEAPF32,value>>2)};var _glClearColor=(x0,x1,x2,x3)=>GLctx.clearColor(x0,x1,x2,x3);var _glClearDepthf=x0=>GLctx.clearDepth(x0);var _glColorMask=(red,green,blue,alpha)=>{GLctx.colorMask(!!red,!!green,!!blue,!!alpha)};var _glCompileShader=shader=>{GLctx.compileShader(GL.shaders[shader])};var _glCompressedTexImage2D=(target,level,internalFormat,width,height,border,imageSize,data)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,HEAPU8,data,imageSize);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,data?HEAPU8.subarray(data,data+imageSize):null)};var _glCompressedTexImage3D=(target,level,internalFormat,width,height,depth,border,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,imageSize,data)}else{GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,HEAPU8,data,imageSize)}};var _glCompressedTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}};var _glCopyBufferSubData=(x0,x1,x2,x3,x4)=>GLctx.copyBufferSubData(x0,x1,x2,x3,x4);var _glCreateProgram=()=>{var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id};var _glCreateShader=shaderType=>{var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id};var _glCullFace=x0=>GLctx.cullFace(x0);var _glDeleteBuffers=(n,buffers)=>{for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}};var _glDeleteFramebuffers=(n,framebuffers)=>{for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}};var _glDeleteProgram=id=>{if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null};var _glDeleteQueries=(n,ids)=>{for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx.deleteQuery(query);GL.queries[id]=null}};var _glDeleteRenderbuffers=(n,renderbuffers)=>{for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}};var _glDeleteShader=id=>{if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null};var _glDeleteSync=id=>{if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null};var _glDeleteTextures=(n,textures)=>{for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}};var _glDeleteVertexArrays=(n,vaos)=>{for(var i=0;i>2];GLctx.deleteVertexArray(GL.vaos[id]);GL.vaos[id]=null}};var _glDepthFunc=x0=>GLctx.depthFunc(x0);var _glDepthMask=flag=>{GLctx.depthMask(!!flag)};var _glDisable=x0=>GLctx.disable(x0);var _glDisableVertexAttribArray=index=>{GLctx.disableVertexAttribArray(index)};var _glDrawArrays=(mode,first,count)=>{GLctx.drawArrays(mode,first,count)};var _glDrawArraysInstanced=(mode,first,count,primcount)=>{GLctx.drawArraysInstanced(mode,first,count,primcount)};var tempFixedLengthArray=[];var _glDrawBuffers=(n,bufs)=>{var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx.drawBuffers(bufArray)};var _glDrawElements=(mode,count,type,indices)=>{GLctx.drawElements(mode,count,type,indices)};var _glDrawElementsInstanced=(mode,count,type,indices,primcount)=>{GLctx.drawElementsInstanced(mode,count,type,indices,primcount)};var _glEnable=x0=>GLctx.enable(x0);var _glEnableVertexAttribArray=index=>{GLctx.enableVertexAttribArray(index)};var _glEndTransformFeedback=()=>GLctx.endTransformFeedback();var _glFenceSync=(condition,flags)=>{var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}return 0};var _glFinish=()=>GLctx.finish();var _glFramebufferRenderbuffer=(target,attachment,renderbuffertarget,renderbuffer)=>{GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])};var _glFramebufferTexture2D=(target,attachment,textarget,texture,level)=>{GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)};var _glFramebufferTextureLayer=(target,attachment,texture,level,layer)=>{GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)};var _glFrontFace=x0=>GLctx.frontFace(x0);var _glGenBuffers=(n,buffers)=>{GL.genObject(n,buffers,"createBuffer",GL.buffers)};var _glGenFramebuffers=(n,ids)=>{GL.genObject(n,ids,"createFramebuffer",GL.framebuffers)};var _glGenQueries=(n,ids)=>{GL.genObject(n,ids,"createQuery",GL.queries)};var _glGenRenderbuffers=(n,renderbuffers)=>{GL.genObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)};var _glGenTextures=(n,textures)=>{GL.genObject(n,textures,"createTexture",GL.textures)};var _glGenVertexArrays=(n,arrays)=>{GL.genObject(n,arrays,"createVertexArray",GL.vaos)};var _glGenerateMipmap=x0=>GLctx.generateMipmap(x0);var writeI53ToI64=(ptr,num)=>{HEAPU32[ptr>>2]=num;var lower=HEAPU32[ptr>>2];HEAPU32[ptr+4>>2]=(num-lower)/4294967296};var webglGetExtensions=function $webglGetExtensions(){var exts=getEmscriptenSupportedExtensions(GLctx);exts=exts.concat(exts.map(e=>"GL_"+e));return exts};var emscriptenWebGLGet=(name_,p,type)=>{if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}ret=webglGetExtensions().length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Unknown object returned from WebGL getParameter(${name_})! (error: ${e})`);return}}break;default:GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Native code calling glGet${type}v(${name_}) and it returns ${result} of type ${typeof result}!`);return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p]=ret?1:0;break}};var _glGetFloatv=(name_,p)=>emscriptenWebGLGet(name_,p,2);var _glGetInteger64v=(name_,p)=>{emscriptenWebGLGet(name_,p,1)};var _glGetIntegerv=(name_,p)=>emscriptenWebGLGet(name_,p,0);var _glGetProgramInfoLog=(program,maxLength,length,infoLog)=>{var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _glGetProgramiv=(program,pname,p)=>{if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}};var _glGetShaderInfoLog=(shader,maxLength,length,infoLog)=>{var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _glGetShaderiv=(shader,pname,p)=>{if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}};var _glGetString=name_=>{var ret=GL.stringCache[name_];if(!ret){switch(name_){case 7939:ret=stringToNewUTF8(webglGetExtensions().join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=s?stringToNewUTF8(s):0;break;case 7938:var glVersion=GLctx.getParameter(7938);if(GL.currentContext.version>=2)glVersion=`OpenGL ES 3.0 (${glVersion})`;else{glVersion=`OpenGL ES 2.0 (${glVersion})`}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion=`OpenGL ES GLSL ES ${ver_num[1]} (${glslVersion})`}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280)}GL.stringCache[name_]=ret}return ret};var _glGetSynciv=(sync,pname,bufSize,length,values)=>{if(bufSize<0){GL.recordError(1281);return}if(!values){GL.recordError(1281);return}var ret=GLctx.getSyncParameter(GL.syncs[sync],pname);if(ret!==null){HEAP32[values>>2]=ret;if(length)HEAP32[length>>2]=1}};var _glGetUniformBlockIndex=(program,uniformBlockName)=>GLctx.getUniformBlockIndex(GL.programs[program],UTF8ToString(uniformBlockName));var jstoi_q=str=>parseInt(str);var webglGetLeftBracePos=name=>name.slice(-1)=="]"&&name.lastIndexOf("[");var webglPrepareUniformLocationsBeforeFirstUse=program=>{var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j{name=UTF8ToString(name);if(program=GL.programs[program]){webglPrepareUniformLocationsBeforeFirstUse(program);var uniformLocsById=program.uniformLocsById;var arrayIndex=0;var uniformBaseName=name;var leftBrace=webglGetLeftBracePos(name);if(leftBrace>0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex{program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}};var _glPixelStorei=(pname,param)=>{if(pname==3317){GL.unpackAlignment=param}else if(pname==3314){GL.unpackRowLength=param}GLctx.pixelStorei(pname,param)};var _glReadBuffer=x0=>GLctx.readBuffer(x0);var computeUnpackAlignedImageSize=(width,height,sizePerPixel)=>{function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=(GL.unpackRowLength||width)*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,GL.unpackAlignment);return height*alignedRowSize};var colorChannelsInGlTextureFormat=format=>{var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1};var heapObjectForWebGLType=type=>{type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16};var toTypedArrayIndex=(pointer,heap)=>pointer>>>31-Math.clz32(heap.BYTES_PER_ELEMENT);var emscriptenWebGLGetTexPixelData=(type,format,width,height,pixels,internalFormat)=>{var heap=heapObjectForWebGLType(type);var sizePerPixel=colorChannelsInGlTextureFormat(format)*heap.BYTES_PER_ELEMENT;var bytes=computeUnpackAlignedImageSize(width,height,sizePerPixel);return heap.subarray(toTypedArrayIndex(pixels,heap),toTypedArrayIndex(pixels+bytes,heap))};var _glReadPixels=(x,y,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels);return}var heap=heapObjectForWebGLType(type);var target=toTypedArrayIndex(pixels,heap);GLctx.readPixels(x,y,width,height,format,type,heap,target);return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)};var _glRenderbufferStorage=(x0,x1,x2,x3)=>GLctx.renderbufferStorage(x0,x1,x2,x3);var _glRenderbufferStorageMultisample=(x0,x1,x2,x3,x4)=>GLctx.renderbufferStorageMultisample(x0,x1,x2,x3,x4);var _glScissor=(x0,x1,x2,x3)=>GLctx.scissor(x0,x1,x2,x3);var _glShaderSource=(shader,count,string,length)=>{var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)};var _glTexImage2D=(target,level,internalFormat,width,height,border,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);var index=toTypedArrayIndex(pixels,heap);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,index);return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null;GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixelData)};var _glTexImage3D=(target,level,internalFormat,width,height,depth,border,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,null)}};var _glTexParameterf=(x0,x1,x2)=>GLctx.texParameterf(x0,x1,x2);var _glTexParameteri=(x0,x1,x2)=>GLctx.texParameteri(x0,x1,x2);var _glTexStorage2D=(x0,x1,x2,x3,x4)=>GLctx.texStorage2D(x0,x1,x2,x3,x4);var _glTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}};var _glTransformFeedbackVaryings=(program,count,varyings,bufferMode)=>{program=GL.programs[program];var vars=[];for(var i=0;i>2]));GLctx.transformFeedbackVaryings(program,vars,bufferMode)};var webglGetUniformLocation=location=>{var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?`[${webglLoc}]`:""))}return webglLoc}else{GL.recordError(1282)}};var _glUniform1f=(location,v0)=>{GLctx.uniform1f(webglGetUniformLocation(location),v0)};var _glUniform1i=(location,v0)=>{GLctx.uniform1i(webglGetUniformLocation(location),v0)};var miniTempWebGLIntBuffers=[];var _glUniform1iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1iv(webglGetUniformLocation(location),HEAP32,value>>2,count);return}if(count<=288){var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2]}}else{var view=HEAP32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1iv(webglGetUniformLocation(location),view)};var _glUniform1ui=(location,v0)=>{GLctx.uniform1ui(webglGetUniformLocation(location),v0)};var _glUniform1uiv=(location,count,value)=>{count&&GLctx.uniform1uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count)};var _glUniform2f=(location,v0,v1)=>{GLctx.uniform2f(webglGetUniformLocation(location),v0,v1)};var miniTempWebGLFloatBuffers=[];var _glUniform2fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*2);return}if(count<=144){var view=miniTempWebGLFloatBuffers[2*count];for(var i=0;i<2*count;i+=2){view[i]=HEAPF32[value+4*i>>2];view[i+1]=HEAPF32[value+(4*i+4)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2fv(webglGetUniformLocation(location),view)};var _glUniform2iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2iv(webglGetUniformLocation(location),HEAP32,value>>2,count*2);return}if(count<=144){var view=miniTempWebGLIntBuffers[2*count];for(var i=0;i<2*count;i+=2){view[i]=HEAP32[value+4*i>>2];view[i+1]=HEAP32[value+(4*i+4)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2iv(webglGetUniformLocation(location),view)};var _glUniform3fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform3fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*3);return}if(count<=96){var view=miniTempWebGLFloatBuffers[3*count];for(var i=0;i<3*count;i+=3){view[i]=HEAPF32[value+4*i>>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*12>>2)}GLctx.uniform3fv(webglGetUniformLocation(location),view)};var _glUniform4f=(location,v0,v1,v2,v3)=>{GLctx.uniform4f(webglGetUniformLocation(location),v0,v1,v2,v3)};var _glUniform4fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform4fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*4);return}if(count<=72){var view=miniTempWebGLFloatBuffers[4*count];var heap=HEAPF32;value=value>>2;for(var i=0;i<4*count;i+=4){var dst=value+i;view[i]=heap[dst];view[i+1]=heap[dst+1];view[i+2]=heap[dst+2];view[i+3]=heap[dst+3]}}else{var view=HEAPF32.subarray(value>>2,value+count*16>>2)}GLctx.uniform4fv(webglGetUniformLocation(location),view)};var _glUniformBlockBinding=(program,uniformBlockIndex,uniformBlockBinding)=>{program=GL.programs[program];GLctx.uniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding)};var _glUniformMatrix3fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*9);return}if(count<=32){var view=miniTempWebGLFloatBuffers[9*count];for(var i=0;i<9*count;i+=9){view[i]=HEAPF32[value+4*i>>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2];view[i+4]=HEAPF32[value+(4*i+16)>>2];view[i+5]=HEAPF32[value+(4*i+20)>>2];view[i+6]=HEAPF32[value+(4*i+24)>>2];view[i+7]=HEAPF32[value+(4*i+28)>>2];view[i+8]=HEAPF32[value+(4*i+32)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*36>>2)}GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,view)};var _glUniformMatrix4fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*16);return}if(count<=18){var view=miniTempWebGLFloatBuffers[16*count];var heap=HEAPF32;value=value>>2;for(var i=0;i<16*count;i+=16){var dst=value+i;view[i]=heap[dst];view[i+1]=heap[dst+1];view[i+2]=heap[dst+2];view[i+3]=heap[dst+3];view[i+4]=heap[dst+4];view[i+5]=heap[dst+5];view[i+6]=heap[dst+6];view[i+7]=heap[dst+7];view[i+8]=heap[dst+8];view[i+9]=heap[dst+9];view[i+10]=heap[dst+10];view[i+11]=heap[dst+11];view[i+12]=heap[dst+12];view[i+13]=heap[dst+13];view[i+14]=heap[dst+14];view[i+15]=heap[dst+15]}}else{var view=HEAPF32.subarray(value>>2,value+count*64>>2)}GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,view)};var _glUseProgram=program=>{program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program};var _glVertexAttrib4f=(x0,x1,x2,x3,x4)=>GLctx.vertexAttrib4f(x0,x1,x2,x3,x4);var _glVertexAttribDivisor=(index,divisor)=>{GLctx.vertexAttribDivisor(index,divisor)};var _glVertexAttribI4ui=(x0,x1,x2,x3,x4)=>GLctx.vertexAttribI4ui(x0,x1,x2,x3,x4);var _glVertexAttribIPointer=(index,size,type,stride,ptr)=>{GLctx.vertexAttribIPointer(index,size,type,stride,ptr)};var _glVertexAttribPointer=(index,size,type,normalized,stride,ptr)=>{GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)};var _glViewport=(x0,x1,x2,x3)=>GLctx.viewport(x0,x1,x2,x3);var GodotRuntime={get_func:function(ptr){return wasmTable.get(ptr)},error:function(){err.apply(null,Array.from(arguments))},print:function(){out.apply(null,Array.from(arguments))},malloc:function(p_size){return _malloc(p_size)},free:function(p_ptr){_free(p_ptr)},getHeapValue:function(p_ptr,p_type){return getValue(p_ptr,p_type)},setHeapValue:function(p_ptr,p_value,p_type){setValue(p_ptr,p_value,p_type)},heapSub:function(p_heap,p_ptr,p_len){const bytes=p_heap.BYTES_PER_ELEMENT;return p_heap.subarray(p_ptr/bytes,p_ptr/bytes+p_len)},heapSlice:function(p_heap,p_ptr,p_len){const bytes=p_heap.BYTES_PER_ELEMENT;return p_heap.slice(p_ptr/bytes,p_ptr/bytes+p_len)},heapCopy:function(p_dst,p_src,p_ptr){const bytes=p_src.BYTES_PER_ELEMENT;return p_dst.set(p_src,p_ptr/bytes)},parseString:function(p_ptr){return UTF8ToString(p_ptr)},parseStringArray:function(p_ptr,p_size){const strings=[];const ptrs=GodotRuntime.heapSub(HEAP32,p_ptr,p_size);ptrs.forEach(function(ptr){strings.push(GodotRuntime.parseString(ptr))});return strings},strlen:function(p_str){return lengthBytesUTF8(p_str)},allocString:function(p_str){const length=GodotRuntime.strlen(p_str)+1;const c_str=GodotRuntime.malloc(length);stringToUTF8(p_str,c_str,length);return c_str},allocStringArray:function(p_strings){const size=p_strings.length;const c_ptr=GodotRuntime.malloc(size*4);for(let i=0;i>2)+i]=GodotRuntime.allocString(p_strings[i])}return c_ptr},freeStringArray:function(p_ptr,p_len){for(let i=0;i>2)+i])}GodotRuntime.free(p_ptr)},stringToHeap:function(p_str,p_ptr,p_len){return stringToUTF8Array(p_str,HEAP8,p_ptr,p_len)}};var GodotConfig={canvas:null,locale:"en",canvas_resize_policy:2,virtual_keyboard:false,persistent_drops:false,on_execute:null,on_exit:null,init_config:function(p_opts){GodotConfig.canvas_resize_policy=p_opts["canvasResizePolicy"];GodotConfig.canvas=p_opts["canvas"];GodotConfig.locale=p_opts["locale"]||GodotConfig.locale;GodotConfig.virtual_keyboard=p_opts["virtualKeyboard"];GodotConfig.persistent_drops=!!p_opts["persistentDrops"];GodotConfig.on_execute=p_opts["onExecute"];GodotConfig.on_exit=p_opts["onExit"];if(p_opts["focusCanvas"]){GodotConfig.canvas.focus()}},locate_file:function(file){return Module["locateFile"](file)},clear:function(){GodotConfig.canvas=null;GodotConfig.locale="en";GodotConfig.canvas_resize_policy=2;GodotConfig.virtual_keyboard=false;GodotConfig.persistent_drops=false;GodotConfig.on_execute=null;GodotConfig.on_exit=null}};var GodotFS={ENOENT:44,_idbfs:false,_syncing:false,_mount_points:[],is_persistent:function(){return GodotFS._idbfs?1:0},init:function(persistentPaths){GodotFS._idbfs=false;if(!Array.isArray(persistentPaths)){return Promise.reject(new Error("Persistent paths must be an array"))}if(!persistentPaths.length){return Promise.resolve()}GodotFS._mount_points=persistentPaths.slice();function createRecursive(dir){try{FS.stat(dir)}catch(e){if(e.errno!==GodotFS.ENOENT){GodotRuntime.error(e)}FS.mkdirTree(dir)}}GodotFS._mount_points.forEach(function(path){createRecursive(path);FS.mount(IDBFS,{},path)});return new Promise(function(resolve,reject){FS.syncfs(true,function(err){if(err){GodotFS._mount_points=[];GodotFS._idbfs=false;GodotRuntime.print(`IndexedDB not available: ${err.message}`)}else{GodotFS._idbfs=true}resolve(err)})})},deinit:function(){GodotFS._mount_points.forEach(function(path){try{FS.unmount(path)}catch(e){GodotRuntime.print("Already unmounted",e)}if(GodotFS._idbfs&&IDBFS.dbs[path]){IDBFS.dbs[path].close();delete IDBFS.dbs[path]}});GodotFS._mount_points=[];GodotFS._idbfs=false;GodotFS._syncing=false},sync:function(){if(GodotFS._syncing){GodotRuntime.error("Already syncing!");return Promise.resolve()}GodotFS._syncing=true;return new Promise(function(resolve,reject){FS.syncfs(false,function(error){if(error){GodotRuntime.error(`Failed to save IDB file system: ${error.message}`)}GodotFS._syncing=false;resolve(error)})})},copy_to_fs:function(path,buffer){const idx=path.lastIndexOf("/");let dir="/";if(idx>0){dir=path.slice(0,idx)}try{FS.stat(dir)}catch(e){if(e.errno!==GodotFS.ENOENT){GodotRuntime.error(e)}FS.mkdirTree(dir)}FS.writeFile(path,new Uint8Array(buffer))}};var GodotOS={request_quit:function(){},_async_cbs:[],_fs_sync_promise:null,atexit:function(p_promise_cb){GodotOS._async_cbs.push(p_promise_cb)},cleanup:function(exit_code){const cb=GodotConfig.on_exit;GodotFS.deinit();GodotConfig.clear();if(cb){cb(exit_code)}},finish_async:function(callback){GodotOS._fs_sync_promise.then(function(err){const promises=[];GodotOS._async_cbs.forEach(function(cb){promises.push(new Promise(cb))});return Promise.all(promises)}).then(function(){return GodotFS.sync()}).then(function(err){setTimeout(function(){callback()},0)})}};var GodotAudio={MAX_VOLUME_CHANNELS:8,GodotChannel:{CHANNEL_L:0,CHANNEL_R:1,CHANNEL_C:3,CHANNEL_LFE:4,CHANNEL_RL:5,CHANNEL_RR:6,CHANNEL_SL:7,CHANNEL_SR:8},WebChannel:{CHANNEL_L:0,CHANNEL_R:1,CHANNEL_SL:2,CHANNEL_SR:3,CHANNEL_C:4,CHANNEL_LFE:5},samples:null,Sample:class Sample{static getSample(id){if(!GodotAudio.samples.has(id)){throw new ReferenceError(`Could not find sample "${id}"`)}return GodotAudio.samples.get(id)}static getSampleOrNull(id){return GodotAudio.samples.get(id)??null}static create(params,options={}){const sample=new GodotAudio.Sample(params,options);GodotAudio.samples.set(params.id,sample);return sample}static delete(id){GodotAudio.samples.delete(id)}constructor(params,options={}){this.id=params.id;this._audioBuffer=null;this.numberOfChannels=options.numberOfChannels??2;this.sampleRate=options.sampleRate??44100;this.loopMode=options.loopMode??"disabled";this.loopBegin=options.loopBegin??0;this.loopEnd=options.loopEnd??0;this.setAudioBuffer(params.audioBuffer)}getAudioBuffer(){return this._duplicateAudioBuffer()}setAudioBuffer(val){this._audioBuffer=val}clear(){this.setAudioBuffer(null);GodotAudio.Sample.delete(this.id)}_duplicateAudioBuffer(){if(this._audioBuffer==null){throw new Error("couldn't duplicate a null audioBuffer")}const channels=new Array(this._audioBuffer.numberOfChannels);for(let i=0;i{if(!this.isCanceled){this._positionWorklet=this.createPositionWorklet();this._source.connect(this._positionWorklet);if(start){this.start()}}}).catch(addModuleError=>{GodotRuntime.error("Failed to create PositionWorklet.",addModuleError)})}}createPositionWorklet(){const worklet=new AudioWorkletNode(GodotAudio.ctx,"godot-position-reporting-processor");worklet.port.onmessage=event=>{switch(event.data["type"]){case"position":this._playbackPosition=parseInt(event.data.data,10)/this.getSample().sampleRate+this.offset;break;default:}};return worklet}clear(){this.isCanceled=true;this.isPaused=false;this.pauseTime=0;if(this._source!=null){this._source.removeEventListener("ended",this._onended);this._onended=null;if(this.isStarted){this._source.stop()}this._source.disconnect();this._source=null}for(const sampleNodeBus of this._sampleNodeBuses.values()){sampleNodeBus.clear()}this._sampleNodeBuses.clear();if(this._positionWorklet){this._positionWorklet.disconnect();this._positionWorklet.port.onmessage=null;this._positionWorklet=null}GodotAudio.SampleNode.delete(this.id)}_resetSourceStartTime(){this._sourceStartTime=GodotAudio.ctx.currentTime}_syncPlaybackRate(){this._source.playbackRate.value=this.getPlaybackRate()*this.getPitchScale()}_restart(){if(this._source!=null){this._source.disconnect()}this._source=GodotAudio.ctx.createBufferSource();this._source.buffer=this.getSample().getAudioBuffer();for(const sampleNodeBus of this._sampleNodeBuses.values()){this.connect(sampleNodeBus.getInputNode())}this._addEndedListener();const pauseTime=this.isPaused?this.pauseTime:0;this.connectPositionWorklet();this._source.start(this.startTime,this.offset+pauseTime);this.isStarted=true}_pause(){this.isPaused=true;this.pauseTime=(GodotAudio.ctx.currentTime-this._sourceStartTime)/this.getPlaybackRate();this._source.stop()}_unpause(){this._restart();this.isPaused=false;this.pauseTime=0}_addEndedListener(){if(this._onended!=null){this._source.removeEventListener("ended",this._onended)}const self=this;this._onended=_=>{if(self.isPaused){return}switch(self.getSample().loopMode){case"disabled":{const id=this.id;self.stop();if(GodotAudio.sampleFinishedCallback!=null){const idCharPtr=GodotRuntime.allocString(id);GodotAudio.sampleFinishedCallback(idCharPtr);GodotRuntime.free(idCharPtr)}}break;case"forward":case"backward":self.restart();break;default:}};this._source.addEventListener("ended",this._onended)}},buses:null,busSolo:null,Bus:class Bus{static getCount(){return GodotAudio.buses.length}static setCount(val){const buses=GodotAudio.buses;if(val===buses.length){return}if(val=GodotAudio.buses.length){throw new ReferenceError(`invalid bus index "${index}"`)}return GodotAudio.buses[index]}static getBusOrNull(index){if(index<0||index>=GodotAudio.buses.length){return null}return GodotAudio.buses[index]}static move(fromIndex,toIndex){const movedBus=GodotAudio.Bus.getBusOrNull(fromIndex);if(movedBus==null){return}const buses=GodotAudio.buses.filter((_,i)=>i!==fromIndex);buses.splice(toIndex-1,0,movedBus);GodotAudio.buses=buses}static addAt(index){const newBus=GodotAudio.Bus.create();if(index!==newBus.getId()){GodotAudio.Bus.move(newBus.getId(),index)}}static create(){const newBus=new GodotAudio.Bus;const isFirstBus=GodotAudio.buses.length===0;GodotAudio.buses.push(newBus);if(isFirstBus){newBus.setSend(null)}else{newBus.setSend(GodotAudio.Bus.getBus(0))}return newBus}constructor(){this._sampleNodes=new Set;this.isSolo=false;this._send=null;this._gainNode=GodotAudio.ctx.createGain();this._soloNode=GodotAudio.ctx.createGain();this._muteNode=GodotAudio.ctx.createGain();this._gainNode.connect(this._soloNode).connect(this._muteNode)}getId(){return GodotAudio.buses.indexOf(this)}getVolumeDb(){return GodotAudio.linear_to_db(this._gainNode.gain.value)}setVolumeDb(val){const linear=GodotAudio.db_to_linear(val);if(isFinite(linear)){this._gainNode.gain.value=linear}}getSend(){return this._send}setSend(val){this._send=val;if(val==null){if(this.getId()==0){this.getOutputNode().connect(GodotAudio.ctx.destination);return}throw new Error(`Cannot send to "${val}" without the bus being at index 0 (current index: ${this.getId()})`)}this.connect(val)}getInputNode(){return this._gainNode}getOutputNode(){return this._muteNode}mute(enable){this._muteNode.gain.value=enable?0:1}solo(enable){if(this.isSolo===enable){return}if(enable){if(GodotAudio.busSolo!=null&&GodotAudio.busSolo!==this){GodotAudio.busSolo._disableSolo()}this._enableSolo();return}this._disableSolo()}addSampleNode(sampleNode){this._sampleNodes.add(sampleNode);sampleNode.getOutputNode().connect(this.getInputNode())}removeSampleNode(sampleNode){this._sampleNodes.delete(sampleNode);sampleNode.getOutputNode().disconnect()}connect(bus){if(bus==null){throw new Error("cannot connect to null bus")}this.getOutputNode().disconnect();this.getOutputNode().connect(bus.getInputNode());return bus}clear(){GodotAudio.buses=GodotAudio.buses.filter(v=>v!==this)}_syncSampleNodes(){const sampleNodes=Array.from(this._sampleNodes);for(let i=0;iotherBus!==this);for(let i=0;iotherBus!==this);for(let i=0;iGodotAudio.Bus.getBus(busIndex));sampleNode.setVolumes(buses,volumes)},set_sample_bus_count:function(count){GodotAudio.Bus.setCount(count)},remove_sample_bus:function(index){const bus=GodotAudio.Bus.getBusOrNull(index);if(bus==null){return}bus.clear()},add_sample_bus:function(atPos){GodotAudio.Bus.addAt(atPos)},move_sample_bus:function(busIndex,toPos){GodotAudio.Bus.move(busIndex,toPos)},set_sample_bus_send:function(busIndex,sendIndex){const bus=GodotAudio.Bus.getBusOrNull(busIndex);if(bus==null){return}let targetBus=GodotAudio.Bus.getBusOrNull(sendIndex);if(targetBus==null){targetBus=GodotAudio.Bus.getBus(0)}bus.setSend(targetBus)},set_sample_bus_volume_db:function(busIndex,volumeDb){const bus=GodotAudio.Bus.getBusOrNull(busIndex);if(bus==null){return}bus.setVolumeDb(volumeDb)},set_sample_bus_solo:function(busIndex,enable){const bus=GodotAudio.Bus.getBusOrNull(busIndex);if(bus==null){return}bus.solo(enable)},set_sample_bus_mute:function(busIndex,enable){const bus=GodotAudio.Bus.getBusOrNull(busIndex);if(bus==null){return}bus.mute(enable)}};function _godot_audio_get_sample_playback_position(playbackObjectIdStrPtr){const playbackObjectId=GodotRuntime.parseString(playbackObjectIdStrPtr);const sampleNode=GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);if(sampleNode==null){return 0}return sampleNode.getPlaybackPosition()}function _godot_audio_has_script_processor(){return GodotAudio.ctx&&GodotAudio.ctx.createScriptProcessor?1:0}function _godot_audio_has_worklet(){return GodotAudio.ctx&&GodotAudio.ctx.audioWorklet?1:0}function _godot_audio_init(p_mix_rate,p_latency,p_state_change,p_latency_update){const statechange=GodotRuntime.get_func(p_state_change);const latencyupdate=GodotRuntime.get_func(p_latency_update);const mix_rate=GodotRuntime.getHeapValue(p_mix_rate,"i32");const channels=GodotAudio.init(mix_rate,p_latency,statechange,latencyupdate);GodotRuntime.setHeapValue(p_mix_rate,GodotAudio.ctx.sampleRate,"i32");return channels}function _godot_audio_input_start(){return GodotAudio.create_input(function(input){input.connect(GodotAudio.driver.get_node())})}function _godot_audio_input_stop(){if(GodotAudio.input){const tracks=GodotAudio.input["mediaStream"]["getTracks"]();for(let i=0;i=size){const high=size-wpos;wbuf.set(buffer.subarray(wpos,size));pending_samples-=high;wpos=0}if(pending_samples>0){wbuf.set(buffer.subarray(wpos,wpos+pending_samples),tot_sent-pending_samples)}port.postMessage({cmd:"chunk",data:wbuf.subarray(0,tot_sent)});wpos+=pending_samples;pending_samples=0}this.receive=function(recv_buf){const buffer=GodotRuntime.heapSub(HEAPF32,p_in_buf,p_in_size);const from=rpos;let to_write=recv_buf.length;let high=0;if(rpos+to_write>=p_in_size){high=p_in_size-rpos;buffer.set(recv_buf.subarray(0,high),rpos);to_write-=high;rpos=0}if(to_write){buffer.set(recv_buf.subarray(high,to_write),rpos)}in_callback(from,recv_buf.length);rpos+=to_write};this.consumed=function(size,port){pending_samples+=size;send(port)}}GodotAudioWorklet.ring_buffer=new RingBuffer;GodotAudioWorklet.promise.then(function(){const node=GodotAudioWorklet.worklet;const buffer=GodotRuntime.heapSlice(HEAPF32,p_out_buf,p_out_size);node.connect(GodotAudio.ctx.destination);node.port.postMessage({cmd:"start_nothreads",data:[buffer,p_in_size]});node.port.onmessage=function(event){if(!GodotAudioWorklet.worklet){return}if(event.data["cmd"]==="read"){const read=event.data["data"];GodotAudioWorklet.ring_buffer.consumed(read,GodotAudioWorklet.worklet.port)}else if(event.data["cmd"]==="input"){const buf=event.data["data"];if(buf.length>p_in_size){GodotRuntime.error("Input chunk is too big");return}GodotAudioWorklet.ring_buffer.receive(buf)}else{GodotRuntime.error(event.data)}}})},get_node:function(){return GodotAudioWorklet.worklet},close:function(){return new Promise(function(resolve,reject){if(GodotAudioWorklet.promise===null){return}const p=GodotAudioWorklet.promise;p.then(function(){GodotAudioWorklet.worklet.port.postMessage({cmd:"stop",data:null});GodotAudioWorklet.worklet.disconnect();GodotAudioWorklet.worklet.port.onmessage=null;GodotAudioWorklet.worklet=null;GodotAudioWorklet.promise=null;resolve()}).catch(function(err){GodotRuntime.error(err)})})}};function _godot_audio_worklet_create(channels){try{GodotAudioWorklet.create(channels)}catch(e){GodotRuntime.error("Error starting AudioDriverWorklet",e);return 1}return 0}function _godot_audio_worklet_start_no_threads(p_out_buf,p_out_size,p_out_callback,p_in_buf,p_in_size,p_in_callback){const out_callback=GodotRuntime.get_func(p_out_callback);const in_callback=GodotRuntime.get_func(p_in_callback);GodotAudioWorklet.start_no_threads(p_out_buf,p_out_size,out_callback,p_in_buf,p_in_size,in_callback)}function _godot_js_config_canvas_id_get(p_ptr,p_ptr_max){GodotRuntime.stringToHeap(`#${GodotConfig.canvas.id}`,p_ptr,p_ptr_max)}function _godot_js_config_locale_get(p_ptr,p_ptr_max){GodotRuntime.stringToHeap(GodotConfig.locale,p_ptr,p_ptr_max)}var GodotDisplayCursor={shape:"default",visible:true,cursors:{},set_style:function(style){GodotConfig.canvas.style.cursor=style},set_shape:function(shape){GodotDisplayCursor.shape=shape;let css=shape;if(shape in GodotDisplayCursor.cursors){const c=GodotDisplayCursor.cursors[shape];css=`url("${c.url}") ${c.x} ${c.y}, default`}if(GodotDisplayCursor.visible){GodotDisplayCursor.set_style(css)}},clear:function(){GodotDisplayCursor.set_style("");GodotDisplayCursor.shape="default";GodotDisplayCursor.visible=true;Object.keys(GodotDisplayCursor.cursors).forEach(function(key){URL.revokeObjectURL(GodotDisplayCursor.cursors[key]);delete GodotDisplayCursor.cursors[key]})},lockPointer:function(){const canvas=GodotConfig.canvas;if(canvas.requestPointerLock){canvas.requestPointerLock()}},releasePointer:function(){if(document.exitPointerLock){document.exitPointerLock()}},isPointerLocked:function(){return document.pointerLockElement===GodotConfig.canvas}};var GodotEventListeners={handlers:[],has:function(target,event,method,capture){return GodotEventListeners.handlers.findIndex(function(e){return e.target===target&&e.event===event&&e.method===method&&e.capture===capture})!==-1},add:function(target,event,method,capture){if(GodotEventListeners.has(target,event,method,capture)){return}function Handler(p_target,p_event,p_method,p_capture){this.target=p_target;this.event=p_event;this.method=p_method;this.capture=p_capture}GodotEventListeners.handlers.push(new Handler(target,event,method,capture));target.addEventListener(event,method,capture)},clear:function(){GodotEventListeners.handlers.forEach(function(h){h.target.removeEventListener(h.event,h.method,h.capture)});GodotEventListeners.handlers.length=0}};var _emscripten_webgl_do_get_current_context=()=>GL.currentContext?GL.currentContext.handle:0;var _emscripten_webgl_get_current_context=_emscripten_webgl_do_get_current_context;var GodotDisplayScreen={desired_size:[0,0],hidpi:true,getPixelRatio:function(){return GodotDisplayScreen.hidpi?window.devicePixelRatio||1:1},isFullscreen:function(){const elem=document.fullscreenElement||document.mozFullscreenElement||document.webkitFullscreenElement||document.msFullscreenElement;if(elem){return elem===GodotConfig.canvas}return document.fullscreen||document.mozFullScreen||document.webkitIsFullscreen},hasFullscreen:function(){return document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled},requestFullscreen:function(){if(!GodotDisplayScreen.hasFullscreen()){return 1}const canvas=GodotConfig.canvas;try{const promise=(canvas.requestFullscreen||canvas.msRequestFullscreen||canvas.mozRequestFullScreen||canvas.mozRequestFullscreen||canvas.webkitRequestFullscreen).call(canvas);if(promise){promise.catch(function(){})}}catch(e){return 1}return 0},exitFullscreen:function(){if(!GodotDisplayScreen.isFullscreen()){return 0}try{const promise=document.exitFullscreen();if(promise){promise.catch(function(){})}}catch(e){return 1}return 0},_updateGL:function(){const gl_context_handle=_emscripten_webgl_get_current_context();const gl=GL.getContext(gl_context_handle);if(gl){GL.resizeOffscreenFramebuffer(gl)}},updateSize:function(){const isFullscreen=GodotDisplayScreen.isFullscreen();const wantsFullWindow=GodotConfig.canvas_resize_policy===2;const noResize=GodotConfig.canvas_resize_policy===0;const dWidth=GodotDisplayScreen.desired_size[0];const dHeight=GodotDisplayScreen.desired_size[1];const canvas=GodotConfig.canvas;let width=dWidth;let height=dHeight;if(noResize){if(canvas.width!==width||canvas.height!==height){GodotDisplayScreen.desired_size=[canvas.width,canvas.height];GodotDisplayScreen._updateGL();return 1}return 0}const scale=GodotDisplayScreen.getPixelRatio();if(isFullscreen||wantsFullWindow){width=window.innerWidth*scale;height=window.innerHeight*scale}const csw=`${width/scale}px`;const csh=`${height/scale}px`;if(canvas.style.width!==csw||canvas.style.height!==csh||canvas.width!==width||canvas.height!==height){canvas.width=width;canvas.height=height;canvas.style.width=csw;canvas.style.height=csh;GodotDisplayScreen._updateGL();return 1}return 0}};var GodotDisplayVK={textinput:null,textarea:null,available:function(){return GodotConfig.virtual_keyboard&&"ontouchstart"in window},init:function(input_cb){function create(what){const elem=document.createElement(what);elem.style.display="none";elem.style.position="absolute";elem.style.zIndex="-1";elem.style.background="transparent";elem.style.padding="0px";elem.style.margin="0px";elem.style.overflow="hidden";elem.style.width="0px";elem.style.height="0px";elem.style.border="0px";elem.style.outline="none";elem.readonly=true;elem.disabled=true;GodotEventListeners.add(elem,"input",function(evt){const c_str=GodotRuntime.allocString(elem.value);input_cb(c_str,elem.selectionEnd);GodotRuntime.free(c_str)},false);GodotEventListeners.add(elem,"blur",function(evt){elem.style.display="none";elem.readonly=true;elem.disabled=true},false);GodotConfig.canvas.insertAdjacentElement("beforebegin",elem);return elem}GodotDisplayVK.textinput=create("input");GodotDisplayVK.textarea=create("textarea");GodotDisplayVK.updateSize()},show:function(text,type,start,end){if(!GodotDisplayVK.textinput||!GodotDisplayVK.textarea){return}if(GodotDisplayVK.textinput.style.display!==""||GodotDisplayVK.textarea.style.display!==""){GodotDisplayVK.hide()}GodotDisplayVK.updateSize();let elem=GodotDisplayVK.textinput;switch(type){case 0:elem.type="text";elem.inputmode="";break;case 1:elem=GodotDisplayVK.textarea;break;case 2:elem.type="text";elem.inputmode="numeric";break;case 3:elem.type="text";elem.inputmode="decimal";break;case 4:elem.type="tel";elem.inputmode="";break;case 5:elem.type="email";elem.inputmode="";break;case 6:elem.type="password";elem.inputmode="";break;case 7:elem.type="url";elem.inputmode="";break;default:elem.type="text";elem.inputmode="";break}elem.readonly=false;elem.disabled=false;elem.value=text;elem.style.display="block";elem.focus();elem.setSelectionRange(start,end)},hide:function(){if(!GodotDisplayVK.textinput||!GodotDisplayVK.textarea){return}[GodotDisplayVK.textinput,GodotDisplayVK.textarea].forEach(function(elem){elem.blur();elem.style.display="none";elem.value=""})},updateSize:function(){if(!GodotDisplayVK.textinput||!GodotDisplayVK.textarea){return}const rect=GodotConfig.canvas.getBoundingClientRect();function update(elem){elem.style.left=`${rect.left}px`;elem.style.top=`${rect.top}px`;elem.style.width=`${rect.width}px`;elem.style.height=`${rect.height}px`}update(GodotDisplayVK.textinput);update(GodotDisplayVK.textarea)},clear:function(){if(GodotDisplayVK.textinput){GodotDisplayVK.textinput.remove();GodotDisplayVK.textinput=null}if(GodotDisplayVK.textarea){GodotDisplayVK.textarea.remove();GodotDisplayVK.textarea=null}}};var GodotDisplay={window_icon:"",getDPI:function(){const dpi=Math.round(window.devicePixelRatio*96);return dpi>=96?dpi:96}};function _godot_js_display_alert(p_text){window.alert(GodotRuntime.parseString(p_text))}function _godot_js_display_canvas_focus(){GodotConfig.canvas.focus()}function _godot_js_display_canvas_is_focused(){return document.activeElement===GodotConfig.canvas}function _godot_js_display_clipboard_get(callback){const func=GodotRuntime.get_func(callback);try{navigator.clipboard.readText().then(function(result){const ptr=GodotRuntime.allocString(result);func(ptr);GodotRuntime.free(ptr)}).catch(function(e){})}catch(e){}}function _godot_js_display_clipboard_set(p_text){const text=GodotRuntime.parseString(p_text);if(!navigator.clipboard||!navigator.clipboard.writeText){return 1}navigator.clipboard.writeText(text).catch(function(e){GodotRuntime.error("Setting OS clipboard is only possible from an input callback for the Web platform. Exception:",e)});return 0}function _godot_js_display_cursor_is_hidden(){return!GodotDisplayCursor.visible}function _godot_js_display_cursor_is_locked(){return GodotDisplayCursor.isPointerLocked()?1:0}function _godot_js_display_cursor_lock_set(p_lock){if(p_lock){GodotDisplayCursor.lockPointer()}else{GodotDisplayCursor.releasePointer()}}function _godot_js_display_cursor_set_custom_shape(p_shape,p_ptr,p_len,p_hotspot_x,p_hotspot_y){const shape=GodotRuntime.parseString(p_shape);const old_shape=GodotDisplayCursor.cursors[shape];if(p_len>0){const png=new Blob([GodotRuntime.heapSlice(HEAPU8,p_ptr,p_len)],{type:"image/png"});const url=URL.createObjectURL(png);GodotDisplayCursor.cursors[shape]={url:url,x:p_hotspot_x,y:p_hotspot_y}}else{delete GodotDisplayCursor.cursors[shape]}if(shape===GodotDisplayCursor.shape){GodotDisplayCursor.set_shape(GodotDisplayCursor.shape)}if(old_shape){URL.revokeObjectURL(old_shape.url)}}function _godot_js_display_cursor_set_shape(p_string){GodotDisplayCursor.set_shape(GodotRuntime.parseString(p_string))}function _godot_js_display_cursor_set_visible(p_visible){const visible=p_visible!==0;if(visible===GodotDisplayCursor.visible){return}GodotDisplayCursor.visible=visible;if(visible){GodotDisplayCursor.set_shape(GodotDisplayCursor.shape)}else{GodotDisplayCursor.set_style("none")}}function _godot_js_display_desired_size_set(width,height){GodotDisplayScreen.desired_size=[width,height];GodotDisplayScreen.updateSize()}function _godot_js_display_fullscreen_cb(callback){const canvas=GodotConfig.canvas;const func=GodotRuntime.get_func(callback);function change_cb(evt){if(evt.target===canvas){func(GodotDisplayScreen.isFullscreen())}}GodotEventListeners.add(document,"fullscreenchange",change_cb,false);GodotEventListeners.add(document,"mozfullscreenchange",change_cb,false);GodotEventListeners.add(document,"webkitfullscreenchange",change_cb,false)}function _godot_js_display_fullscreen_exit(){return GodotDisplayScreen.exitFullscreen()}function _godot_js_display_fullscreen_request(){return GodotDisplayScreen.requestFullscreen()}function _godot_js_display_has_webgl(p_version){if(p_version!==1&&p_version!==2){return false}try{return!!document.createElement("canvas").getContext(p_version===2?"webgl2":"webgl")}catch(e){}return false}function _godot_js_display_is_swap_ok_cancel(){const win=["Windows","Win64","Win32","WinCE"];const plat=navigator.platform||"";if(win.indexOf(plat)!==-1){return 1}return 0}function _godot_js_display_notification_cb(callback,p_enter,p_exit,p_in,p_out){const canvas=GodotConfig.canvas;const func=GodotRuntime.get_func(callback);const notif=[p_enter,p_exit,p_in,p_out];["mouseover","mouseleave","focus","blur"].forEach(function(evt_name,idx){GodotEventListeners.add(canvas,evt_name,function(){func(notif[idx])},true)})}function _godot_js_display_pixel_ratio_get(){return GodotDisplayScreen.getPixelRatio()}function _godot_js_display_screen_dpi_get(){return GodotDisplay.getDPI()}function _godot_js_display_screen_size_get(width,height){const scale=GodotDisplayScreen.getPixelRatio();GodotRuntime.setHeapValue(width,window.screen.width*scale,"i32");GodotRuntime.setHeapValue(height,window.screen.height*scale,"i32")}function _godot_js_display_setup_canvas(p_width,p_height,p_fullscreen,p_hidpi){const canvas=GodotConfig.canvas;GodotEventListeners.add(canvas,"contextmenu",function(ev){ev.preventDefault()},false);GodotEventListeners.add(canvas,"webglcontextlost",function(ev){alert("WebGL context lost, please reload the page");ev.preventDefault()},false);GodotDisplayScreen.hidpi=!!p_hidpi;switch(GodotConfig.canvas_resize_policy){case 0:GodotDisplayScreen.desired_size=[canvas.width,canvas.height];break;case 1:GodotDisplayScreen.desired_size=[p_width,p_height];break;default:canvas.style.position="absolute";canvas.style.top=0;canvas.style.left=0;break}GodotDisplayScreen.updateSize();if(p_fullscreen){GodotDisplayScreen.requestFullscreen()}}function _godot_js_display_size_update(){const updated=GodotDisplayScreen.updateSize();if(updated){GodotDisplayVK.updateSize()}return updated}function _godot_js_display_touchscreen_is_available(){return"ontouchstart"in window}function _godot_js_display_tts_available(){return"speechSynthesis"in window}function _godot_js_display_vk_available(){return GodotDisplayVK.available()}function _godot_js_display_vk_cb(p_input_cb){const input_cb=GodotRuntime.get_func(p_input_cb);if(GodotDisplayVK.available()){GodotDisplayVK.init(input_cb)}}function _godot_js_display_vk_hide(){GodotDisplayVK.hide()}function _godot_js_display_vk_show(p_text,p_type,p_start,p_end){const text=GodotRuntime.parseString(p_text);const start=p_start>0?p_start:0;const end=p_end>0?p_end:start;GodotDisplayVK.show(text,p_type,start,end)}function _godot_js_display_window_blur_cb(callback){const func=GodotRuntime.get_func(callback);GodotEventListeners.add(window,"blur",function(){func()},false)}function _godot_js_display_window_icon_set(p_ptr,p_len){let link=document.getElementById("-gd-engine-icon");const old_icon=GodotDisplay.window_icon;if(p_ptr){if(link===null){link=document.createElement("link");link.rel="icon";link.id="-gd-engine-icon";document.head.appendChild(link)}const png=new Blob([GodotRuntime.heapSlice(HEAPU8,p_ptr,p_len)],{type:"image/png"});GodotDisplay.window_icon=URL.createObjectURL(png);link.href=GodotDisplay.window_icon}else{if(link){link.remove()}GodotDisplay.window_icon=null}if(old_icon){URL.revokeObjectURL(old_icon)}}function _godot_js_display_window_size_get(p_width,p_height){GodotRuntime.setHeapValue(p_width,GodotConfig.canvas.width,"i32");GodotRuntime.setHeapValue(p_height,GodotConfig.canvas.height,"i32")}function _godot_js_display_window_title_set(p_data){document.title=GodotRuntime.parseString(p_data)}function _godot_js_eval(p_js,p_use_global_ctx,p_union_ptr,p_byte_arr,p_byte_arr_write,p_callback){const js_code=GodotRuntime.parseString(p_js);let eval_ret=null;try{if(p_use_global_ctx){const global_eval=eval;eval_ret=global_eval(js_code)}else{eval_ret=eval(js_code)}}catch(e){GodotRuntime.error(e)}switch(typeof eval_ret){case"boolean":GodotRuntime.setHeapValue(p_union_ptr,eval_ret,"i32");return 1;case"number":GodotRuntime.setHeapValue(p_union_ptr,eval_ret,"double");return 3;case"string":GodotRuntime.setHeapValue(p_union_ptr,GodotRuntime.allocString(eval_ret),"*");return 4;case"object":if(eval_ret===null){break}if(ArrayBuffer.isView(eval_ret)&&!(eval_ret instanceof Uint8Array)){eval_ret=new Uint8Array(eval_ret.buffer)}else if(eval_ret instanceof ArrayBuffer){eval_ret=new Uint8Array(eval_ret)}if(eval_ret instanceof Uint8Array){const func=GodotRuntime.get_func(p_callback);const bytes_ptr=func(p_byte_arr,p_byte_arr_write,eval_ret.length);HEAPU8.set(eval_ret,bytes_ptr);return 29}break}return 0}var IDHandler={_last_id:0,_references:{},get:function(p_id){return IDHandler._references[p_id]},add:function(p_data){const id=++IDHandler._last_id;IDHandler._references[id]=p_data;return id},remove:function(p_id){delete IDHandler._references[p_id]}};var GodotFetch={onread:function(id,result){const obj=IDHandler.get(id);if(!obj){return}if(result.value){obj.chunks.push(result.value)}obj.reading=false;obj.done=result.done},onresponse:function(id,response){const obj=IDHandler.get(id);if(!obj){return}let chunked=false;response.headers.forEach(function(value,header){const v=value.toLowerCase().trim();const h=header.toLowerCase().trim();if(h==="transfer-encoding"&&v==="chunked"){chunked=true}});obj.status=response.status;obj.response=response;obj.reader=response.body?.getReader();obj.chunked=chunked},onerror:function(id,err){GodotRuntime.error(err);const obj=IDHandler.get(id);if(!obj){return}obj.error=err},create:function(method,url,headers,body){const obj={request:null,response:null,reader:null,error:null,done:false,reading:false,status:0,chunks:[]};const id=IDHandler.add(obj);const init={method:method,headers:headers,body:body};obj.request=fetch(url,init);obj.request.then(GodotFetch.onresponse.bind(null,id)).catch(GodotFetch.onerror.bind(null,id));return id},free:function(id){const obj=IDHandler.get(id);if(!obj){return}IDHandler.remove(id);if(!obj.request){return}obj.request.then(function(response){response.abort()}).catch(function(e){})},read:function(id){const obj=IDHandler.get(id);if(!obj){return}if(obj.reader&&!obj.reading){if(obj.done){obj.reader=null;return}obj.reading=true;obj.reader.read().then(GodotFetch.onread.bind(null,id)).catch(GodotFetch.onerror.bind(null,id))}else if(obj.reader==null&&obj.response.body==null){obj.reading=true;GodotFetch.onread(id,{value:undefined,done:true})}}};function _godot_js_fetch_create(p_method,p_url,p_headers,p_headers_size,p_body,p_body_size){const method=GodotRuntime.parseString(p_method);const url=GodotRuntime.parseString(p_url);const headers=GodotRuntime.parseStringArray(p_headers,p_headers_size);const body=p_body_size?GodotRuntime.heapSlice(HEAP8,p_body,p_body_size):null;return GodotFetch.create(method,url,headers.map(function(hv){const idx=hv.indexOf(":");if(idx<=0){return[]}return[hv.slice(0,idx).trim(),hv.slice(idx+1).trim()]}).filter(function(v){return v.length===2}),body)}function _godot_js_fetch_free(id){GodotFetch.free(id)}function _godot_js_fetch_http_status_get(p_id){const obj=IDHandler.get(p_id);if(!obj||!obj.response){return 0}return obj.status}function _godot_js_fetch_is_chunked(p_id){const obj=IDHandler.get(p_id);if(!obj||!obj.response){return-1}return obj.chunked?1:0}function _godot_js_fetch_read_chunk(p_id,p_buf,p_buf_size){const obj=IDHandler.get(p_id);if(!obj||!obj.response){return 0}let to_read=p_buf_size;const chunks=obj.chunks;while(to_read&&chunks.length){const chunk=obj.chunks[0];if(chunk.length>to_read){GodotRuntime.heapCopy(HEAP8,chunk.slice(0,to_read),p_buf);chunks[0]=chunk.slice(to_read);to_read=0}else{GodotRuntime.heapCopy(HEAP8,chunk,p_buf);to_read-=chunk.length;chunks.pop()}}if(!chunks.length){GodotFetch.read(p_id)}return p_buf_size-to_read}function _godot_js_fetch_read_headers(p_id,p_parse_cb,p_ref){const obj=IDHandler.get(p_id);if(!obj||!obj.response){return 1}const cb=GodotRuntime.get_func(p_parse_cb);const arr=[];obj.response.headers.forEach(function(v,h){arr.push(`${h}:${v}`)});const c_ptr=GodotRuntime.allocStringArray(arr);cb(arr.length,c_ptr,p_ref);GodotRuntime.freeStringArray(c_ptr,arr.length);return 0}function _godot_js_fetch_state_get(p_id){const obj=IDHandler.get(p_id);if(!obj){return-1}if(obj.error){return-1}if(!obj.response){return 0}if(obj.reader||obj.response.body==null&&!obj.done){return 1}if(obj.done){return 2}return-1}var GodotInputGamepads={samples:[],get_pads:function(){try{const pads=navigator.getGamepads();if(pads){return pads}return[]}catch(e){return[]}},get_samples:function(){return GodotInputGamepads.samples},get_sample:function(index){const samples=GodotInputGamepads.samples;return index=0){os="Android"}else if(ua.indexOf("Linux")>=0){os="Linux"}else if(ua.indexOf("iPhone")>=0){os="iOS"}else if(ua.indexOf("Macintosh")>=0){os="MacOSX"}else if(ua.indexOf("Windows")>=0){os="Windows"}const id=pad.id;const exp1=/vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i;const exp2=/^([0-9a-f]+)-([0-9a-f]+)-/i;let vendor="";let product="";if(exp1.test(id)){const match=exp1.exec(id);vendor=match[1].padStart(4,"0");product=match[2].padStart(4,"0")}else if(exp2.test(id)){const match=exp2.exec(id);vendor=match[1].padStart(4,"0");product=match[2].padStart(4,"0")}if(!vendor||!product){return`${os}Unknown`}return os+vendor+product}};var GodotInputDragDrop={promises:[],pending_files:[],add_entry:function(entry){if(entry.isDirectory){GodotInputDragDrop.add_dir(entry)}else if(entry.isFile){GodotInputDragDrop.add_file(entry)}else{GodotRuntime.error("Unrecognized entry...",entry)}},add_dir:function(entry){GodotInputDragDrop.promises.push(new Promise(function(resolve,reject){const reader=entry.createReader();reader.readEntries(function(entries){for(let i=0;i{const path=elem["path"];GodotFS.copy_to_fs(DROP+path,elem["data"]);let idx=path.indexOf("/");if(idx===-1){drops.push(DROP+path)}else{const sub=path.substr(0,idx);idx=sub.indexOf("/");if(idx<0&&drops.indexOf(DROP+sub)===-1){drops.push(DROP+sub)}}files.push(DROP+path)});GodotInputDragDrop.promises=[];GodotInputDragDrop.pending_files=[];callback(drops);if(GodotConfig.persistent_drops){GodotOS.atexit(function(resolve,reject){GodotInputDragDrop.remove_drop(files,DROP);resolve()})}else{GodotInputDragDrop.remove_drop(files,DROP)}})},remove_drop:function(files,drop_path){const dirs=[drop_path.substr(0,drop_path.length-1)];files.forEach(function(file){FS.unlink(file);let dir=file.replace(drop_path,"");let idx=dir.lastIndexOf("/");while(idx>0){dir=dir.substr(0,idx);if(dirs.indexOf(drop_path+dir)===-1){dirs.push(drop_path+dir)}idx=dir.lastIndexOf("/")}});dirs.sort(function(a,b){const al=(a.match(/\//g)||[]).length;const bl=(b.match(/\//g)||[]).length;if(al>bl){return-1}else if(al-1){clearFocusTimerInterval()}if(GodotIME.ime==null){return}GodotIME.active=active;if(active){GodotIME.ime.style.display="block";GodotIME.focusTimerIntervalId=setInterval(focusTimer,100)}else{GodotIME.ime.style.display="none";GodotConfig.canvas.focus()}},ime_position:function(x,y){if(GodotIME.ime==null){return}const canvas=GodotConfig.canvas;const rect=canvas.getBoundingClientRect();const rw=canvas.width/rect.width;const rh=canvas.height/rect.height;const clx=x/rw+rect.x;const cly=y/rh+rect.y;GodotIME.ime.style.left=`${clx}px`;GodotIME.ime.style.top=`${cly}px`},init:function(ime_cb,key_cb,code,key){function key_event_cb(pressed,evt){const modifiers=GodotIME.getModifiers(evt);GodotRuntime.stringToHeap(evt.code,code,32);GodotRuntime.stringToHeap(evt.key,key,32);key_cb(pressed,evt.repeat,modifiers);evt.preventDefault()}function ime_event_cb(event){if(GodotIME.ime==null){return}switch(event.type){case"compositionstart":ime_cb(0,null);GodotIME.ime.innerHTML="";break;case"compositionupdate":{const ptr=GodotRuntime.allocString(event.data);ime_cb(1,ptr);GodotRuntime.free(ptr)}break;case"compositionend":{const ptr=GodotRuntime.allocString(event.data);ime_cb(2,ptr);GodotRuntime.free(ptr);GodotIME.ime.innerHTML=""}break;default:}}const ime=document.createElement("div");ime.className="ime";ime.style.background="none";ime.style.opacity=0;ime.style.position="fixed";ime.style.textAlign="left";ime.style.fontSize="1px";ime.style.left="0px";ime.style.top="0px";ime.style.width="100%";ime.style.height="40px";ime.style.pointerEvents="none";ime.style.display="none";ime.contentEditable="true";GodotEventListeners.add(ime,"compositionstart",ime_event_cb,false);GodotEventListeners.add(ime,"compositionupdate",ime_event_cb,false);GodotEventListeners.add(ime,"compositionend",ime_event_cb,false);GodotEventListeners.add(ime,"keydown",key_event_cb.bind(null,1),false);GodotEventListeners.add(ime,"keyup",key_event_cb.bind(null,0),false);ime.onblur=function(){this.style.display="none";GodotConfig.canvas.focus();GodotIME.active=false};GodotConfig.canvas.parentElement.appendChild(ime);GodotIME.ime=ime},clear:function(){if(GodotIME.ime==null){return}if(GodotIME.focusTimerIntervalId>-1){clearInterval(GodotIME.focusTimerIntervalId);GodotIME.focusTimerIntervalId=-1}GodotIME.ime.remove();GodotIME.ime=null}};var GodotInput={getModifiers:function(evt){return evt.shiftKey+0+(evt.altKey+0<<1)+(evt.ctrlKey+0<<2)+(evt.metaKey+0<<3)},computePosition:function(evt,rect){const canvas=GodotConfig.canvas;const rw=canvas.width/rect.width;const rh=canvas.height/rect.height;const x=(evt.clientX-rect.x)*rw;const y=(evt.clientY-rect.y)*rh;return[x,y]}};function _godot_js_input_drop_files_cb(callback){const func=GodotRuntime.get_func(callback);const dropFiles=function(files){const args=files||[];if(!args.length){return}const argc=args.length;const argv=GodotRuntime.allocStringArray(args);func(argv,argc);GodotRuntime.freeStringArray(argv,argc)};const canvas=GodotConfig.canvas;GodotEventListeners.add(canvas,"dragover",function(ev){ev.preventDefault()},false);GodotEventListeners.add(canvas,"drop",GodotInputDragDrop.handler(dropFiles))}function _godot_js_input_gamepad_cb(change_cb){const onchange=GodotRuntime.get_func(change_cb);GodotInputGamepads.init(onchange)}function _godot_js_input_gamepad_sample(){GodotInputGamepads.sample();return 0}function _godot_js_input_gamepad_sample_count(){return GodotInputGamepads.get_samples().length}function _godot_js_input_gamepad_sample_get(p_index,r_btns,r_btns_num,r_axes,r_axes_num,r_standard){const sample=GodotInputGamepads.get_sample(p_index);if(!sample||!sample.connected){return 1}const btns=sample.buttons;const btns_len=btns.length<16?btns.length:16;for(let i=0;i{const inputs=[...midi.inputs.values()];const inputNames=inputs.map(input=>input.name);const c_ptr=GodotRuntime.allocStringArray(inputNames);setInputNamesCb(inputNames.length,c_ptr);GodotRuntime.freeStringArray(c_ptr,inputNames.length);inputs.forEach((input,i)=>{const abortController=new AbortController;GodotWebMidi.abortControllers.push(abortController);input.addEventListener("midimessage",event=>{const status=event.data[0];const data=event.data.slice(1);const size=data.length;if(size>dataBufferLen){throw new Error(`data too big ${size} > ${dataBufferLen}`)}HEAPU8.set(data,pDataBuffer);onMidiMessageCb(i,status,pDataBuffer,data.length)},{signal:abortController.signal})})});return 0}var GodotWebSocket={_onopen:function(p_id,callback,event){const ref=IDHandler.get(p_id);if(!ref){return}const c_str=GodotRuntime.allocString(ref.protocol);callback(c_str);GodotRuntime.free(c_str)},_onmessage:function(p_id,callback,event){const ref=IDHandler.get(p_id);if(!ref){return}let buffer;let is_string=0;if(event.data instanceof ArrayBuffer){buffer=new Uint8Array(event.data)}else if(event.data instanceof Blob){GodotRuntime.error("Blob type not supported");return}else if(typeof event.data==="string"){is_string=1;const enc=new TextEncoder("utf-8");buffer=new Uint8Array(enc.encode(event.data))}else{GodotRuntime.error("Unknown message type");return}const len=buffer.length*buffer.BYTES_PER_ELEMENT;const out=GodotRuntime.malloc(len);HEAPU8.set(buffer,out);callback(out,len,is_string);GodotRuntime.free(out)},_onerror:function(p_id,callback,event){const ref=IDHandler.get(p_id);if(!ref){return}callback()},_onclose:function(p_id,callback,event){const ref=IDHandler.get(p_id);if(!ref){return}const c_str=GodotRuntime.allocString(event.reason);callback(event.code,c_str,event.wasClean?1:0);GodotRuntime.free(c_str)},send:function(p_id,p_data){const ref=IDHandler.get(p_id);if(!ref||ref.readyState!==ref.OPEN){return 1}ref.send(p_data);return 0},bufferedAmount:function(p_id){const ref=IDHandler.get(p_id);if(!ref){return 0}return ref.bufferedAmount},create:function(socket,p_on_open,p_on_message,p_on_error,p_on_close){const id=IDHandler.add(socket);socket.onopen=GodotWebSocket._onopen.bind(null,id,p_on_open);socket.onmessage=GodotWebSocket._onmessage.bind(null,id,p_on_message);socket.onerror=GodotWebSocket._onerror.bind(null,id,p_on_error);socket.onclose=GodotWebSocket._onclose.bind(null,id,p_on_close);return id},close:function(p_id,p_code,p_reason){const ref=IDHandler.get(p_id);if(ref&&ref.readyState=Number.MIN_SAFE_INTEGER&&heap_value<=Number.MAX_SAFE_INTEGER?Number(heap_value):heap_value}case 3:return Number(GodotRuntime.getHeapValue(val,"double"));case 4:return GodotRuntime.parseString(GodotRuntime.getHeapValue(val,"*"));case 24:return GodotJSWrapper.get_proxied_value(GodotRuntime.getHeapValue(val,"i64"));default:return undefined}},js2variant:function(p_val,p_exchange){if(p_val===undefined||p_val===null){return 0}const type=typeof p_val;if(type==="boolean"){GodotRuntime.setHeapValue(p_exchange,p_val,"i64");return 1}else if(type==="number"){if(Number.isInteger(p_val)){GodotRuntime.setHeapValue(p_exchange,p_val,"i64");return 2}GodotRuntime.setHeapValue(p_exchange,p_val,"double");return 3}else if(type==="bigint"){GodotRuntime.setHeapValue(p_exchange,p_val,"i64");return 2}else if(type==="string"){const c_str=GodotRuntime.allocString(p_val);GodotRuntime.setHeapValue(p_exchange,c_str,"*");return 4}const id=GodotJSWrapper.get_proxied(p_val);GodotRuntime.setHeapValue(p_exchange,id,"i64");return 24},isBuffer:function(obj){return obj instanceof ArrayBuffer||ArrayBuffer.isView(obj)}};function _godot_js_wrapper_create_cb(p_ref,p_func){const func=GodotRuntime.get_func(p_func);let id=0;const cb=function(){if(!GodotJSWrapper.get_proxied_value(id)){return undefined}GodotJSWrapper.cb_ret=null;const args=Array.from(arguments);const argsProxy=new GodotJSWrapper.MyProxy(args);func(p_ref,argsProxy.get_id(),args.length);argsProxy.unref();const ret=GodotJSWrapper.cb_ret;GodotJSWrapper.cb_ret=null;return ret};id=GodotJSWrapper.get_proxied(cb);return id}function _godot_js_wrapper_create_object(p_object,p_args,p_argc,p_convert_callback,p_exchange,p_lock,p_free_lock_callback){const name=GodotRuntime.parseString(p_object);if(typeof window[name]==="undefined"){return-1}const convert=GodotRuntime.get_func(p_convert_callback);const freeLock=GodotRuntime.get_func(p_free_lock_callback);const args=new Array(p_argc);for(let i=0;i{if(GodotWebXR.session&&GodotWebXR.space){const onFrame=function(time,frame){GodotWebXR.frame=frame;GodotWebXR.pose=frame.getViewerPose(GodotWebXR.space);callback(time);GodotWebXR.frame=null;GodotWebXR.pose=null};GodotWebXR.session.requestAnimationFrame(onFrame)}else{GodotWebXR.orig_requestAnimationFrame(callback)}},monkeyPatchRequestAnimationFrame:enable=>{if(GodotWebXR.orig_requestAnimationFrame===null){GodotWebXR.orig_requestAnimationFrame=Browser.requestAnimationFrame}Browser.requestAnimationFrame=enable?GodotWebXR.requestAnimationFrame:GodotWebXR.orig_requestAnimationFrame},pauseResumeMainLoop:()=>{Browser.mainLoop.pause();runtimeKeepalivePush();window.setTimeout(function(){runtimeKeepalivePop();Browser.mainLoop.resume()},0)},getLayer:()=>{const new_view_count=GodotWebXR.pose?GodotWebXR.pose.views.length:1;let layer=GodotWebXR.layer;if(layer&&GodotWebXR.view_count===new_view_count){return layer}if(!GodotWebXR.session||!GodotWebXR.gl_binding){return null}const gl=GodotWebXR.gl;layer=GodotWebXR.gl_binding.createProjectionLayer({textureType:new_view_count>1?"texture-array":"texture",colorFormat:gl.RGBA8,depthFormat:gl.DEPTH_COMPONENT24});GodotWebXR.session.updateRenderState({layers:[layer]});GodotWebXR.layer=layer;GodotWebXR.view_count=new_view_count;return layer},getSubImage:()=>{if(!GodotWebXR.pose){return null}const layer=GodotWebXR.getLayer();if(layer===null){return null}return GodotWebXR.gl_binding.getViewSubImage(layer,GodotWebXR.pose.views[0])},getTextureId:texture=>{if(texture.name!==undefined){return texture.name}const id=GL.getNewId(GL.textures);texture.name=id;GL.textures[id]=texture;return id},addInputSource:input_source=>{let name=-1;if(input_source.targetRayMode==="tracked-pointer"&&input_source.handedness==="left"){name=0}else if(input_source.targetRayMode==="tracked-pointer"&&input_source.handedness==="right"){name=1}else{for(let i=2;i<16;i++){if(!GodotWebXR.input_sources[i]){name=i;break}}}if(name>=0){GodotWebXR.input_sources[name]=input_source;input_source.name=name;if(input_source.targetRayMode==="screen"){let touch_index=-1;for(let i=0;i<5;i++){if(!GodotWebXR.touches[i]){touch_index=i;break}}if(touch_index>=0){GodotWebXR.touches[touch_index]=input_source;input_source.touch_index=touch_index}}}return name},removeInputSource:input_source=>{if(input_source.name!==undefined){const name=input_source.name;if(name>=0&&name<16){GodotWebXR.input_sources[name]=null}if(input_source.touch_index!==undefined){const touch_index=input_source.touch_index;if(touch_index>=0&&touch_index<5){GodotWebXR.touches[touch_index]=null}}return name}return-1},getInputSourceId:input_source=>{if(input_source!==undefined){return input_source.name}return-1},getTouchIndex:input_source=>{if(input_source.touch_index!==undefined){return input_source.touch_index}return-1}};function _godot_webxr_get_bounds_geometry(r_points){if(!GodotWebXR.space||!GodotWebXR.space.boundsGeometry){return 0}const point_count=GodotWebXR.space.boundsGeometry.length;if(point_count===0){return 0}const buf=GodotRuntime.malloc(point_count*3*4);for(let i=0;i=0){matrix=views[p_view].transform.matrix}else{matrix=GodotWebXR.pose.transform.matrix}for(let i=0;i<16;i++){GodotRuntime.setHeapValue(r_transform+i*4,matrix[i],"float")}return true}function _godot_webxr_get_velocity_texture(){const subimage=GodotWebXR.getSubImage();if(subimage===null){return 0}if(!subimage.motionVectorTexture){return 0}return GodotWebXR.getTextureId(subimage.motionVectorTexture)}function _godot_webxr_get_view_count(){if(!GodotWebXR.session||!GodotWebXR.pose){return 1}const view_count=GodotWebXR.pose.views.length;return view_count>0?view_count:1}function _godot_webxr_get_visibility_state(){if(!GodotWebXR.session||!GodotWebXR.session.visibilityState){return 0}return GodotRuntime.allocString(GodotWebXR.session.visibilityState)}var _godot_webxr_initialize=function(p_session_mode,p_required_features,p_optional_features,p_requested_reference_spaces,p_on_session_started,p_on_session_ended,p_on_session_failed,p_on_input_event,p_on_simple_event){GodotWebXR.monkeyPatchRequestAnimationFrame(true);const session_mode=GodotRuntime.parseString(p_session_mode);const required_features=GodotRuntime.parseString(p_required_features).split(",").map(s=>s.trim()).filter(s=>s!=="");const optional_features=GodotRuntime.parseString(p_optional_features).split(",").map(s=>s.trim()).filter(s=>s!=="");const requested_reference_space_types=GodotRuntime.parseString(p_requested_reference_spaces).split(",").map(s=>s.trim());const onstarted=GodotRuntime.get_func(p_on_session_started);const onended=GodotRuntime.get_func(p_on_session_ended);const onfailed=GodotRuntime.get_func(p_on_session_failed);const oninputevent=GodotRuntime.get_func(p_on_input_event);const onsimpleevent=GodotRuntime.get_func(p_on_simple_event);const session_init={};if(required_features.length>0){session_init["requiredFeatures"]=required_features}if(optional_features.length>0){session_init["optionalFeatures"]=optional_features}navigator.xr.requestSession(session_mode,session_init).then(function(session){GodotWebXR.session=session;session.addEventListener("end",function(evt){onended()});session.addEventListener("inputsourceschange",function(evt){evt.added.forEach(GodotWebXR.addInputSource);evt.removed.forEach(GodotWebXR.removeInputSource)});["selectstart","selectend","squeezestart","squeezeend"].forEach((input_event,index)=>{session.addEventListener(input_event,function(evt){GodotWebXR.frame=evt.frame;oninputevent(index,GodotWebXR.getInputSourceId(evt.inputSource));GodotWebXR.frame=null})});session.addEventListener("visibilitychange",function(evt){const c_str=GodotRuntime.allocString("visibility_state_changed");onsimpleevent(c_str);GodotRuntime.free(c_str)});GodotWebXR.onsimpleevent=onsimpleevent;const gl_context_handle=_emscripten_webgl_get_current_context();const gl=GL.getContext(gl_context_handle).GLctx;GodotWebXR.gl=gl;gl.makeXRCompatible().then(function(){GodotWebXR.gl_binding=new XRWebGLBinding(session,gl);GodotWebXR.getLayer();function onReferenceSpaceSuccess(reference_space,reference_space_type){GodotWebXR.space=reference_space;reference_space.onreset=function(evt){const c_str=GodotRuntime.allocString("reference_space_reset");onsimpleevent(c_str);GodotRuntime.free(c_str)};GodotWebXR.pauseResumeMainLoop();window.setTimeout(function(){const reference_space_c_str=GodotRuntime.allocString(reference_space_type);const enabled_features="enabledFeatures"in session?Array.from(session.enabledFeatures):[];const enabled_features_c_str=GodotRuntime.allocString(enabled_features.join(","));const environment_blend_mode="environmentBlendMode"in session?session.environmentBlendMode:"";const environment_blend_mode_c_str=GodotRuntime.allocString(environment_blend_mode);onstarted(reference_space_c_str,enabled_features_c_str,environment_blend_mode_c_str);GodotRuntime.free(reference_space_c_str);GodotRuntime.free(enabled_features_c_str);GodotRuntime.free(environment_blend_mode_c_str)},0)}function requestReferenceSpace(){const reference_space_type=requested_reference_space_types.shift();session.requestReferenceSpace(reference_space_type).then(refSpace=>{onReferenceSpaceSuccess(refSpace,reference_space_type)}).catch(()=>{if(requested_reference_space_types.length===0){const c_str=GodotRuntime.allocString("Unable to get any of the requested reference space types");onfailed(c_str);GodotRuntime.free(c_str)}else{requestReferenceSpace()}})}requestReferenceSpace()}).catch(function(error){const c_str=GodotRuntime.allocString(`Unable to make WebGL context compatible with WebXR: ${error}`);onfailed(c_str);GodotRuntime.free(c_str)})}).catch(function(error){const c_str=GodotRuntime.allocString(`Unable to start session: ${error}`);onfailed(c_str);GodotRuntime.free(c_str)})};function _godot_webxr_is_session_supported(p_session_mode,p_callback){const session_mode=GodotRuntime.parseString(p_session_mode);const cb=GodotRuntime.get_func(p_callback);if(navigator.xr){navigator.xr.isSessionSupported(session_mode).then(function(supported){const c_str=GodotRuntime.allocString(session_mode);cb(c_str,supported?1:0);GodotRuntime.free(c_str)})}else{const c_str=GodotRuntime.allocString(session_mode);cb(c_str,0);GodotRuntime.free(c_str)}}function _godot_webxr_is_supported(){return!!navigator.xr}var _godot_webxr_uninitialize=function(){if(GodotWebXR.session){GodotWebXR.session.end().catch(e=>{})}GodotWebXR.session=null;GodotWebXR.gl_binding=null;GodotWebXR.layer=null;GodotWebXR.space=null;GodotWebXR.frame=null;GodotWebXR.pose=null;GodotWebXR.view_count=1;GodotWebXR.input_sources=new Array(16);GodotWebXR.touches=new Array(5);GodotWebXR.onsimpleevent=null;GodotWebXR.monkeyPatchRequestAnimationFrame(false);GodotWebXR.pauseResumeMainLoop()};function _godot_webxr_update_input_source(p_input_source_id,r_target_pose,r_target_ray_mode,r_touch_index,r_has_grip_pose,r_grip_pose,r_has_standard_mapping,r_button_count,r_buttons,r_axes_count,r_axes,r_has_hand_data,r_hand_joints,r_hand_radii){if(!GodotWebXR.session||!GodotWebXR.frame){return 0}if(p_input_source_id<0||p_input_source_id>=GodotWebXR.input_sources.length||!GodotWebXR.input_sources[p_input_source_id]){return false}const input_source=GodotWebXR.input_sources[p_input_source_id];const frame=GodotWebXR.frame;const space=GodotWebXR.space;const target_pose=frame.getPose(input_source.targetRaySpace,space);if(!target_pose){return false}const target_pose_matrix=target_pose.transform.matrix;for(let i=0;i<16;i++){GodotRuntime.setHeapValue(r_target_pose+i*4,target_pose_matrix[i],"float")}let target_ray_mode=0;switch(input_source.targetRayMode){case"gaze":target_ray_mode=1;break;case"tracked-pointer":target_ray_mode=2;break;case"screen":target_ray_mode=3;break;default:}GodotRuntime.setHeapValue(r_target_ray_mode,target_ray_mode,"i32");GodotRuntime.setHeapValue(r_touch_index,GodotWebXR.getTouchIndex(input_source),"i32");let has_grip_pose=false;if(input_source.gripSpace){const grip_pose=frame.getPose(input_source.gripSpace,space);if(grip_pose){const grip_pose_matrix=grip_pose.transform.matrix;for(let i=0;i<16;i++){GodotRuntime.setHeapValue(r_grip_pose+i*4,grip_pose_matrix[i],"float")}has_grip_pose=true}}GodotRuntime.setHeapValue(r_has_grip_pose,has_grip_pose?1:0,"i32");let has_standard_mapping=false;let button_count=0;let axes_count=0;if(input_source.gamepad){if(input_source.gamepad.mapping==="xr-standard"){has_standard_mapping=true}button_count=Math.min(input_source.gamepad.buttons.length,10);for(let i=0;i{const c_str=GodotRuntime.allocString("display_refresh_rate_changed");GodotWebXR.onsimpleevent(c_str);GodotRuntime.free(c_str)})}var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackSave=()=>_emscripten_stack_get_current();var stackRestore=val=>__emscripten_stack_restore(val);var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();Module["requestFullscreen"]=Browser.requestFullscreen;Module["requestAnimationFrame"]=Browser.requestAnimationFrame;Module["setCanvasSize"]=Browser.setCanvasSize;Module["pauseMainLoop"]=Browser.mainLoop.pause;Module["resumeMainLoop"]=Browser.mainLoop.resume;Module["getUserMedia"]=Browser.getUserMedia;Module["createContext"]=Browser.createContext;var preloadedImages={};var preloadedAudios={};var GLctx;for(var i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));var miniTempWebGLIntBuffersStorage=new Int32Array(288);for(var i=0;i<=288;++i){miniTempWebGLIntBuffers[i]=miniTempWebGLIntBuffersStorage.subarray(0,i)}var miniTempWebGLFloatBuffersStorage=new Float32Array(288);for(var i=0;i<=288;++i){miniTempWebGLFloatBuffers[i]=miniTempWebGLFloatBuffersStorage.subarray(0,i)}Module["request_quit"]=function(){GodotOS.request_quit()};Module["onExit"]=GodotOS.cleanup;GodotOS._fs_sync_promise=Promise.resolve();Module["initConfig"]=GodotConfig.init_config;Module["initFS"]=GodotFS.init;Module["copyToFS"]=GodotFS.copy_to_fs;GodotOS.atexit(function(resolve,reject){GodotDisplayCursor.clear();resolve()});GodotOS.atexit(function(resolve,reject){GodotEventListeners.clear();resolve()});GodotOS.atexit(function(resolve,reject){GodotDisplayVK.clear();resolve()});GodotOS.atexit(function(resolve,reject){GodotIME.clear();resolve()});GodotJSWrapper.proxies=new Map;var wasmImports={Jc:___call_sighandler,cd:___syscall_chdir,Xa:___syscall_chmod,dd:___syscall_faccessat,$c:___syscall_fchmod,V:___syscall_fcntl64,_c:___syscall_fstat64,Wc:___syscall_ftruncate64,Uc:___syscall_getcwd,Ic:___syscall_getdents64,gd:___syscall_ioctl,Yc:___syscall_lstat64,Qc:___syscall_mkdirat,Pc:___syscall_mknodat,Xc:___syscall_newfstatat,Wa:___syscall_openat,Hc:___syscall_readlinkat,Gc:___syscall_renameat,Ta:___syscall_rmdir,Zc:___syscall_stat64,Fc:___syscall_statfs64,Ec:___syscall_symlink,Ua:___syscall_unlinkat,ed:__abort_js,ad:__emscripten_get_now_is_monotonic,Mc:__emscripten_runtime_keepalive_clear,Nc:__gmtime_js,Oc:__localtime_js,fd:__tzset_js,Pe:_emscripten_cancel_main_loop,za:_emscripten_date_now,Qe:_emscripten_force_exit,Dc:_emscripten_get_heap_max,ha:_emscripten_get_now,Bc:_emscripten_resize_heap,Cc:_emscripten_set_canvas_element_size,La:_emscripten_set_main_loop,Pa:_emscripten_webgl_commit_frame,Zb:_emscripten_webgl_create_context,Hb:_emscripten_webgl_destroy_context,Xb:_emscripten_webgl_enable_extension,td:_emscripten_webgl_get_supported_extensions,Yb:_emscripten_webgl_make_context_current,Sc:_environ_get,Tc:_environ_sizes_get,Ka:_exit,ma:_fd_close,Va:_fd_fdstat_get,Ya:_fd_read,Rc:_fd_seek,Aa:_fd_write,i:_glActiveTexture,bb:_glAttachShader,ea:_glBeginTransformFeedback,b:_glBindBuffer,y:_glBindBufferBase,Da:_glBindBufferRange,d:_glBindFramebuffer,ta:_glBindRenderbuffer,c:_glBindTexture,e:_glBindVertexArray,Md:_glBlendColor,J:_glBlendEquation,ja:_glBlendFunc,D:_glBlendFuncSeparate,ga:_glBlitFramebuffer,h:_glBufferData,R:_glBufferSubData,P:_glCheckFramebufferStatus,G:_glClear,Fa:_glClearBufferfv,Q:_glClearColor,aa:_glClearDepthf,Y:_glColorMask,db:_glCompileShader,ld:_glCompressedTexImage2D,nd:_glCompressedTexImage3D,md:_glCompressedTexSubImage3D,pd:_glCopyBufferSubData,yd:_glCreateProgram,fb:_glCreateShader,la:_glCullFace,o:_glDeleteBuffers,v:_glDeleteFramebuffers,fa:_glDeleteProgram,jd:_glDeleteQueries,oa:_glDeleteRenderbuffers,X:_glDeleteShader,kb:_glDeleteSync,l:_glDeleteTextures,L:_glDeleteVertexArrays,U:_glDepthFunc,w:_glDepthMask,g:_glDisable,p:_glDisableVertexAttribArray,F:_glDrawArrays,Z:_glDrawArraysInstanced,ka:_glDrawBuffers,M:_glDrawElements,N:_glDrawElementsInstanced,x:_glEnable,f:_glEnableVertexAttribArray,da:_glEndTransformFeedback,jb:_glFenceSync,Ed:_glFinish,Ia:_glFramebufferRenderbuffer,t:_glFramebufferTexture2D,$:_glFramebufferTextureLayer,hb:_glFrontFace,m:_glGenBuffers,B:_glGenFramebuffers,kd:_glGenQueries,Ja:_glGenRenderbuffers,r:_glGenTextures,K:_glGenVertexArrays,id:_glGenerateMipmap,qd:_glGetFloatv,sd:_glGetInteger64v,na:_glGetIntegerv,ud:_glGetProgramInfoLog,ab:_glGetProgramiv,cb:_glGetShaderInfoLog,ra:_glGetShaderiv,_:_glGetString,Od:_glGetSynciv,Bd:_glGetUniformBlockIndex,Ea:_glGetUniformLocation,vd:_glLinkProgram,Ba:_glPixelStorei,ib:_glReadBuffer,Ca:_glReadPixels,Hd:_glRenderbufferStorage,_a:_glRenderbufferStorageMultisample,va:_glScissor,eb:_glShaderSource,q:_glTexImage2D,T:_glTexImage3D,Ha:_glTexParameterf,a:_glTexParameteri,Ga:_glTexStorage2D,Za:_glTexSubImage3D,wd:_glTransformFeedbackVaryings,k:_glUniform1f,E:_glUniform1i,zd:_glUniform1iv,u:_glUniform1ui,sa:_glUniform1uiv,ba:_glUniform2f,H:_glUniform2fv,ia:_glUniform2iv,s:_glUniform3fv,O:_glUniform4f,C:_glUniform4fv,Ad:_glUniformBlockBinding,gb:_glUniformMatrix3fv,I:_glUniformMatrix4fv,n:_glUseProgram,ua:_glVertexAttrib4f,z:_glVertexAttribDivisor,ca:_glVertexAttribI4ui,S:_glVertexAttribIPointer,j:_glVertexAttribPointer,A:_glViewport,Ce:_godot_audio_get_sample_playback_position,rd:_godot_audio_has_script_processor,Dd:_godot_audio_has_worklet,ff:_godot_audio_init,uc:_godot_audio_input_start,nc:_godot_audio_input_stop,gf:_godot_audio_is_available,pa:_godot_audio_resume,Nd:_godot_audio_sample_bus_add,Ld:_godot_audio_sample_bus_move,Td:_godot_audio_sample_bus_remove,ae:_godot_audio_sample_bus_set_count,Fd:_godot_audio_sample_bus_set_mute,Jd:_godot_audio_sample_bus_set_send,Gd:_godot_audio_sample_bus_set_solo,Id:_godot_audio_sample_bus_set_volume_db,Ne:_godot_audio_sample_is_active,Ub:_godot_audio_sample_register_stream,Kd:_godot_audio_sample_set_finished_callback,We:_godot_audio_sample_set_pause,ke:_godot_audio_sample_set_volumes_linear,Bb:_godot_audio_sample_start,ef:_godot_audio_sample_stop,dc:_godot_audio_sample_stream_is_registered,Jb:_godot_audio_sample_unregister_stream,te:_godot_audio_sample_update_pitch_scale,od:_godot_audio_script_create,hd:_godot_audio_script_start,Cd:_godot_audio_worklet_create,xd:_godot_audio_worklet_start_no_threads,cc:_godot_js_config_canvas_id_get,Fe:_godot_js_config_locale_get,Oe:_godot_js_display_alert,oc:_godot_js_display_canvas_focus,pc:_godot_js_display_canvas_is_focused,ec:_godot_js_display_clipboard_get,fc:_godot_js_display_clipboard_set,rc:_godot_js_display_cursor_is_hidden,qc:_godot_js_display_cursor_is_locked,xa:_godot_js_display_cursor_lock_set,Sa:_godot_js_display_cursor_set_custom_shape,sc:_godot_js_display_cursor_set_shape,ya:_godot_js_display_cursor_set_visible,Ab:_godot_js_display_desired_size_set,Mb:_godot_js_display_fullscreen_cb,zb:_godot_js_display_fullscreen_exit,yb:_godot_js_display_fullscreen_request,_b:_godot_js_display_has_webgl,ac:_godot_js_display_is_swap_ok_cancel,Kb:_godot_js_display_notification_cb,Db:_godot_js_display_pixel_ratio_get,Eb:_godot_js_display_screen_dpi_get,Fb:_godot_js_display_screen_size_get,bc:_godot_js_display_setup_canvas,Lc:_godot_js_display_size_update,mc:_godot_js_display_touchscreen_is_available,Gb:_godot_js_display_tts_available,Oa:_godot_js_display_vk_available,Ib:_godot_js_display_vk_cb,kc:_godot_js_display_vk_hide,lc:_godot_js_display_vk_show,Lb:_godot_js_display_window_blur_cb,Qa:_godot_js_display_window_icon_set,Na:_godot_js_display_window_size_get,Cb:_godot_js_display_window_title_set,Ve:_godot_js_eval,wb:_godot_js_fetch_create,Ma:_godot_js_fetch_free,tb:_godot_js_fetch_http_status_get,vb:_godot_js_fetch_is_chunked,ub:_godot_js_fetch_read_chunk,df:_godot_js_fetch_read_headers,wa:_godot_js_fetch_state_get,Pb:_godot_js_input_drop_files_cb,Ob:_godot_js_input_gamepad_cb,xb:_godot_js_input_gamepad_sample,hc:_godot_js_input_gamepad_sample_count,gc:_godot_js_input_gamepad_sample_get,Rb:_godot_js_input_key_cb,Wb:_godot_js_input_mouse_button_cb,Vb:_godot_js_input_mouse_move_cb,Tb:_godot_js_input_mouse_wheel_cb,Qb:_godot_js_input_paste_cb,Sb:_godot_js_input_touch_cb,He:_godot_js_input_vibrate_handheld,Ra:_godot_js_is_ime_focused,Se:_godot_js_os_download_buffer,Le:_godot_js_os_execute,rb:_godot_js_os_finish_async,De:_godot_js_os_fs_is_persistent,Me:_godot_js_os_fs_sync,Je:_godot_js_os_has_feature,Ke:_godot_js_os_hw_concurrency_get,$b:_godot_js_os_request_quit_cb,Ie:_godot_js_os_shell_open,Ee:_godot_js_pwa_cb,Ge:_godot_js_pwa_update,qb:_godot_js_rtc_datachannel_close,se:_godot_js_rtc_datachannel_connect,pe:_godot_js_rtc_datachannel_destroy,ue:_godot_js_rtc_datachannel_get_buffered_amount,ye:_godot_js_rtc_datachannel_id_get,ve:_godot_js_rtc_datachannel_is_negotiated,ze:_godot_js_rtc_datachannel_is_ordered,re:_godot_js_rtc_datachannel_label_get,xe:_godot_js_rtc_datachannel_max_packet_lifetime_get,we:_godot_js_rtc_datachannel_max_retransmits_get,qe:_godot_js_rtc_datachannel_protocol_get,Be:_godot_js_rtc_datachannel_ready_state_get,Ae:_godot_js_rtc_datachannel_send,pb:_godot_js_rtc_pc_close,je:_godot_js_rtc_pc_create,ie:_godot_js_rtc_pc_datachannel_create,ob:_godot_js_rtc_pc_destroy,le:_godot_js_rtc_pc_ice_candidate_add,ne:_godot_js_rtc_pc_local_description_set,oe:_godot_js_rtc_pc_offer_create,me:_godot_js_rtc_pc_remote_description_set,jc:_godot_js_set_ime_active,Nb:_godot_js_set_ime_cb,ic:_godot_js_set_ime_position,yc:_godot_js_tts_get_voices,zc:_godot_js_tts_is_paused,Ac:_godot_js_tts_is_speaking,wc:_godot_js_tts_pause,vc:_godot_js_tts_resume,xc:_godot_js_tts_speak,tc:_godot_js_tts_stop,Vc:_godot_js_webmidi_close_midi_inputs,bd:_godot_js_webmidi_open_midi_inputs,fe:_godot_js_websocket_buffered_amount,ee:_godot_js_websocket_close,he:_godot_js_websocket_create,nb:_godot_js_websocket_destroy,ge:_godot_js_websocket_send,Ze:_godot_js_wrapper_create_cb,Xe:_godot_js_wrapper_create_object,Ye:_godot_js_wrapper_interface_get,$e:_godot_js_wrapper_object_call,bf:_godot_js_wrapper_object_get,sb:_godot_js_wrapper_object_getvar,Ue:_godot_js_wrapper_object_is_buffer,cf:_godot_js_wrapper_object_set,_e:_godot_js_wrapper_object_set_cb_ret,af:_godot_js_wrapper_object_setvar,Te:_godot_js_wrapper_object_transfer_buffer,Re:_godot_js_wrapper_object_unref,$a:_godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR,W:_godot_webgl2_glFramebufferTextureMultiviewOVR,qa:_godot_webgl2_glGetBufferSubData,be:_godot_webxr_get_bounds_geometry,Sd:_godot_webxr_get_color_texture,Rd:_godot_webxr_get_depth_texture,$d:_godot_webxr_get_frame_rate,Ud:_godot_webxr_get_projection_for_view,Vd:_godot_webxr_get_render_target_size,Zd:_godot_webxr_get_supported_frame_rates,lb:_godot_webxr_get_transform_for_view,Qd:_godot_webxr_get_velocity_texture,mb:_godot_webxr_get_view_count,ce:_godot_webxr_get_visibility_state,Xd:_godot_webxr_initialize,de:_godot_webxr_is_session_supported,Yd:_godot_webxr_is_supported,Wd:_godot_webxr_uninitialize,Pd:_godot_webxr_update_input_source,_d:_godot_webxr_update_target_frame_rate,Kc:_proc_exit};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["jf"])();var _free=a0=>(_free=wasmExports["kf"])(a0);var __Z14godot_web_mainiPPc=Module["__Z14godot_web_mainiPPc"]=(a0,a1)=>(__Z14godot_web_mainiPPc=Module["__Z14godot_web_mainiPPc"]=wasmExports["lf"])(a0,a1);var _main=Module["_main"]=(a0,a1)=>(_main=Module["_main"]=wasmExports["mf"])(a0,a1);var _malloc=a0=>(_malloc=wasmExports["nf"])(a0);var _fflush=a0=>(_fflush=wasmExports["of"])(a0);var __emwebxr_on_input_event=Module["__emwebxr_on_input_event"]=(a0,a1)=>(__emwebxr_on_input_event=Module["__emwebxr_on_input_event"]=wasmExports["pf"])(a0,a1);var __emwebxr_on_simple_event=Module["__emwebxr_on_simple_event"]=a0=>(__emwebxr_on_simple_event=Module["__emwebxr_on_simple_event"]=wasmExports["qf"])(a0);var ___funcs_on_exit=()=>(___funcs_on_exit=wasmExports["sf"])();var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["tf"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["uf"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["vf"])();Module["callMain"]=callMain;Module["cwrap"]=cwrap;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args=[]){var entryFunction=_main;args.unshift(thisProgram);var argc=args.length;var argv=stackAlloc((argc+1)*4);var argv_ptr=argv;args.forEach(arg=>{HEAPU32[argv_ptr>>2]=stringToUTF8OnStack(arg);argv_ptr+=4});HEAPU32[argv_ptr>>2]=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(args=arguments_){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=false;if(Module["noInitialRun"])shouldRunNow=false;run();moduleRtn=readyPromise; + + + return moduleRtn; +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = Godot; +else if (typeof define === 'function' && define['amd']) + define([], () => Godot); + +const Features = { + /** + * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for. + * + * @param {number=} [majorVersion=1] The major WebGL version to check for. + * @returns {boolean} If the given major version of WebGL is available. + * @function Engine.isWebGLAvailable + */ + isWebGLAvailable: function (majorVersion = 1) { + try { + return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]); + } catch (e) { /* Not available */ } + return false; + }, + + /** + * Check whether the Fetch API available and supports streaming responses. + * + * @returns {boolean} If the Fetch API is available and supports streaming responses. + * @function Engine.isFetchAvailable + */ + isFetchAvailable: function () { + return 'fetch' in window && 'Response' in window && 'body' in window.Response.prototype; + }, + + /** + * Check whether the engine is running in a Secure Context. + * + * @returns {boolean} If the engine is running in a Secure Context. + * @function Engine.isSecureContext + */ + isSecureContext: function () { + return window['isSecureContext'] === true; + }, + + /** + * Check whether the engine is cross origin isolated. + * This value is dependent on Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers sent by the server. + * + * @returns {boolean} If the engine is running in a Secure Context. + * @function Engine.isSecureContext + */ + isCrossOriginIsolated: function () { + return window['crossOriginIsolated'] === true; + }, + + /** + * Check whether SharedBufferArray is available. + * + * Most browsers require the page to be running in a secure context, and the + * the server to provide specific CORS headers for SharedArrayBuffer to be available. + * + * @returns {boolean} If SharedArrayBuffer is available. + * @function Engine.isSharedArrayBufferAvailable + */ + isSharedArrayBufferAvailable: function () { + return 'SharedArrayBuffer' in window; + }, + + /** + * Check whether the AudioContext supports AudioWorkletNodes. + * + * @returns {boolean} If AudioWorkletNode is available. + * @function Engine.isAudioWorkletAvailable + */ + isAudioWorkletAvailable: function () { + return 'AudioContext' in window && 'audioWorklet' in AudioContext.prototype; + }, + + /** + * Return an array of missing required features (as string). + * + * @returns {Array} A list of human-readable missing features. + * @function Engine.getMissingFeatures + * @param {{threads: (boolean|undefined)}} supportedFeatures + */ + getMissingFeatures: function (supportedFeatures = {}) { + const { + // Quotes are needed for the Closure compiler. + 'threads': supportsThreads = true, + } = supportedFeatures; + + const missing = []; + if (!Features.isWebGLAvailable(2)) { + missing.push('WebGL2 - Check web browser configuration and hardware support'); + } + if (!Features.isFetchAvailable()) { + missing.push('Fetch - Check web browser version'); + } + if (!Features.isSecureContext()) { + missing.push('Secure Context - Check web server configuration (use HTTPS)'); + } + + if (supportsThreads) { + if (!Features.isCrossOriginIsolated()) { + missing.push('Cross-Origin Isolation - Check that the web server configuration sends the correct headers.'); + } + if (!Features.isSharedArrayBufferAvailable()) { + missing.push('SharedArrayBuffer - Check that the web server configuration sends the correct headers.'); + } + } + + // Audio is normally optional since we have a dummy fallback. + return missing; + }, +}; + +const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars + function getTrackedResponse(response, load_status) { + function onloadprogress(reader, controller) { + return reader.read().then(function (result) { + if (load_status.done) { + return Promise.resolve(); + } + if (result.value) { + controller.enqueue(result.value); + load_status.loaded += result.value.length; + } + if (!result.done) { + return onloadprogress(reader, controller); + } + load_status.done = true; + return Promise.resolve(); + }); + } + const reader = response.body.getReader(); + return new Response(new ReadableStream({ + start: function (controller) { + onloadprogress(reader, controller).then(function () { + controller.close(); + }); + }, + }), { headers: response.headers }); + } + + function loadFetch(file, tracker, fileSize, raw) { + tracker[file] = { + total: fileSize || 0, + loaded: 0, + done: false, + }; + return fetch(file).then(function (response) { + if (!response.ok) { + return Promise.reject(new Error(`Failed loading file '${file}'`)); + } + const tr = getTrackedResponse(response, tracker[file]); + if (raw) { + return Promise.resolve(tr); + } + return tr.arrayBuffer(); + }); + } + + function retry(func, attempts = 1) { + function onerror(err) { + if (attempts <= 1) { + return Promise.reject(err); + } + return new Promise(function (resolve, reject) { + setTimeout(function () { + retry(func, attempts - 1).then(resolve).catch(reject); + }, 1000); + }); + } + return func().catch(onerror); + } + + const DOWNLOAD_ATTEMPTS_MAX = 4; + const loadingFiles = {}; + const lastProgress = { loaded: 0, total: 0 }; + let progressFunc = null; + + const animateProgress = function () { + let loaded = 0; + let total = 0; + let totalIsValid = true; + let progressIsFinal = true; + + Object.keys(loadingFiles).forEach(function (file) { + const stat = loadingFiles[file]; + if (!stat.done) { + progressIsFinal = false; + } + if (!totalIsValid || stat.total === 0) { + totalIsValid = false; + total = 0; + } else { + total += stat.total; + } + loaded += stat.loaded; + }); + if (loaded !== lastProgress.loaded || total !== lastProgress.total) { + lastProgress.loaded = loaded; + lastProgress.total = total; + if (typeof progressFunc === 'function') { + progressFunc(loaded, total); + } + } + if (!progressIsFinal) { + requestAnimationFrame(animateProgress); + } + }; + + this.animateProgress = animateProgress; + + this.setProgressFunc = function (callback) { + progressFunc = callback; + }; + + this.loadPromise = function (file, fileSize, raw = false) { + return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX); + }; + + this.preloadedFiles = []; + this.preload = function (pathOrBuffer, destPath, fileSize) { + let buffer = null; + if (typeof pathOrBuffer === 'string') { + const me = this; + return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) { + me.preloadedFiles.push({ + path: destPath || pathOrBuffer, + buffer: buf, + }); + return Promise.resolve(); + }); + } else if (pathOrBuffer instanceof ArrayBuffer) { + buffer = new Uint8Array(pathOrBuffer); + } else if (ArrayBuffer.isView(pathOrBuffer)) { + buffer = new Uint8Array(pathOrBuffer.buffer); + } + if (buffer) { + this.preloadedFiles.push({ + path: destPath, + buffer: pathOrBuffer, + }); + return Promise.resolve(); + } + return Promise.reject(new Error('Invalid object for preloading')); + }; +}; + +/** + * An object used to configure the Engine instance based on godot export options, and to override those in custom HTML + * templates if needed. + * + * @header Engine configuration + * @summary The Engine configuration object. This is just a typedef, create it like a regular object, e.g.: + * + * ``const MyConfig = { executable: 'godot', unloadAfterInit: false }`` + * + * @typedef {Object} EngineConfig + */ +const EngineConfig = {}; // eslint-disable-line no-unused-vars + +/** + * @struct + * @constructor + * @ignore + */ +const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-vars + const cfg = /** @lends {InternalConfig.prototype} */ { + /** + * Whether to unload the engine automatically after the instance is initialized. + * + * @memberof EngineConfig + * @default + * @type {boolean} + */ + unloadAfterInit: true, + /** + * The HTML DOM Canvas object to use. + * + * By default, the first canvas element in the document will be used is none is specified. + * + * @memberof EngineConfig + * @default + * @type {?HTMLCanvasElement} + */ + canvas: null, + /** + * The name of the WASM file without the extension. (Set by Godot Editor export process). + * + * @memberof EngineConfig + * @default + * @type {string} + */ + executable: '', + /** + * An alternative name for the game pck to load. The executable name is used otherwise. + * + * @memberof EngineConfig + * @default + * @type {?string} + */ + mainPack: null, + /** + * Specify a language code to select the proper localization for the game. + * + * The browser locale will be used if none is specified. See complete list of + * :ref:`supported locales `. + * + * @memberof EngineConfig + * @type {?string} + * @default + */ + locale: null, + /** + * The canvas resize policy determines how the canvas should be resized by Godot. + * + * ``0`` means Godot won't do any resizing. This is useful if you want to control the canvas size from + * javascript code in your template. + * + * ``1`` means Godot will resize the canvas on start, and when changing window size via engine functions. + * + * ``2`` means Godot will adapt the canvas size to match the whole browser window. + * + * @memberof EngineConfig + * @type {number} + * @default + */ + canvasResizePolicy: 2, + /** + * The arguments to be passed as command line arguments on startup. + * + * See :ref:`command line tutorial `. + * + * **Note**: :js:meth:`startGame ` will always add the ``--main-pack`` argument. + * + * @memberof EngineConfig + * @type {Array} + * @default + */ + args: [], + /** + * When enabled, the game canvas will automatically grab the focus when the engine starts. + * + * @memberof EngineConfig + * @type {boolean} + * @default + */ + focusCanvas: true, + /** + * When enabled, this will turn on experimental virtual keyboard support on mobile. + * + * @memberof EngineConfig + * @type {boolean} + * @default + */ + experimentalVK: false, + /** + * The progressive web app service worker to install. + * @memberof EngineConfig + * @default + * @type {string} + */ + serviceWorker: '', + /** + * @ignore + * @type {Array.} + */ + persistentPaths: ['/userfs'], + /** + * @ignore + * @type {boolean} + */ + persistentDrops: false, + /** + * @ignore + * @type {Array.} + */ + gdextensionLibs: [], + /** + * @ignore + * @type {Array.} + */ + fileSizes: [], + /** + * A callback function for handling Godot's ``OS.execute`` calls. + * + * This is for example used in the Web Editor template to switch between project manager and editor, and for running the game. + * + * @callback EngineConfig.onExecute + * @param {string} path The path that Godot's wants executed. + * @param {Array.} args The arguments of the "command" to execute. + */ + /** + * @ignore + * @type {?function(string, Array.)} + */ + onExecute: null, + /** + * A callback function for being notified when the Godot instance quits. + * + * **Note**: This function will not be called if the engine crashes or become unresponsive. + * + * @callback EngineConfig.onExit + * @param {number} status_code The status code returned by Godot on exit. + */ + /** + * @ignore + * @type {?function(number)} + */ + onExit: null, + /** + * A callback function for displaying download progress. + * + * The function is called once per frame while downloading files, so the usage of ``requestAnimationFrame()`` + * is not necessary. + * + * If the callback function receives a total amount of bytes as 0, this means that it is impossible to calculate. + * Possible reasons include: + * + * - Files are delivered with server-side chunked compression + * - Files are delivered with server-side compression on Chromium + * - Not all file downloads have started yet (usually on servers without multi-threading) + * + * @callback EngineConfig.onProgress + * @param {number} current The current amount of downloaded bytes so far. + * @param {number} total The total amount of bytes to be downloaded. + */ + /** + * @ignore + * @type {?function(number, number)} + */ + onProgress: null, + /** + * A callback function for handling the standard output stream. This method should usually only be used in debug pages. + * + * By default, ``console.log()`` is used. + * + * @callback EngineConfig.onPrint + * @param {...*} [var_args] A variadic number of arguments to be printed. + */ + /** + * @ignore + * @type {?function(...*)} + */ + onPrint: function () { + console.log.apply(console, Array.from(arguments)); // eslint-disable-line no-console + }, + /** + * A callback function for handling the standard error stream. This method should usually only be used in debug pages. + * + * By default, ``console.error()`` is used. + * + * @callback EngineConfig.onPrintError + * @param {...*} [var_args] A variadic number of arguments to be printed as errors. + */ + /** + * @ignore + * @type {?function(...*)} + */ + onPrintError: function (var_args) { + console.error.apply(console, Array.from(arguments)); // eslint-disable-line no-console + }, + }; + + /** + * @ignore + * @struct + * @constructor + * @param {EngineConfig} opts + */ + function Config(opts) { + this.update(opts); + } + + Config.prototype = cfg; + + /** + * @ignore + * @param {EngineConfig} opts + */ + Config.prototype.update = function (opts) { + const config = opts || {}; + // NOTE: We must explicitly pass the default, accessing it via + // the key will fail due to closure compiler renames. + function parse(key, def) { + if (typeof (config[key]) === 'undefined') { + return def; + } + return config[key]; + } + // Module config + this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit); + this.onPrintError = parse('onPrintError', this.onPrintError); + this.onPrint = parse('onPrint', this.onPrint); + this.onProgress = parse('onProgress', this.onProgress); + + // Godot config + this.canvas = parse('canvas', this.canvas); + this.executable = parse('executable', this.executable); + this.mainPack = parse('mainPack', this.mainPack); + this.locale = parse('locale', this.locale); + this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy); + this.persistentPaths = parse('persistentPaths', this.persistentPaths); + this.persistentDrops = parse('persistentDrops', this.persistentDrops); + this.experimentalVK = parse('experimentalVK', this.experimentalVK); + this.focusCanvas = parse('focusCanvas', this.focusCanvas); + this.serviceWorker = parse('serviceWorker', this.serviceWorker); + this.gdextensionLibs = parse('gdextensionLibs', this.gdextensionLibs); + this.fileSizes = parse('fileSizes', this.fileSizes); + this.args = parse('args', this.args); + this.onExecute = parse('onExecute', this.onExecute); + this.onExit = parse('onExit', this.onExit); + }; + + /** + * @ignore + * @param {string} loadPath + * @param {Response} response + */ + Config.prototype.getModuleConfig = function (loadPath, response) { + let r = response; + const gdext = this.gdextensionLibs; + return { + 'print': this.onPrint, + 'printErr': this.onPrintError, + 'thisProgram': this.executable, + 'noExitRuntime': false, + 'dynamicLibraries': [`${loadPath}.side.wasm`].concat(this.gdextensionLibs), + 'instantiateWasm': function (imports, onSuccess) { + function done(result) { + onSuccess(result['instance'], result['module']); + } + if (typeof (WebAssembly.instantiateStreaming) !== 'undefined') { + WebAssembly.instantiateStreaming(Promise.resolve(r), imports).then(done); + } else { + r.arrayBuffer().then(function (buffer) { + WebAssembly.instantiate(buffer, imports).then(done); + }); + } + r = null; + return {}; + }, + 'locateFile': function (path) { + if (!path.startsWith('godot.')) { + return path; + } else if (path.endsWith('.audio.worklet.js')) { + return `${loadPath}.audio.worklet.js`; + } else if (path.endsWith('.audio.position.worklet.js')) { + return `${loadPath}.audio.position.worklet.js`; + } else if (path.endsWith('.js')) { + return `${loadPath}.js`; + } else if (path in gdext) { + return path; + } else if (path.endsWith('.side.wasm')) { + return `${loadPath}.side.wasm`; + } else if (path.endsWith('.wasm')) { + return `${loadPath}.wasm`; + } + return path; + }, + }; + }; + + /** + * @ignore + * @param {function()} cleanup + */ + Config.prototype.getGodotConfig = function (cleanup) { + // Try to find a canvas + if (!(this.canvas instanceof HTMLCanvasElement)) { + const nodes = document.getElementsByTagName('canvas'); + if (nodes.length && nodes[0] instanceof HTMLCanvasElement) { + const first = nodes[0]; + this.canvas = /** @type {!HTMLCanvasElement} */ (first); + } + if (!this.canvas) { + throw new Error('No canvas found in page'); + } + } + // Canvas can grab focus on click, or key events won't work. + if (this.canvas.tabIndex < 0) { + this.canvas.tabIndex = 0; + } + + // Browser locale, or custom one if defined. + let locale = this.locale; + if (!locale) { + locale = navigator.languages ? navigator.languages[0] : navigator.language; + locale = locale.split('.')[0]; + } + locale = locale.replace('-', '_'); + const onExit = this.onExit; + + // Godot configuration. + return { + 'canvas': this.canvas, + 'canvasResizePolicy': this.canvasResizePolicy, + 'locale': locale, + 'persistentDrops': this.persistentDrops, + 'virtualKeyboard': this.experimentalVK, + 'focusCanvas': this.focusCanvas, + 'onExecute': this.onExecute, + 'onExit': function (p_code) { + cleanup(); // We always need to call the cleanup callback to free memory. + if (typeof (onExit) === 'function') { + onExit(p_code); + } + }, + }; + }; + return new Config(initConfig); +}; + +/** + * Projects exported for the Web expose the :js:class:`Engine` class to the JavaScript environment, that allows + * fine control over the engine's start-up process. + * + * This API is built in an asynchronous manner and requires basic understanding + * of `Promises `__. + * + * @module Engine + * @header Web export JavaScript reference + */ +const Engine = (function () { + const preloader = new Preloader(); + + let loadPromise = null; + let loadPath = ''; + let initPromise = null; + + /** + * @classdesc The ``Engine`` class provides methods for loading and starting exported projects on the Web. For default export + * settings, this is already part of the exported HTML page. To understand practical use of the ``Engine`` class, + * see :ref:`Custom HTML page for Web export `. + * + * @description Create a new Engine instance with the given configuration. + * + * @global + * @constructor + * @param {EngineConfig} initConfig The initial config for this instance. + */ + function Engine(initConfig) { // eslint-disable-line no-shadow + this.config = new InternalConfig(initConfig); + this.rtenv = null; + } + + /** + * Load the engine from the specified base path. + * + * @param {string} basePath Base path of the engine to load. + * @param {number=} [size=0] The file size if known. + * @returns {Promise} A Promise that resolves once the engine is loaded. + * + * @function Engine.load + */ + Engine.load = function (basePath, size) { + if (loadPromise == null) { + loadPath = basePath; + loadPromise = preloader.loadPromise(`${loadPath}.wasm`, size, true); + requestAnimationFrame(preloader.animateProgress); + } + return loadPromise; + }; + + /** + * Unload the engine to free memory. + * + * This method will be called automatically depending on the configuration. See :js:attr:`unloadAfterInit`. + * + * @function Engine.unload + */ + Engine.unload = function () { + loadPromise = null; + }; + + /** + * Safe Engine constructor, creates a new prototype for every new instance to avoid prototype pollution. + * @ignore + * @constructor + */ + function SafeEngine(initConfig) { + const proto = /** @lends Engine.prototype */ { + /** + * Initialize the engine instance. Optionally, pass the base path to the engine to load it, + * if it hasn't been loaded yet. See :js:meth:`Engine.load`. + * + * @param {string=} basePath Base path of the engine to load. + * @return {Promise} A ``Promise`` that resolves once the engine is loaded and initialized. + */ + init: function (basePath) { + if (initPromise) { + return initPromise; + } + if (loadPromise == null) { + if (!basePath) { + initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.')); + return initPromise; + } + Engine.load(basePath, this.config.fileSizes[`${basePath}.wasm`]); + } + const me = this; + function doInit(promise) { + // Care! Promise chaining is bogus with old emscripten versions. + // This caused a regression with the Mono build (which uses an older emscripten version). + // Make sure to test that when refactoring. + return new Promise(function (resolve, reject) { + promise.then(function (response) { + const cloned = new Response(response.clone().body, { 'headers': [['content-type', 'application/wasm']] }); + Godot(me.config.getModuleConfig(loadPath, cloned)).then(function (module) { + const paths = me.config.persistentPaths; + module['initFS'](paths).then(function (err) { + me.rtenv = module; + if (me.config.unloadAfterInit) { + Engine.unload(); + } + resolve(); + }); + }); + }); + }); + } + preloader.setProgressFunc(this.config.onProgress); + initPromise = doInit(loadPromise); + return initPromise; + }, + + /** + * Load a file so it is available in the instance's file system once it runs. Must be called **before** starting the + * instance. + * + * If not provided, the ``path`` is derived from the URL of the loaded file. + * + * @param {string|ArrayBuffer} file The file to preload. + * + * If a ``string`` the file will be loaded from that path. + * + * If an ``ArrayBuffer`` or a view on one, the buffer will used as the content of the file. + * + * @param {string=} path Path by which the file will be accessible. Required, if ``file`` is not a string. + * + * @returns {Promise} A Promise that resolves once the file is loaded. + */ + preloadFile: function (file, path) { + return preloader.preload(file, path, this.config.fileSizes[file]); + }, + + /** + * Start the engine instance using the given override configuration (if any). + * :js:meth:`startGame ` can be used in typical cases instead. + * + * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init `. + * The engine must be loaded beforehand. + * + * Fails if a canvas cannot be found on the page, or not specified in the configuration. + * + * @param {EngineConfig} override An optional configuration override. + * @return {Promise} Promise that resolves once the engine started. + */ + start: function (override) { + this.config.update(override); + const me = this; + return me.init().then(function () { + if (!me.rtenv) { + return Promise.reject(new Error('The engine must be initialized before it can be started')); + } + + let config = {}; + try { + config = me.config.getGodotConfig(function () { + me.rtenv = null; + }); + } catch (e) { + return Promise.reject(e); + } + // Godot configuration. + me.rtenv['initConfig'](config); + + // Preload GDExtension libraries. + if (me.config.gdextensionLibs.length > 0 && !me.rtenv['loadDynamicLibrary']) { + return Promise.reject(new Error('GDExtension libraries are not supported by this engine version. ' + + 'Enable "Extensions Support" for your export preset and/or build your custom template with "dlink_enabled=yes".')); + } + return new Promise(function (resolve, reject) { + for (const file of preloader.preloadedFiles) { + me.rtenv['copyToFS'](file.path, file.buffer); + } + preloader.preloadedFiles.length = 0; // Clear memory + me.rtenv['callMain'](me.config.args); + initPromise = null; + me.installServiceWorker(); + resolve(); + }); + }); + }, + + /** + * Start the game instance using the given configuration override (if any). + * + * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init `. + * + * This will load the engine if it is not loaded, and preload the main pck. + * + * This method expects the initial config (or the override) to have both the :js:attr:`executable` and :js:attr:`mainPack` + * properties set (normally done by the editor during export). + * + * @param {EngineConfig} override An optional configuration override. + * @return {Promise} Promise that resolves once the game started. + */ + startGame: function (override) { + this.config.update(override); + // Add main-pack argument. + const exe = this.config.executable; + const pack = this.config.mainPack || `${exe}.pck`; + this.config.args = ['--main-pack', pack].concat(this.config.args); + // Start and init with execName as loadPath if not inited. + const me = this; + return Promise.all([ + this.init(exe), + this.preloadFile(pack, pack), + ]).then(function () { + return me.start.apply(me); + }); + }, + + /** + * Create a file at the specified ``path`` with the passed as ``buffer`` in the instance's file system. + * + * @param {string} path The location where the file will be created. + * @param {ArrayBuffer} buffer The content of the file. + */ + copyToFS: function (path, buffer) { + if (this.rtenv == null) { + throw new Error('Engine must be inited before copying files'); + } + this.rtenv['copyToFS'](path, buffer); + }, + + /** + * Request that the current instance quit. + * + * This is akin the user pressing the close button in the window manager, and will + * have no effect if the engine has crashed, or is stuck in a loop. + * + */ + requestQuit: function () { + if (this.rtenv) { + this.rtenv['request_quit'](); + } + }, + + /** + * Install the progressive-web app service worker. + * @returns {Promise} The service worker registration promise. + */ + installServiceWorker: function () { + if (this.config.serviceWorker && 'serviceWorker' in navigator) { + try { + return navigator.serviceWorker.register(this.config.serviceWorker); + } catch (e) { + return Promise.reject(e); + } + } + return Promise.resolve(); + }, + }; + + Engine.prototype = proto; + // Closure compiler exported instance methods. + Engine.prototype['init'] = Engine.prototype.init; + Engine.prototype['preloadFile'] = Engine.prototype.preloadFile; + Engine.prototype['start'] = Engine.prototype.start; + Engine.prototype['startGame'] = Engine.prototype.startGame; + Engine.prototype['copyToFS'] = Engine.prototype.copyToFS; + Engine.prototype['requestQuit'] = Engine.prototype.requestQuit; + Engine.prototype['installServiceWorker'] = Engine.prototype.installServiceWorker; + // Also expose static methods as instance methods + Engine.prototype['load'] = Engine.load; + Engine.prototype['unload'] = Engine.unload; + return new Engine(initConfig); + } + + // Closure compiler exported static methods. + SafeEngine['load'] = Engine.load; + SafeEngine['unload'] = Engine.unload; + + // Feature-detection utilities. + SafeEngine['isWebGLAvailable'] = Features.isWebGLAvailable; + SafeEngine['isFetchAvailable'] = Features.isFetchAvailable; + SafeEngine['isSecureContext'] = Features.isSecureContext; + SafeEngine['isCrossOriginIsolated'] = Features.isCrossOriginIsolated; + SafeEngine['isSharedArrayBufferAvailable'] = Features.isSharedArrayBufferAvailable; + SafeEngine['isAudioWorkletAvailable'] = Features.isAudioWorkletAvailable; + SafeEngine['getMissingFeatures'] = Features.getMissingFeatures; + + return SafeEngine; +}()); +if (typeof window !== 'undefined') { + window['Engine'] = Engine; +} diff --git a/builds/web/GGJ - Bubbles.manifest.json b/builds/web/GGJ - Bubbles.manifest.json new file mode 100644 index 0000000..1380b39 --- /dev/null +++ b/builds/web/GGJ - Bubbles.manifest.json @@ -0,0 +1 @@ +{"background_color":"#000000","display":"standalone","icons":[{"sizes":"144x144","src":"GGJ - Bubbles.144x144.png","type":"image/png"},{"sizes":"180x180","src":"GGJ - Bubbles.180x180.png","type":"image/png"},{"sizes":"512x512","src":"GGJ - Bubbles.512x512.png","type":"image/png"}],"name":"GGJ - Bubbles","orientation":"any","start_url":"./GGJ - Bubbles.html"} \ No newline at end of file diff --git a/builds/web/GGJ - Bubbles.offline.html b/builds/web/GGJ - Bubbles.offline.html new file mode 100644 index 0000000..ae5298a --- /dev/null +++ b/builds/web/GGJ - Bubbles.offline.html @@ -0,0 +1,41 @@ + + + + + + + You are offline + + + +

You are offline

+

This application requires an Internet connection to run for the first time.

+

Press the button below to try reloading:

+ + + + diff --git a/builds/web/GGJ - Bubbles.pck b/builds/web/GGJ - Bubbles.pck new file mode 100644 index 0000000..431a8c0 Binary files /dev/null and b/builds/web/GGJ - Bubbles.pck differ diff --git a/builds/web/GGJ - Bubbles.png b/builds/web/GGJ - Bubbles.png new file mode 100644 index 0000000..766b0b6 Binary files /dev/null and b/builds/web/GGJ - Bubbles.png differ diff --git a/builds/web/GGJ - Bubbles.png.import b/builds/web/GGJ - Bubbles.png.import new file mode 100644 index 0000000..186dc03 --- /dev/null +++ b/builds/web/GGJ - Bubbles.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bc0pbl340u1mk" +path="res://.godot/imported/GGJ - Bubbles.png-c9b465c3dcf33bab019204bd843d60df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://builds/web/GGJ - Bubbles.png" +dest_files=["res://.godot/imported/GGJ - Bubbles.png-c9b465c3dcf33bab019204bd843d60df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/builds/web/GGJ - Bubbles.service.worker.js b/builds/web/GGJ - Bubbles.service.worker.js new file mode 100644 index 0000000..f750711 --- /dev/null +++ b/builds/web/GGJ - Bubbles.service.worker.js @@ -0,0 +1,166 @@ +// This service worker is required to expose an exported Godot project as a +// Progressive Web App. It provides an offline fallback page telling the user +// that they need an Internet connection to run the project if desired. +// Incrementing CACHE_VERSION will kick off the install event and force +// previously cached resources to be updated from the network. +/** @type {string} */ +const CACHE_VERSION = '1737827462|10598079671'; +/** @type {string} */ +const CACHE_PREFIX = 'GGJ - Bubbles-sw-cache-'; +const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION; +/** @type {string} */ +const OFFLINE_URL = 'GGJ - Bubbles.offline.html'; +/** @type {boolean} */ +const ENSURE_CROSSORIGIN_ISOLATION_HEADERS = true; +// Files that will be cached on load. +/** @type {string[]} */ +const CACHED_FILES = ["GGJ - Bubbles.html","GGJ - Bubbles.js","GGJ - Bubbles.offline.html","GGJ - Bubbles.icon.png","GGJ - Bubbles.apple-touch-icon.png","GGJ - Bubbles.audio.worklet.js","GGJ - Bubbles.audio.position.worklet.js"]; +// Files that we might not want the user to preload, and will only be cached on first load. +/** @type {string[]} */ +const CACHEABLE_FILES = ["GGJ - Bubbles.wasm","GGJ - Bubbles.pck"]; +const FULL_CACHE = CACHED_FILES.concat(CACHEABLE_FILES); + +self.addEventListener('install', (event) => { + event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(CACHED_FILES))); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(caches.keys().then( + function (keys) { + // Remove old caches. + return Promise.all(keys.filter((key) => key.startsWith(CACHE_PREFIX) && key !== CACHE_NAME).map((key) => caches.delete(key))); + } + ).then(function () { + // Enable navigation preload if available. + return ('navigationPreload' in self.registration) ? self.registration.navigationPreload.enable() : Promise.resolve(); + })); +}); + +/** + * Ensures that the response has the correct COEP/COOP headers + * @param {Response} response + * @returns {Response} + */ +function ensureCrossOriginIsolationHeaders(response) { + if (response.headers.get('Cross-Origin-Embedder-Policy') === 'require-corp' + && response.headers.get('Cross-Origin-Opener-Policy') === 'same-origin') { + return response; + } + + const crossOriginIsolatedHeaders = new Headers(response.headers); + crossOriginIsolatedHeaders.set('Cross-Origin-Embedder-Policy', 'require-corp'); + crossOriginIsolatedHeaders.set('Cross-Origin-Opener-Policy', 'same-origin'); + const newResponse = new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: crossOriginIsolatedHeaders, + }); + + return newResponse; +} + +/** + * Calls fetch and cache the result if it is cacheable + * @param {FetchEvent} event + * @param {Cache} cache + * @param {boolean} isCacheable + * @returns {Response} + */ +async function fetchAndCache(event, cache, isCacheable) { + // Use the preloaded response, if it's there + /** @type { Response } */ + let response = await event.preloadResponse; + if (response == null) { + // Or, go over network. + response = await self.fetch(event.request); + } + + if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) { + response = ensureCrossOriginIsolationHeaders(response); + } + + if (isCacheable) { + // And update the cache + cache.put(event.request, response.clone()); + } + + return response; +} + +self.addEventListener( + 'fetch', + /** + * Triggered on fetch + * @param {FetchEvent} event + */ + (event) => { + const isNavigate = event.request.mode === 'navigate'; + const url = event.request.url || ''; + const referrer = event.request.referrer || ''; + const base = referrer.slice(0, referrer.lastIndexOf('/') + 1); + const local = url.startsWith(base) ? url.replace(base, '') : ''; + const isCacheable = FULL_CACHE.some((v) => v === local) || (base === referrer && base.endsWith(CACHED_FILES[0])); + if (isNavigate || isCacheable) { + event.respondWith((async () => { + // Try to use cache first + const cache = await caches.open(CACHE_NAME); + if (isNavigate) { + // Check if we have full cache during HTML page request. + /** @type {Response[]} */ + const fullCache = await Promise.all(FULL_CACHE.map((name) => cache.match(name))); + const missing = fullCache.some((v) => v === undefined); + if (missing) { + try { + // Try network if some cached file is missing (so we can display offline page in case). + const response = await fetchAndCache(event, cache, isCacheable); + return response; + } catch (e) { + // And return the hopefully always cached offline page in case of network failure. + console.error('Network error: ', e); // eslint-disable-line no-console + return caches.match(OFFLINE_URL); + } + } + } + let cached = await cache.match(event.request); + if (cached != null) { + if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) { + cached = ensureCrossOriginIsolationHeaders(cached); + } + return cached; + } + // Try network if don't have it in cache. + const response = await fetchAndCache(event, cache, isCacheable); + return response; + })()); + } else if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) { + event.respondWith((async () => { + let response = await fetch(event.request); + response = ensureCrossOriginIsolationHeaders(response); + return response; + })()); + } + } +); + +self.addEventListener('message', (event) => { + // No cross origin + if (event.origin !== self.origin) { + return; + } + const id = event.source.id || ''; + const msg = event.data || ''; + // Ensure it's one of our clients. + self.clients.get(id).then(function (client) { + if (!client) { + return; // Not a valid client. + } + if (msg === 'claim') { + self.skipWaiting().then(() => self.clients.claim()); + } else if (msg === 'clear') { + caches.delete(CACHE_NAME); + } else if (msg === 'update') { + self.skipWaiting().then(() => self.clients.claim()).then(() => self.clients.matchAll()).then((all) => all.forEach((c) => c.navigate(c.url))); + } + }); +}); + diff --git a/builds/web/GGJ - Bubbles.wasm b/builds/web/GGJ - Bubbles.wasm new file mode 100644 index 0000000..8a616f1 Binary files /dev/null and b/builds/web/GGJ - Bubbles.wasm differ diff --git a/default_bus_layout.tres b/default_bus_layout.tres new file mode 100644 index 0000000..e2d3fad --- /dev/null +++ b/default_bus_layout.tres @@ -0,0 +1,19 @@ +[gd_resource type="AudioBusLayout" load_steps=3 format=3 uid="uid://c1epgb48io8f7"] + +[sub_resource type="AudioEffectSpectrumAnalyzer" id="AudioEffectSpectrumAnalyzer_j3pel"] +resource_name = "SpectrumAnalyzer" + +[sub_resource type="AudioEffectCapture" id="AudioEffectCapture_j3pel"] +resource_name = "Capture" + +[resource] +bus/1/name = &"Record" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/1/effect/0/effect = SubResource("AudioEffectSpectrumAnalyzer_j3pel") +bus/1/effect/0/enabled = true +bus/1/effect/1/effect = SubResource("AudioEffectCapture_j3pel") +bus/1/effect/1/enabled = true diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..1a5570b --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="Sky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..afb27cc --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,777 @@ +[preset.0] + +name="macOS" +platform="macOS" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +export/distribution_type=1 +binary_format/architecture="universal" +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +application/icon="" +application/icon_interpolation=4 +application/bundle_identifier="" +application/signature="" +application/app_category="Games" +application/short_version="" +application/version="" +application/copyright="" +application/copyright_localized={} +application/min_macos_version_x86_64="10.12" +application/min_macos_version_arm64="11.00" +application/export_angle=0 +display/high_res=true +application/additional_plist_content="" +xcode/platform_build="14C18" +xcode/sdk_version="13.1" +xcode/sdk_build="22C55" +xcode/sdk_name="macosx13.1" +xcode/xcode_version="1420" +xcode/xcode_build="14C18" +codesign/codesign=3 +codesign/installer_identity="" +codesign/apple_team_id="" +codesign/identity="" +codesign/entitlements/custom_file="" +codesign/entitlements/allow_jit_code_execution=false +codesign/entitlements/allow_unsigned_executable_memory=false +codesign/entitlements/allow_dyld_environment_variables=false +codesign/entitlements/disable_library_validation=false +codesign/entitlements/audio_input=true +codesign/entitlements/camera=false +codesign/entitlements/location=false +codesign/entitlements/address_book=false +codesign/entitlements/calendars=false +codesign/entitlements/photos_library=false +codesign/entitlements/apple_events=false +codesign/entitlements/debugging=false +codesign/entitlements/app_sandbox/enabled=false +codesign/entitlements/app_sandbox/network_server=false +codesign/entitlements/app_sandbox/network_client=false +codesign/entitlements/app_sandbox/device_usb=false +codesign/entitlements/app_sandbox/device_bluetooth=false +codesign/entitlements/app_sandbox/files_downloads=0 +codesign/entitlements/app_sandbox/files_pictures=0 +codesign/entitlements/app_sandbox/files_music=0 +codesign/entitlements/app_sandbox/files_movies=0 +codesign/entitlements/app_sandbox/files_user_selected=0 +codesign/entitlements/app_sandbox/helper_executables=[] +codesign/entitlements/additional="" +codesign/custom_options=PackedStringArray() +notarization/notarization=0 +privacy/microphone_usage_description="" +privacy/microphone_usage_description_localized={} +privacy/camera_usage_description="" +privacy/camera_usage_description_localized={} +privacy/location_usage_description="" +privacy/location_usage_description_localized={} +privacy/address_book_usage_description="" +privacy/address_book_usage_description_localized={} +privacy/calendar_usage_description="" +privacy/calendar_usage_description_localized={} +privacy/photos_library_usage_description="" +privacy/photos_library_usage_description_localized={} +privacy/desktop_folder_usage_description="" +privacy/desktop_folder_usage_description_localized={} +privacy/documents_folder_usage_description="" +privacy/documents_folder_usage_description_localized={} +privacy/downloads_folder_usage_description="" +privacy/downloads_folder_usage_description_localized={} +privacy/network_volumes_usage_description="" +privacy/network_volumes_usage_description_localized={} +privacy/removable_volumes_usage_description="" +privacy/removable_volumes_usage_description_localized={} +privacy/tracking_enabled=false +privacy/tracking_domains=PackedStringArray() +privacy/collected_data/name/collected=false +privacy/collected_data/name/linked_to_user=false +privacy/collected_data/name/used_for_tracking=false +privacy/collected_data/name/collection_purposes=0 +privacy/collected_data/email_address/collected=false +privacy/collected_data/email_address/linked_to_user=false +privacy/collected_data/email_address/used_for_tracking=false +privacy/collected_data/email_address/collection_purposes=0 +privacy/collected_data/phone_number/collected=false +privacy/collected_data/phone_number/linked_to_user=false +privacy/collected_data/phone_number/used_for_tracking=false +privacy/collected_data/phone_number/collection_purposes=0 +privacy/collected_data/physical_address/collected=false +privacy/collected_data/physical_address/linked_to_user=false +privacy/collected_data/physical_address/used_for_tracking=false +privacy/collected_data/physical_address/collection_purposes=0 +privacy/collected_data/other_contact_info/collected=false +privacy/collected_data/other_contact_info/linked_to_user=false +privacy/collected_data/other_contact_info/used_for_tracking=false +privacy/collected_data/other_contact_info/collection_purposes=0 +privacy/collected_data/health/collected=false +privacy/collected_data/health/linked_to_user=false +privacy/collected_data/health/used_for_tracking=false +privacy/collected_data/health/collection_purposes=0 +privacy/collected_data/fitness/collected=false +privacy/collected_data/fitness/linked_to_user=false +privacy/collected_data/fitness/used_for_tracking=false +privacy/collected_data/fitness/collection_purposes=0 +privacy/collected_data/payment_info/collected=false +privacy/collected_data/payment_info/linked_to_user=false +privacy/collected_data/payment_info/used_for_tracking=false +privacy/collected_data/payment_info/collection_purposes=0 +privacy/collected_data/credit_info/collected=false +privacy/collected_data/credit_info/linked_to_user=false +privacy/collected_data/credit_info/used_for_tracking=false +privacy/collected_data/credit_info/collection_purposes=0 +privacy/collected_data/other_financial_info/collected=false +privacy/collected_data/other_financial_info/linked_to_user=false +privacy/collected_data/other_financial_info/used_for_tracking=false +privacy/collected_data/other_financial_info/collection_purposes=0 +privacy/collected_data/precise_location/collected=false +privacy/collected_data/precise_location/linked_to_user=false +privacy/collected_data/precise_location/used_for_tracking=false +privacy/collected_data/precise_location/collection_purposes=0 +privacy/collected_data/coarse_location/collected=false +privacy/collected_data/coarse_location/linked_to_user=false +privacy/collected_data/coarse_location/used_for_tracking=false +privacy/collected_data/coarse_location/collection_purposes=0 +privacy/collected_data/sensitive_info/collected=false +privacy/collected_data/sensitive_info/linked_to_user=false +privacy/collected_data/sensitive_info/used_for_tracking=false +privacy/collected_data/sensitive_info/collection_purposes=0 +privacy/collected_data/contacts/collected=false +privacy/collected_data/contacts/linked_to_user=false +privacy/collected_data/contacts/used_for_tracking=false +privacy/collected_data/contacts/collection_purposes=0 +privacy/collected_data/emails_or_text_messages/collected=false +privacy/collected_data/emails_or_text_messages/linked_to_user=false +privacy/collected_data/emails_or_text_messages/used_for_tracking=false +privacy/collected_data/emails_or_text_messages/collection_purposes=0 +privacy/collected_data/photos_or_videos/collected=false +privacy/collected_data/photos_or_videos/linked_to_user=false +privacy/collected_data/photos_or_videos/used_for_tracking=false +privacy/collected_data/photos_or_videos/collection_purposes=0 +privacy/collected_data/audio_data/collected=false +privacy/collected_data/audio_data/linked_to_user=false +privacy/collected_data/audio_data/used_for_tracking=false +privacy/collected_data/audio_data/collection_purposes=0 +privacy/collected_data/gameplay_content/collected=false +privacy/collected_data/gameplay_content/linked_to_user=false +privacy/collected_data/gameplay_content/used_for_tracking=false +privacy/collected_data/gameplay_content/collection_purposes=0 +privacy/collected_data/customer_support/collected=false +privacy/collected_data/customer_support/linked_to_user=false +privacy/collected_data/customer_support/used_for_tracking=false +privacy/collected_data/customer_support/collection_purposes=0 +privacy/collected_data/other_user_content/collected=false +privacy/collected_data/other_user_content/linked_to_user=false +privacy/collected_data/other_user_content/used_for_tracking=false +privacy/collected_data/other_user_content/collection_purposes=0 +privacy/collected_data/browsing_history/collected=false +privacy/collected_data/browsing_history/linked_to_user=false +privacy/collected_data/browsing_history/used_for_tracking=false +privacy/collected_data/browsing_history/collection_purposes=0 +privacy/collected_data/search_hhistory/collected=false +privacy/collected_data/search_hhistory/linked_to_user=false +privacy/collected_data/search_hhistory/used_for_tracking=false +privacy/collected_data/search_hhistory/collection_purposes=0 +privacy/collected_data/user_id/collected=false +privacy/collected_data/user_id/linked_to_user=false +privacy/collected_data/user_id/used_for_tracking=false +privacy/collected_data/user_id/collection_purposes=0 +privacy/collected_data/device_id/collected=false +privacy/collected_data/device_id/linked_to_user=false +privacy/collected_data/device_id/used_for_tracking=false +privacy/collected_data/device_id/collection_purposes=0 +privacy/collected_data/purchase_history/collected=false +privacy/collected_data/purchase_history/linked_to_user=false +privacy/collected_data/purchase_history/used_for_tracking=false +privacy/collected_data/purchase_history/collection_purposes=0 +privacy/collected_data/product_interaction/collected=false +privacy/collected_data/product_interaction/linked_to_user=false +privacy/collected_data/product_interaction/used_for_tracking=false +privacy/collected_data/product_interaction/collection_purposes=0 +privacy/collected_data/advertising_data/collected=false +privacy/collected_data/advertising_data/linked_to_user=false +privacy/collected_data/advertising_data/used_for_tracking=false +privacy/collected_data/advertising_data/collection_purposes=0 +privacy/collected_data/other_usage_data/collected=false +privacy/collected_data/other_usage_data/linked_to_user=false +privacy/collected_data/other_usage_data/used_for_tracking=false +privacy/collected_data/other_usage_data/collection_purposes=0 +privacy/collected_data/crash_data/collected=false +privacy/collected_data/crash_data/linked_to_user=false +privacy/collected_data/crash_data/used_for_tracking=false +privacy/collected_data/crash_data/collection_purposes=0 +privacy/collected_data/performance_data/collected=false +privacy/collected_data/performance_data/linked_to_user=false +privacy/collected_data/performance_data/used_for_tracking=false +privacy/collected_data/performance_data/collection_purposes=0 +privacy/collected_data/other_diagnostic_data/collected=false +privacy/collected_data/other_diagnostic_data/linked_to_user=false +privacy/collected_data/other_diagnostic_data/used_for_tracking=false +privacy/collected_data/other_diagnostic_data/collection_purposes=0 +privacy/collected_data/environment_scanning/collected=false +privacy/collected_data/environment_scanning/linked_to_user=false +privacy/collected_data/environment_scanning/used_for_tracking=false +privacy/collected_data/environment_scanning/collection_purposes=0 +privacy/collected_data/hands/collected=false +privacy/collected_data/hands/linked_to_user=false +privacy/collected_data/hands/used_for_tracking=false +privacy/collected_data/hands/collection_purposes=0 +privacy/collected_data/head/collected=false +privacy/collected_data/head/linked_to_user=false +privacy/collected_data/head/used_for_tracking=false +privacy/collected_data/head/collection_purposes=0 +privacy/collected_data/other_data_types/collected=false +privacy/collected_data/other_data_types/linked_to_user=false +privacy/collected_data/other_data_types/used_for_tracking=false +privacy/collected_data/other_data_types/collection_purposes=0 +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" + +[preset.1] + +name="iOS" +platform="iOS" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="builds/ios/GGJ - Bubbles.ipa" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.1.options] + +custom_template/debug="" +custom_template/release="" +architectures/arm64=true +application/app_store_team_id=" 46G677SS3Y" +application/export_method_debug=1 +application/code_sign_identity_debug="" +application/code_sign_identity_release="" +application/provisioning_profile_specifier_debug="" +application/provisioning_profile_specifier_release="" +application/export_method_release=1 +application/targeted_device_family=0 +application/bundle_identifier="dev.rinma.bubbles" +application/signature="" +application/short_version="" +application/version="" +application/min_ios_version="14.0" +application/additional_plist_content="" +application/icon_interpolation=4 +application/export_project_only=false +application/delete_old_export_files_unconditionally=false +application/generate_simulator_library_if_missing=true +entitlements/increased_memory_limit=false +entitlements/game_center=false +entitlements/push_notifications="Disabled" +entitlements/additional="" +capabilities/access_wifi=false +capabilities/performance_gaming_tier=false +capabilities/performance_a12=false +capabilities/additional=PackedStringArray() +user_data/accessible_from_files_app=false +user_data/accessible_from_itunes_sharing=false +privacy/camera_usage_description="" +privacy/camera_usage_description_localized={} +privacy/microphone_usage_description="" +privacy/microphone_usage_description_localized={} +privacy/photolibrary_usage_description="" +privacy/photolibrary_usage_description_localized={} +privacy/file_timestamp_access_reasons=3 +privacy/system_boot_time_access_reasons=1 +privacy/disk_space_access_reasons=3 +privacy/active_keyboard_access_reasons=0 +privacy/user_defaults_access_reasons=0 +privacy/tracking_enabled=false +privacy/tracking_domains=PackedStringArray() +privacy/collected_data/name/collected=false +privacy/collected_data/name/linked_to_user=false +privacy/collected_data/name/used_for_tracking=false +privacy/collected_data/name/collection_purposes=0 +privacy/collected_data/email_address/collected=false +privacy/collected_data/email_address/linked_to_user=false +privacy/collected_data/email_address/used_for_tracking=false +privacy/collected_data/email_address/collection_purposes=0 +privacy/collected_data/phone_number/collected=false +privacy/collected_data/phone_number/linked_to_user=false +privacy/collected_data/phone_number/used_for_tracking=false +privacy/collected_data/phone_number/collection_purposes=0 +privacy/collected_data/physical_address/collected=false +privacy/collected_data/physical_address/linked_to_user=false +privacy/collected_data/physical_address/used_for_tracking=false +privacy/collected_data/physical_address/collection_purposes=0 +privacy/collected_data/other_contact_info/collected=false +privacy/collected_data/other_contact_info/linked_to_user=false +privacy/collected_data/other_contact_info/used_for_tracking=false +privacy/collected_data/other_contact_info/collection_purposes=0 +privacy/collected_data/health/collected=false +privacy/collected_data/health/linked_to_user=false +privacy/collected_data/health/used_for_tracking=false +privacy/collected_data/health/collection_purposes=0 +privacy/collected_data/fitness/collected=false +privacy/collected_data/fitness/linked_to_user=false +privacy/collected_data/fitness/used_for_tracking=false +privacy/collected_data/fitness/collection_purposes=0 +privacy/collected_data/payment_info/collected=false +privacy/collected_data/payment_info/linked_to_user=false +privacy/collected_data/payment_info/used_for_tracking=false +privacy/collected_data/payment_info/collection_purposes=0 +privacy/collected_data/credit_info/collected=false +privacy/collected_data/credit_info/linked_to_user=false +privacy/collected_data/credit_info/used_for_tracking=false +privacy/collected_data/credit_info/collection_purposes=0 +privacy/collected_data/other_financial_info/collected=false +privacy/collected_data/other_financial_info/linked_to_user=false +privacy/collected_data/other_financial_info/used_for_tracking=false +privacy/collected_data/other_financial_info/collection_purposes=0 +privacy/collected_data/precise_location/collected=false +privacy/collected_data/precise_location/linked_to_user=false +privacy/collected_data/precise_location/used_for_tracking=false +privacy/collected_data/precise_location/collection_purposes=0 +privacy/collected_data/coarse_location/collected=false +privacy/collected_data/coarse_location/linked_to_user=false +privacy/collected_data/coarse_location/used_for_tracking=false +privacy/collected_data/coarse_location/collection_purposes=0 +privacy/collected_data/sensitive_info/collected=false +privacy/collected_data/sensitive_info/linked_to_user=false +privacy/collected_data/sensitive_info/used_for_tracking=false +privacy/collected_data/sensitive_info/collection_purposes=0 +privacy/collected_data/contacts/collected=false +privacy/collected_data/contacts/linked_to_user=false +privacy/collected_data/contacts/used_for_tracking=false +privacy/collected_data/contacts/collection_purposes=0 +privacy/collected_data/emails_or_text_messages/collected=false +privacy/collected_data/emails_or_text_messages/linked_to_user=false +privacy/collected_data/emails_or_text_messages/used_for_tracking=false +privacy/collected_data/emails_or_text_messages/collection_purposes=0 +privacy/collected_data/photos_or_videos/collected=false +privacy/collected_data/photos_or_videos/linked_to_user=false +privacy/collected_data/photos_or_videos/used_for_tracking=false +privacy/collected_data/photos_or_videos/collection_purposes=0 +privacy/collected_data/audio_data/collected=false +privacy/collected_data/audio_data/linked_to_user=false +privacy/collected_data/audio_data/used_for_tracking=false +privacy/collected_data/audio_data/collection_purposes=0 +privacy/collected_data/gameplay_content/collected=false +privacy/collected_data/gameplay_content/linked_to_user=false +privacy/collected_data/gameplay_content/used_for_tracking=false +privacy/collected_data/gameplay_content/collection_purposes=0 +privacy/collected_data/customer_support/collected=false +privacy/collected_data/customer_support/linked_to_user=false +privacy/collected_data/customer_support/used_for_tracking=false +privacy/collected_data/customer_support/collection_purposes=0 +privacy/collected_data/other_user_content/collected=false +privacy/collected_data/other_user_content/linked_to_user=false +privacy/collected_data/other_user_content/used_for_tracking=false +privacy/collected_data/other_user_content/collection_purposes=0 +privacy/collected_data/browsing_history/collected=false +privacy/collected_data/browsing_history/linked_to_user=false +privacy/collected_data/browsing_history/used_for_tracking=false +privacy/collected_data/browsing_history/collection_purposes=0 +privacy/collected_data/search_hhistory/collected=false +privacy/collected_data/search_hhistory/linked_to_user=false +privacy/collected_data/search_hhistory/used_for_tracking=false +privacy/collected_data/search_hhistory/collection_purposes=0 +privacy/collected_data/user_id/collected=false +privacy/collected_data/user_id/linked_to_user=false +privacy/collected_data/user_id/used_for_tracking=false +privacy/collected_data/user_id/collection_purposes=0 +privacy/collected_data/device_id/collected=false +privacy/collected_data/device_id/linked_to_user=false +privacy/collected_data/device_id/used_for_tracking=false +privacy/collected_data/device_id/collection_purposes=0 +privacy/collected_data/purchase_history/collected=false +privacy/collected_data/purchase_history/linked_to_user=false +privacy/collected_data/purchase_history/used_for_tracking=false +privacy/collected_data/purchase_history/collection_purposes=0 +privacy/collected_data/product_interaction/collected=false +privacy/collected_data/product_interaction/linked_to_user=false +privacy/collected_data/product_interaction/used_for_tracking=false +privacy/collected_data/product_interaction/collection_purposes=0 +privacy/collected_data/advertising_data/collected=false +privacy/collected_data/advertising_data/linked_to_user=false +privacy/collected_data/advertising_data/used_for_tracking=false +privacy/collected_data/advertising_data/collection_purposes=0 +privacy/collected_data/other_usage_data/collected=false +privacy/collected_data/other_usage_data/linked_to_user=false +privacy/collected_data/other_usage_data/used_for_tracking=false +privacy/collected_data/other_usage_data/collection_purposes=0 +privacy/collected_data/crash_data/collected=false +privacy/collected_data/crash_data/linked_to_user=false +privacy/collected_data/crash_data/used_for_tracking=false +privacy/collected_data/crash_data/collection_purposes=0 +privacy/collected_data/performance_data/collected=false +privacy/collected_data/performance_data/linked_to_user=false +privacy/collected_data/performance_data/used_for_tracking=false +privacy/collected_data/performance_data/collection_purposes=0 +privacy/collected_data/other_diagnostic_data/collected=false +privacy/collected_data/other_diagnostic_data/linked_to_user=false +privacy/collected_data/other_diagnostic_data/used_for_tracking=false +privacy/collected_data/other_diagnostic_data/collection_purposes=0 +privacy/collected_data/environment_scanning/collected=false +privacy/collected_data/environment_scanning/linked_to_user=false +privacy/collected_data/environment_scanning/used_for_tracking=false +privacy/collected_data/environment_scanning/collection_purposes=0 +privacy/collected_data/hands/collected=false +privacy/collected_data/hands/linked_to_user=false +privacy/collected_data/hands/used_for_tracking=false +privacy/collected_data/hands/collection_purposes=0 +privacy/collected_data/head/collected=false +privacy/collected_data/head/linked_to_user=false +privacy/collected_data/head/used_for_tracking=false +privacy/collected_data/head/collection_purposes=0 +privacy/collected_data/other_data_types/collected=false +privacy/collected_data/other_data_types/linked_to_user=false +privacy/collected_data/other_data_types/used_for_tracking=false +privacy/collected_data/other_data_types/collection_purposes=0 +icons/icon_1024x1024="" +icons/icon_1024x1024_dark="" +icons/icon_1024x1024_tinted="" +icons/settings_58x58="" +icons/settings_58x58_dark="" +icons/settings_58x58_tinted="" +icons/settings_87x87="" +icons/settings_87x87_dark="" +icons/settings_87x87_tinted="" +icons/notification_40x40="" +icons/notification_40x40_dark="" +icons/notification_40x40_tinted="" +icons/notification_60x60="" +icons/notification_60x60_dark="" +icons/notification_60x60_tinted="" +icons/notification_76x76="" +icons/notification_76x76_dark="" +icons/notification_76x76_tinted="" +icons/notification_114x114="" +icons/notification_114x114_dark="" +icons/notification_114x114_tinted="" +icons/spotlight_80x80="" +icons/spotlight_80x80_dark="" +icons/spotlight_80x80_tinted="" +icons/spotlight_120x120="" +icons/spotlight_120x120_dark="" +icons/spotlight_120x120_tinted="" +icons/iphone_120x120="" +icons/iphone_120x120_dark="" +icons/iphone_120x120_tinted="" +icons/iphone_180x180="" +icons/iphone_180x180_dark="" +icons/iphone_180x180_tinted="" +icons/ipad_167x167="" +icons/ipad_167x167_dark="" +icons/ipad_167x167_tinted="" +icons/ipad_152x152="" +icons/ipad_152x152_dark="" +icons/ipad_152x152_tinted="" +icons/ios_128x128="" +icons/ios_128x128_dark="" +icons/ios_128x128_tinted="" +icons/ios_192x192="" +icons/ios_192x192_dark="" +icons/ios_192x192_tinted="" +icons/ios_136x136="" +icons/ios_136x136_dark="" +icons/ios_136x136_tinted="" +icons/app_store_1024x1024="" +icons/app_store_1024x1024_dark="" +icons/app_store_1024x1024_tinted="" +storyboard/image_scale_mode=0 +storyboard/custom_image@2x="" +storyboard/custom_image@3x="" +storyboard/use_custom_bg_color=false +storyboard/custom_bg_color=Color(0, 0, 0, 1) + +[preset.2] + +name="Web" +platform="Web" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="builds/web/GGJ - Bubbles.html" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.2.options] + +custom_template/debug="" +custom_template/release="" +variant/extensions_support=false +variant/thread_support=false +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=true +html/export_icon=true +html/custom_html_shell="" +html/head_include="" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +html/experimental_virtual_keyboard=false +progressive_web_app/enabled=true +progressive_web_app/ensure_cross_origin_isolation_headers=true +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) + +[preset.3] + +name="Android" +platform="Android" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.3.options] + +custom_template/debug="" +custom_template/release="" +gradle_build/use_gradle_build=false +gradle_build/gradle_build_directory="" +gradle_build/android_source_template="" +gradle_build/compress_native_libraries=false +gradle_build/export_format=0 +gradle_build/min_sdk="" +gradle_build/target_sdk="" +architectures/armeabi-v7a=false +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=false +version/code=1 +version/name="" +package/unique_name="com.example.$genname" +package/name="" +package/signed=true +package/app_category=2 +package/retain_data_on_uninstall=false +package/exclude_from_recents=false +package/show_in_android_tv=false +package/show_in_app_library=true +package/show_as_launcher_app=false +launcher_icons/main_192x192="" +launcher_icons/adaptive_foreground_432x432="" +launcher_icons/adaptive_background_432x432="" +launcher_icons/adaptive_monochrome_432x432="" +graphics/opengl_debug=false +xr_features/xr_mode=0 +wear_os/swipe_to_dismiss=true +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="" +permissions/custom_permissions=PackedStringArray() +permissions/access_checkin_properties=false +permissions/access_coarse_location=false +permissions/access_fine_location=false +permissions/access_location_extra_commands=false +permissions/access_media_location=false +permissions/access_mock_location=false +permissions/access_network_state=false +permissions/access_surface_flinger=false +permissions/access_wifi_state=false +permissions/account_manager=false +permissions/add_voicemail=false +permissions/authenticate_accounts=false +permissions/battery_stats=false +permissions/bind_accessibility_service=false +permissions/bind_appwidget=false +permissions/bind_device_admin=false +permissions/bind_input_method=false +permissions/bind_nfc_service=false +permissions/bind_notification_listener_service=false +permissions/bind_print_service=false +permissions/bind_remoteviews=false +permissions/bind_text_service=false +permissions/bind_vpn_service=false +permissions/bind_wallpaper=false +permissions/bluetooth=false +permissions/bluetooth_admin=false +permissions/bluetooth_privileged=false +permissions/brick=false +permissions/broadcast_package_removed=false +permissions/broadcast_sms=false +permissions/broadcast_sticky=false +permissions/broadcast_wap_push=false +permissions/call_phone=false +permissions/call_privileged=false +permissions/camera=false +permissions/capture_audio_output=false +permissions/capture_secure_video_output=false +permissions/capture_video_output=false +permissions/change_component_enabled_state=false +permissions/change_configuration=false +permissions/change_network_state=false +permissions/change_wifi_multicast_state=false +permissions/change_wifi_state=false +permissions/clear_app_cache=false +permissions/clear_app_user_data=false +permissions/control_location_updates=false +permissions/delete_cache_files=false +permissions/delete_packages=false +permissions/device_power=false +permissions/diagnostic=false +permissions/disable_keyguard=false +permissions/dump=false +permissions/expand_status_bar=false +permissions/factory_test=false +permissions/flashlight=false +permissions/force_back=false +permissions/get_accounts=false +permissions/get_package_size=false +permissions/get_tasks=false +permissions/get_top_activity_info=false +permissions/global_search=false +permissions/hardware_test=false +permissions/inject_events=false +permissions/install_location_provider=false +permissions/install_packages=false +permissions/install_shortcut=false +permissions/internal_system_window=false +permissions/internet=false +permissions/kill_background_processes=false +permissions/location_hardware=false +permissions/manage_accounts=false +permissions/manage_app_tokens=false +permissions/manage_documents=false +permissions/manage_external_storage=false +permissions/master_clear=false +permissions/media_content_control=false +permissions/modify_audio_settings=false +permissions/modify_phone_state=false +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 +permissions/read_contacts=false +permissions/read_external_storage=false +permissions/read_frame_buffer=false +permissions/read_history_bookmarks=false +permissions/read_input_state=false +permissions/read_logs=false +permissions/read_media_audio=false +permissions/read_media_images=false +permissions/read_media_video=false +permissions/read_media_visual_user_selected=false +permissions/read_phone_state=false +permissions/read_profile=false +permissions/read_sms=false +permissions/read_social_stream=false +permissions/read_sync_settings=false +permissions/read_sync_stats=false +permissions/read_user_dictionary=false +permissions/reboot=false +permissions/receive_boot_completed=false +permissions/receive_mms=false +permissions/receive_sms=false +permissions/receive_wap_push=false +permissions/record_audio=false +permissions/reorder_tasks=false +permissions/restart_packages=false +permissions/send_respond_via_message=false +permissions/send_sms=false +permissions/set_activity_watcher=false +permissions/set_alarm=false +permissions/set_always_finish=false +permissions/set_animation_scale=false +permissions/set_debug_app=false +permissions/set_orientation=false +permissions/set_pointer_speed=false +permissions/set_preferred_applications=false +permissions/set_process_limit=false +permissions/set_time=false +permissions/set_time_zone=false +permissions/set_wallpaper=false +permissions/set_wallpaper_hints=false +permissions/signal_persistent_processes=false +permissions/status_bar=false +permissions/subscribed_feeds_read=false +permissions/subscribed_feeds_write=false +permissions/system_alert_window=false +permissions/transmit_ir=false +permissions/uninstall_shortcut=false +permissions/update_device_stats=false +permissions/use_credentials=false +permissions/use_sip=false +permissions/vibrate=false +permissions/wake_lock=false +permissions/write_apn_settings=false +permissions/write_calendar=false +permissions/write_call_log=false +permissions/write_contacts=false +permissions/write_external_storage=false +permissions/write_gservices=false +permissions/write_history_bookmarks=false +permissions/write_profile=false +permissions/write_secure_settings=false +permissions/write_settings=false +permissions/write_sms=false +permissions/write_social_stream=false +permissions/write_sync_settings=false +permissions/write_user_dictionary=false diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..1a10d58 --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..d98402d --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://l3lv532kcq48" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/label_settings.tres b/label_settings.tres new file mode 100644 index 0000000..b270dfc --- /dev/null +++ b/label_settings.tres @@ -0,0 +1,6 @@ +[gd_resource type="LabelSettings" format=3 uid="uid://dl21bg2o5ltaj"] + +[resource] +font_size = 64 +outline_size = 8 +outline_color = Color(0, 0, 0, 1) diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..42a865f --- /dev/null +++ b/project.godot @@ -0,0 +1,46 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[ai_assistant_hub] + +base_url="http://127.0.0.1:11434" +llm_api="ollama_api" + +[application] + +config/name="GGJ - Bubbles" +run/main_scene="uid://bwe0t2hwl5pv1" +config/features=PackedStringArray("4.4") +config/icon="res://icon.svg" + +[audio] + +driver/enable_input=true + +[autoload] + +State="*res://scripts/state.gd" + +[display] + +window/size/viewport_width=678 +window/size/viewport_height=1699 +window/size/resizable=false +window/stretch/mode="canvas_items" +window/handheld/orientation=1 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/ai_assistant_hub/plugin.cfg", "res://addons/code_time/plugin.cfg", "res://addons/ridiculous_coding/plugin.cfg") + +[rendering] + +renderer/rendering_method="mobile" +textures/vram_compression/import_etc2_astc=true diff --git a/readme-example.gif b/readme-example.gif new file mode 100644 index 0000000..4ea9a8f Binary files /dev/null and b/readme-example.gif differ diff --git a/ridiculous.png b/ridiculous.png new file mode 100644 index 0000000..3fb1c38 Binary files /dev/null and b/ridiculous.png differ diff --git a/ridiculous.png.import b/ridiculous.png.import new file mode 100644 index 0000000..1247bbc --- /dev/null +++ b/ridiculous.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bt4jyqkkjkt33" +path="res://.godot/imported/ridiculous.png-a64a78ecb1ff54430110261923f7dd4f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ridiculous.png" +dest_files=["res://.godot/imported/ridiculous.png-a64a78ecb1ff54430110261923f7dd4f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/ridiculous.svg b/ridiculous.svg new file mode 100644 index 0000000..76ed5e4 --- /dev/null +++ b/ridiculous.svg @@ -0,0 +1,25481 @@ + + diff --git a/ridiculous.svg.import b/ridiculous.svg.import new file mode 100644 index 0000000..08fabd3 --- /dev/null +++ b/ridiculous.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5kge3jnwrr75" +path="res://.godot/imported/ridiculous.svg-e21db04d9320e3effae5aa7f8ec5a426.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ridiculous.svg" +dest_files=["res://.godot/imported/ridiculous.svg-e21db04d9320e3effae5aa7f8ec5a426.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/scenes/blower.tscn b/scenes/blower.tscn new file mode 100644 index 0000000..d4abd36 --- /dev/null +++ b/scenes/blower.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=8 format=3 uid="uid://j473t8iwu45y"] + +[ext_resource type="Texture2D" uid="uid://dg6g5gxw8s4ll" path="res://assets/Blower.svg" id="1_5ackw"] +[ext_resource type="Script" uid="uid://btm0qil4ekmuj" path="res://scripts/blower.gd" id="1_uq2md"] +[ext_resource type="Texture2D" uid="uid://d0iedh3qhku3k" path="res://assets/soap.svg" id="3_mkv3y"] +[ext_resource type="Shader" uid="uid://bblenww4c3cgx" path="res://shaders/bubble.gdshader" id="4_mkv3y"] +[ext_resource type="Shader" uid="uid://ds78mh2iyge6a" path="res://shaders/soap.gdshader" id="5_rr1ey"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_mkv3y"] +shader = ExtResource("5_rr1ey") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_uq2md"] +shader = ExtResource("4_mkv3y") +shader_parameter/distortionView = 0.03 +shader_parameter/speedView = 0.5 +shader_parameter/distortionVertex = 0.03 +shader_parameter/speedVertex = 0.1 + +[node name="blower" type="Node2D"] +script = ExtResource("1_uq2md") + +[node name="BlowerSoap" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_mkv3y") +position = Vector2(230.125, 125.875) +scale = Vector2(0.35498, 0.35498) +texture = ExtResource("3_mkv3y") + +[node name="Blower" type="Sprite2D" parent="."] +position = Vector2(237, 322) +scale = Vector2(0.335113, 0.335113) +texture = ExtResource("1_5ackw") + +[node name="SoapBubbles" type="CPUParticles2D" parent="."] +material = SubResource("ShaderMaterial_uq2md") +position = Vector2(229, 119) +amount = 16 +texture = ExtResource("3_mkv3y") +lifetime = 8.0 +explosiveness = 0.1 +randomness = 0.2 +lifetime_randomness = 0.2 +direction = Vector2(90, 0) +spread = 120.0 +gravity = Vector2(400, -50) +scale_amount_min = 0.05 +scale_amount_max = 0.4 diff --git a/scenes/bubbles.tscn b/scenes/bubbles.tscn new file mode 100644 index 0000000..fb7d164 --- /dev/null +++ b/scenes/bubbles.tscn @@ -0,0 +1,3 @@ +[gd_scene format=3 uid="uid://dhlibtgq2xi7n"] + +[node name="bubbles" type="Node2D"] diff --git a/scenes/game.tscn b/scenes/game.tscn new file mode 100644 index 0000000..53944bd --- /dev/null +++ b/scenes/game.tscn @@ -0,0 +1,138 @@ +[gd_scene load_steps=13 format=3 uid="uid://bwe0t2hwl5pv1"] + +[ext_resource type="Script" uid="uid://b6if2dhse646b" path="res://scripts/game.gd" id="1_lnu2h"] +[ext_resource type="PackedScene" uid="uid://j473t8iwu45y" path="res://scenes/blower.tscn" id="1_uwrxv"] +[ext_resource type="Texture2D" uid="uid://1memr7pxwyl5" path="res://assets/background.svg" id="2_lbhrr"] +[ext_resource type="PackedScene" uid="uid://bofnkey7erc2b" path="res://scenes/recorder.tscn" id="2_yqjtg"] +[ext_resource type="LabelSettings" uid="uid://dl21bg2o5ltaj" path="res://label_settings.tres" id="5_iywne"] +[ext_resource type="Texture2D" uid="uid://dg6g5gxw8s4ll" path="res://assets/Blower.svg" id="5_p57ef"] +[ext_resource type="AudioStream" uid="uid://c7dphq7kgyqki" path="res://assets/Cheerful Annoyance.ogg" id="7_u5sy4"] + +[sub_resource type="Animation" id="Animation_yqjtg"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("blower:rotation") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.940732] +} + +[sub_resource type="Animation" id="Animation_uwrxv"] +resource_name = "blower_state" +step = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("blower:rotation") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 0.940732] +} + +[sub_resource type="Animation" id="Animation_lnu2h"] +resource_name = "blower_up" +step = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("blower:rotation") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.940732, 0.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_lnu2h"] +_data = { +&"RESET": SubResource("Animation_yqjtg"), +&"blower_down": SubResource("Animation_uwrxv"), +&"blower_up": SubResource("Animation_lnu2h") +} + +[sub_resource type="Theme" id="Theme_p57ef"] +Button/font_sizes/font_size = 32 +Button/fonts/font = null + +[node name="Game" type="Node2D"] +script = ExtResource("1_lnu2h") + +[node name="Background" type="Sprite2D" parent="."] +position = Vector2(166, 805.938) +scale = Vector2(1.5138, 2.35532) +texture = ExtResource("2_lbhrr") + +[node name="Recorder" parent="." instance=ExtResource("2_yqjtg")] + +[node name="blower" parent="." instance=ExtResource("1_uwrxv")] +position = Vector2(2.99997, 994) +rotation = 0.940732 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_lnu2h") +} + +[node name="Button" type="Button" parent="."] +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = 227.0 +offset_top = 1491.0 +offset_right = 420.0 +offset_bottom = 1634.0 +grow_horizontal = 2 +grow_vertical = 0 +size_flags_horizontal = 8 +size_flags_vertical = 4 +theme = SubResource("Theme_p57ef") +text = "Press +to +Blow" +icon = ExtResource("5_p57ef") +icon_alignment = 2 +expand_icon = true + +[node name="PointsTimer" type="Timer" parent="."] +wait_time = 0.25 +autostart = true + +[node name="PointsLabel" type="Label" parent="."] +offset_left = 17.0 +offset_top = 8.0 +offset_right = 245.0 +offset_bottom = 96.0 +text = "Points: " +label_settings = ExtResource("5_iywne") + +[node name="Points" type="Label" parent="."] +offset_left = 253.0 +offset_top = 10.0 +offset_right = 293.0 +offset_bottom = 98.0 +text = "0" +label_settings = ExtResource("5_iywne") + +[node name="BackgroundMusic" type="AudioStreamPlayer" parent="."] +stream = ExtResource("7_u5sy4") +volume_db = -12.0 +autoplay = true +parameters/looping = true + +[connection signal="button_down" from="Button" to="." method="_on_button_button_down"] +[connection signal="button_up" from="Button" to="." method="_on_button_button_up"] +[connection signal="timeout" from="PointsTimer" to="." method="_on_points_timer_timeout"] diff --git a/scenes/recorder.tscn b/scenes/recorder.tscn new file mode 100644 index 0000000..7bd2f89 --- /dev/null +++ b/scenes/recorder.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://bofnkey7erc2b"] + +[ext_resource type="Script" uid="uid://c4p5w41264ws" path="res://scripts/recorder.gd" id="1_5whi4"] + +[node name="Recorder" type="AudioStreamPlayer2D"] +bus = &"Record" +playback_type = 1 +script = ExtResource("1_5whi4") diff --git a/scripts/blower.gd b/scripts/blower.gd new file mode 100644 index 0000000..67ff3a3 --- /dev/null +++ b/scripts/blower.gd @@ -0,0 +1,4 @@ +extends Node2D + +func _process(_delta: float) -> void: + $SoapBubbles.emitting = State.blowing and State.blower_up diff --git a/scripts/blower.gd.uid b/scripts/blower.gd.uid new file mode 100644 index 0000000..4e0ad52 --- /dev/null +++ b/scripts/blower.gd.uid @@ -0,0 +1 @@ +uid://btm0qil4ekmuj diff --git a/scripts/game.gd b/scripts/game.gd new file mode 100644 index 0000000..915259f --- /dev/null +++ b/scripts/game.gd @@ -0,0 +1,18 @@ +extends Node2D + +func _process(_delta: float) -> void: + $Points.text = str(State.points) + +func _on_button_button_down(): + State.blower_up = true + $PointsTimer.paused = false + $AnimationPlayer.current_animation = "blower_up" + +func _on_button_button_up(): + State.blower_up = false + $PointsTimer.paused = true + $AnimationPlayer.current_animation = "blower_down" + +func _on_points_timer_timeout(): + if State.blowing: + State.points += 1 diff --git a/scripts/game.gd.uid b/scripts/game.gd.uid new file mode 100644 index 0000000..22686e5 --- /dev/null +++ b/scripts/game.gd.uid @@ -0,0 +1 @@ +uid://b6if2dhse646b diff --git a/scripts/recorder.gd b/scripts/recorder.gd new file mode 100644 index 0000000..5157da5 --- /dev/null +++ b/scripts/recorder.gd @@ -0,0 +1,40 @@ +extends Node + +var mic_stream: AudioStreamMicrophone +var spectrum_instance: AudioEffectSpectrumAnalyzerInstance + +func _ready(): + mic_stream = AudioStreamMicrophone.new() + $".".stream = mic_stream + $".".bus = "Record" + $".".play() + + # Get the Spectrum Analyzer instance from the audio bus + var bus_index = AudioServer.get_bus_index("Record") + var spectrum_effect = AudioServer.get_bus_effect(bus_index, 0) # 0 for the first effect + if spectrum_effect is AudioEffectSpectrumAnalyzer: + spectrum_instance = AudioServer.get_bus_effect_instance(bus_index, 0) + else: + print("Spectrum Analyzer effect not found on Record") + +func _process(_delta): + if spectrum_instance: + var magnitude = spectrum_instance.get_magnitude_for_frequency_range(20, 20000) + var max_magnitude = magnitude.x + + # Convert magnitude to dB + var db = linear_to_db(max_magnitude) + print("Volume (dB): ", db) + + if State.blower_up: + if db > -30: + State.blowing = true + else: + State.blowing = false + + +# Convert linear magnitude to decibels +func linear_to_db(linear: float) -> float: + if linear <= 0.0001: # Avoid log of zero + return -80.0 # Silence threshold + return 20.0 * (log(linear) / log(10)) diff --git a/scripts/recorder.gd.uid b/scripts/recorder.gd.uid new file mode 100644 index 0000000..26d7e90 --- /dev/null +++ b/scripts/recorder.gd.uid @@ -0,0 +1 @@ +uid://c4p5w41264ws diff --git a/scripts/state.gd b/scripts/state.gd new file mode 100644 index 0000000..f871c43 --- /dev/null +++ b/scripts/state.gd @@ -0,0 +1,6 @@ +extends Node + +var blower_up := false +var blowing := false + +var points := 0 diff --git a/scripts/state.gd.uid b/scripts/state.gd.uid new file mode 100644 index 0000000..fc2f229 --- /dev/null +++ b/scripts/state.gd.uid @@ -0,0 +1 @@ +uid://25euaoxr7uon diff --git a/shaders/bubble.gdshader b/shaders/bubble.gdshader new file mode 100644 index 0000000..fcd598d --- /dev/null +++ b/shaders/bubble.gdshader @@ -0,0 +1,30 @@ +shader_type canvas_item; + +uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap; +uniform float distortionView : hint_range(0.0, 0.3, 0.005) = 0.03; +uniform float speedView : hint_range(0.0, 1.0, 0.005) = 0.5; +uniform sampler2D noiseViewX; +uniform sampler2D noiseViewY; + +// vertex uniforms +uniform float distortionVertex : hint_range(0.0, 0.3, 0.005) = 0.03; +uniform float speedVertex : hint_range(0.0, 1.0, 0.005) = 0.1; +uniform sampler2D noiseVertex; + +void vertex() { + VERTEX += vec2(sin(TIME * 15.0f) * 7.5f, 0); +} + +void fragment() { + vec4 pixel_color = texture(TEXTURE, UV); + + float noiseValueX = (texture(noiseViewX, UV + (TIME * speedView)).r * 2.0) - 1.0; // Range: -1.0 to 1.0 + float noiseValueY = (texture(noiseViewY, UV + (TIME * speedView)).r * 2.0) - 1.0; // Range: -1.0 to 1.0 + vec2 noiseDistort = vec2(noiseValueX, noiseValueY) * distortionView; + vec3 distortedScreenTexture = vec3(texture(SCREEN_TEXTURE, SCREEN_UV + noiseDistort).rgb); + + if (pixel_color.a > 0.6f) { + vec4 new_color = vec4(0.2f, 0.1f, 0.3f, 0.4f); + COLOR = vec4(new_color.r + distortedScreenTexture.r, new_color.r + distortedScreenTexture.r, new_color.r + distortedScreenTexture.r, 0.4f); + } +} \ No newline at end of file diff --git a/shaders/bubble.gdshader.uid b/shaders/bubble.gdshader.uid new file mode 100644 index 0000000..88fc3c7 --- /dev/null +++ b/shaders/bubble.gdshader.uid @@ -0,0 +1 @@ +uid://bblenww4c3cgx diff --git a/shaders/learn.tres b/shaders/learn.tres new file mode 100644 index 0000000..6208101 --- /dev/null +++ b/shaders/learn.tres @@ -0,0 +1,121 @@ +[gd_resource type="VisualShader" load_steps=12 format=3 uid="uid://c8kholmrafem8"] + +[sub_resource type="VisualShaderNodeVectorOp" id="VisualShaderNodeVectorOp_rpjy3"] +default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(1, 0, 1)] +operator = 2 + +[sub_resource type="VisualShaderNodeVectorOp" id="VisualShaderNodeVectorOp_cxtpw"] +default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(1, 0, 1)] + +[sub_resource type="VisualShaderNodeColorParameter" id="VisualShaderNodeColorParameter_rpjy3"] +parameter_name = "replacement_color" + +[sub_resource type="VisualShaderNodeInput" id="VisualShaderNodeInput_jm1fk"] +expanded_output_ports = [0] +input_name = "color" + +[sub_resource type="VisualShaderNodeVectorOp" id="VisualShaderNodeVectorOp_glpyr"] +default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(1, 0, 1)] +operator = 2 + +[sub_resource type="VisualShaderNodeVectorLen" id="VisualShaderNodeVectorLen_b8j78"] + +[sub_resource type="VisualShaderNodeVectorOp" id="VisualShaderNodeVectorOp_452jj"] +default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(1, 0, 1)] +operator = 2 + +[sub_resource type="VisualShaderNodeStep" id="VisualShaderNodeStep_604ce"] +default_input_values = [0, 0.01, 1, 0.0] + +[sub_resource type="VisualShaderNodeFloatFunc" id="VisualShaderNodeFloatFunc_1gm2x"] +function = 31 + +[sub_resource type="VisualShaderNodeColorConstant" id="VisualShaderNodeColorConstant_r716q"] +constant = Color(0.153024, 0.27858, 0.323299, 1) + +[sub_resource type="VisualShaderNodeFloatOp" id="VisualShaderNodeFloatOp_d0qtb"] + +[resource] +code = "shader_type canvas_item; +render_mode blend_mix; + +uniform vec4 replacement_color : source_color; + + + +void fragment() { +// Input:2 + vec4 n_out2p0 = COLOR; + float n_out2p2 = n_out2p0.g; + + +// VectorOp:3 + vec3 n_in3p1 = vec3(1.00000, 0.00000, 1.00000); + vec3 n_out3p0 = vec3(n_out2p0.xyz) * n_in3p1; + + +// VectorLen:4 + float n_out4p0 = length(n_out3p0); + + +// Step:6 + float n_in6p0 = 0.01000; + float n_out6p0 = step(n_in6p0, n_out4p0); + + +// VectorOp:10 + vec3 n_out10p0 = vec3(n_out6p0) * vec3(n_out2p0.xyz); + + +// FloatFunc:7 + float n_out7p0 = 1.0 - n_out6p0; + + +// FloatOp:9 + float n_out9p0 = n_out7p0 + n_out2p2; + + +// ColorParameter:12 + vec4 n_out12p0 = replacement_color; + + +// VectorOp:5 + vec3 n_out5p0 = vec3(n_out9p0) * vec3(n_out12p0.xyz); + + +// VectorOp:11 + vec3 n_out11p0 = n_out10p0 + n_out5p0; + + +// Output:0 + COLOR.rgb = n_out11p0; + + +} +" +mode = 1 +flags/light_only = false +nodes/fragment/0/position = Vector2(1640, -100) +nodes/fragment/2/node = SubResource("VisualShaderNodeInput_jm1fk") +nodes/fragment/2/position = Vector2(-1640, 260) +nodes/fragment/3/node = SubResource("VisualShaderNodeVectorOp_glpyr") +nodes/fragment/3/position = Vector2(-1100, -60) +nodes/fragment/4/node = SubResource("VisualShaderNodeVectorLen_b8j78") +nodes/fragment/4/position = Vector2(-780, 0) +nodes/fragment/5/node = SubResource("VisualShaderNodeVectorOp_452jj") +nodes/fragment/5/position = Vector2(640, 140) +nodes/fragment/6/node = SubResource("VisualShaderNodeStep_604ce") +nodes/fragment/6/position = Vector2(-440, -40) +nodes/fragment/7/node = SubResource("VisualShaderNodeFloatFunc_1gm2x") +nodes/fragment/7/position = Vector2(-80, 40) +nodes/fragment/8/node = SubResource("VisualShaderNodeColorConstant_r716q") +nodes/fragment/8/position = Vector2(80, 800) +nodes/fragment/9/node = SubResource("VisualShaderNodeFloatOp_d0qtb") +nodes/fragment/9/position = Vector2(260, 160) +nodes/fragment/10/node = SubResource("VisualShaderNodeVectorOp_rpjy3") +nodes/fragment/10/position = Vector2(620, -180) +nodes/fragment/11/node = SubResource("VisualShaderNodeVectorOp_cxtpw") +nodes/fragment/11/position = Vector2(1160, 0) +nodes/fragment/12/node = SubResource("VisualShaderNodeColorParameter_rpjy3") +nodes/fragment/12/position = Vector2(0, 460) +nodes/fragment/connections = PackedInt32Array(2, 0, 3, 0, 3, 0, 4, 0, 4, 0, 6, 1, 6, 0, 7, 0, 7, 0, 9, 0, 9, 0, 5, 0, 2, 2, 9, 1, 6, 0, 10, 0, 2, 0, 10, 1, 10, 0, 11, 0, 5, 0, 11, 1, 11, 0, 0, 0, 12, 0, 5, 1) diff --git a/shaders/soap.gdshader b/shaders/soap.gdshader new file mode 100644 index 0000000..bcb9d05 --- /dev/null +++ b/shaders/soap.gdshader @@ -0,0 +1,16 @@ +shader_type canvas_item; + +uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap; + +void vertex() { + VERTEX += vec2(sin(TIME * 15.0f) * 7.5f, 0); +} + +void fragment() { + vec4 pixel_color = texture(TEXTURE, UV); + + if (pixel_color.a > 0.6f) { + vec4 new_color = vec4(0.2f, 0.1f, 0.3f, 0.4f); + COLOR = new_color; + } +} \ No newline at end of file diff --git a/shaders/soap.gdshader.uid b/shaders/soap.gdshader.uid new file mode 100644 index 0000000..7565d5a --- /dev/null +++ b/shaders/soap.gdshader.uid @@ -0,0 +1 @@ +uid://ds78mh2iyge6a