From b2ecac82db172151cf3ab47968a9d76f98f24375 Mon Sep 17 00:00:00 2001 From: dogboydog Date: Sat, 2 Nov 2024 11:11:29 -0400 Subject: [PATCH] v0.2.14 Gdscript Integration (#71) * yarncommands for gdscript * add sample that registers commands and implements a simple view with GDScript * add GetVariantValue to InMemoryVariableStorage.cs, add more examples of cross language scripting * update CHANGELOG.md, update version to 0.2.14 * add GDScript options list view example * add example options array --------- Co-authored-by: Daniel Barry --- .github/RELEASE_TEMPLATE.md | 14 + .gitignore | 1 + CHANGELOG.md | 7 + .../CrossLanguageScriptingExample.gd | 48 ++++ .../Dialogue/GDScriptIntegration.yarn | 22 ++ .../Dialogue/GDScriptIntegration.yarn.import | 14 + Samples/GDScriptIntegration/GDS4F12.tmp | 271 ++++++++++++++++++ Samples/GDScriptIntegration/GDS68E3.tmp | 271 ++++++++++++++++++ .../GDScriptIntegration.yarnproject | 10 + .../GDScriptIntegration.yarnproject.import | 14 + .../GDScriptIntegrationSample.tscn | 225 +++++++++++++++ .../SimpleGDScriptLineView.gd | 58 ++++ .../SimpleGDScriptOptionView.gd | 14 + .../SimpleGDScriptOptionView.tscn | 43 +++ .../SimpleGDScriptOptionsListView.gd | 55 ++++ Samples/NotoSerifJP-Regular.otf.import | 1 + Samples/SQLiteVariableStorage/SQLSample.tscn | 4 +- Samples/SampleEntryPoint.cs | 5 +- Samples/SampleEntryPoint.tscn | 39 ++- YarnSpinner-Godot.csproj | 3 +- .../Runtime/Commands/ActionManager.cs | 4 + .../Runtime/DialogueRunner.cs | 155 ++++++++++ .../Runtime/InMemoryVariableStorage.cs | 33 +++ .../Runtime/LineProviders/TextLineProvider.cs | 6 +- .../YarnSpinner-Godot/Runtime/Localization.cs | 1 + .../Runtime/Views/GDScriptViewAdapter.cs | 232 +++++++++++++++ .../Scenes/DefaultDialogueSystem.tscn | 24 +- .../YarnSpinner-Godot/YarnSpinner-Godot.props | 2 +- addons/YarnSpinner-Godot/plugin.cfg | 4 +- project.godot | 32 +-- 30 files changed, 1568 insertions(+), 44 deletions(-) create mode 100644 .github/RELEASE_TEMPLATE.md create mode 100644 Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd create mode 100644 Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn create mode 100644 Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn.import create mode 100644 Samples/GDScriptIntegration/GDS4F12.tmp create mode 100644 Samples/GDScriptIntegration/GDS68E3.tmp create mode 100644 Samples/GDScriptIntegration/GDScriptIntegration.yarnproject create mode 100644 Samples/GDScriptIntegration/GDScriptIntegration.yarnproject.import create mode 100644 Samples/GDScriptIntegration/GDScriptIntegrationSample.tscn create mode 100644 Samples/GDScriptIntegration/SimpleGDScriptLineView.gd create mode 100644 Samples/GDScriptIntegration/SimpleGDScriptOptionView.gd create mode 100644 Samples/GDScriptIntegration/SimpleGDScriptOptionView.tscn create mode 100644 Samples/GDScriptIntegration/SimpleGDScriptOptionsListView.gd create mode 100644 addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs diff --git a/.github/RELEASE_TEMPLATE.md b/.github/RELEASE_TEMPLATE.md new file mode 100644 index 0000000..a5ebfd5 --- /dev/null +++ b/.github/RELEASE_TEMPLATE.md @@ -0,0 +1,14 @@ + +See [here](https://docs.yarnspinner.dev/beginners-guide/making-a-game/yarn-spinner-for-godot) for installation. +You can clone the repo at this tag, or download the below zip and use the addons/ folder inside it to follow the setup instructions. +If you open the complete repository as a Godot project, you can try a variety of samples demonstrating code and end results of various features of YarnSpinner for Godot. + +## 👩‍🚒 Getting Help + +There are several places you can go to get help with Yarn Spinner. + +* Join the [Yarn Spinner Discord](https://discord.gg/yarnspinner) (`#godot` channel). +* To report a bug, [file an issue on GitHub](https://github.com/YarnSpinnerTool/YarnSpinner-Godot/issues). + +* If you would like to support the development of this plugin, other open source contributions, and video games by the maintainer, +consider donating at [ko-fi.com/decrepitgames](https://ko-fi.com/decrepitgames). \ No newline at end of file diff --git a/.gitignore b/.gitignore index 26370b3..15ad0ac 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ Temporary Items .import build *.csproj.old* +*DotSettings.user \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5cd45..103be53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.2.14] 2024-11-02 +* GDScript: Add GDScriptViewAdapter, a C# Script which allows you to write custom dialogue views in GDScript. See GDScriptViewAdapter.cs for more details. +* GDScript: Add new method AddCommandHandlerCallable to DialogueRunner, allowing commands to be registered from GDScript. GDScript command handlers that use asynchronous `await` functionality are also supported as blocking YarnSpinner commands, similar to using `async Task` commands in C#. +* Samples: A new sample has been added called GDScriptIntegration, which demonstrates making a simple custom view in GDScript, and using cross-language scripting to access methods on C# components such as the DialogueRunner and InMemoryVariableStorage +* Add new method to InMemoryVariableStorage: `public Variant GetVariantValue(string variableName)`. An example of a method that allows you to retrieve YarnSpinner story variables from GDScript. +* Thanks to @dbaz for taking my old code from the gdscript_integration branch and making sure it compiles with the current version of the plugin + ## [0.2.13] 2024-09-15 * General code cleanup by @valkyrienyanko. Make use of some C# language features such as target-typed `new()` to simplify code. * Potential breaking change: DispatchCommandToNode on DialogueRunner has been marked static. Users generally do not need to call this method directly, so it shouldn't affect most or possibly any projects. diff --git a/Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd b/Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd new file mode 100644 index 0000000..12dba6c --- /dev/null +++ b/Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd @@ -0,0 +1,48 @@ +# Demonstrates interacting with C# YarnSpinner-Godot +# components from GDScript. + +extends Control +@export var dialogue_runner: DialogueRunner +@export var logo: Control +@export var yarn_project: YarnProject +func _ready() -> void: + dialogue_runner.AddCommandHandlerCallable("show_logo", show_logo) + dialogue_runner.onDialogueComplete.connect(on_dialogue_complete) + var var_name : String = "$myVariableSetFromGDScript" + dialogue_runner.variableStorage.SetValue( var_name, "Yay!") + var var_value = dialogue_runner.variableStorage.GetVariantValue(var_name) + print("Value of %s: %s" % [var_name, var_value]) + +func show_logo(logo_path) -> void: + """ + Async handler for the <> command. + """ + print("Showing logo...") + var logo_load_error: Error = ResourceLoader.load_threaded_request(logo_path) + if logo_load_error != OK: + print("Failed to load logo %s: Error %s" % [logo_path, logo_load_error]) + return + + var logo_load_status : ResourceLoader.ThreadLoadStatus = ResourceLoader.ThreadLoadStatus.THREAD_LOAD_IN_PROGRESS + while logo_load_status == ResourceLoader.ThreadLoadStatus.THREAD_LOAD_IN_PROGRESS: + logo_load_status = ResourceLoader.load_threaded_get_status(logo_path) + await get_tree().process_frame + + if logo_load_status != ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED: + print("Failed to load %s. Error: %s" % [logo_path, logo_load_status]) + return + logo.texture = ResourceLoader.load_threaded_get(logo_path) + logo.visible = true + + var tween := create_tween() + tween.tween_property(logo, "modulate", Color.BLUE , 1.0) + await tween.finished + tween = create_tween() + tween.tween_property(logo, "modulate", Color.WHITE , 1.0) + await tween.finished + await get_tree().create_timer(1.0).timeout + print("Hiding logo...") + logo.visible = false + +func on_dialogue_complete() -> void: + print("Dialogue completed.") diff --git a/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn b/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn new file mode 100644 index 0000000..583a1c4 --- /dev/null +++ b/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn @@ -0,0 +1,22 @@ +title: GDScriptIntegration +tags: +--- +<> +Gary: So, can I use [fx type="wave"]GDScript[/fx] with YarnSpinner? #my_metadata #line:02fa0cd +Derrick: Many features can be used with GDScript. For example, you can make a custom view like the one in this scene. #line:09c1c2c +Derrick: You can also interact with DialogueRunner and variable storage nodes with cross-language scripting. #line:0b086af +Derrick: And you can also write commands in GDScript by using AddCommandHandlerCallable. Your GDScript commands can even use 'await'. Look: #line:0c22e97 +<> +Derrick: Do you know about options, too? You can make an "OptionsListView" with GDScript, too. +-> Yes + Derrick: Nice. + <> +-> No + Derrick: Well, you can prompt the player to pick an option. + <> +=== + +title: GDScriptIntegrationFinish +--- +Gary: {$myVariableSetFromGDScript} That's cool. I will look at the GDScriptIntegration sample code to learn about this. #line:020bb2b +=== \ No newline at end of file diff --git a/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn.import b/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn.import new file mode 100644 index 0000000..cab757d --- /dev/null +++ b/Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn.import @@ -0,0 +1,14 @@ +[remap] + +importer="yarnscript" +type="Resource" +uid="uid://bfoa7hr2oeryv" +path="res://.godot/imported/GDScriptIntegration.yarn-db27ea88bc8a407b72b6da1ec02824b9.tres" + +[deps] + +source_file="res://Samples/GDScriptIntegration/Dialogue/GDScriptIntegration.yarn" +dest_files=["res://.godot/imported/GDScriptIntegration.yarn-db27ea88bc8a407b72b6da1ec02824b9.tres"] + +[params] + diff --git a/Samples/GDScriptIntegration/GDS4F12.tmp b/Samples/GDScriptIntegration/GDS4F12.tmp new file mode 100644 index 0000000..4040c3f --- /dev/null +++ b/Samples/GDScriptIntegration/GDS4F12.tmp @@ -0,0 +1,271 @@ +[gd_scene load_steps=12 format=3 uid="uid://hvtydqjfdlf5"] + +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs" id="1_1er8s"] +[ext_resource type="Resource" uid="uid://c0qdj0h48fu6a" path="res://Samples/GDScriptIntegration/GDScriptIntegration.yarnproject" id="2_15buf"] +[ext_resource type="Resource" uid="uid://c631us202ijmk" path="res://Samples/MarkupPalette/example_markup_palette.tres" id="3_3qr8r"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd" id="3_05a7h"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs" id="3_8do86"] +[ext_resource type="Script" path="res://Samples/ReturnOnComplete.cs" id="4_6b5el"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/SimpleGDScriptLineView.gd" id="4_ixyx4"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/Views/OptionsListView.cs" id="5_jois6"] +[ext_resource type="PackedScene" uid="uid://b56ngcrq31nc5" path="res://addons/YarnSpinner-Godot/Scenes/OptionView.tscn" id="6_0v15m"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs" id="8_fijq0"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs" id="9_a8pi7"] + +[node name="PaletteSample" type="Node2D"] + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +layer = 0 + +[node name="BackgroundColor" type="ColorRect" parent="CanvasLayer"] +z_index = -7 +z_as_relative = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0.00260118, 0.121715, 0.193433, 1) + +[node name="GDScriptYarnSpinnerCanvasLayer" type="CanvasLayer" parent="."] + +[node name="DialogueRunner" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("variableStorage", "dialogueViews", "lineProvider")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_1er8s") +yarnProject = ExtResource("2_15buf") +variableStorage = NodePath("../InMemoryVariableStorage") +dialogueViews = [NodePath("../LineViewAdapter"), NodePath("../OptionsListView")] +startNode = "GDScriptIntegration" +lineProvider = NodePath("../TextLineProvider") + +[node name="CrossLanguageScriptingExample" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("dialogue_runner")] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("3_05a7h") +dialogue_runner = NodePath("../DialogueRunner") + +[node name="VariableDebugText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer"] + +[node name="LineViewAdapter" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("GDScriptView")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_8do86") +GDScriptView = NodePath("SimpleGDScriptLineView") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LineText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -610.0 +offset_top = -352.0 +offset_right = 640.0 +offset_bottom = 193.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "The dialogue text should appear here!" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/LineText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_right = -12.0 +offset_bottom = -158.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CharacterNameText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +self_modulate = Color(0.321569, 0.87451, 0.254902, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -607.0 +offset_top = -415.0 +offset_right = 279.0 +offset_bottom = -362.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "Character Name" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/CharacterNameText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="ContinueButton" type="Button" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = 337.0 +offset_top = -463.0 +offset_right = 569.0 +offset_bottom = -363.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_default_cursor_shape = 2 +theme_override_font_sizes/font_size = 36 +text = "Continue" + +[node name="SimpleGDScriptLineView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter" node_paths=PackedStringArray("continue_button")] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("4_ixyx4") +continue_button = NodePath("../ViewControl/ContinueButton") + +[node name="OptionsListView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("lastLineCharacterNameText", "lastLineText", "viewControl", "boxContainer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("5_jois6") +optionViewPrefab = ExtResource("6_0v15m") +palette = ExtResource("3_3qr8r") +lastLineCharacterNameText = NodePath("ViewControl/LastLineCharacterNameText") +lastLineText = NodePath("ViewControl/LastLineText") +viewControl = NodePath("ViewControl") +boxContainer = NodePath("ViewControl/VBoxContainer") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +custom_minimum_size = Vector2(160, 0) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -547.0 +offset_top = -213.0 +offset_right = 547.0 +offset_bottom = 287.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LastLineText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +visible = false +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -610.0 +offset_top = 100.0 +offset_right = 279.0 +offset_bottom = 185.0 +grow_horizontal = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "The last line text goes here" +scroll_active = false + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl/LastLineText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LastLineCharacterNameText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +visible = false +self_modulate = Color(0.321569, 0.87451, 0.254902, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -608.0 +offset_top = -416.0 +offset_right = 278.0 +offset_bottom = -363.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "Character Name" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl/LastLineCharacterNameText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="InMemoryVariableStorage" type="Node" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("8_fijq0") + +[node name="TextLineProvider" type="Node2D" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("9_a8pi7") + +[node name="ReturnOnComplete" type="Node2D" parent="." node_paths=PackedStringArray("dialogueRunner")] +script = ExtResource("4_6b5el") +dialogueRunner = NodePath("../GDScriptYarnSpinnerCanvasLayer/DialogueRunner") diff --git a/Samples/GDScriptIntegration/GDS68E3.tmp b/Samples/GDScriptIntegration/GDS68E3.tmp new file mode 100644 index 0000000..4040c3f --- /dev/null +++ b/Samples/GDScriptIntegration/GDS68E3.tmp @@ -0,0 +1,271 @@ +[gd_scene load_steps=12 format=3 uid="uid://hvtydqjfdlf5"] + +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs" id="1_1er8s"] +[ext_resource type="Resource" uid="uid://c0qdj0h48fu6a" path="res://Samples/GDScriptIntegration/GDScriptIntegration.yarnproject" id="2_15buf"] +[ext_resource type="Resource" uid="uid://c631us202ijmk" path="res://Samples/MarkupPalette/example_markup_palette.tres" id="3_3qr8r"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd" id="3_05a7h"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs" id="3_8do86"] +[ext_resource type="Script" path="res://Samples/ReturnOnComplete.cs" id="4_6b5el"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/SimpleGDScriptLineView.gd" id="4_ixyx4"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/Views/OptionsListView.cs" id="5_jois6"] +[ext_resource type="PackedScene" uid="uid://b56ngcrq31nc5" path="res://addons/YarnSpinner-Godot/Scenes/OptionView.tscn" id="6_0v15m"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs" id="8_fijq0"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs" id="9_a8pi7"] + +[node name="PaletteSample" type="Node2D"] + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +layer = 0 + +[node name="BackgroundColor" type="ColorRect" parent="CanvasLayer"] +z_index = -7 +z_as_relative = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0.00260118, 0.121715, 0.193433, 1) + +[node name="GDScriptYarnSpinnerCanvasLayer" type="CanvasLayer" parent="."] + +[node name="DialogueRunner" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("variableStorage", "dialogueViews", "lineProvider")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_1er8s") +yarnProject = ExtResource("2_15buf") +variableStorage = NodePath("../InMemoryVariableStorage") +dialogueViews = [NodePath("../LineViewAdapter"), NodePath("../OptionsListView")] +startNode = "GDScriptIntegration" +lineProvider = NodePath("../TextLineProvider") + +[node name="CrossLanguageScriptingExample" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("dialogue_runner")] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("3_05a7h") +dialogue_runner = NodePath("../DialogueRunner") + +[node name="VariableDebugText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer"] + +[node name="LineViewAdapter" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("GDScriptView")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_8do86") +GDScriptView = NodePath("SimpleGDScriptLineView") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LineText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -610.0 +offset_top = -352.0 +offset_right = 640.0 +offset_bottom = 193.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "The dialogue text should appear here!" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/LineText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_right = -12.0 +offset_bottom = -158.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CharacterNameText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +self_modulate = Color(0.321569, 0.87451, 0.254902, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -607.0 +offset_top = -415.0 +offset_right = 279.0 +offset_bottom = -362.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "Character Name" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/CharacterNameText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="ContinueButton" type="Button" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = 337.0 +offset_top = -463.0 +offset_right = 569.0 +offset_bottom = -363.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_default_cursor_shape = 2 +theme_override_font_sizes/font_size = 36 +text = "Continue" + +[node name="SimpleGDScriptLineView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter" node_paths=PackedStringArray("continue_button")] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("4_ixyx4") +continue_button = NodePath("../ViewControl/ContinueButton") + +[node name="OptionsListView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("lastLineCharacterNameText", "lastLineText", "viewControl", "boxContainer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("5_jois6") +optionViewPrefab = ExtResource("6_0v15m") +palette = ExtResource("3_3qr8r") +lastLineCharacterNameText = NodePath("ViewControl/LastLineCharacterNameText") +lastLineText = NodePath("ViewControl/LastLineText") +viewControl = NodePath("ViewControl") +boxContainer = NodePath("ViewControl/VBoxContainer") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +custom_minimum_size = Vector2(160, 0) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -547.0 +offset_top = -213.0 +offset_right = 547.0 +offset_bottom = 287.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LastLineText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +visible = false +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -610.0 +offset_top = 100.0 +offset_right = 279.0 +offset_bottom = 185.0 +grow_horizontal = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "The last line text goes here" +scroll_active = false + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl/LastLineText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LastLineCharacterNameText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +visible = false +self_modulate = Color(0.321569, 0.87451, 0.254902, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -608.0 +offset_top = -416.0 +offset_right = 278.0 +offset_bottom = -363.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "Character Name" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl/LastLineCharacterNameText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="InMemoryVariableStorage" type="Node" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("8_fijq0") + +[node name="TextLineProvider" type="Node2D" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("9_a8pi7") + +[node name="ReturnOnComplete" type="Node2D" parent="." node_paths=PackedStringArray("dialogueRunner")] +script = ExtResource("4_6b5el") +dialogueRunner = NodePath("../GDScriptYarnSpinnerCanvasLayer/DialogueRunner") diff --git a/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject b/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject new file mode 100644 index 0000000..5cbeb26 --- /dev/null +++ b/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject @@ -0,0 +1,10 @@ +{ + "projectFileVersion": 2, + "sourceFiles": [ + "**/*.yarn" + ], + "excludeFiles": [], + "localisation": {}, + "baseLanguage": "en", + "compilerOptions": {} +} \ No newline at end of file diff --git a/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject.import b/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject.import new file mode 100644 index 0000000..7d456e9 --- /dev/null +++ b/Samples/GDScriptIntegration/GDScriptIntegration.yarnproject.import @@ -0,0 +1,14 @@ +[remap] + +importer="yarnproject" +type="Resource" +uid="uid://c0qdj0h48fu6a" +path="res://.godot/imported/GDScriptIntegration.yarnproject-dbec9ba712db1ca78d4a6bcd0bf67c57.tres" + +[deps] + +source_file="res://Samples/GDScriptIntegration/GDScriptIntegration.yarnproject" +dest_files=["res://.godot/imported/GDScriptIntegration.yarnproject-dbec9ba712db1ca78d4a6bcd0bf67c57.tres"] + +[params] + diff --git a/Samples/GDScriptIntegration/GDScriptIntegrationSample.tscn b/Samples/GDScriptIntegration/GDScriptIntegrationSample.tscn new file mode 100644 index 0000000..52ee862 --- /dev/null +++ b/Samples/GDScriptIntegration/GDScriptIntegrationSample.tscn @@ -0,0 +1,225 @@ +[gd_scene load_steps=11 format=3 uid="uid://hvtydqjfdlf5"] + +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs" id="1_1er8s"] +[ext_resource type="Resource" uid="uid://c0qdj0h48fu6a" path="res://Samples/GDScriptIntegration/GDScriptIntegration.yarnproject" id="2_15buf"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/CrossLanguageScriptingExample.gd" id="3_05a7h"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs" id="3_8do86"] +[ext_resource type="Script" path="res://Samples/ReturnOnComplete.cs" id="4_6b5el"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/SimpleGDScriptLineView.gd" id="4_ixyx4"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs" id="8_fijq0"] +[ext_resource type="Script" path="res://addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs" id="9_a8pi7"] +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/SimpleGDScriptOptionsListView.gd" id="9_dujw4"] +[ext_resource type="PackedScene" uid="uid://bwrwry811vrgh" path="res://Samples/GDScriptIntegration/SimpleGDScriptOptionView.tscn" id="10_s0v7h"] + +[node name="PaletteSample" type="Node2D"] + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +layer = 0 + +[node name="BackgroundColor" type="ColorRect" parent="CanvasLayer"] +z_index = -7 +z_as_relative = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0.00260118, 0.121715, 0.193433, 1) + +[node name="GDScriptYarnSpinnerCanvasLayer" type="CanvasLayer" parent="."] + +[node name="DialogueRunner" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("variableStorage", "dialogueViews", "lineProvider")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_1er8s") +yarnProject = ExtResource("2_15buf") +variableStorage = NodePath("../InMemoryVariableStorage") +dialogueViews = [NodePath("../LineViewAdapter"), NodePath("../OptionsListView")] +startNode = "GDScriptIntegration" +lineProvider = NodePath("../TextLineProvider") + +[node name="CrossLanguageScriptingExample" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("dialogue_runner", "logo")] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("3_05a7h") +dialogue_runner = NodePath("../DialogueRunner") +logo = NodePath("../Logo") +yarn_project = ExtResource("2_15buf") + +[node name="VariableDebugText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer"] + +[node name="LineViewAdapter" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("GDScriptView")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_8do86") +GDScriptView = NodePath("SimpleGDScriptLineView") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LineText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -610.0 +offset_top = -352.0 +offset_right = 640.0 +offset_bottom = 193.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "The dialogue text should appear here!" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/LineText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_right = -12.0 +offset_bottom = -158.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CharacterNameText" type="RichTextLabel" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +self_modulate = Color(0.321569, 0.87451, 0.254902, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -607.0 +offset_top = -415.0 +offset_right = 279.0 +offset_bottom = -362.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 +theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 +bbcode_enabled = true +text = "Character Name" + +[node name="ColorRect" type="ColorRect" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl/CharacterNameText"] +modulate = Color(0.203922, 0.192157, 0.192157, 0.458824) +show_behind_parent = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="ContinueButton" type="Button" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter/ViewControl"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = 337.0 +offset_top = -463.0 +offset_right = 569.0 +offset_bottom = -363.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_default_cursor_shape = 2 +theme_override_font_sizes/font_size = 36 +text = "Continue" + +[node name="SimpleGDScriptLineView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/LineViewAdapter" node_paths=PackedStringArray("continue_button", "character_name_label", "line_text_label")] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("4_ixyx4") +continue_button = NodePath("../ViewControl/ContinueButton") +character_name_label = NodePath("../ViewControl/CharacterNameText") +line_text_label = NodePath("../ViewControl/LineText") + +[node name="OptionsListView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer" node_paths=PackedStringArray("GDScriptView")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_8do86") +GDScriptView = NodePath("SimpleGDScriptOptionsListView") + +[node name="ViewControl" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView/ViewControl"] +custom_minimum_size = Vector2(160, 0) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -547.0 +offset_top = -213.0 +offset_right = 547.0 +offset_bottom = 287.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SimpleGDScriptOptionsListView" type="Control" parent="GDScriptYarnSpinnerCanvasLayer/OptionsListView" node_paths=PackedStringArray("options_container", "view_control")] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("9_dujw4") +option_view_prefab = ExtResource("10_s0v7h") +options_container = NodePath("../ViewControl/VBoxContainer") +view_control = NodePath("../ViewControl") + +[node name="InMemoryVariableStorage" type="Node" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("8_fijq0") + +[node name="TextLineProvider" type="Node2D" parent="GDScriptYarnSpinnerCanvasLayer"] +script = ExtResource("9_a8pi7") + +[node name="Logo" type="TextureRect" parent="GDScriptYarnSpinnerCanvasLayer"] +visible = false +offset_left = 554.0 +offset_top = 358.0 +offset_right = 1066.0 +offset_bottom = 870.0 + +[node name="ReturnOnComplete" type="Node2D" parent="." node_paths=PackedStringArray("dialogueRunner")] +script = ExtResource("4_6b5el") +dialogueRunner = NodePath("../GDScriptYarnSpinnerCanvasLayer/DialogueRunner") diff --git a/Samples/GDScriptIntegration/SimpleGDScriptLineView.gd b/Samples/GDScriptIntegration/SimpleGDScriptLineView.gd new file mode 100644 index 0000000..da97d89 --- /dev/null +++ b/Samples/GDScriptIntegration/SimpleGDScriptLineView.gd @@ -0,0 +1,58 @@ +# Example of a simple custom dialogue view written in GDScript with +# the help of a corresponding GDScriptViewAdapter node.s +extends Node + + +@export var continue_button : Button +@export var character_name_label : RichTextLabel +@export var line_text_label : RichTextLabel + +var on_line_finished : Callable + +func dialogue_started() -> void: + print("Dialogue started ") + +func run_line(line: Dictionary, on_dialogue_line_finished: Callable) -> void: + # line is a Dictionary converted from the LocalizedLine C# Class + # example: + # {"metadata":["my_metadata"], + # "text": + # { + # "attributes":[ + # { "length":8,"name":"fx","position":20,"properties":[{"type":"wave"}]}, + # { "length":6,"name":"character","position":0,"properties":[{"name":"Gary"}]} + # ], + # "text":"Gary: So, can I use GDScript with YarnSpinner?", + # "text_without_character_name":"So, can I use GDScript with YarnSpinner?" + # } + # } + print('Line: ' + JSON.stringify(line)) + continue_button.pressed.connect(continue_line) + self.on_line_finished = on_dialogue_line_finished + line_text_label.text = line["text"]["text_without_character_name"] + var character_name : String = "" + var character_name_offset: int = 0 + for attribute: Dictionary in line["text"]["attributes"]: + if attribute["name"] == "character": + character_name = attribute["properties"][0]["name"] + character_name_offset = line["text"]["text"].find(line["text"]["text_without_character_name"]) + break + for attribute: Dictionary in line["text"]["attributes"]: + # Example of using YarnSpinner markup like [fx type="wave"] in the yarn script. + if attribute["name"] == "fx": + for property in attribute["properties"]: + if property["type"] == "wave": + var wave_begin = "[wave]" + line_text_label.text = line_text_label.text.insert(attribute["position"] - character_name_offset, wave_begin) + line_text_label.text = line_text_label.text.insert(attribute["position"] - character_name_offset + len(wave_begin) + attribute["length"], "[/wave]") + character_name_label.text = character_name + if not (character_name): + character_name_label.visible = false + + +func continue_line() -> void: + continue_button.pressed.disconnect(continue_line) + self.on_line_finished.call() + +func dialogue_complete() -> void: + print("Dialogue complete ") diff --git a/Samples/GDScriptIntegration/SimpleGDScriptOptionView.gd b/Samples/GDScriptIntegration/SimpleGDScriptOptionView.gd new file mode 100644 index 0000000..d8599e9 --- /dev/null +++ b/Samples/GDScriptIntegration/SimpleGDScriptOptionView.gd @@ -0,0 +1,14 @@ +extends Button +class_name SimpleGDScriptOptionView +@export var label: RichTextLabel + +var option_dict: Dictionary +var on_option_selected: Callable +func set_option(option: Dictionary, on_selected: Callable) -> void: + option_dict = option + label.text = option["line"]["text"]["text"] + on_option_selected = on_selected + pressed.connect(on_pressed) + +func on_pressed()-> void: + on_option_selected.call(option_dict["dialogue_option_id"]) diff --git a/Samples/GDScriptIntegration/SimpleGDScriptOptionView.tscn b/Samples/GDScriptIntegration/SimpleGDScriptOptionView.tscn new file mode 100644 index 0000000..09a213e --- /dev/null +++ b/Samples/GDScriptIntegration/SimpleGDScriptOptionView.tscn @@ -0,0 +1,43 @@ +[gd_scene load_steps=2 format=3 uid="uid://bwrwry811vrgh"] + +[ext_resource type="Script" path="res://Samples/GDScriptIntegration/SimpleGDScriptOptionView.gd" id="1_33ld1"] + +[node name="SimpleGDScriptOptionView" type="Button" node_paths=PackedStringArray("label")] +custom_minimum_size = Vector2(0, 26) +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -113.0 +offset_top = -142.0 +offset_right = 119.0 +offset_bottom = -42.0 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 36 +script = ExtResource("1_33ld1") +label = NodePath("VBoxContainer/RichTextLabel") + +[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 +scale = Vector2(1, 0.992647) +alignment = 1 + +[node name="RichTextLabel" type="RichTextLabel" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 6 +mouse_filter = 2 +theme_override_font_sizes/bold_italics_font_size = 24 +theme_override_font_sizes/italics_font_size = 24 +theme_override_font_sizes/mono_font_size = 24 +theme_override_font_sizes/normal_font_size = 24 +theme_override_font_sizes/bold_font_size = 24 +bbcode_enabled = true +text = "[center]Option View[/center]" +fit_content = true +scroll_active = false diff --git a/Samples/GDScriptIntegration/SimpleGDScriptOptionsListView.gd b/Samples/GDScriptIntegration/SimpleGDScriptOptionsListView.gd new file mode 100644 index 0000000..5e5ed17 --- /dev/null +++ b/Samples/GDScriptIntegration/SimpleGDScriptOptionsListView.gd @@ -0,0 +1,55 @@ +extends Node +# Example of writing an options list view in GDScript +@export var option_view_prefab : PackedScene +@export var options_container: Container +@export var view_control: Control + +var option_selected_handler: Callable + +func _ready() -> void: + view_control.visible = false + +# Example options array: +#[ + #{ + #"dialogue_option_id": 0, + #"is_available": true, + #"line": { + #"metadata": [], + #"text": { + #"attributes": [], + #"text": "Yes", + #"text_without_character_name": "Yes" + #} + #}, + #"text_id": "line:b7aaff9b" + #}, + #{ + #"dialogue_option_id": 1, + #"is_available": true, + #"line": { + #"metadata": [], + #"text": { + #"attributes": [], + #"text": "No", + #"text_without_character_name": "No" + #} + #}, + #"text_id": "line:9bcbf175" + #} +#] +func run_options(options: Array, on_option_selected: Callable) -> void: + print("Options: %s" % JSON.stringify(options)) + option_selected_handler = on_option_selected + for option in options: + var option_view: SimpleGDScriptOptionView = option_view_prefab.instantiate() + option_view.set_option(option, select_option) + options_container.add_child(option_view) + + view_control.visible = true + +func select_option(option_id: int) -> void: + option_selected_handler.call(option_id) + view_control.visible = false + while options_container.get_child_count() > 0: + options_container.remove_child(options_container.get_child(0)) diff --git a/Samples/NotoSerifJP-Regular.otf.import b/Samples/NotoSerifJP-Regular.otf.import index 4599a62..194492b 100644 --- a/Samples/NotoSerifJP-Regular.otf.import +++ b/Samples/NotoSerifJP-Regular.otf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/NotoSerifJP-Regular.otf-96fe9c5cca20d15489aa5 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/Samples/SQLiteVariableStorage/SQLSample.tscn b/Samples/SQLiteVariableStorage/SQLSample.tscn index 84907fb..4defade 100644 --- a/Samples/SQLiteVariableStorage/SQLSample.tscn +++ b/Samples/SQLiteVariableStorage/SQLSample.tscn @@ -17,10 +17,10 @@ startAutomatically = true [node name="LineView" parent="RoundedYarnSpinnerCanvasLayer" index="2"] useFadeEffect = true -fadeInTime = 1.0 +fadeInTime = 0.2 [node name="OptionsListView" parent="RoundedYarnSpinnerCanvasLayer" index="3"] -fadeTime = 2.0 +fadeTime = 0.2 [node name="ColorRect" type="ColorRect" parent="RoundedYarnSpinnerCanvasLayer"] z_index = -6 diff --git a/Samples/SampleEntryPoint.cs b/Samples/SampleEntryPoint.cs index b46f13f..86a6d38 100644 --- a/Samples/SampleEntryPoint.cs +++ b/Samples/SampleEntryPoint.cs @@ -14,6 +14,7 @@ public partial class SampleEntryPoint : CanvasLayer [Export] private Button _pausingTypewriterButton; [Export] private Button _roundedViewsButton; [Export] private Button _sqliteButton; + [Export] private Button _gdscriptButton; /// /// Resource path to the packed scene of entry point scene @@ -38,6 +39,8 @@ public override void _Ready() _roundedViewsButton.Pressed += () => LoadSample( "res://Samples/RoundedViews/RoundedSample.tscn" ); + _gdscriptButton.Pressed += () => LoadSample + ("res://Samples/GDScriptIntegration/GDScriptIntegrationSample.tscn"); _sqliteButton.Pressed += () => LoadSample("res://Samples/SQLiteVariableStorage/SQLSample.tscn"); _spaceButton.GrabFocus(); } @@ -55,7 +58,7 @@ public void LoadSample(string samplePath) /// public static void Return() { - var root = ((SceneTree) Engine.GetMainLoop()).Root; + var root = ((SceneTree)Engine.GetMainLoop()).Root; var nodesToFree = new List(); for (var i = 0; i < root.GetChildCount(); i++) { diff --git a/Samples/SampleEntryPoint.tscn b/Samples/SampleEntryPoint.tscn index af34a56..c1f1633 100644 --- a/Samples/SampleEntryPoint.tscn +++ b/Samples/SampleEntryPoint.tscn @@ -4,7 +4,7 @@ [ext_resource type="Theme" uid="uid://b2mp0b1wvnu8s" path="res://Samples/sample_default_theme.tres" id="2"] [ext_resource type="Texture2D" uid="uid://pbrr5yyepbx8" path="res://addons/YarnSpinner-Godot/Editor/Icons/YarnSpinnerLogo.png" id="3_bcudv"] -[node name="SampleEntryPoint" type="CanvasLayer" node_paths=PackedStringArray("_spaceButton", "_visualNovelButton", "_markupPaletteButton", "_pausingTypewriterButton", "_roundedViewsButton", "_sqliteButton")] +[node name="SampleEntryPoint" type="CanvasLayer" node_paths=PackedStringArray("_spaceButton", "_visualNovelButton", "_markupPaletteButton", "_pausingTypewriterButton", "_roundedViewsButton", "_sqliteButton", "_gdscriptButton")] script = ExtResource("1") _spaceButton = NodePath("HBoxContainer/VBoxContainer/Space") _visualNovelButton = NodePath("HBoxContainer/VBoxContainer2/Visual Novel") @@ -12,6 +12,7 @@ _markupPaletteButton = NodePath("HBoxContainer/VBoxContainer3/MarkupPalette") _pausingTypewriterButton = NodePath("HBoxContainer/VBoxContainer4/PausingTypewriter") _roundedViewsButton = NodePath("HBoxContainer/VBoxContainer5/RoundedViews") _sqliteButton = NodePath("HBoxContainer/VBoxContainer6/SQLite") +_gdscriptButton = NodePath("HBoxContainer/VBoxContainer7/GDScriptIntegration") [node name="ColorRect" type="ColorRect" parent="."] anchors_preset = 15 @@ -92,7 +93,8 @@ theme = ExtResource("2") theme_override_colors/font_outline_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 5 theme_override_font_sizes/font_size = 27 -text = "Visual Novel" +text = "Visual + Novel" [node name="features" type="RichTextLabel" parent="HBoxContainer/VBoxContainer2"] layout_mode = 2 @@ -115,7 +117,8 @@ theme = ExtResource("2") theme_override_colors/font_outline_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 5 theme_override_font_sizes/font_size = 27 -text = "Markup Palette" +text = "Markup +Palette" [node name="features" type="RichTextLabel" parent="HBoxContainer/VBoxContainer3"] layout_mode = 2 @@ -136,7 +139,8 @@ theme = ExtResource("2") theme_override_colors/font_outline_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 5 theme_override_font_sizes/font_size = 27 -text = "Pausing Typewriter" +text = "Pausing +Typewriter" [node name="features" type="RichTextLabel" parent="HBoxContainer/VBoxContainer4"] layout_mode = 2 @@ -178,7 +182,8 @@ theme = ExtResource("2") theme_override_colors/font_outline_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 5 theme_override_font_sizes/font_size = 27 -text = "SQLite Variable +text = "SQLite +Variable Storage" [node name="features" type="RichTextLabel" parent="HBoxContainer/VBoxContainer6"] @@ -186,5 +191,27 @@ layout_mode = 2 size_flags_vertical = 3 theme_override_font_sizes/normal_font_size = 28 bbcode_enabled = true -text = "Custom variable storage script that saves variables in a SQLite database" +text = "Custom variable storage script storing variables in a SQLite database" +scroll_active = false + +[node name="VBoxContainer7" type="VBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="GDScriptIntegration" type="Button" parent="HBoxContainer/VBoxContainer7"] +custom_minimum_size = Vector2(180, 100) +layout_mode = 2 +theme = ExtResource("2") +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 5 +theme_override_font_sizes/font_size = 27 +text = "GDScript" + +[node name="features" type="RichTextLabel" parent="HBoxContainer/VBoxContainer7"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/normal_font_size = 28 +bbcode_enabled = true +text = "Demonstrates the use of GDScript with this plugin +(Godot C# Engine still required)" scroll_active = false diff --git a/YarnSpinner-Godot.csproj b/YarnSpinner-Godot.csproj index 459d2d5..900a982 100644 --- a/YarnSpinner-Godot.csproj +++ b/YarnSpinner-Godot.csproj @@ -1,4 +1,4 @@ - + net6.0 true @@ -12,6 +12,7 @@ + diff --git a/addons/YarnSpinner-Godot/Runtime/Commands/ActionManager.cs b/addons/YarnSpinner-Godot/Runtime/Commands/ActionManager.cs index da12eb4..6772a14 100644 --- a/addons/YarnSpinner-Godot/Runtime/Commands/ActionManager.cs +++ b/addons/YarnSpinner-Godot/Runtime/Commands/ActionManager.cs @@ -151,6 +151,10 @@ private static Converter CreateConverter(MethodInfo method, ParameterInfo parame { try { + if (targetType == typeof(Variant)) + { + return Variant.From(arg); + } return Convert.ChangeType(arg, targetType, CultureInfo.InvariantCulture); } catch (Exception e) diff --git a/addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs b/addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs index e8ac61d..11afe80 100644 --- a/addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs +++ b/addons/YarnSpinner-Godot/Runtime/DialogueRunner.cs @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. using Godot.Collections; using Yarn; using Node = Godot.Node; +using Array = Godot.Collections.Array; namespace YarnSpinnerGodot; @@ -436,6 +437,160 @@ public void Stop() #region CommandsAndFunctions + /// + /// Cast a list of arguments from a .yarn script to the type that the handler + /// expects based on type hinting. Used to cross back over from C# to GDScript + /// + /// List of Variant.Types in order of the arguments + /// from the caller's command or function handler + /// The name of the function or command + /// being registered, for error logging purposes + /// params array of arguments to cast to their expected types + /// + /// + private static Array CastToExpectedTypes(List argTypes, + string commandOrFunctionName, + params Variant[] args) + { + var castArgs = new Array(); + var argIndex = 0; + foreach (var arg in args) + { + var argType = argTypes[argIndex]; + var castArg = argType switch + { + Variant.Type.Bool => arg.AsBool(), + Variant.Type.Int => arg.AsInt32(), + Variant.Type.Float => arg.AsSingle(), + Variant.Type.String => arg.AsString(), + Variant.Type.Callable => arg.AsCallable(), + // if no type hint is given, assume string type + Variant.Type.Nil => arg.AsString(), + _ => Variant.From(null), + }; + castArgs.Add(castArg); + if (castArg.Obj == null) + { + GD.PushError( + $"Argument for the handler for '{commandOrFunctionName}'" + + $" at index {argIndex} has unexpected type {argType}"); + } + + argIndex++; + } + + return castArgs; + } + + /// + /// Add a command handler using a Callable rather than a C# delegate. + /// Mostly useful for integrating with GDScript. + /// If the last argument to your handler is a Callable, your command will be + /// considered an async blocking command. When the work for your command is done, + /// call the Callable that the DialogueRunner will pass to your handler. Then + /// the dialogue will continue. + /// + /// Callables are only supported as the last argument to your handler for the + /// purpose of making your command blocking. + /// + /// The name of the command. + /// The Callable for the that + /// will be invoked when the command is called. + public void AddCommandHandlerCallable(string commandName, Callable handler) + { + if (!IsInstanceValid(handler.Target)) + { + GD.PushError( + $"Callable provided to {nameof(AddCommandHandlerCallable)} is invalid. " + + "Could the Node associated with the callable have been freed?"); + return; + } + + var methodInfo = handler.Target.GetMethodList().Where(dict => + dict["name"].AsString().Equals(handler.Method.ToString())).ToList(); + + if (methodInfo.Count == 0) + { + GD.PushError(); + return; + } + + var argsCount = methodInfo[0]["args"].AsGodotArray().Count; + var argTypes = methodInfo[0]["args"].AsGodotArray().ToList() + .ConvertAll((argDictionary) => + (Variant.Type) argDictionary.AsGodotDictionary()["type"].AsInt32()); + var invalidTargetMsg = + $"Handler node for {commandName} is invalid. Was it freed?"; + + + async Task GenerateCommandHandler(params Variant[] handlerArgs) + { + if (!IsInstanceValid(handler.Target)) + { + GD.PushError(invalidTargetMsg); + return; + } + + // how many milliseconds to wait between completion checks for async commands + const int completePollMs = 40; + var castArgs = CastToExpectedTypes(argTypes, commandName, handlerArgs); + + var current = handler.Call(castArgs.ToArray()); + if (current.As().GetClass() == "GDScriptFunctionState") + { + // callable is from GDScript with await statements + await ((SceneTree) Engine.GetMainLoop()).ToSignal(current.AsGodotObject(), "completed"); + } + } + + switch (argsCount) + { + case 0: + AddCommandHandler(commandName, + async Task () => await GenerateCommandHandler()); + break; + case 1: + AddCommandHandler(commandName, + async Task (Variant arg0) => + await GenerateCommandHandler(arg0)); + break; + case 2: + AddCommandHandler(commandName, + async Task (Variant arg0, Variant arg1) => + await GenerateCommandHandler(arg0, arg1)); + break; + case 3: + AddCommandHandler(commandName, + async Task (Variant arg0, Variant arg1, Variant arg2) => + await GenerateCommandHandler(arg0, arg1, arg2)); + break; + case 4: + AddCommandHandler(commandName, + async Task (Variant arg0, Variant arg1, Variant arg2, + Variant arg3) => + await GenerateCommandHandler(arg0, arg1, arg2, arg3)); + break; + case 5: + AddCommandHandler(commandName, + async Task (Variant arg0, Variant arg1, Variant arg2, + Variant arg3, Variant arg4) => + await GenerateCommandHandler(arg0, arg1, arg2, arg3, arg4)); + break; + case 6: + AddCommandHandler(commandName, + async Task (Variant arg0, Variant arg1, Variant arg2, + Variant arg3, Variant arg4, Variant arg5) => + await GenerateCommandHandler(arg0, arg1, arg2, + arg3, arg4, arg5)); + break; + default: + GD.PushError($"You have specified a command handler with too " + + $"many arguments at {argsCount}. The maximum supported " + + $"number of arguments to a command handler is 6."); + break; + } + } + /// /// Adds a command handler. Dialogue will pause execution after the /// command is called. diff --git a/addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs b/addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs index 73f63e1..9e97043 100644 --- a/addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs +++ b/addons/YarnSpinner-Godot/Runtime/InMemoryVariableStorage.cs @@ -225,6 +225,39 @@ public override bool TryGetValue(string variableName, out T result) } } + /// + /// Example method to get a variable as a variant, so that this method can be called from GDScript. + /// + public Variant GetVariantValue(string variableName) + { + if (variables.TryGetValue(variableName, out var variable)) + { + var type = variableTypes[variableName]; + if (type == typeof(float)) + { + return Variant.From(System.Convert.ToSingle(variable)); + } + + if (type == typeof(string)) + { + return Variant.From(System.Convert.ToString(variable)); + } + + if (type == typeof(bool)) + { + return Variant.From(System.Convert.ToBoolean(variable)); + } + + GD.Print($"{variableName} is not a valid type"); + } + else + { + GD.Print($"Could not find variable: {variableName}"); + } + + return new Variant(); + } + /// /// Removes all variables from storage. /// diff --git a/addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs b/addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs index c3aa849..91b42c7 100644 --- a/addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs +++ b/addons/YarnSpinner-Godot/Runtime/LineProviders/TextLineProvider.cs @@ -1,4 +1,5 @@ #nullable disable +using System; using System.Collections.Generic; using Godot; @@ -10,13 +11,12 @@ public partial class TextLineProvider : LineProviderBehaviour /// Specifies the language code to use for text content /// for this . /// - [Language] - [Export] public string textLanguageCode = System.Globalization.CultureInfo.CurrentCulture.Name; + [Language] [Export] public string textLanguageCode = System.Globalization.CultureInfo.CurrentCulture.Name; public override LocalizedLine GetLocalizedLine(Yarn.Line line) { string text; - // By default this provider will treat "en" as matching "en-UK", "en-US" etc. You can + // By default, this provider will treat "en" as matching "en-UK", "en-US" etc. You can // remap language codes how you like if you don't want this behavior if (textLanguageCode.ToLower().StartsWith(YarnProject.baseLocalization.LocaleCode.ToLower())) { diff --git a/addons/YarnSpinner-Godot/Runtime/Localization.cs b/addons/YarnSpinner-Godot/Runtime/Localization.cs index ff7080a..986c06b 100644 --- a/addons/YarnSpinner-Godot/Runtime/Localization.cs +++ b/addons/YarnSpinner-Godot/Runtime/Localization.cs @@ -17,6 +17,7 @@ namespace YarnSpinnerGodot; /// https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html#doc-locales /// [Tool] +[GlobalClass] public partial class Localization : Resource { [Export] diff --git a/addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs b/addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs new file mode 100644 index 0000000..cd4eeb6 --- /dev/null +++ b/addons/YarnSpinner-Godot/Runtime/Views/GDScriptViewAdapter.cs @@ -0,0 +1,232 @@ +using System; +using Godot; +using Yarn.Markup; + +namespace YarnSpinnerGodot; + +/// +/// Wrapper which allows you to implement a YarnSpinner DialogueViewBase via +/// GDScript by calling snake_case versions of each method. +/// Add this script to a node for each GDScript view you want to implement, +/// then assign GDScriptView in the inspector to the node with your view script +/// written in GDScript. +/// +/// +/// Note: You still have to use the version of Godot which supports C# in order to use +/// this plugin. +/// +[GlobalClass] +public partial class GDScriptViewAdapter : Node, DialogueViewBase +{ + /// + /// Assign this node to the node with the GDScript implementing your view attached. + /// + [Export] public Node GDScriptView; + + public Action requestInterrupt { get; set; } + + /// + /// Implement a GDScript method dialogue_started() -> void + public void DialogueStarted() + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "dialogue_started"; + if (GDScriptView.HasMethod(gdScriptName)) + { + GDScriptView.Call(gdScriptName); + } + } + + /// + /// Implement a GDScript method run_line(line: dict, on_dialogue_line_finished: Callable) -> void + public void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished) + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "run_line"; + if (!GDScriptView.HasMethod(gdScriptName)) + { + // The default implementation does nothing, and immediately calls + // onDialogueLineFinished. + onDialogueLineFinished?.Invoke(); + } + else + { + GDScriptView.Call(gdScriptName, LocalizedLineToDict(dialogueLine), + Callable.From(onDialogueLineFinished)); + } + } + + /// + /// Convert a LocalizedLine to a Godot Dictionary that is more accessible from GDScript. + /// # example: + /// {"metadata":["my_metadata"], + /// "text": + /// { + /// "attributes":[ + /// { "length":8,"name":"fx","position":20,"properties":[{"type":"wave"}]}, + /// { "length":6,"name":"character","position":0,"properties":[{"name":"Gary"}]} + /// ], + /// "text":"Gary: So, can I use GDScript with YarnSpinner?", + /// "text_without_character_name":"So, can I use GDScript with YarnSpinner?" + /// } + /// } + /// + /// + /// + public static Godot.Collections.Dictionary LocalizedLineToDict(LocalizedLine dialogueLine) + { + var dialogueLineDict = new Godot.Collections.Dictionary(); + var metadataArray = new Godot.Collections.Array(); + metadataArray.AddRange(dialogueLine.Metadata ?? Array.Empty()); + dialogueLineDict["metadata"] = metadataArray; + + var textDict = new Godot.Collections.Dictionary(); + textDict["text"] = dialogueLine.Text.Text; + textDict["text_without_character_name"] = dialogueLine.TextWithoutCharacterName.Text; + var attributesList = new Godot.Collections.Array(); + foreach (var attribute in dialogueLine.Text.Attributes) + { + var attributeDict = new Godot.Collections.Dictionary(); + attributeDict["name"] = attribute.Name; + attributeDict["length"] = attribute.Length; + attributeDict["position"] = attribute.Position; + var propertiesList = new Godot.Collections.Array(); + foreach (var property in attribute.Properties) + { + var propertyDict = new Godot.Collections.Dictionary(); + var castValue = property.Value.Type switch + { + MarkupValueType.Integer => Variant.From(property.Value.IntegerValue), + MarkupValueType.Float => Variant.From(property.Value.FloatValue), + MarkupValueType.String => Variant.From(property.Value.StringValue), + MarkupValueType.Bool => Variant.From(property.Value.BoolValue), + _ => new Variant(), + }; + + propertyDict[property.Key] = castValue; + propertiesList.Add(propertyDict); + } + + attributeDict["properties"] = propertiesList; + attributesList.Add(attributeDict); + } + + textDict["attributes"] = attributesList; + dialogueLineDict["text"] = textDict; + return dialogueLineDict; + } + + /// + /// Implement a GDScript method interrupt_line(line: dict, on_dialogue_line_finished: Callable) + public void InterruptLine(LocalizedLine dialogueLine, Action onDialogueLineFinished) + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "interrupt_line"; + if (!GDScriptView.HasMethod(gdScriptName)) + { + // the default implementation does nothing + onDialogueLineFinished?.Invoke(); + } + else + { + GDScriptView.Call(gdScriptName, LocalizedLineToDict(dialogueLine), + Callable.From(onDialogueLineFinished)); + } + } + + /// + /// Implement a GDScript method dismiss_line(on_dismissal_complete: Callable) -> void + public void DismissLine(Action onDismissalComplete) + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "dismiss_line"; + if (!GDScriptView.HasMethod(gdScriptName)) + { + // The default implementation does nothing, and immediately calls + // onDialogueLineFinished. + onDismissalComplete?.Invoke(); + } + else + { + GDScriptView.Call(gdScriptName, Callable.From(onDismissalComplete)); + } + } + + /// + /// Implement a GDScript method run_options(options: Array, on_option_selected: Callable (single int parameter)) -> void + public void RunOptions(DialogueOption[] dialogueOptions, + Action onOptionSelected) + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "run_options"; + if (!GDScriptView.HasMethod(gdScriptName)) + { + return; + } + + var dialogueOptionsList = new Godot.Collections.Array(); + foreach (var option in dialogueOptions) + { + var optionDict = new Godot.Collections.Dictionary(); + optionDict["dialogue_option_id"] = option.DialogueOptionID; + optionDict["text_id"] = option.TextID; + optionDict["line"] = LocalizedLineToDict(option.Line); + optionDict["is_available"] = option.IsAvailable; + dialogueOptionsList.Add(optionDict); + } + + GDScriptView.Call(gdScriptName, dialogueOptionsList, Callable.From(onOptionSelected)); + } + + /// + /// Implement a GDScript method dialogue_complete() -> void + public void DialogueComplete() + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "dialogue_complete"; + if (GDScriptView.HasMethod(gdScriptName)) + { + GDScriptView.Call(gdScriptName); + } + } + + /// + /// Implement a GDScript method user_requested_view_advancement() -> void + public void UserRequestedViewAdvancement() + { + if (!IsInstanceValid(GDScriptView)) + { + return; + } + + const string gdScriptName = "user_requested_view_advancement"; + if (GDScriptView.HasMethod(gdScriptName)) + { + GDScriptView.Call(gdScriptName); + } + } +} \ No newline at end of file diff --git a/addons/YarnSpinner-Godot/Scenes/DefaultDialogueSystem.tscn b/addons/YarnSpinner-Godot/Scenes/DefaultDialogueSystem.tscn index ffaac70..631ec24 100644 --- a/addons/YarnSpinner-Godot/Scenes/DefaultDialogueSystem.tscn +++ b/addons/YarnSpinner-Godot/Scenes/DefaultDialogueSystem.tscn @@ -62,11 +62,11 @@ offset_right = 640.0 offset_bottom = 193.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_font_sizes/normal_font_size = 36 -theme_override_font_sizes/bold_font_size = 36 -theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 bbcode_enabled = true text = "The dialogue text should appear here!" @@ -97,11 +97,11 @@ offset_right = 279.0 offset_bottom = -362.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_font_sizes/normal_font_size = 36 -theme_override_font_sizes/bold_font_size = 36 -theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 bbcode_enabled = true text = "Character Name" @@ -178,11 +178,11 @@ offset_top = 100.0 offset_right = 279.0 offset_bottom = 185.0 grow_horizontal = 2 -theme_override_font_sizes/normal_font_size = 36 -theme_override_font_sizes/bold_font_size = 36 -theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 bbcode_enabled = true text = "The last line text goes here" scroll_active = false @@ -212,11 +212,11 @@ offset_right = 278.0 offset_bottom = -363.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_font_sizes/normal_font_size = 36 -theme_override_font_sizes/bold_font_size = 36 -theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/bold_italics_font_size = 36 +theme_override_font_sizes/italics_font_size = 36 theme_override_font_sizes/mono_font_size = 36 +theme_override_font_sizes/normal_font_size = 36 +theme_override_font_sizes/bold_font_size = 36 bbcode_enabled = true text = "Character Name" diff --git a/addons/YarnSpinner-Godot/YarnSpinner-Godot.props b/addons/YarnSpinner-Godot/YarnSpinner-Godot.props index cae54e6..16bfb14 100644 --- a/addons/YarnSpinner-Godot/YarnSpinner-Godot.props +++ b/addons/YarnSpinner-Godot/YarnSpinner-Godot.props @@ -11,7 +11,7 @@ - + addons\YarnSpinner-Godot\Runtime\DLLs\YarnSpinner.dll diff --git a/addons/YarnSpinner-Godot/plugin.cfg b/addons/YarnSpinner-Godot/plugin.cfg index 3601080..41594de 100644 --- a/addons/YarnSpinner-Godot/plugin.cfg +++ b/addons/YarnSpinner-Godot/plugin.cfg @@ -2,6 +2,6 @@ name="YarnSpinner-Godot" description="Yarn language based dialogue system plugin for Godot" -author="dogboydog" -version="0.2.13" +author="Decrepit Games" +version="0.2.14" script="YarnSpinnerPlugin.cs" diff --git a/project.godot b/project.godot index 4643f36..965ec5d 100644 --- a/project.godot +++ b/project.godot @@ -13,7 +13,7 @@ config_version=5 config/name="YarnSpinner-Godot" config/description="Samples for YarnSpinner-Godot dialogue system plugin" run/main_scene="res://Samples/SampleEntryPoint.tscn" -config/features=PackedStringArray("4.2", "C#") +config/features=PackedStringArray("4.3", "C#") boot_splash/bg_color=Color(0.0313726, 0.0705882, 0.0313726, 1) [display] @@ -48,53 +48,53 @@ common/drop_mouse_on_gui_input_disabled=true ui_left={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) ] } ui_right={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) ] } ui_up={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) ] } ui_down={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) ] } left={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } right={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) ] } interact={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] }