├── .gitignore
├── addons
└── EXP-System-Dialog
│ ├── Images
│ ├── de.png
│ ├── se.png
│ └── icon.png
│ ├── Resource_NodeTemplate
│ └── EXP_NodeTemplate.gd
│ ├── plugin.cfg
│ ├── Resource_BakedStory
│ └── EXP_BakedStory.gd
│ ├── Resource_EditorStory
│ └── EXP_EditorStory.gd
│ ├── Dialog Editor
│ ├── Nodes
│ │ └── Line
│ │ │ ├── icon_distraction_free.svg
│ │ │ ├── line_node.gd
│ │ │ └── Line_Node.tscn
│ ├── Editor
│ │ ├── editor.gd
│ │ └── Editor.tscn
│ ├── Dialog_Editor.tscn
│ └── dialog_editor.gd
│ ├── Story Editor
│ ├── Rename Record Box
│ │ ├── rename_record_box.gd
│ │ └── Rename_Record_Box.tscn
│ ├── Dialog Record
│ │ ├── dialog_record.gd
│ │ └── Dialog_Record.tscn
│ ├── Story_Editor.tscn
│ └── story_editor.gd
│ ├── Reference_StoryReader
│ └── EXP_StoryReader.gd
│ └── plugin.gd
├── Dialog-System-Example
├── fonts
│ ├── Cave-Story.ttf
│ ├── body_font.tres
│ ├── option_font.tres
│ └── speaker_font.tres
├── textures
│ ├── characters
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ └── 5.png
│ └── menu_section_9patch.png
├── scenes
│ ├── Option.gd
│ └── Option.tscn
├── scripts
│ ├── simulatedregistry.gd
│ └── dialog_player.gd
├── stories
│ ├── Example_Story_Temp_Baked.tres
│ └── Example_Story_Temp.tres
└── Dialog_Player.tscn
├── project.godot
├── LICENSE.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .import*
2 | **/*.import
3 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Images/de.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/addons/EXP-System-Dialog/Images/de.png
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Images/se.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/addons/EXP-System-Dialog/Images/se.png
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/addons/EXP-System-Dialog/Images/icon.png
--------------------------------------------------------------------------------
/Dialog-System-Example/fonts/Cave-Story.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/fonts/Cave-Story.ttf
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/characters/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/characters/1.png
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/characters/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/characters/2.png
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/characters/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/characters/3.png
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/characters/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/characters/4.png
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/characters/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/characters/5.png
--------------------------------------------------------------------------------
/Dialog-System-Example/textures/menu_section_9patch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EXPWorlds/Godot-Dialog-System/HEAD/Dialog-System-Example/textures/menu_section_9patch.png
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Resource_NodeTemplate/EXP_NodeTemplate.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Resource
3 |
4 | var TYPE = "EXP_Dialog_Node_Template_editor"
5 |
6 | export(String) var template
7 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="EXP_Dialog"
4 | description="EXPWorld's dialog technology."
5 | author="David Lipps @ EXPWorlds"
6 | version="1.1.0"
7 | script="plugin.gd"
8 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Resource_BakedStory/EXP_BakedStory.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Resource
3 |
4 | export(String) var TYPE = "EXP_Baked_Story"
5 | export(Dictionary) var story : Dictionary
6 | export(Dictionary) var names : Dictionary
7 |
--------------------------------------------------------------------------------
/Dialog-System-Example/fonts/body_font.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="DynamicFont" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://Dialog-System-Example/fonts/Cave-Story.ttf" type="DynamicFontData" id=1]
4 |
5 | [resource]
6 | size = 35
7 | font_data = ExtResource( 1 )
8 |
--------------------------------------------------------------------------------
/Dialog-System-Example/fonts/option_font.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="DynamicFont" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://Dialog-System-Example/fonts/Cave-Story.ttf" type="DynamicFontData" id=1]
4 |
5 | [resource]
6 | size = 42
7 | font_data = ExtResource( 1 )
8 |
--------------------------------------------------------------------------------
/Dialog-System-Example/fonts/speaker_font.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="DynamicFont" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://Dialog-System-Example/fonts/Cave-Story.ttf" type="DynamicFontData" id=1]
4 |
5 | [resource]
6 | size = 39
7 | font_data = ExtResource( 1 )
8 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Resource_EditorStory/EXP_EditorStory.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Resource
3 |
4 | export(String) var TYPE = "EXP_Story_editor"
5 |
6 | export(Dictionary) var names : Dictionary
7 | export(Dictionary) var story : Dictionary
8 | export(Array) var available_dids : Array
9 | export(Array) var groups : Array
10 |
--------------------------------------------------------------------------------
/Dialog-System-Example/scenes/Option.gd:
--------------------------------------------------------------------------------
1 | extends NinePatchRect
2 |
3 | signal clicked(slot)
4 |
5 | onready var _Button = $Button
6 |
7 | var slot
8 |
9 | # Callback Methods
10 |
11 | func _on_Button_pressed():
12 | emit_signal("clicked", slot)
13 |
14 | # Public
15 |
16 | func set_text(new_text : String):
17 | _Button.text = new_text
18 |
19 |
--------------------------------------------------------------------------------
/Dialog-System-Example/scripts/simulatedregistry.gd:
--------------------------------------------------------------------------------
1 | extends Node
2 |
3 | var registry = {"Test": "*this*",
4 | "PLAYER/LEVEL": 99,
5 | "PI": PI,
6 | "DATE": OS.get_datetime(),
7 | "SYSTEM": OS.get_name()}
8 |
9 | # Public Methods
10 |
11 | func lookup(name : String):
12 | if registry.has(name):
13 | return registry[name]
14 | else:
15 | return ""
16 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/icon_distraction_free.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Editor/editor.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends WindowDialog
3 |
4 | onready var _Text_Editor = self.get_node("VBoxContainer/TextEdit")
5 |
6 | var _Target_Node
7 |
8 | #Public Methods
9 |
10 | func set_target_node(node):
11 | self._Target_Node = node
12 | self._Text_Editor.text = node.get_text()
13 |
14 | #Callback Methods
15 |
16 | func _on_OK_BTN_pressed():
17 | self.visible = false
18 |
19 |
20 | func _on_TextEdit_text_changed():
21 | self._Target_Node.set_text(self._Text_Editor.text)
22 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=4
10 |
11 | _global_script_classes=[ ]
12 | _global_script_class_icons={
13 |
14 | }
15 |
16 | [application]
17 |
18 | config/name="EXP-Dialog-System"
19 | config/icon="res://addons/EXP-System-Dialog/Images/icon.png"
20 |
21 | [display]
22 |
23 | window/size/width=1280
24 | window/size/height=720
25 |
26 | [editor_plugins]
27 |
28 | enabled=PoolStringArray( "EXP-System-Dialog" )
29 |
30 | [locale]
31 |
32 | translation_remaps={
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/Rename Record Box/rename_record_box.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends WindowDialog
3 |
4 | signal rename_BTN_pressed(text)
5 |
6 | onready var _Name_LineEdit = self.get_node("MarginContainer/VBoxContainer/Name_LineEdit")
7 |
8 | var _Target_Record = null
9 |
10 | #Public Methods
11 |
12 | func get_target_record():
13 | return self._Target_Record
14 |
15 |
16 | func set_target_record(record):
17 | self._Target_Record = record
18 | self._Name_LineEdit.text = record.get_record_name()
19 |
20 | #Callback Methods
21 |
22 | func _on_Cancel_BTN_pressed():
23 | self.visible = false
24 |
25 |
26 | func _on_Rename_BTN_pressed():
27 | self.visible = false
28 | self.emit_signal("rename_BTN_pressed", self._Name_LineEdit.text)
29 |
30 |
31 | func _on_Name_LineEdit_text_entered(new_text):
32 | self._on_Rename_BTN_pressed()
33 |
--------------------------------------------------------------------------------
/Dialog-System-Example/scenes/Option.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=4 format=2]
2 |
3 | [ext_resource path="res://Dialog-System-Example/fonts/option_font.tres" type="DynamicFont" id=1]
4 | [ext_resource path="res://Dialog-System-Example/textures/menu_section_9patch.png" type="Texture" id=2]
5 | [ext_resource path="res://Dialog-System-Example/scenes/Option.gd" type="Script" id=3]
6 |
7 | [node name="Option" type="NinePatchRect"]
8 | anchor_top = 1.0
9 | anchor_right = 1.0
10 | anchor_bottom = 1.0
11 | grow_vertical = 0
12 | rect_min_size = Vector2( 0, 48 )
13 | size_flags_horizontal = 3
14 | texture = ExtResource( 2 )
15 | patch_margin_left = 3
16 | patch_margin_top = 3
17 | patch_margin_right = 3
18 | patch_margin_bottom = 3
19 | script = ExtResource( 3 )
20 | __meta__ = {
21 | "_edit_use_anchors_": false
22 | }
23 |
24 | [node name="Button" type="Button" parent="."]
25 | anchor_right = 1.0
26 | anchor_bottom = 1.0
27 | custom_fonts/font = ExtResource( 1 )
28 | text = "This is a test option."
29 | flat = true
30 | [connection signal="pressed" from="Button" to="." method="_on_Button_pressed"]
31 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 David E Lipps
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Editor/Editor.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Dialog Editor/Editor/editor.gd" type="Script" id=1]
4 |
5 | [node name="WindowDialog" type="WindowDialog"]
6 | anchor_left = 0.5
7 | anchor_top = 0.5
8 | anchor_right = 0.5
9 | anchor_bottom = 0.5
10 | margin_left = -540.0
11 | margin_top = -384.0
12 | margin_right = 540.0
13 | margin_bottom = 384.0
14 | window_title = "Node Editor"
15 | resizable = true
16 | script = ExtResource( 1 )
17 | __meta__ = {
18 | "_edit_use_anchors_": false
19 | }
20 |
21 | [node name="VBoxContainer" type="VBoxContainer" parent="."]
22 | anchor_right = 1.0
23 | anchor_bottom = 1.0
24 | margin_left = 13.0
25 | margin_top = 13.0
26 | margin_right = -13.0
27 | margin_bottom = -13.0
28 | __meta__ = {
29 | "_edit_use_anchors_": false
30 | }
31 |
32 | [node name="TextEdit" type="TextEdit" parent="VBoxContainer"]
33 | margin_right = 1054.0
34 | margin_bottom = 718.0
35 | size_flags_horizontal = 3
36 | size_flags_vertical = 3
37 | smooth_scrolling = true
38 | __meta__ = {
39 | "_edit_use_anchors_": false
40 | }
41 |
42 | [node name="OK_BTN" type="Button" parent="VBoxContainer"]
43 | margin_left = 503.0
44 | margin_top = 722.0
45 | margin_right = 550.0
46 | margin_bottom = 742.0
47 | hint_tooltip = "Close the node editor."
48 | size_flags_horizontal = 4
49 | size_flags_vertical = 0
50 | text = "Close"
51 | [connection signal="text_changed" from="VBoxContainer/TextEdit" to="." method="_on_TextEdit_text_changed"]
52 | [connection signal="pressed" from="VBoxContainer/OK_BTN" to="." method="_on_OK_BTN_pressed"]
53 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Reference_StoryReader/EXP_StoryReader.gd:
--------------------------------------------------------------------------------
1 | extends Reference
2 |
3 | var _story : Dictionary = {}
4 | var _names : Dictionary = {}
5 |
6 |
7 | func get_dids() -> Array:
8 | return self._story.keys()
9 |
10 |
11 | func get_did_via_record_name(name : String) -> int:
12 | assert(self._names.has(name))
13 | return self._names[name]
14 |
15 |
16 | func get_nid_from_slot(did : int, nid : int, slot : int) -> int:
17 | assert(self.has_did(did))
18 | assert(self.has_nid(did, nid))
19 | assert(self.has_slot(did, nid, slot))
20 | return self._story[did]["nodes"][nid]["links"][slot]
21 |
22 |
23 | func get_nid_via_exact_text(did : int, query : String) -> int:
24 | assert(self.has_did(did))
25 |
26 | for nid in self._story[did]["nodes"].keys():
27 | if self._story[did]["nodes"][nid]["text"] == query:
28 | return nid
29 | return -1
30 |
31 |
32 | func get_nids(did : int) -> Array:
33 | assert(self.has_did(did))
34 | return self._story[did]["nodes"].keys()
35 |
36 |
37 | func get_slot_count(did : int, nid : int) -> int:
38 | assert(self.has_did(did))
39 | assert(self.has_nid(did, nid))
40 | return self._story[did]["nodes"][nid]["links"].size()
41 |
42 |
43 | func get_slots(did : int, nid : int) -> Array:
44 | assert(self.has_did(did))
45 | assert(self.has_nid(did, nid))
46 | return self._story[did]["nodes"][nid]["links"].keys()
47 |
48 |
49 | func get_text(did : int, nid : int) -> String:
50 | assert(self.has_did(did))
51 | assert(self.has_nid(did, nid))
52 | return self._story[did]["nodes"][nid]["text"]
53 |
54 |
55 | func has_did(did : int) -> bool:
56 | return self._story.has(did)
57 |
58 |
59 | func has_nid(did : int, nid : int) -> bool:
60 | assert(self.has_did(did))
61 | return self._story[did]["nodes"].has(nid)
62 |
63 |
64 | func has_record_name(name : String) -> bool:
65 | return self._names.has(name)
66 |
67 |
68 | func has_slot(did: int, nid : int, slot : int) -> bool:
69 | assert(self.has_did(did))
70 | assert(self.has_nid(did, nid))
71 | return self._story[did]["nodes"][nid]["links"].has(slot)
72 |
73 |
74 | func read(file):
75 | if not "TYPE" in file:
76 | print_debug("Story reader failed to open file: ", str(file.filename))
77 | return
78 | if not file.TYPE == "EXP_Baked_Story":
79 | print_debug("Story reader failed to open file: ", str(file.filename))
80 | return
81 |
82 | self._story = file.story
83 | self._names = file.names
84 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/Dialog Record/dialog_record.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Control
3 |
4 | signal changed_human_readable_text(did, text)
5 | signal checked(this)
6 | signal edit_pressed(did)
7 | signal rename_pressed(this)
8 | signal unchecked(this)
9 |
10 | onready var _DID_LBL = self.get_node("ColorRect/HBoxContainer/DID_LBL")
11 | onready var _Human_Readable_LineEdit = self.get_node("ColorRect/HBoxContainer/Human_Readable_LineEdit")
12 | onready var _Group_List = self.get_node("ColorRect/HBoxContainer/Group_BTN")
13 | onready var _Name_BTN = self.get_node("ColorRect/HBoxContainer/Name_BTN")
14 | onready var _Select_CheckBox = self.get_node("ColorRect/HBoxContainer/CheckBox")
15 |
16 | var _did : int = -1
17 | var _Story_Editor
18 |
19 | #Virtual Methods
20 |
21 | func _ready():
22 | self.update_human_readable_description("Human Readable Description")
23 |
24 | #Callback Methods
25 |
26 | func _on_CheckBox_toggled(button_pressed):
27 | if button_pressed:
28 | self.emit_signal("checked", self)
29 | else:
30 | self.emit_signal("unchecked", self)
31 |
32 |
33 | func _on_Edit_BTN_pressed():
34 | self.emit_signal("edit_pressed", self._did)
35 |
36 |
37 | func _on_Group_BTN_pressed():
38 | var groups = self._Story_Editor.dialog_get_groups(self._did)
39 | self._Group_List.clear()
40 | self._Group_List.text = "TAGS"
41 | for group in groups:
42 | self._Group_List.get_popup().add_item(group)
43 | for idx in range(self._Group_List.get_item_count()):
44 | self._Group_List.set_item_disabled(idx, true)
45 |
46 |
47 | func _on_Human_Readable_LineEdit_focus_exited():
48 | self._Human_Readable_LineEdit.deselect()
49 |
50 |
51 | func _on_Human_Readable_LineEdit_text_changed(new_text):
52 | self.emit_signal("changed_human_readable_text", self._did, new_text)
53 |
54 |
55 | func _on_Name_BTN_pressed():
56 | emit_signal("rename_pressed", self)
57 |
58 | #Public Methods
59 |
60 | func check():
61 | self._Select_CheckBox.pressed = true
62 |
63 |
64 | func get_did():
65 | return self._did
66 |
67 |
68 | func get_record_name():
69 | return self._Name_BTN.text
70 |
71 |
72 | func set_did(new_did : int):
73 | self._did = new_did
74 | self._DID_LBL.text = "DID: " + str(new_did)
75 |
76 |
77 | func set_record_name(rename : String):
78 | self._Name_BTN.text = rename
79 |
80 |
81 | func set_story_editor(editor):
82 | self._Story_Editor = editor
83 |
84 |
85 | func uncheck():
86 | self._Select_CheckBox.pressed = false
87 |
88 |
89 | func update_human_readable_description(new_text):
90 | self._Human_Readable_LineEdit.text = new_text
91 | self.emit_signal("changed_human_readable_text", self._did, new_text)
92 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Dialog_Editor.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Dialog Editor/dialog_editor.gd" type="Script" id=1]
4 |
5 | [node name="Dialog_Editor" type="Control"]
6 | anchor_right = 1.0
7 | anchor_bottom = 1.0
8 | rect_min_size = Vector2( 0, 256 )
9 | script = ExtResource( 1 )
10 | __meta__ = {
11 | "_edit_use_anchors_": false
12 | }
13 |
14 | [node name="VBoxContainer" type="VBoxContainer" parent="."]
15 | anchor_right = 1.0
16 | anchor_bottom = 1.0
17 | __meta__ = {
18 | "_edit_use_anchors_": false
19 | }
20 |
21 | [node name="VBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
22 | margin_right = 1024.0
23 | margin_bottom = 20.0
24 |
25 | [node name="Close_BTN" type="Button" parent="VBoxContainer/VBoxContainer"]
26 | margin_right = 47.0
27 | margin_bottom = 20.0
28 | hint_tooltip = "Close the Dialog Editor."
29 | text = "Close"
30 |
31 | [node name="Back_BTN" type="Button" parent="VBoxContainer/VBoxContainer"]
32 | margin_left = 51.0
33 | margin_right = 93.0
34 | margin_bottom = 20.0
35 | hint_tooltip = "Return to the Story Editor."
36 | text = "Back"
37 |
38 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/VBoxContainer"]
39 | margin_left = 97.0
40 | margin_right = 101.0
41 | margin_bottom = 20.0
42 |
43 | [node name="Add_Node_BTN" type="Button" parent="VBoxContainer/VBoxContainer"]
44 | margin_left = 105.0
45 | margin_right = 180.0
46 | margin_bottom = 20.0
47 | hint_tooltip = "Add a new node to the graph."
48 | text = "Add Node"
49 |
50 | [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/VBoxContainer"]
51 | margin_left = 184.0
52 | margin_right = 188.0
53 | margin_bottom = 20.0
54 |
55 | [node name="Human_Readable_LBL" type="Label" parent="VBoxContainer/VBoxContainer"]
56 | margin_left = 192.0
57 | margin_top = 3.0
58 | margin_right = 378.0
59 | margin_bottom = 17.0
60 | text = "Human Readable Description"
61 |
62 | [node name="GraphEdit" type="GraphEdit" parent="VBoxContainer"]
63 | margin_top = 24.0
64 | margin_right = 1024.0
65 | margin_bottom = 600.0
66 | size_flags_horizontal = 3
67 | size_flags_vertical = 3
68 | right_disconnects = true
69 | __meta__ = {
70 | "_edit_use_anchors_": false
71 | }
72 | [connection signal="pressed" from="VBoxContainer/VBoxContainer/Close_BTN" to="." method="_on_Close_BTN_pressed"]
73 | [connection signal="pressed" from="VBoxContainer/VBoxContainer/Back_BTN" to="." method="_on_Back_BTN_pressed"]
74 | [connection signal="pressed" from="VBoxContainer/VBoxContainer/Add_Node_BTN" to="." method="_on_Add_Node_BTN_pressed"]
75 | [connection signal="connection_request" from="VBoxContainer/GraphEdit" to="." method="_on_GraphEdit_connection_request"]
76 | [connection signal="disconnection_request" from="VBoxContainer/GraphEdit" to="." method="_on_GraphEdit_disconnection_request"]
77 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/Rename Record Box/Rename_Record_Box.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Story Editor/Rename Record Box/rename_record_box.gd" type="Script" id=1]
4 |
5 | [node name="Record_Rename_Box" type="WindowDialog"]
6 | anchor_left = 0.5
7 | anchor_top = 0.5
8 | anchor_right = 0.5
9 | anchor_bottom = 0.5
10 | margin_left = -193.0
11 | margin_top = -64.0
12 | margin_right = 193.0
13 | margin_bottom = 52.0
14 | rect_min_size = Vector2( 386, 116 )
15 | size_flags_horizontal = 3
16 | size_flags_vertical = 3
17 | window_title = "Rename Dialog Record"
18 | script = ExtResource( 1 )
19 | __meta__ = {
20 | "_edit_use_anchors_": false
21 | }
22 |
23 | [node name="MarginContainer" type="MarginContainer" parent="."]
24 | anchor_right = 1.0
25 | anchor_bottom = 1.0
26 | margin_left = 8.0
27 | margin_top = 8.0
28 | margin_right = -8.0
29 | margin_bottom = -8.0
30 | size_flags_horizontal = 3
31 | size_flags_vertical = 3
32 | __meta__ = {
33 | "_edit_use_anchors_": false
34 | }
35 |
36 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
37 | margin_right = 370.0
38 | margin_bottom = 100.0
39 | size_flags_horizontal = 3
40 | size_flags_vertical = 3
41 | __meta__ = {
42 | "_edit_use_anchors_": false
43 | }
44 |
45 | [node name="Name_LBL" type="Label" parent="MarginContainer/VBoxContainer"]
46 | margin_right = 370.0
47 | margin_bottom = 14.0
48 | text = "Name:"
49 |
50 | [node name="Spacer2" type="Control" parent="MarginContainer/VBoxContainer"]
51 | margin_top = 18.0
52 | margin_right = 370.0
53 | margin_bottom = 22.0
54 | rect_min_size = Vector2( 0, 4 )
55 |
56 | [node name="Name_LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
57 | margin_top = 26.0
58 | margin_right = 370.0
59 | margin_bottom = 50.0
60 |
61 | [node name="Spacer3" type="Control" parent="MarginContainer/VBoxContainer"]
62 | margin_top = 54.0
63 | margin_right = 370.0
64 | margin_bottom = 58.0
65 | rect_min_size = Vector2( 0, 4 )
66 |
67 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
68 | margin_top = 62.0
69 | margin_right = 370.0
70 | margin_bottom = 82.0
71 | alignment = 1
72 |
73 | [node name="Cancel_BTN" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
74 | margin_left = 106.0
75 | margin_right = 160.0
76 | margin_bottom = 20.0
77 | text = "Cancel"
78 |
79 | [node name="Spacer" type="Control" parent="MarginContainer/VBoxContainer/HBoxContainer"]
80 | margin_left = 164.0
81 | margin_right = 196.0
82 | margin_bottom = 20.0
83 | rect_min_size = Vector2( 32, 0 )
84 |
85 | [node name="Rename_BTN" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
86 | margin_left = 200.0
87 | margin_right = 264.0
88 | margin_bottom = 20.0
89 | text = "Rename"
90 | [connection signal="text_entered" from="MarginContainer/VBoxContainer/Name_LineEdit" to="." method="_on_Name_LineEdit_text_entered"]
91 | [connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/Cancel_BTN" to="." method="_on_Cancel_BTN_pressed"]
92 | [connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/Rename_BTN" to="." method="_on_Rename_BTN_pressed"]
93 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/line_node.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends GraphNode
3 |
4 | signal changed_offset(nid, vec2)
5 | signal changed_size(this)
6 | signal changed_slots(this)
7 | signal erased(this)
8 | signal pressed_editor(this)
9 | signal pressed_load(this)
10 | signal pressed_save(this)
11 | signal text_changed(nid, new_text)
12 |
13 | onready var _Link_SpinBox = self.get_node("VBoxContainer/HBoxContainer/Link_SpinBox")
14 | onready var _Text_Editor = self.get_node("VBoxContainer/HBoxContainer2/TextEdit")
15 |
16 | var _nid : int = 0
17 | var _slot_amount : int = 1
18 |
19 | #Virtual Methods
20 |
21 | func _ready():
22 | self._update_slots()
23 |
24 | #Callback Methods
25 |
26 | func _on_Editor_BTN_pressed():
27 | self.emit_signal("pressed_editor", self)
28 |
29 |
30 | func _on_Line_close_request():
31 | self.emit_signal("erased", self)
32 |
33 |
34 | func _on_Line_offset_changed():
35 | self.emit_signal("changed_offset", self._nid, self.offset)
36 |
37 |
38 | func _on_Line_resize_request(new_minsize):
39 | self.rect_size = new_minsize
40 | self.emit_signal("changed_size", self)
41 |
42 |
43 | func _on_Link_SpinBox_value_changed(value):
44 | self._slot_amount = int(self._Link_SpinBox.value)
45 | self._update_slots()
46 | self.emit_signal("changed_slots", self)
47 |
48 |
49 | func _on_Load_BTN_pressed():
50 | self.emit_signal("pressed_load", self)
51 |
52 |
53 | func _on_Save_BTN_pressed():
54 | self.emit_signal("pressed_save", self)
55 |
56 |
57 | func _on_TextEdit_text_changed():
58 | self.emit_signal("text_changed", self._nid, self._Text_Editor.text)
59 |
60 | #Public Methods
61 |
62 | func get_nid() -> int:
63 | return self._nid
64 |
65 |
66 | func get_slot_amount() -> int:
67 | return self._slot_amount
68 |
69 |
70 | func get_text() -> String:
71 | return self._Text_Editor.text
72 |
73 |
74 | func set_nid(new_nid):
75 | self._nid = new_nid
76 | var new_name = "NID " + str(new_nid)
77 | self.title = new_name
78 | self.name = new_name
79 |
80 |
81 | func set_slot_amount(new_amount : int):
82 | self._slot_amount = new_amount
83 |
84 |
85 | func set_text(new_text : String):
86 | self._Text_Editor.text = new_text
87 | self.emit_signal("text_changed", self._nid, new_text)
88 |
89 | #Private Methods
90 |
91 | func _clear_link_labels():
92 | var children = self.get_children()
93 | for child in children:
94 | if child is Label:
95 | child.free()
96 |
97 |
98 | func _update_slots():
99 | self.clear_all_slots()
100 | self._clear_link_labels()
101 | self.set_slot(0, true, 0, Color(1.0, 1.0, 1.0, 1.0), true, 0, Color(1.0, 1.0, 1.0, 1.0), null, null)
102 | var base_link_label = Label.new()
103 | base_link_label.text = "0"
104 | base_link_label.align = Label.ALIGN_RIGHT
105 | self.add_child(base_link_label)
106 | self.move_child(base_link_label, 0)
107 | var last_output_link_label = base_link_label
108 | for slot in range(1, self._slot_amount):
109 | self.set_slot(slot, false, 0, Color(1.0, 1.0, 1.0, 1.0), true, 0, Color(1.0, 1.0, 1.0, 1.0), null, null)
110 | var output_link_label = Label.new()
111 | output_link_label.text = str(slot)
112 | output_link_label.align = Label.ALIGN_RIGHT
113 | self.add_child_below_node(last_output_link_label, output_link_label)
114 | last_output_link_label = output_link_label
115 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/plugin.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends EditorPlugin
3 |
4 | var _Story_Editor = preload("res://addons/EXP-System-Dialog/Story Editor/Story_Editor.tscn")
5 | var _Story_Editor_Instance
6 | var _Story_Editor_BTN : ToolButton
7 | var _Dialog_Editor = preload("res://addons/EXP-System-Dialog/Dialog Editor/Dialog_Editor.tscn")
8 | var _Dialog_Editor_Instance
9 | var _Dialog_Editor_BTN : ToolButton
10 |
11 |
12 | func _enter_tree():
13 |
14 | self._Story_Editor_Instance = self._Story_Editor.instance()
15 | self._Story_Editor_Instance.connect("close_pressed", self, "_on_StoryFile_Editor_close_pressed")
16 | self._Story_Editor_BTN = self.add_control_to_bottom_panel(self._Story_Editor_Instance, "EXP StoryFile Editor")
17 | self._Story_Editor_Instance.visible = false
18 | self._Story_Editor_BTN.visible = false
19 | self.add_tool_menu_item("EXP StoryFile Editor", self, "_on_StoryFile_Editor_opened")
20 |
21 | self._Dialog_Editor_Instance = self._Dialog_Editor.instance()
22 | self._Dialog_Editor_BTN = self.add_control_to_bottom_panel(self._Dialog_Editor_Instance, "EXP Dialog Editor")
23 | self._Dialog_Editor_BTN.visible = false
24 |
25 | self._Story_Editor_Instance.connect("dialog_edit_pressed",
26 | self, "_on_Story_Editor_dialog_edit_pressed")
27 | self._Story_Editor_Instance.connect("dialog_edit_pressed",
28 | self._Dialog_Editor_Instance, "_on_Story_Editor_dialog_edit_pressed")
29 | self._Dialog_Editor_Instance.connect("close_BTN_pressed", self,
30 | "_on_Dialog_Editor_close_BTN_pressed")
31 | self._Dialog_Editor_Instance.connect("back_BTN_pressed", self,
32 | "_on_Dialog_Editor_back_BTN_pressed")
33 | self._Story_Editor_Instance.connect("changed_story", self,
34 | "_on_Story_Editor_changed_story")
35 |
36 |
37 | func _exit_tree():
38 |
39 | self._Story_Editor_Instance.disconnect("dialog_edit_pressed",
40 | self, "_on_Story_Editor_dialog_edit_pressed")
41 | self._Story_Editor_Instance.disconnect("dialog_edit_pressed",
42 | self._Dialog_Editor_Instance, "_on_Story_Editor_dialog_edit_pressed")
43 | self._Dialog_Editor_Instance.disconnect("close_BTN_pressed", self,
44 | "_on_Dialog_Editor_close_BTN_pressed")
45 | self._Dialog_Editor_Instance.disconnect("back_BTN_pressed", self,
46 | "_on_Dialog_Editor_back_BTN_pressed")
47 | self._Story_Editor_Instance.disconnect("changed_story", self,
48 | "_on_Story_Editor_changed_story")
49 |
50 | self.remove_control_from_bottom_panel(self._Story_Editor_Instance)
51 | self._Story_Editor_Instance.queue_free()
52 | self.remove_control_from_bottom_panel(self._Dialog_Editor_Instance)
53 | self._Dialog_Editor_Instance.queue_free()
54 | self.remove_tool_menu_item("EXP StoryFile Editor")
55 |
56 |
57 | func _on_Story_Editor_dialog_edit_pressed(story, did):
58 | self._Dialog_Editor_BTN.visible = true
59 | self._Story_Editor_BTN.pressed = false
60 | self._Dialog_Editor_BTN.pressed = true
61 | self._Dialog_Editor_BTN.emit_signal("pressed")
62 |
63 |
64 | func _on_Dialog_Editor_close_BTN_pressed():
65 | self._Dialog_Editor_BTN.pressed = false
66 | self._Dialog_Editor_BTN.visible = false
67 |
68 |
69 | func _on_Story_Editor_changed_story():
70 | self._Dialog_Editor_BTN.visible = false
71 |
72 |
73 | func _on_Dialog_Editor_back_BTN_pressed():
74 | self._Story_Editor_BTN.pressed = true
75 | self._Dialog_Editor_BTN.pressed = false
76 | self._Story_Editor_BTN.emit_signal("pressed")
77 |
78 |
79 | func _on_StoryFile_Editor_opened(trash_parameter):
80 | self._Story_Editor_BTN.visible = true
81 |
82 |
83 | func _on_StoryFile_Editor_close_pressed():
84 | self._Story_Editor_Instance.visible = false
85 | self._Story_Editor_BTN.visible = false
86 | self._Dialog_Editor_Instance.visible = false
87 | self._Dialog_Editor_BTN.visible = false
88 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/Dialog Record/Dialog_Record.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=4 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Story Editor/Dialog Record/dialog_record.gd" type="Script" id=1]
4 |
5 | [sub_resource type="StyleBoxFlat" id=1]
6 | bg_color = Color( 0.6, 0.6, 0.6, 0 )
7 | border_color = Color( 0.8, 0.8, 0.8, 0 )
8 | shadow_color = Color( 0, 0, 0, 0 )
9 |
10 | [sub_resource type="StyleBoxFlat" id=2]
11 | bg_color = Color( 0.6, 0.6, 0.6, 0 )
12 | border_color = Color( 0.8, 0.8, 0.8, 0 )
13 | shadow_color = Color( 0, 0, 0, 0 )
14 |
15 | [node name="Dialog_Record" type="Control"]
16 | anchor_right = 1.0
17 | rect_min_size = Vector2( 0, 28 )
18 | script = ExtResource( 1 )
19 | __meta__ = {
20 | "_edit_use_anchors_": false
21 | }
22 |
23 | [node name="ColorRect" type="ColorRect" parent="."]
24 | anchor_right = 1.0
25 | anchor_bottom = 1.0
26 | rect_min_size = Vector2( 0, 24 )
27 | size_flags_horizontal = 3
28 | color = Color( 1, 1, 1, 0.12549 )
29 | __meta__ = {
30 | "_edit_use_anchors_": false
31 | }
32 |
33 | [node name="HBoxContainer" type="HBoxContainer" parent="ColorRect"]
34 | anchor_right = 1.0
35 | anchor_bottom = 1.0
36 | __meta__ = {
37 | "_edit_use_anchors_": false
38 | }
39 |
40 | [node name="CheckBox" type="CheckBox" parent="ColorRect/HBoxContainer"]
41 | margin_right = 24.0
42 | margin_bottom = 28.0
43 |
44 | [node name="DID_LBL" type="Label" parent="ColorRect/HBoxContainer"]
45 | margin_left = 28.0
46 | margin_top = 7.0
47 | margin_right = 85.0
48 | margin_bottom = 21.0
49 | text = "DID: 101"
50 |
51 | [node name="VSeparator" type="VSeparator" parent="ColorRect/HBoxContainer"]
52 | margin_left = 89.0
53 | margin_right = 93.0
54 | margin_bottom = 28.0
55 |
56 | [node name="Edit_BTN" type="Button" parent="ColorRect/HBoxContainer"]
57 | margin_left = 97.0
58 | margin_right = 138.0
59 | margin_bottom = 28.0
60 | hint_tooltip = "Edit this dialog record in the Dialog Editor."
61 | text = "EDIT"
62 | flat = true
63 |
64 | [node name="VSeparator4" type="VSeparator" parent="ColorRect/HBoxContainer"]
65 | margin_left = 142.0
66 | margin_right = 146.0
67 | margin_bottom = 28.0
68 |
69 | [node name="Name_BTN" type="Button" parent="ColorRect/HBoxContainer"]
70 | margin_left = 150.0
71 | margin_right = 200.0
72 | margin_bottom = 28.0
73 | hint_tooltip = "Edit this dialog record in the Dialog Editor."
74 | text = "NAME"
75 | flat = true
76 |
77 | [node name="VSeparator2" type="VSeparator" parent="ColorRect/HBoxContainer"]
78 | margin_left = 204.0
79 | margin_right = 208.0
80 | margin_bottom = 28.0
81 |
82 | [node name="Group_BTN" type="OptionButton" parent="ColorRect/HBoxContainer"]
83 | margin_left = 212.0
84 | margin_right = 274.0
85 | margin_bottom = 28.0
86 | hint_tooltip = "View groups applied to this dialog record."
87 | text = "TAGS"
88 | flat = true
89 |
90 | [node name="VSeparator3" type="VSeparator" parent="ColorRect/HBoxContainer"]
91 | margin_left = 278.0
92 | margin_right = 282.0
93 | margin_bottom = 28.0
94 |
95 | [node name="Human_Readable_LineEdit" type="LineEdit" parent="ColorRect/HBoxContainer"]
96 | margin_left = 286.0
97 | margin_right = 476.0
98 | margin_bottom = 28.0
99 | custom_styles/focus = SubResource( 1 )
100 | custom_styles/normal = SubResource( 2 )
101 | text = "Human Readable Description"
102 | expand_to_text_length = true
103 | context_menu_enabled = false
104 | [connection signal="toggled" from="ColorRect/HBoxContainer/CheckBox" to="." method="_on_CheckBox_toggled"]
105 | [connection signal="pressed" from="ColorRect/HBoxContainer/Edit_BTN" to="." method="_on_Edit_BTN_pressed"]
106 | [connection signal="pressed" from="ColorRect/HBoxContainer/Name_BTN" to="." method="_on_Name_BTN_pressed"]
107 | [connection signal="pressed" from="ColorRect/HBoxContainer/Group_BTN" to="." method="_on_Group_BTN_pressed"]
108 | [connection signal="focus_exited" from="ColorRect/HBoxContainer/Human_Readable_LineEdit" to="." method="_on_Human_Readable_LineEdit_focus_exited"]
109 | [connection signal="text_changed" from="ColorRect/HBoxContainer/Human_Readable_LineEdit" to="." method="_on_Human_Readable_LineEdit_text_changed"]
110 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/Line_Node.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=3 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/line_node.gd" type="Script" id=1]
4 | [ext_resource path="res://addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/icon_distraction_free.svg" type="Texture" id=2]
5 |
6 |
7 | [node name="Line" type="GraphNode"]
8 | margin_right = 324.0
9 | margin_bottom = 137.0
10 | rect_min_size = Vector2( 324, 137 )
11 | title = "Line"
12 | offset = Vector2( 40, 40 )
13 | show_close = true
14 | resizable = true
15 | slot/0/left_enabled = true
16 | slot/0/left_type = 0
17 | slot/0/left_color = Color( 1, 1, 1, 1 )
18 | slot/0/right_enabled = true
19 | slot/0/right_type = 0
20 | slot/0/right_color = Color( 1, 1, 1, 1 )
21 | slot/1/left_enabled = false
22 | slot/1/left_type = 0
23 | slot/1/left_color = Color( 1, 1, 1, 1 )
24 | slot/1/right_enabled = false
25 | slot/1/right_type = 0
26 | slot/1/right_color = Color( 1, 1, 1, 1 )
27 | script = ExtResource( 1 )
28 | __meta__ = {
29 | "_edit_use_anchors_": false
30 | }
31 |
32 | [node name="VBoxContainer" type="VBoxContainer" parent="."]
33 | margin_left = 16.0
34 | margin_top = 39.0
35 | margin_right = 380.0
36 | margin_bottom = 131.0
37 | size_flags_horizontal = 3
38 | size_flags_vertical = 3
39 | __meta__ = {
40 | "_edit_use_anchors_": false
41 | }
42 |
43 | [node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
44 | margin_right = 364.0
45 | margin_bottom = 64.0
46 |
47 | [node name="TextEdit" type="TextEdit" parent="VBoxContainer/HBoxContainer2"]
48 | margin_right = 328.0
49 | margin_bottom = 64.0
50 | rect_min_size = Vector2( 256, 64 )
51 | size_flags_horizontal = 3
52 | size_flags_vertical = 3
53 | smooth_scrolling = true
54 |
55 | [node name="Editor_BTN" type="Button" parent="VBoxContainer/HBoxContainer2"]
56 | margin_left = 332.0
57 | margin_right = 364.0
58 | margin_bottom = 64.0
59 | rect_min_size = Vector2( 32, 0 )
60 | hint_tooltip = "Expand this node's text editor."
61 | icon = ExtResource( 2 )
62 | expand_icon = true
63 |
64 | [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
65 | margin_top = 68.0
66 | margin_right = 364.0
67 | margin_bottom = 92.0
68 | alignment = 1
69 |
70 | [node name="Save_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
71 | margin_right = 125.0
72 | margin_bottom = 24.0
73 | hint_tooltip = "Save the current text as a template that can quickly loaded into new nodes."
74 | text = "Save Template As"
75 |
76 | [node name="Load_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
77 | margin_left = 129.0
78 | margin_right = 235.0
79 | margin_bottom = 24.0
80 | hint_tooltip = "Load a template from file."
81 | text = "Load Template"
82 |
83 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
84 | margin_left = 239.0
85 | margin_right = 243.0
86 | margin_bottom = 24.0
87 |
88 | [node name="Link_LBL" type="Label" parent="VBoxContainer/HBoxContainer"]
89 | margin_left = 247.0
90 | margin_top = 5.0
91 | margin_right = 286.0
92 | margin_bottom = 19.0
93 | text = "Slots: "
94 |
95 | [node name="Link_SpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"]
96 | margin_left = 290.0
97 | margin_right = 364.0
98 | margin_bottom = 24.0
99 | hint_tooltip = "Change the number of output slots on this node."
100 | min_value = 1.0
101 | value = 1.0
102 | rounded = true
103 | allow_greater = true
104 | [connection signal="close_request" from="." to="." method="_on_Line_close_request"]
105 | [connection signal="offset_changed" from="." to="." method="_on_Line_offset_changed"]
106 | [connection signal="resize_request" from="." to="." method="_on_Line_resize_request"]
107 | [connection signal="text_changed" from="VBoxContainer/HBoxContainer2/TextEdit" to="." method="_on_TextEdit_text_changed"]
108 | [connection signal="pressed" from="VBoxContainer/HBoxContainer2/Editor_BTN" to="." method="_on_Editor_BTN_pressed"]
109 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Save_BTN" to="." method="_on_Save_BTN_pressed"]
110 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Load_BTN" to="." method="_on_Load_BTN_pressed"]
111 | [connection signal="value_changed" from="VBoxContainer/HBoxContainer/Link_SpinBox" to="." method="_on_Link_SpinBox_value_changed"]
112 |
--------------------------------------------------------------------------------
/Dialog-System-Example/stories/Example_Story_Temp_Baked.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Resource_BakedStory/EXP_BakedStory.gd" type="Script" id=1]
4 |
5 | [resource]
6 | script = ExtResource( 1 )
7 | TYPE = "EXP_Baked_Story"
8 | story = {
9 | 1: {
10 | "nodes": {
11 | 1: {
12 | "links": {
13 | 0: 2
14 | },
15 | "text": ""
16 | },
17 | 2: {
18 | "links": {
19 | 0: 3
20 | },
21 | "text": "Dave the Dev
22 | "
23 | },
24 | 3: {
25 | "links": {
26 | 0: 4
27 | },
28 | "text": "Dave the Dev
29 | "
30 | },
31 | 4: {
32 | "links": {
33 | 0: 5
34 | },
35 | "text": "Twitter-Kun
36 | "
37 | },
38 | 5: {
39 | "links": {
40 | 0: 7
41 | },
42 | "text": "Insta-Chan
43 | "
44 | },
45 | 6: {
46 | "links": {
47 |
48 | },
49 | "text": ""
50 | },
51 | 7: {
52 | "links": {
53 | 0: 6
54 | },
55 | "text": "Dave the Dev
56 | "
57 | }
58 | }
59 | },
60 | 2: {
61 | "nodes": {
62 | 1: {
63 | "links": {
64 | 0: 2
65 | },
66 | "text": ""
67 | },
68 | 2: {
69 | "links": {
70 | 0: 4
71 | },
72 | "text": "DaveTheDev
73 | "
74 | },
75 | 3: {
76 | "links": {
77 |
78 | },
79 | "text": ""
80 | },
81 | 4: {
82 | "links": {
83 | 0: 7
84 | },
85 | "text": "Program
86 | "
87 | },
88 | 5: {
89 | "links": {
90 | 0: 6
91 | },
92 | "text": "Program
93 | "
94 | },
95 | 6: {
96 | "links": {
97 | 0: 3
98 | },
99 | "text": "Program
100 | "
101 | },
102 | 7: {
103 | "links": {
104 | 0: 5
105 | },
106 | "text": "Program
107 | "
108 | }
109 | }
110 | },
111 | 3: {
112 | "nodes": {
113 | 1: {
114 | "links": {
115 | 0: 3
116 | },
117 | "text": ""
118 | },
119 | 2: {
120 | "links": {
121 |
122 | },
123 | "text": ""
124 | },
125 | 3: {
126 | "links": {
127 | 0: 4,
128 | 1: 5,
129 | 2: 6
130 | },
131 | "text": "Wizard
132 |
133 | {\"Red Potion\" : 0, \"Blue Potion\" : 1, \"Green Potion\" : 2}"
134 | },
135 | 4: {
136 | "links": {
137 | 0: 7
138 | },
139 | "text": "Red Potion
140 | "
141 | },
142 | 5: {
143 | "links": {
144 | 0: 7
145 | },
146 | "text": "Blue Potion
147 | "
148 | },
149 | 6: {
150 | "links": {
151 | 0: 7
152 | },
153 | "text": "Green Potion
154 | "
155 | },
156 | 7: {
157 | "links": {
158 | 0: 2
159 | },
160 | "text": "Wizard
161 | "
162 | }
163 | }
164 | },
165 | 4: {
166 | "nodes": {
167 | 1: {
168 | "links": {
169 |
170 | },
171 | "text": "{
172 | \"1\" : \"res://Dialog-System-Example/textures/characters/1.png\",
173 | \"2\" : \"res://Dialog-System-Example/textures/characters/2.png\",
174 | \"3\" : \"res://Dialog-System-Example/textures/characters/3.png\",
175 | \"4\" : \"res://Dialog-System-Example/textures/characters/4.png\",
176 | \"5\" : \"res://Dialog-System-Example/textures/characters/5.png\"
177 | }"
178 | }
179 | }
180 | },
181 | 5: {
182 | "nodes": {
183 | 1: {
184 | "links": {
185 | 0: 3
186 | },
187 | "text": ""
188 | },
189 | 2: {
190 | "links": {
191 |
192 | },
193 | "text": ""
194 | },
195 | 3: {
196 | "links": {
197 | 0: 4
198 | },
199 | "text": "One
200 |
201 | 1"
202 | },
203 | 4: {
204 | "links": {
205 | 0: 5
206 | },
207 | "text": "Two
208 |
209 | 2"
210 | },
211 | 5: {
212 | "links": {
213 | 0: 6
214 | },
215 | "text": "Three
216 |
217 | 3"
218 | },
219 | 6: {
220 | "links": {
221 | 0: 7
222 | },
223 | "text": "Four
224 |
225 | 4"
226 | },
227 | 7: {
228 | "links": {
229 | 0: 2
230 | },
231 | "text": "Five
232 |
233 | 5"
234 | }
235 | }
236 | }
237 | }
238 | names = {
239 | "DialogPlayer/CharacterTextures": 5,
240 | "DialogPlayer/ChoiceBranching": 3,
241 | "DialogPlayer/Test": 1,
242 | "DialogPlayer/TextureLibrary": 4,
243 | "DialogPlayer/VariableInjection": 2
244 | }
245 |
--------------------------------------------------------------------------------
/Dialog-System-Example/scripts/dialog_player.gd:
--------------------------------------------------------------------------------
1 | extends Node
2 |
3 | onready var _Body_AnimationPlayer = find_node("Body_AnimationPlayer")
4 | onready var _Body_LBL = find_node("Body_Label")
5 | onready var _Character_Texture = find_node("Character_Texture")
6 | onready var _Dialog_Box = find_node("Dialog_Box")
7 | onready var _Option_List = find_node("Option_List")
8 | onready var _Registry = find_node("Simulated_Registry")
9 | onready var _SelectChoice_Icon = find_node("SelectChoice_NinePatchRect")
10 | onready var _Speaker_LBL = find_node("Speaker_Label")
11 | onready var _SpaceBar_Icon = find_node("SpaceBar_NinePatchRect")
12 |
13 | onready var _Option_Button_Scene = load("res://Dialog-System-Example/scenes/Option.tscn")
14 |
15 | var _did = 0
16 | var _nid = 0
17 | var _final_nid = 0
18 | var _Story_Reader
19 | var _texture_library : Dictionary = {}
20 |
21 | # Virtual Methods
22 |
23 | func _ready():
24 | var Story_Reader_Class = load("res://addons/EXP-System-Dialog/Reference_StoryReader/EXP_StoryReader.gd")
25 | _Story_Reader = Story_Reader_Class.new()
26 |
27 | var story = load("res://Dialog-System-Example/stories/Example_Story_Temp_Baked.tres")
28 | _Story_Reader.read(story)
29 |
30 | _load_textures()
31 |
32 | _Dialog_Box.visible = false
33 | _SpaceBar_Icon.visible = false
34 | _SelectChoice_Icon.visible = false
35 | _Option_List.visible = false
36 | _Character_Texture.visible = false
37 |
38 | _clear_options()
39 |
40 | play_dialog("DialogPlayer/CharacterTextures")
41 |
42 |
43 | func _input(event):
44 | if event is InputEventKey:
45 | if event.pressed == true and event.scancode == KEY_SPACE:
46 | _on_Dialog_Player_pressed_spacebar()
47 |
48 | # Callback Methods
49 |
50 | func _on_Body_AnimationPlayer_animation_finished(anim_name):
51 | if _Option_List.get_child_count() == 0:
52 | _SpaceBar_Icon.visible = true
53 | else:
54 | _SelectChoice_Icon.visible = true
55 | _Option_List.visible = true
56 |
57 |
58 | func _on_Dialog_Player_pressed_spacebar():
59 | if _is_waiting():
60 | _SpaceBar_Icon.visible = false
61 | _Character_Texture.visible = false
62 | _get_next_node()
63 | if _is_playing():
64 | _play_node()
65 |
66 |
67 | func _on_Option_clicked(slot : int):
68 | _SelectChoice_Icon.visible = false
69 | _Option_List.visible = false
70 | _Character_Texture.visible = false
71 | _get_next_node(slot)
72 | _clear_options()
73 | if _is_playing():
74 | _play_node()
75 |
76 | # Public Methods
77 |
78 | func play_dialog(record_name : String):
79 | _did = _Story_Reader.get_did_via_record_name(record_name)
80 | _nid = _Story_Reader.get_nid_via_exact_text(_did, "")
81 | _final_nid = _Story_Reader.get_nid_via_exact_text(_did, "")
82 | _get_next_node()
83 | _play_node()
84 | _Dialog_Box.visible = true
85 |
86 | # Private Methods
87 |
88 | func _clear_options():
89 | var children = _Option_List.get_children()
90 | for child in children:
91 | _Option_List.remove_child(child)
92 | child.queue_free()
93 | _Option_List.rect_size.y = 48
94 | _Option_List.rect_position.y = -60
95 |
96 |
97 | func _display_image(key : String):
98 | _Character_Texture.texture = _texture_library[key]
99 | _Character_Texture.visible = true
100 |
101 |
102 | func _get_next_node(slot : int = 0):
103 | _nid = _Story_Reader.get_nid_from_slot(_did, _nid, slot)
104 |
105 | if _nid == _final_nid:
106 | _Dialog_Box.visible = false
107 |
108 |
109 | func _get_tagged_text(tag : String, text : String):
110 | var start_tag = "<" + tag + ">"
111 | var end_tag = "" + tag + ">"
112 | var start_index = text.find(start_tag) + start_tag.length()
113 | var end_index = text.find(end_tag)
114 | var substr_length = end_index - start_index
115 | return text.substr(start_index, substr_length)
116 |
117 |
118 | func _inject_variables(text : String) -> String:
119 | var variable_count = text.count("")
120 |
121 | for i in range(variable_count):
122 | var variable_name = _get_tagged_text("variable", text)
123 | var variable_value = _Registry.lookup(variable_name)
124 | var start_index = text.find("")
125 | var end_index = text.find("") + "".length()
126 | var substr_length = end_index - start_index
127 | text.erase(start_index, substr_length)
128 | text = text.insert(start_index, str(variable_value))
129 |
130 | return text
131 |
132 |
133 | func _is_playing():
134 | return _Dialog_Box.visible
135 |
136 |
137 | func _is_waiting():
138 | return _SpaceBar_Icon.visible
139 |
140 |
141 | func _load_textures():
142 | var did = _Story_Reader.get_did_via_record_name("DialogPlayer/TextureLibrary")
143 | var json_text = _Story_Reader.get_text(did, 1)
144 | var raw_texture_library : Dictionary = parse_json(json_text)
145 |
146 | for key in raw_texture_library:
147 | var texture_path = raw_texture_library[key]
148 | var loaded_texture = load(texture_path)
149 | _texture_library[key] = loaded_texture
150 |
151 |
152 | func _play_node():
153 | var text = _Story_Reader.get_text(_did, _nid)
154 | text = _inject_variables(text)
155 | var speaker = _get_tagged_text("speaker", text)
156 | var dialog = _get_tagged_text("dialog", text)
157 | if "" in text:
158 | var options = _get_tagged_text("choiceJSON", text)
159 | _populate_choices(options)
160 | if "" in text:
161 | var library_key = _get_tagged_text("image", text)
162 | _display_image(library_key)
163 |
164 | _Speaker_LBL.text = speaker
165 | _Body_LBL.text = dialog
166 | _Body_AnimationPlayer.play("TextDisplay")
167 |
168 |
169 | func _populate_choices(JSONtext : String):
170 | var choices : Dictionary = parse_json(JSONtext)
171 |
172 | for text in choices:
173 | var slot = choices[text]
174 | var new_option_button = _Option_Button_Scene.instance()
175 | _Option_List.add_child(new_option_button)
176 | new_option_button.slot = slot
177 | new_option_button.set_text(text)
178 | new_option_button.connect("clicked", self, "_on_Option_clicked")
179 |
--------------------------------------------------------------------------------
/Dialog-System-Example/Dialog_Player.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=10 format=2]
2 |
3 | [ext_resource path="res://Dialog-System-Example/scripts/dialog_player.gd" type="Script" id=1]
4 | [ext_resource path="res://Dialog-System-Example/textures/menu_section_9patch.png" type="Texture" id=2]
5 | [ext_resource path="res://Dialog-System-Example/fonts/speaker_font.tres" type="DynamicFont" id=3]
6 | [ext_resource path="res://Dialog-System-Example/fonts/body_font.tres" type="DynamicFont" id=4]
7 | [ext_resource path="res://Dialog-System-Example/scripts/simulatedregistry.gd" type="Script" id=5]
8 | [ext_resource path="res://Dialog-System-Example/scenes/Option.tscn" type="PackedScene" id=6]
9 |
10 | [sub_resource type="Animation" id=1]
11 | resource_name = "TextDisplay"
12 | tracks/0/type = "value"
13 | tracks/0/path = NodePath(".:percent_visible")
14 | tracks/0/interp = 1
15 | tracks/0/loop_wrap = true
16 | tracks/0/imported = false
17 | tracks/0/enabled = true
18 | tracks/0/keys = {
19 | "times": PoolRealArray( 0, 1 ),
20 | "transitions": PoolRealArray( 1, 1 ),
21 | "update": 0,
22 | "values": [ 0.0, 1.0 ]
23 | }
24 |
25 | [sub_resource type="Animation" id=2]
26 | loop = true
27 | tracks/0/type = "value"
28 | tracks/0/path = NodePath(".:rect_scale")
29 | tracks/0/interp = 1
30 | tracks/0/loop_wrap = true
31 | tracks/0/imported = false
32 | tracks/0/enabled = true
33 | tracks/0/keys = {
34 | "times": PoolRealArray( 0, 0.5, 1 ),
35 | "transitions": PoolRealArray( 1, 1, 1 ),
36 | "update": 0,
37 | "values": [ Vector2( 1, 1 ), Vector2( 1.1, 1.1 ), Vector2( 1, 1 ) ]
38 | }
39 |
40 | [sub_resource type="Animation" id=3]
41 | loop = true
42 | tracks/0/type = "value"
43 | tracks/0/path = NodePath(".:rect_scale")
44 | tracks/0/interp = 1
45 | tracks/0/loop_wrap = true
46 | tracks/0/imported = false
47 | tracks/0/enabled = true
48 | tracks/0/keys = {
49 | "times": PoolRealArray( 0, 0.5, 1 ),
50 | "transitions": PoolRealArray( 1, 1, 1 ),
51 | "update": 0,
52 | "values": [ Vector2( 1, 1 ), Vector2( 1.1, 1.1 ), Vector2( 1, 1 ) ]
53 | }
54 |
55 | [node name="Dialog_Player" type="Node"]
56 | script = ExtResource( 1 )
57 |
58 | [node name="Dialog_Box" type="Control" parent="."]
59 | anchor_right = 1.0
60 | anchor_bottom = 1.0
61 | __meta__ = {
62 | "_edit_use_anchors_": false
63 | }
64 |
65 | [node name="Body_NinePatchRect" type="NinePatchRect" parent="Dialog_Box"]
66 | anchor_left = 0.5
67 | anchor_top = 0.5
68 | anchor_right = 0.5
69 | anchor_bottom = 0.5
70 | margin_left = -360.0
71 | margin_top = -128.0
72 | margin_right = 360.0
73 | margin_bottom = 128.0
74 | rect_min_size = Vector2( 720, 256 )
75 | texture = ExtResource( 2 )
76 | patch_margin_left = 3
77 | patch_margin_top = 3
78 | patch_margin_right = 3
79 | patch_margin_bottom = 3
80 | __meta__ = {
81 | "_edit_use_anchors_": false
82 | }
83 |
84 | [node name="MarginContainer" type="MarginContainer" parent="Dialog_Box/Body_NinePatchRect"]
85 | anchor_right = 1.0
86 | anchor_bottom = 1.0
87 | margin_left = 16.0
88 | margin_top = 48.0
89 | margin_right = -16.0
90 | margin_bottom = -48.0
91 | __meta__ = {
92 | "_edit_use_anchors_": false
93 | }
94 |
95 | [node name="Body_Label" type="Label" parent="Dialog_Box/Body_NinePatchRect/MarginContainer"]
96 | margin_right = 688.0
97 | margin_bottom = 160.0
98 | size_flags_horizontal = 3
99 | size_flags_vertical = 3
100 | custom_fonts/font = ExtResource( 4 )
101 | text = "This is where the dialog text is displayed.
102 | Press F6 to run this example. ^_^"
103 | autowrap = true
104 |
105 | [node name="Body_AnimationPlayer" type="AnimationPlayer" parent="Dialog_Box/Body_NinePatchRect/MarginContainer/Body_Label"]
106 | anims/TextDisplay = SubResource( 1 )
107 |
108 | [node name="Speaker_NinePatchRect" type="NinePatchRect" parent="Dialog_Box/Body_NinePatchRect"]
109 | margin_left = -24.3768
110 | margin_top = -13.2964
111 | margin_right = 155.623
112 | margin_bottom = 34.7036
113 | rect_min_size = Vector2( 180, 48 )
114 | texture = ExtResource( 2 )
115 | patch_margin_left = 3
116 | patch_margin_top = 3
117 | patch_margin_right = 3
118 | patch_margin_bottom = 3
119 | __meta__ = {
120 | "_edit_use_anchors_": false
121 | }
122 |
123 | [node name="Speaker_Label" type="Label" parent="Dialog_Box/Body_NinePatchRect/Speaker_NinePatchRect"]
124 | anchor_right = 1.0
125 | anchor_bottom = 1.0
126 | custom_fonts/font = ExtResource( 3 )
127 | text = "Speaker"
128 | align = 1
129 | valign = 1
130 | __meta__ = {
131 | "_edit_use_anchors_": false
132 | }
133 |
134 | [node name="SpaceBar_NinePatchRect" type="NinePatchRect" parent="Dialog_Box/Body_NinePatchRect"]
135 | anchor_left = 1.0
136 | anchor_top = 1.0
137 | anchor_right = 1.0
138 | anchor_bottom = 1.0
139 | margin_left = -231.69
140 | margin_top = -40.0
141 | margin_right = 24.3102
142 | margin_bottom = 8.0
143 | rect_min_size = Vector2( 180, 48 )
144 | texture = ExtResource( 2 )
145 | patch_margin_left = 3
146 | patch_margin_top = 3
147 | patch_margin_right = 3
148 | patch_margin_bottom = 3
149 | __meta__ = {
150 | "_edit_use_anchors_": false
151 | }
152 |
153 | [node name="SpaceBar_Label" type="Label" parent="Dialog_Box/Body_NinePatchRect/SpaceBar_NinePatchRect"]
154 | anchor_right = 1.0
155 | anchor_bottom = 1.0
156 | custom_fonts/font = ExtResource( 3 )
157 | text = "Press SpaceBar"
158 | align = 1
159 | valign = 1
160 | __meta__ = {
161 | "_edit_use_anchors_": false
162 | }
163 |
164 | [node name="SpaceBar_AnimationPlayer" type="AnimationPlayer" parent="Dialog_Box/Body_NinePatchRect/SpaceBar_NinePatchRect"]
165 | autoplay = "Blink"
166 | anims/Blink = SubResource( 2 )
167 |
168 | [node name="SelectChoice_NinePatchRect" type="NinePatchRect" parent="Dialog_Box/Body_NinePatchRect"]
169 | anchor_left = 1.0
170 | anchor_top = 1.0
171 | anchor_right = 1.0
172 | anchor_bottom = 1.0
173 | margin_left = -231.69
174 | margin_top = -40.0
175 | margin_right = 24.3102
176 | margin_bottom = 8.0
177 | rect_min_size = Vector2( 180, 48 )
178 | texture = ExtResource( 2 )
179 | patch_margin_left = 3
180 | patch_margin_top = 3
181 | patch_margin_right = 3
182 | patch_margin_bottom = 3
183 | __meta__ = {
184 | "_edit_use_anchors_": false
185 | }
186 |
187 | [node name="SelectChoice_Label" type="Label" parent="Dialog_Box/Body_NinePatchRect/SelectChoice_NinePatchRect"]
188 | anchor_right = 1.0
189 | anchor_bottom = 1.0
190 | custom_fonts/font = ExtResource( 3 )
191 | text = "Select Choice"
192 | align = 1
193 | valign = 1
194 | __meta__ = {
195 | "_edit_use_anchors_": false
196 | }
197 |
198 | [node name="SelectChoice_AnimationPlayer" type="AnimationPlayer" parent="Dialog_Box/Body_NinePatchRect/SelectChoice_NinePatchRect"]
199 | autoplay = "Blink"
200 | anims/Blink = SubResource( 3 )
201 |
202 | [node name="Option_List" type="VBoxContainer" parent="Dialog_Box/Body_NinePatchRect"]
203 | anchor_left = 1.0
204 | anchor_right = 1.0
205 | margin_left = -540.0
206 | margin_top = -60.0
207 | margin_bottom = -12.0
208 | grow_vertical = 0
209 | alignment = 2
210 | __meta__ = {
211 | "_edit_use_anchors_": false
212 | }
213 |
214 | [node name="Option" parent="Dialog_Box/Body_NinePatchRect/Option_List" instance=ExtResource( 6 )]
215 | anchor_top = 0.0
216 | anchor_right = 0.0
217 | anchor_bottom = 0.0
218 | margin_right = 540.0
219 | margin_bottom = 48.0
220 |
221 | [node name="Option2" parent="Dialog_Box/Body_NinePatchRect/Option_List" instance=ExtResource( 6 )]
222 | anchor_top = 0.0
223 | anchor_right = 0.0
224 | anchor_bottom = 0.0
225 | margin_top = 52.0
226 | margin_right = 540.0
227 | margin_bottom = 100.0
228 |
229 | [node name="Option3" parent="Dialog_Box/Body_NinePatchRect/Option_List" instance=ExtResource( 6 )]
230 | anchor_top = 0.0
231 | anchor_right = 0.0
232 | anchor_bottom = 0.0
233 | margin_top = 104.0
234 | margin_right = 540.0
235 | margin_bottom = 152.0
236 |
237 | [node name="Character_Texture" type="TextureRect" parent="Dialog_Box/Body_NinePatchRect"]
238 | margin_left = -16.0
239 | margin_top = -180.0
240 | margin_right = 144.0
241 | margin_bottom = -20.0
242 |
243 | [node name="Simulated_Registry" type="Node" parent="."]
244 | script = ExtResource( 5 )
245 | [connection signal="animation_finished" from="Dialog_Box/Body_NinePatchRect/MarginContainer/Body_Label/Body_AnimationPlayer" to="." method="_on_Body_AnimationPlayer_animation_finished"]
246 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Dialog Editor/dialog_editor.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Control
3 |
4 | signal back_BTN_pressed
5 | signal close_BTN_pressed
6 |
7 | onready var _Graph = self.get_node("VBoxContainer/GraphEdit")
8 | onready var _Human_Readable_LBL = self.get_node("VBoxContainer/VBoxContainer/Human_Readable_LBL")
9 |
10 | var _Editor_TSCN = preload("res://addons/EXP-System-Dialog/Dialog Editor/Editor/Editor.tscn")
11 | var _LineNode = preload("res://addons/EXP-System-Dialog/Dialog Editor/Nodes/Line/Line_Node.tscn")
12 | var _NodeTemplate= preload("res://addons/EXP-System-Dialog/Resource_NodeTemplate/EXP_NodeTemplate.gd")
13 |
14 | var _did : int = -1
15 | var _Editor
16 | var _Load_Template : EditorFileDialog
17 | var _Save_Template_As : EditorFileDialog
18 | var _Story_Editor
19 | var _Target_Node
20 |
21 | #Virtual Methods
22 |
23 | func _ready():
24 | self._setup_dialogs()
25 | self._Editor = _Editor_TSCN.instance()
26 | self.add_child(self._Editor)
27 |
28 | #Callback Methods
29 |
30 | func _on_Add_Node_BTN_pressed():
31 | var new_nid = self._Story_Editor.create_node(self._did, "line")
32 | var new_line_node = self._LineNode.instance()
33 | new_line_node.offset += self._Graph.scroll_offset
34 | new_line_node.set_nid(new_nid)
35 | new_line_node.connect("erased", self, "_on_Node_erased")
36 | new_line_node.connect("changed_offset", self, "_on_Node_changed_offset")
37 | new_line_node.connect("text_changed", self, "_on_Node_text_changed")
38 | new_line_node.connect("pressed_save", self, "_on_Node_pressed_save")
39 | new_line_node.connect("pressed_load", self, "_on_Node_pressed_load")
40 | new_line_node.connect("pressed_editor", self, "_on_Node_pressed_editor")
41 | new_line_node.connect("changed_slots", self, "_on_Node_changed_slots")
42 | new_line_node.connect("changed_size", self, "_on_Node_changed_size")
43 | var slot_count = self._Story_Editor.get_node_property(self._did, new_nid, "slot_amount")
44 | self._Story_Editor.set_node_property(self._did, new_nid, "rect_size", new_line_node.rect_size)
45 | new_line_node.set_slot_amount(slot_count)
46 | self._Graph.add_child(new_line_node)
47 |
48 |
49 | func _on_Back_BTN_pressed():
50 | self.emit_signal("back_BTN_pressed")
51 |
52 |
53 | func _on_Close_BTN_pressed():
54 | self.emit_signal("close_BTN_pressed")
55 |
56 |
57 | func _on_GraphEdit_connection_request(from, from_slot, to, to_slot):
58 | self._Graph.connect_node(from, from_slot, to, to_slot)
59 | var from_node = self._Graph.get_node(from)
60 | var to_node = self._Graph.get_node(to)
61 | var from_node_nid = from_node.get_nid()
62 | var to_node_nid = to_node.get_nid()
63 | self._Story_Editor.set_link(self._did, from_node_nid, from_slot, to_node_nid)
64 |
65 |
66 | func _on_GraphEdit_disconnection_request(from, from_slot, to, to_slot):
67 | self._Graph.disconnect_node(from, from_slot, to, to_slot)
68 | var from_node = self._Graph.get_node(from)
69 | var from_node_nid = from_node.get_nid()
70 | self._Story_Editor.erase_link(self._did, from_node_nid, from_slot)
71 |
72 |
73 | func _on_Load_Template_file_selected(filename):
74 | var file_data = load(filename)
75 | if not file_data.TYPE == "EXP_Dialog_Node_Template_editor":
76 | return
77 |
78 | self._Target_Node.set_text(file_data.template)
79 |
80 |
81 | func _on_Node_changed_offset(nid, new_offset):
82 | self._Story_Editor.set_node_property(self._did, nid, "graph_offset", new_offset)
83 |
84 |
85 | func _on_Node_changed_size(changed_node):
86 | var changed_node_nid = changed_node.get_nid()
87 | self._Story_Editor.set_node_property(self._did, changed_node_nid, "rect_size", changed_node.rect_size)
88 |
89 |
90 | func _on_Node_changed_slots(changed_node):
91 | var changed_node_nid = changed_node.get_nid()
92 | self._unlink_nid_from_story(changed_node_nid)
93 | self._disconnect_all_from(changed_node)
94 | self._Story_Editor.set_node_property(self._did, changed_node_nid, "slot_amount",
95 | changed_node.get_slot_amount())
96 |
97 |
98 | func _on_Node_erased(deleted_node):
99 | var deleted_nid = deleted_node.get_nid()
100 | self._unlink_nid_from_story(deleted_nid)
101 | self._Story_Editor.erase_node(self._did, deleted_nid)
102 | self._disconnect_all_from(deleted_node)
103 | deleted_node.queue_free()
104 |
105 |
106 | func _on_Node_pressed_editor(node):
107 | self._Editor.set_target_node(node)
108 | self._Editor.visible = true
109 |
110 |
111 | func _on_Node_pressed_load(node):
112 | self._Target_Node = node
113 | self._Load_Template.popup_centered_ratio(0.7)
114 |
115 |
116 | func _on_Node_pressed_save(node):
117 | self._Target_Node = node
118 | self._Save_Template_As.popup_centered_ratio(0.7)
119 |
120 |
121 | func _on_Node_text_changed(nid, new_text):
122 | self._Story_Editor.set_node_property(self._did, nid, "text", new_text)
123 |
124 |
125 | func _on_Save_Template_As_file_selected(filename):
126 | var save_file = _NodeTemplate.new()
127 | save_file.template = self._Target_Node.get_text()
128 | ResourceSaver.save(filename, save_file)
129 |
130 |
131 | func _on_Story_Editor_dialog_edit_pressed(story_editor, did : int):
132 | self._Story_Editor = story_editor
133 | self._did = did
134 | self._Human_Readable_LBL.text = self._Story_Editor.get_dialog_property(self._did, "human_readable_description")
135 | self._clear_nodes()
136 | self._populate_graph()
137 | self._link_node_connections()
138 |
139 | #Private Methods
140 |
141 | func _clear_nodes():
142 | self._Graph.clear_connections()
143 | var children = self._Graph.get_children()
144 | for child in children:
145 | if child is GraphNode:
146 | child.free()
147 |
148 |
149 | func _disconnect_all_from(node):
150 | var node_name = node.name
151 | var connection_list = self._Graph.get_connection_list()
152 | for connection in connection_list:
153 | if connection["from"] == node.name or connection["to"] == node.name:
154 | self._Graph.disconnect_node(connection["from"], connection["from_port"],
155 | connection["to"], connection["to_port"])
156 |
157 |
158 | func _link_node_connections():
159 | var node_IDs = self._Story_Editor.get_nids(self._did)
160 | for nID in node_IDs:
161 | var slots = self._Story_Editor.get_link_slots(self._did, nID)
162 | for slot in slots:
163 | var to_node_nid = self._Story_Editor.get_nid_link_from(self._did, nID, slot)
164 | var to = "NID " + str(to_node_nid)
165 | var from = "NID " + str(nID)
166 | self._Graph.connect_node(from, slot, to, 0)
167 |
168 |
169 | func _populate_graph():
170 | var node_IDs = self._Story_Editor.get_nids(self._did)
171 | for nID in node_IDs:
172 | var new_node : GraphNode
173 | match self._Story_Editor.get_node_property(self._did, nID, "type"):
174 | "line":
175 | new_node = _LineNode.instance()
176 | var slot_count = self._Story_Editor.get_node_property(self._did, nID, "slot_amount")
177 | new_node.set_slot_amount(slot_count)
178 | self._Graph.add_child(new_node)
179 | new_node.connect("erased", self, "_on_Node_erased")
180 | var new_text = self._Story_Editor.get_node_property(self._did, nID, "text")
181 | var new_rect_size = self._Story_Editor.get_node_property(self._did, nID, "rect_size")
182 | new_node.rect_size = new_rect_size
183 | new_node.set_text(new_text)
184 | new_node.connect("text_changed", self, "_on_Node_text_changed")
185 | new_node.connect("pressed_save", self, "_on_Node_pressed_save")
186 | new_node.connect("pressed_load", self, "_on_Node_pressed_load")
187 | new_node.connect("pressed_editor", self, "_on_Node_pressed_editor")
188 | new_node.connect("changed_slots", self, "_on_Node_changed_slots")
189 | new_node.connect("changed_size", self, "_on_Node_changed_size")
190 |
191 | new_node.set_nid(nID)
192 | new_node.offset = self._Story_Editor.get_node_property(self._did, nID, "graph_offset")
193 | new_node.connect("changed_offset", self, "_on_Node_changed_offset")
194 |
195 |
196 | func _setup_dialogs():
197 | self._Load_Template = EditorFileDialog.new()
198 | self._Load_Template.mode = EditorFileDialog.MODE_OPEN_FILE
199 | self._Load_Template.add_filter("*.res ; Template files")
200 | self._Load_Template.resizable = true
201 | self._Load_Template.access = EditorFileDialog.ACCESS_RESOURCES
202 | self._Load_Template.current_dir = "res://"
203 | self._Load_Template.connect("file_selected", self, "_on_Load_Template_file_selected")
204 | self.add_child(self._Load_Template)
205 |
206 | self._Save_Template_As = EditorFileDialog.new()
207 | self._Save_Template_As.mode = EditorFileDialog.MODE_SAVE_FILE
208 | self._Save_Template_As.add_filter("*.res ; Template files")
209 | self._Save_Template_As.resizable = true
210 | self._Save_Template_As.access = EditorFileDialog.ACCESS_RESOURCES
211 | self._Save_Template_As.current_dir = "res://"
212 | self._Save_Template_As.connect("file_selected", self, "_on_Save_Template_As_file_selected")
213 | self.add_child(self._Save_Template_As)
214 |
215 |
216 | func _unlink_nid_from_story(removed_nid):
217 | self._Story_Editor.erase_all_links(self._did, removed_nid)
218 | var nIDs = self._Story_Editor.get_nids(self._did)
219 | for nID in nIDs:
220 | var node_slots = self._Story_Editor.get_link_slots(self._did, nID)
221 | for slot in node_slots:
222 | var to_node_nid = self._Story_Editor.get_nid_link_from(self._did, nID, slot)
223 | if to_node_nid == removed_nid:
224 | self._Story_Editor.erase_link(self._did, nID, slot)
225 |
--------------------------------------------------------------------------------
/Dialog-System-Example/stories/Example_Story_Temp.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Resource_EditorStory/EXP_EditorStory.gd" type="Script" id=1]
4 |
5 | [resource]
6 | script = ExtResource( 1 )
7 | TYPE = "EXP_Story_editor"
8 | names = {
9 | "DialogPlayer/CharacterTextures": 5,
10 | "DialogPlayer/ChoiceBranching": 3,
11 | "DialogPlayer/Test": 1,
12 | "DialogPlayer/TextureLibrary": 4,
13 | "DialogPlayer/VariableInjection": 2
14 | }
15 | story = {
16 | 1: {
17 | "available_nid": [ ],
18 | "groups": [ ],
19 | "human_readable_description": "This is the first test of the vanilla dialog player.",
20 | "name": "DialogPlayer/Test",
21 | "nodes": {
22 | 1: {
23 | "graph_offset": Vector2( 120, 100 ),
24 | "links": {
25 | 0: 2
26 | },
27 | "rect_size": Vector2( 324, 137 ),
28 | "slot_amount": 1,
29 | "text": "",
30 | "type": "line"
31 | },
32 | 2: {
33 | "graph_offset": Vector2( 200, 280 ),
34 | "links": {
35 | 0: 3
36 | },
37 | "rect_size": Vector2( 324, 137 ),
38 | "slot_amount": 1,
39 | "text": "Dave the Dev
40 | ",
41 | "type": "line"
42 | },
43 | 3: {
44 | "graph_offset": Vector2( 280, 460 ),
45 | "links": {
46 | 0: 4
47 | },
48 | "rect_size": Vector2( 324, 137 ),
49 | "slot_amount": 1,
50 | "text": "Dave the Dev
51 | ",
52 | "type": "line"
53 | },
54 | 4: {
55 | "graph_offset": Vector2( 800, 100 ),
56 | "links": {
57 | 0: 5
58 | },
59 | "rect_size": Vector2( 324, 137 ),
60 | "slot_amount": 1,
61 | "text": "Twitter-Kun
62 | ",
63 | "type": "line"
64 | },
65 | 5: {
66 | "graph_offset": Vector2( 880, 280 ),
67 | "links": {
68 | 0: 7
69 | },
70 | "rect_size": Vector2( 324, 137 ),
71 | "slot_amount": 1,
72 | "text": "Insta-Chan
73 | ",
74 | "type": "line"
75 | },
76 | 6: {
77 | "graph_offset": Vector2( 940, 660 ),
78 | "links": {
79 |
80 | },
81 | "rect_size": Vector2( 324, 137 ),
82 | "slot_amount": 1,
83 | "text": "",
84 | "type": "line"
85 | },
86 | 7: {
87 | "graph_offset": Vector2( 940, 460 ),
88 | "links": {
89 | 0: 6
90 | },
91 | "rect_size": Vector2( 324, 137 ),
92 | "slot_amount": 1,
93 | "text": "Dave the Dev
94 | ",
95 | "type": "line"
96 | }
97 | }
98 | },
99 | 2: {
100 | "available_nid": [ ],
101 | "groups": [ ],
102 | "human_readable_description": "This is a test of variable injection.",
103 | "name": "DialogPlayer/VariableInjection",
104 | "nodes": {
105 | 1: {
106 | "graph_offset": Vector2( 80, 100 ),
107 | "links": {
108 | 0: 2
109 | },
110 | "rect_size": Vector2( 324, 137 ),
111 | "slot_amount": 1,
112 | "text": "",
113 | "type": "line"
114 | },
115 | 2: {
116 | "graph_offset": Vector2( 80, 280 ),
117 | "links": {
118 | 0: 4
119 | },
120 | "rect_size": Vector2( 324, 137 ),
121 | "slot_amount": 1,
122 | "text": "DaveTheDev
123 | ",
124 | "type": "line"
125 | },
126 | 3: {
127 | "graph_offset": Vector2( 620, 460 ),
128 | "links": {
129 |
130 | },
131 | "rect_size": Vector2( 324, 137 ),
132 | "slot_amount": 1,
133 | "text": "",
134 | "type": "line"
135 | },
136 | 4: {
137 | "graph_offset": Vector2( 80, 460 ),
138 | "links": {
139 | 0: 7
140 | },
141 | "rect_size": Vector2( 324, 137 ),
142 | "slot_amount": 1,
143 | "text": "Program
144 | ",
145 | "type": "line"
146 | },
147 | 5: {
148 | "graph_offset": Vector2( 620, 100 ),
149 | "links": {
150 | 0: 6
151 | },
152 | "rect_size": Vector2( 324, 137 ),
153 | "slot_amount": 1,
154 | "text": "Program
155 | ",
156 | "type": "line"
157 | },
158 | 6: {
159 | "graph_offset": Vector2( 620, 280 ),
160 | "links": {
161 | 0: 3
162 | },
163 | "rect_size": Vector2( 324, 137 ),
164 | "slot_amount": 1,
165 | "text": "Program
166 | ",
167 | "type": "line"
168 | },
169 | 7: {
170 | "graph_offset": Vector2( 600, -80 ),
171 | "links": {
172 | 0: 5
173 | },
174 | "rect_size": Vector2( 324, 137 ),
175 | "slot_amount": 1,
176 | "text": "Program
177 | ",
178 | "type": "line"
179 | }
180 | }
181 | },
182 | 3: {
183 | "available_nid": [ ],
184 | "groups": [ ],
185 | "human_readable_description": "This is a test of choice branching.",
186 | "name": "DialogPlayer/ChoiceBranching",
187 | "nodes": {
188 | 1: {
189 | "graph_offset": Vector2( 60, 80 ),
190 | "links": {
191 | 0: 3
192 | },
193 | "rect_size": Vector2( 324, 137 ),
194 | "slot_amount": 1,
195 | "text": "",
196 | "type": "line"
197 | },
198 | 2: {
199 | "graph_offset": Vector2( 1140, 340 ),
200 | "links": {
201 |
202 | },
203 | "rect_size": Vector2( 324, 137 ),
204 | "slot_amount": 1,
205 | "text": "",
206 | "type": "line"
207 | },
208 | 3: {
209 | "graph_offset": Vector2( 60, 280 ),
210 | "links": {
211 | 0: 4,
212 | 1: 5,
213 | 2: 6
214 | },
215 | "rect_size": Vector2( 324, 137 ),
216 | "slot_amount": 3,
217 | "text": "Wizard
218 |
219 | {\"Red Potion\" : 0, \"Blue Potion\" : 1, \"Green Potion\" : 2}",
220 | "type": "line"
221 | },
222 | 4: {
223 | "graph_offset": Vector2( 560, 120 ),
224 | "links": {
225 | 0: 7
226 | },
227 | "rect_size": Vector2( 324, 137 ),
228 | "slot_amount": 1,
229 | "text": "Red Potion
230 | ",
231 | "type": "line"
232 | },
233 | 5: {
234 | "graph_offset": Vector2( 560, 340 ),
235 | "links": {
236 | 0: 7
237 | },
238 | "rect_size": Vector2( 324, 137 ),
239 | "slot_amount": 1,
240 | "text": "Blue Potion
241 | ",
242 | "type": "line"
243 | },
244 | 6: {
245 | "graph_offset": Vector2( 580, 540 ),
246 | "links": {
247 | 0: 7
248 | },
249 | "rect_size": Vector2( 324, 137 ),
250 | "slot_amount": 1,
251 | "text": "Green Potion
252 | ",
253 | "type": "line"
254 | },
255 | 7: {
256 | "graph_offset": Vector2( 1140, 120 ),
257 | "links": {
258 | 0: 2
259 | },
260 | "rect_size": Vector2( 324, 137 ),
261 | "slot_amount": 1,
262 | "text": "Wizard
263 | ",
264 | "type": "line"
265 | }
266 | }
267 | },
268 | 4: {
269 | "available_nid": [ ],
270 | "groups": [ ],
271 | "human_readable_description": "A list of texture filepaths.",
272 | "name": "DialogPlayer/TextureLibrary",
273 | "nodes": {
274 | 1: {
275 | "graph_offset": Vector2( 40, 40 ),
276 | "links": {
277 |
278 | },
279 | "rect_size": Vector2( 324, 137 ),
280 | "slot_amount": 1,
281 | "text": "{
282 | \"1\" : \"res://Dialog-System-Example/textures/characters/1.png\",
283 | \"2\" : \"res://Dialog-System-Example/textures/characters/2.png\",
284 | \"3\" : \"res://Dialog-System-Example/textures/characters/3.png\",
285 | \"4\" : \"res://Dialog-System-Example/textures/characters/4.png\",
286 | \"5\" : \"res://Dialog-System-Example/textures/characters/5.png\"
287 | }",
288 | "type": "line"
289 | }
290 | }
291 | },
292 | 5: {
293 | "available_nid": [ ],
294 | "groups": [ ],
295 | "human_readable_description": "New Dialog - Enter Human Readable Description.",
296 | "name": "DialogPlayer/CharacterTextures",
297 | "nodes": {
298 | 1: {
299 | "graph_offset": Vector2( 40, 40 ),
300 | "links": {
301 | 0: 3
302 | },
303 | "rect_size": Vector2( 324, 137 ),
304 | "slot_amount": 1,
305 | "text": "",
306 | "type": "line"
307 | },
308 | 2: {
309 | "graph_offset": Vector2( 600, 520 ),
310 | "links": {
311 |
312 | },
313 | "rect_size": Vector2( 324, 137 ),
314 | "slot_amount": 1,
315 | "text": "",
316 | "type": "line"
317 | },
318 | 3: {
319 | "graph_offset": Vector2( 40, 200 ),
320 | "links": {
321 | 0: 4
322 | },
323 | "rect_size": Vector2( 324, 137 ),
324 | "slot_amount": 1,
325 | "text": "One
326 |
327 | 1",
328 | "type": "line"
329 | },
330 | 4: {
331 | "graph_offset": Vector2( 40, 360 ),
332 | "links": {
333 | 0: 5
334 | },
335 | "rect_size": Vector2( 324, 137 ),
336 | "slot_amount": 1,
337 | "text": "Two
338 |
339 | 2",
340 | "type": "line"
341 | },
342 | 5: {
343 | "graph_offset": Vector2( 600, 40 ),
344 | "links": {
345 | 0: 6
346 | },
347 | "rect_size": Vector2( 324, 137 ),
348 | "slot_amount": 1,
349 | "text": "Three
350 |
351 | 3",
352 | "type": "line"
353 | },
354 | 6: {
355 | "graph_offset": Vector2( 600, 200 ),
356 | "links": {
357 | 0: 7
358 | },
359 | "rect_size": Vector2( 324, 137 ),
360 | "slot_amount": 1,
361 | "text": "Four
362 |
363 | 4",
364 | "type": "line"
365 | },
366 | 7: {
367 | "graph_offset": Vector2( 600, 360 ),
368 | "links": {
369 | 0: 2
370 | },
371 | "rect_size": Vector2( 324, 137 ),
372 | "slot_amount": 1,
373 | "text": "Five
374 |
375 | 5",
376 | "type": "line"
377 | }
378 | }
379 | }
380 | }
381 | available_dids = [ ]
382 | groups = [ ]
383 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## EXP Godot Dialog System v.1.2.0
2 | #### By David Lipps aka Dave the Dev at EXPWorlds for Godot 3.2 Stable
3 | ---
4 | 
5 | 
6 | ---
7 |
8 | This is an addon which assists in the generation, organization, storage, and access of large amounts of text data for Godot Engine projects. It consists of a Story Editor to create and manage lists of dialog records via tags and search filters. And a Dialog Editor through which text/dialog data is created and edited via graph nodes. Although primary designed to handle game dialog, the editors can be used to manage text data for any purpose. Text data can be saved to resource files and read in your game code via the provided helper class.
9 |
10 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=22E6SNKTLTHRL¤cy_code=USD&source=url) If this project helped you out, consider sending some coffee my way so I can stay locked in "the zone" and bring the Godot community more cool free stuff.
11 |
12 | ---
13 | #### Documentation:
14 | - Click [here](https://docs.godotengine.org/en/stable/tutorials/plugins/editor/index.html) to learn how to use Godot plugins.
15 | - Once the plugin is active in the project settings, the StoryFile Editor can be found in the Project -> Tools menu as the "EXP StoryFile Editor". When selected from the tools menu, the StoryFile Editor will become available in bottom panel along with the Output, Debugger, and Animation tools.
16 | - Each project file is called a Story. Each Story contains many Dialogs (DID: Dialog ID) and each dialog record contains numerous Nodes (NID: Node ID). Each node stores text and potentially numerous "slots" that may connect to other nodes. When a slot is connected to another node, it associates itself with the NID of the node its connected to.
17 | - The Dialog System is comprised of two editors. The StoryFile Editor and Dialog Editor. The StoryFile Editor manages all the dialog records in the story, while the Dialog Editor manages the nodes contained in each dialog record.
18 | - The EXP Dialog System is back-end only, it will help you create, manage, store and access text, but parsing the text and putting it to the screen is the responsiblity of each individual project. Once a baked story file is created, the provided StoryFile Reader helper class will allow your project access to data you've created and saved with the Editors.
19 | ##### The StoryFile Editor
20 | - In the StoryFile Editor, dialog records can be created and deleted. Each new dialog record will be assigned a unique DID. To delete a record, check all the records you wish to delete, and press Delete.
21 | - Human readable descriptions can be given to each dialog record to describe its contents, which can be searched using the filter tools.
22 | - Under the "Story" menu, using Save As, saves all the text and node connection data to a Story File Resource, plus information needed to load the data back into the editors for further editing. However, using Bake As saves only non-extraneous text and node connection data to a Story File Resource. Baked Story Files cannot be reloaded into the editor.
23 | - Tags can be added and removed from the story project via the Tag Manager.
24 | - To apply a tag to a dialog record, select the desired tag from the Tag drop-down menu in the tag section, check all dialog records you wish to apply the tag to, and press apply. And do similarly to remove tags from dialog records.
25 | - Dialog Records can be given a name which is associated with the DID. To give the record a name, click the "NAME" field and rename it. To rename it again, simply click on the name. Just as DIDs are unique to each record, names must be unique too. The editor will not let you use a name that is already in use by other record.
26 | - Typing a query in the "Search By" text box will filter and display only the dialog records whose human readable descriptions match the entered query.
27 | - The Search By menu can be changed between searching the human readable descriptions, by DIDs, and by record names.
28 | - In the Search By Tag menu, tags can be toggled on and off, to show and hide dialog records associated with tags, or records with no tags. The tag menu works in conjunction with the search query, so be mindful of the settings when searching for records.
29 | - To edit the nodes in a dialog record, click the "EDIT" button on the desired record and the Dialog Editor will be opened.
30 | ##### The Dialog Editor
31 | - Click the "Add Node" button to add new nodes. Each new node will be assigned a unique NID.
32 | - Text can be entered into each node. Clicking the expand button will open a larger text editor window.
33 | - You can also save and load text templates to and from nodes in the Dialog Editor to make the process of entering text less painful and error prone.
34 | - Each node has one input slot which any number of nodes may connect to and any number of output slots. Use the Slot spinbox to pick the desired number of output slots.
35 | - For variable injection, custom bbcode style tags can be used. When your project reads a story file, it can parse and replace the text before displaying to the screen.
36 | - For branching logic, slot connections are stored for each node. Simple logic can be represented via your project's custom script to choose which output slot to follow at run time.
37 | - Keep in mind that, while the node connections are stored in the Story File, technically, using node connections is not at all required. Your project can access any DID/NID, at any time, via the StoryFile Reader. However, the node connections can also be read via the StoryFile Reader and used in your project, if required by your use case.
38 | ##### Localization and Text Translation
39 | - The data in a Story Project can be exported to a CSV formatted file. A CSV record will be generated for each node in every dialog record. The text associated with each DID/NID may then be edited/translated in any software capable of using CSV files. Once the edits are complete, the CSV file can be reloaded into the Story Project and the text in each node will be replaced.
40 | - However, keep in mind importing CSV files will not create new dialog records or nodes. For the text to be replaced, the dialog record and nodes must already exist. Be sure the correct story file is loaded in the editor before loading its corresponding CSV file.
41 | - When the edited text is loaded from CSV file, the Story Project can be saved as a unique file for each localization/language required. (ex. Story_en.tres or Story_jp.tres) Your project can choose which story file to load and use in the StoryFile Reader based on local settings.
42 |
43 | #### Using the Story File Reader Helper Class:
44 | Once you've written all your text, and baked a story file, you can use the Story Reader Helper Class to access the text and node connection data from the file in your projects. Here is an example of how to use the story reader and the class documentation...
45 |
46 | var Story_Reader_Class = load("res://addons/EXP-System-Dialog/Reference_StoryReader/EXP_StoryReader.gd")
47 | var Story_Reader = Story_Reader_Class.new()
48 |
49 | var story = load("baked-story.tres")
50 | Story_Reader.read(story)
51 |
52 | var did : int = 1
53 | var nid : int = 1
54 | var text : String = Story_Reader.get_text(did, nid)
55 | print(text)
56 |
57 | ##### func get_dids() -> Array:
58 | >Returns a list (int) of all the DIDs in the Story
59 |
60 | ##### func get_did_via_record_name(name : String) -> int:
61 | >Returns the DID associated with a user defined name.
62 |
63 | ##### func get_nid_from_slot(did : int, nid : int, slot : int) -> int:
64 | >Returns the NID of the node the specified slot is connected to.
65 |
66 | ##### func get_nid_via_exact_text(did : int, query : String) -> int:
67 | >Returns the NID of first node with a text that exactly matches the provided query. Useful for finding the start, finish or any other tagged point in a dialog. Returns -1 if an exact match is not found in any node.
68 |
69 | ##### func get_nids(did : int) -> Array:
70 | >Returns a list (int) of all the NIDs associated with the specified DID.
71 |
72 | ##### func get_slot_count(did : int, nid : int) -> int:
73 | >Slots that are not used in the Dialog Editor get culled in the baking process, therefore, this function returns the count of the slots actually connected to other nodes.
74 |
75 | ##### func get_slots(did : int, nid : int) -> Array:
76 | >Returns a list (int) of all the Slots associated with the specified NID.
77 |
78 | ##### func get_text(did : int, nid : int) -> String:
79 | >Returns the text associated with the specified node.
80 |
81 | ##### func has_did(did : int) -> bool:
82 |
83 | ##### func has_nid(did : int, nid : int) -> bool:
84 |
85 | ##### func has_record_name(name : String) -> bool:
86 |
87 | ##### func has_slot(did: int, nid : int, slot : int) -> bool:
88 |
89 | ##### func read(story : Resource):
90 | >Sets the story resource read by the Story Reader object. Story Readers can only use baked stories. Reading a second baked story will simply replace the first one. Your project is responsible for loading story files into memory.
91 |
92 | ---
93 | #### Change Log
94 | ##### 2020-04-24:
95 | - Incremented to v1.2.0
96 | - Dialog records can be given an optional record name to associate with its DID.
97 | - Dialog records can be searched via the record name.
98 | ##### 2020-03-07:
99 | - Incrimented to v1.1.0
100 | - Added CSV Import/Export to support easy Localization and Text Translation.
101 | - Groups are now Tags.
102 | - Collapsed StoryEditor options into a "Story" dropdown menu.
103 | - Updated the Story Reader helper class.
104 | ##### 2019-11-20:
105 | - Added get_nid_via_exact_text method to Story Reader Helper Class.
106 | ##### 2019-11-19:
107 | - Added option to pick search type, by Human Readable Description, or by DID.
108 | - Removed auto generated Start and Finish nodes, if they are needed, they can be made manually with the normal node.
109 |
110 | ---
111 | I can be reached at: davidlipps.dev@gmail.com
112 | I'd love to hear your thoughts. Especially about how I can improve. I'll do my best to get back to you.
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/Story_Editor.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=2]
2 |
3 | [ext_resource path="res://addons/EXP-System-Dialog/Story Editor/story_editor.gd" type="Script" id=1]
4 |
5 | [node name="Story_Editor" type="Control"]
6 | anchor_right = 1.0
7 | anchor_bottom = 1.0
8 | rect_min_size = Vector2( 0, 256 )
9 | script = ExtResource( 1 )
10 | __meta__ = {
11 | "_edit_use_anchors_": false
12 | }
13 |
14 | [node name="VBoxContainer" type="VBoxContainer" parent="."]
15 | anchor_right = 1.0
16 | anchor_bottom = 1.0
17 | __meta__ = {
18 | "_edit_use_anchors_": false
19 | }
20 |
21 | [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
22 | margin_right = 1024.0
23 | margin_bottom = 20.0
24 |
25 | [node name="Close_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
26 | margin_right = 47.0
27 | margin_bottom = 20.0
28 | text = "Close"
29 |
30 | [node name="VSeparator5" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
31 | margin_left = 51.0
32 | margin_right = 55.0
33 | margin_bottom = 20.0
34 |
35 | [node name="Story" type="MenuButton" parent="VBoxContainer/HBoxContainer"]
36 | margin_left = 59.0
37 | margin_right = 103.0
38 | margin_bottom = 20.0
39 | text = "Story"
40 | items = [ "New Story", null, 0, false, false, 0, 0, null, "", false, "Load Story", null, 0, false, false, 1, 0, null, "", false, "Save Story As", null, 0, false, false, 2, 0, null, "", false, "Bake Story As", null, 0, false, false, 3, 0, null, "", false, "Save CSV As", null, 0, false, false, 4, 0, null, "", false, "Load CSV", null, 0, false, false, 5, 0, null, "", false ]
41 |
42 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
43 | margin_left = 107.0
44 | margin_right = 111.0
45 | margin_bottom = 20.0
46 |
47 | [node name="Label2" type="Label" parent="VBoxContainer/HBoxContainer"]
48 | margin_left = 115.0
49 | margin_top = 3.0
50 | margin_right = 163.0
51 | margin_bottom = 17.0
52 | text = "Dialog: "
53 |
54 | [node name="Create_Dialog_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
55 | margin_left = 167.0
56 | margin_right = 220.0
57 | margin_bottom = 20.0
58 | hint_tooltip = "Create a new dialog record."
59 | text = "Create"
60 |
61 | [node name="Delete_Dialog_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
62 | margin_left = 224.0
63 | margin_right = 279.0
64 | margin_bottom = 20.0
65 | hint_tooltip = "Delete all checked dialog records."
66 | text = "Delete"
67 |
68 | [node name="Check_All_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
69 | margin_left = 283.0
70 | margin_right = 354.0
71 | margin_bottom = 20.0
72 | hint_tooltip = "Check all currently visible dialog records."
73 | text = "Check All"
74 |
75 | [node name="UnCheck_All_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
76 | margin_left = 358.0
77 | margin_right = 447.0
78 | margin_bottom = 20.0
79 | hint_tooltip = "Uncheck all currently visible dialog records."
80 | text = "UnCheck All"
81 |
82 | [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
83 | margin_left = 451.0
84 | margin_right = 455.0
85 | margin_bottom = 20.0
86 |
87 | [node name="Label3" type="Label" parent="VBoxContainer/HBoxContainer"]
88 | margin_left = 459.0
89 | margin_top = 3.0
90 | margin_right = 495.0
91 | margin_bottom = 17.0
92 | text = "Tags: "
93 |
94 | [node name="Group_Manager_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
95 | margin_left = 499.0
96 | margin_right = 590.0
97 | margin_bottom = 20.0
98 | hint_tooltip = "Toggles the Group Manager open and closed. Use the Group Manager to add and delete groups that can be applied to dialog records in this story project."
99 | toggle_mode = true
100 | text = "Tag Manager"
101 |
102 | [node name="VSeparator3" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
103 | margin_left = 594.0
104 | margin_right = 598.0
105 | margin_bottom = 20.0
106 |
107 | [node name="Apply_Group_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
108 | margin_left = 602.0
109 | margin_right = 650.0
110 | margin_bottom = 20.0
111 | hint_tooltip = "Apply the group selected in the Group selector menu to all checked dialog records."
112 | text = "Apply"
113 |
114 | [node name="Remove_Group_BTN" type="Button" parent="VBoxContainer/HBoxContainer"]
115 | margin_left = 654.0
116 | margin_right = 718.0
117 | margin_bottom = 20.0
118 | hint_tooltip = "Remove the group selected in the Group selector menu from all checked dialog records if the group is applied to them."
119 | text = "Remove"
120 |
121 | [node name="Group_Selector_BTN" type="OptionButton" parent="VBoxContainer/HBoxContainer"]
122 | margin_left = 722.0
123 | margin_right = 779.0
124 | margin_bottom = 20.0
125 | hint_tooltip = "Select a group to apply or remove from dialog records."
126 | text = "Tags"
127 |
128 | [node name="VSeparator4" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
129 | margin_left = 783.0
130 | margin_right = 787.0
131 | margin_bottom = 20.0
132 |
133 | [node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer"]
134 | margin_top = 24.0
135 | margin_right = 1024.0
136 | margin_bottom = 572.0
137 | size_flags_horizontal = 3
138 | size_flags_vertical = 3
139 |
140 | [node name="Panel" type="Panel" parent="VBoxContainer/HBoxContainer3"]
141 | margin_right = 1024.0
142 | margin_bottom = 548.0
143 | size_flags_horizontal = 3
144 | size_flags_vertical = 3
145 |
146 | [node name="VScrollBar" type="ScrollContainer" parent="VBoxContainer/HBoxContainer3/Panel"]
147 | anchor_right = 1.0
148 | anchor_bottom = 1.0
149 | rect_min_size = Vector2( 0, 128 )
150 | size_flags_vertical = 3
151 | __meta__ = {
152 | "_edit_use_anchors_": false
153 | }
154 |
155 | [node name="Dialog_Record_Root" type="VBoxContainer" parent="VBoxContainer/HBoxContainer3/Panel/VScrollBar"]
156 | margin_right = 1024.0
157 | margin_bottom = 548.0
158 | rect_min_size = Vector2( 0, 128 )
159 | size_flags_horizontal = 3
160 | size_flags_vertical = 3
161 | __meta__ = {
162 | "_edit_use_anchors_": false
163 | }
164 |
165 | [node name="Group_Manager" type="VBoxContainer" parent="VBoxContainer/HBoxContainer3"]
166 | visible = false
167 | margin_left = 824.0
168 | margin_right = 1080.0
169 | margin_bottom = 716.0
170 | rect_min_size = Vector2( 256, 0 )
171 | __meta__ = {
172 | "_edit_use_anchors_": false
173 | }
174 |
175 | [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer3/Group_Manager"]
176 | margin_right = 256.0
177 | margin_bottom = 24.0
178 |
179 | [node name="Add_Group_LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer"]
180 | margin_right = 156.0
181 | margin_bottom = 24.0
182 | rect_min_size = Vector2( 128, 0 )
183 | size_flags_horizontal = 3
184 |
185 | [node name="Add_Group_BTN" type="Button" parent="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer"]
186 | margin_left = 160.0
187 | margin_right = 197.0
188 | margin_bottom = 24.0
189 | hint_tooltip = "Add a group to this story project."
190 | text = "Add"
191 | __meta__ = {
192 | "_edit_use_anchors_": false
193 | }
194 |
195 | [node name="Delete_Group_BTN" type="Button" parent="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer"]
196 | margin_left = 201.0
197 | margin_right = 256.0
198 | margin_bottom = 24.0
199 | hint_tooltip = "Remove the selected group from this story project."
200 | text = "Delete"
201 |
202 | [node name="Group_ItemList" type="ItemList" parent="VBoxContainer/HBoxContainer3/Group_Manager"]
203 | margin_top = 28.0
204 | margin_right = 256.0
205 | margin_bottom = 716.0
206 | size_flags_vertical = 3
207 |
208 | [node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
209 | margin_top = 576.0
210 | margin_right = 1024.0
211 | margin_bottom = 600.0
212 | rect_min_size = Vector2( 0, 24 )
213 |
214 | [node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"]
215 | margin_top = 5.0
216 | margin_right = 65.0
217 | margin_bottom = 19.0
218 | text = "Search by "
219 |
220 | [node name="Search_OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"]
221 | margin_left = 69.0
222 | margin_right = 233.0
223 | margin_bottom = 24.0
224 | text = "Human Readable LBL"
225 | items = [ "Human Readable LBL", null, false, 0, null, "DID", null, false, 1, null ]
226 | selected = 0
227 |
228 | [node name="Search_LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer2"]
229 | margin_left = 237.0
230 | margin_right = 493.0
231 | margin_bottom = 24.0
232 | rect_min_size = Vector2( 256, 0 )
233 |
234 | [node name="Filter_MenuButton" type="MenuButton" parent="VBoxContainer/HBoxContainer2"]
235 | margin_left = 497.0
236 | margin_right = 537.0
237 | margin_bottom = 24.0
238 | hint_tooltip = "Select the groups that appear listed in the story editor."
239 | keep_pressed_outside = true
240 | text = "Tags"
241 | flat = false
242 | items = [ "-No Tags-", null, 1, true, false, 0, 0, null, "", false ]
243 |
244 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer2"]
245 | margin_left = 541.0
246 | margin_right = 545.0
247 | margin_bottom = 24.0
248 |
249 | [node name="Filename_LBL" type="Label" parent="VBoxContainer/HBoxContainer2"]
250 | margin_left = 549.0
251 | margin_top = 5.0
252 | margin_right = 640.0
253 | margin_bottom = 19.0
254 | text = "Unsaved Story"
255 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Close_BTN" to="." method="_on_Close_BTN_pressed"]
256 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Create_Dialog_BTN" to="." method="_on_Create_Dialog_BTN_pressed"]
257 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Delete_Dialog_BTN" to="." method="_on_Delete_Dialog_BTN_pressed"]
258 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Check_All_BTN" to="." method="_on_Check_All_BTN_pressed"]
259 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/UnCheck_All_BTN" to="." method="_on_Uncheck_All_BTN_pressed"]
260 | [connection signal="toggled" from="VBoxContainer/HBoxContainer/Group_Manager_BTN" to="." method="_on_Group_Manager_BTN_toggled"]
261 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Apply_Group_BTN" to="." method="_on_Apply_Group_BTN_pressed"]
262 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Remove_Group_BTN" to="." method="_on_Remove_Group_BTN_pressed"]
263 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/Group_Selector_BTN" to="." method="_on_Group_Selector_BTN_pressed"]
264 | [connection signal="text_entered" from="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer/Add_Group_LineEdit" to="." method="_on_Add_Group_LineEdit_text_entered"]
265 | [connection signal="pressed" from="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer/Add_Group_BTN" to="." method="_on_Add_Group_BTN_pressed"]
266 | [connection signal="pressed" from="VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer/Delete_Group_BTN" to="." method="_on_Delete_Group_BTN_pressed"]
267 | [connection signal="item_selected" from="VBoxContainer/HBoxContainer2/Search_OptionButton" to="." method="_on_Search_OptionButton_item_selected"]
268 | [connection signal="text_changed" from="VBoxContainer/HBoxContainer2/Search_LineEdit" to="." method="_on_Search_LineEdit_text_changed"]
269 |
--------------------------------------------------------------------------------
/addons/EXP-System-Dialog/Story Editor/story_editor.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends Control
3 |
4 | signal changed_story
5 | signal close_pressed
6 | signal dialog_edit_pressed(story, did)
7 |
8 | onready var _Dialog_Record_Root = self.get_node("VBoxContainer/HBoxContainer3/Panel/VScrollBar/Dialog_Record_Root")
9 | onready var _Dir = Directory.new()
10 | onready var _Filename_LBL = self.get_node("VBoxContainer/HBoxContainer2/Filename_LBL")
11 | onready var _Filter_Menu = self.get_node("VBoxContainer/HBoxContainer2/Filter_MenuButton")
12 | onready var _Group_List = self.get_node("VBoxContainer/HBoxContainer3/Group_Manager/Group_ItemList")
13 | onready var _Group_Manager_Panel = self.get_node("VBoxContainer/HBoxContainer3/Group_Manager")
14 | onready var _Group_Selector = self.get_node("VBoxContainer/HBoxContainer/Group_Selector_BTN")
15 | onready var _New_Group_LineEdit = self.get_node("VBoxContainer/HBoxContainer3/Group_Manager/HBoxContainer/Add_Group_LineEdit")
16 | onready var _Search_LineEdit = self.get_node("VBoxContainer/HBoxContainer2/Search_LineEdit")
17 | onready var _Search_Option_BTN = self.get_node("VBoxContainer/HBoxContainer2/Search_OptionButton")
18 | onready var _Story_Menu = self.get_node("VBoxContainer/HBoxContainer/Story")
19 |
20 | var _Dialog_Record = preload("res://addons/EXP-System-Dialog/Story Editor/Dialog Record/Dialog_Record.tscn")
21 | var _EXP_Baked_Story = preload("res://addons/EXP-System-Dialog/Resource_BakedStory/EXP_BakedStory.gd")
22 | var _EXP_Story = preload("res://addons/EXP-System-Dialog/Resource_EditorStory/EXP_EditorStory.gd")
23 | var _Record_Rename_Box_TSCN = preload("res://addons/EXP-System-Dialog/Story Editor/Rename Record Box/Rename_Record_Box.tscn")
24 |
25 | var _available_dids : Array
26 | var _Bake_Story_As : EditorFileDialog
27 | var _checked_dialogs : Array = []
28 | var _groups : Array
29 | var _Load_CSV : EditorFileDialog
30 | var _Load_Story : EditorFileDialog
31 | var _record_names : Dictionary
32 | var _Record_Rename_Box
33 | var _Save_CSV_As : EditorFileDialog
34 | var _Save_Story_As : EditorFileDialog
35 | var _story : Dictionary
36 |
37 | #Virtual Methods
38 |
39 | func _ready():
40 | self._create_rename_box()
41 | self._populate_story_menu()
42 | self._setup_dialogs()
43 | self._Filter_Menu.get_popup().connect("index_pressed", self, "_on_Filter_Menu_index_pressed")
44 | self._Filter_Menu.get_popup().hide_on_checkable_item_selection = false
45 | self._populate_filter_menu()
46 | self._populate_searchby_menu()
47 |
48 | #Callback Methods
49 |
50 | func _on_Add_Group_BTN_pressed():
51 | self._add_group()
52 |
53 |
54 | func _on_Add_Group_LineEdit_text_entered(new_text):
55 | self._add_group()
56 |
57 |
58 | func _on_Apply_Group_BTN_pressed():
59 | var id = self._Group_Selector.get_selected_id()
60 | if id == -1:
61 | return
62 |
63 | var idx = self._Group_Selector.get_item_index(id)
64 | var group = self._Group_Selector.get_popup().get_item_text(idx)
65 | for record in self._checked_dialogs.duplicate():
66 | var did = record.get_did()
67 | self._dialog_apply_group(did, group)
68 | record.uncheck()
69 | self.emit_signal("changed_story")
70 |
71 |
72 | func _on_Bake_Story_As_file_selected(filename : String):
73 | self._bake_data_to(filename)
74 |
75 |
76 | func _on_Bake_Story_BTN_pressed():
77 | self._Bake_Story_As.popup_centered_ratio(0.7)
78 |
79 |
80 | func _on_Check_All_BTN_pressed():
81 | var records = self._Dialog_Record_Root.get_children()
82 | for record in records:
83 | if record.visible:
84 | record.check()
85 |
86 |
87 | func _on_Close_BTN_pressed():
88 | self.emit_signal("close_pressed")
89 |
90 |
91 | func _on_Create_Dialog_BTN_pressed():
92 | self._create_dialog_record()
93 |
94 |
95 | func _on_Delete_Dialog_BTN_pressed():
96 | self._delete_checked_dialogs()
97 |
98 |
99 | func _on_Delete_Group_BTN_pressed():
100 | var idxs = self._Group_List.get_selected_items()
101 | var group
102 | for idx in idxs:
103 | group = self._Group_List.get_item_text(idx)
104 | self._Group_List.remove_item(idx)
105 | self._delete_group(group)
106 | self._populate_group_selector()
107 | self._populate_filter_menu()
108 |
109 |
110 | func _on_Dialog_changed_human_readable_text(did : int, new_text : String):
111 | self.set_dialog_property(did, "human_readable_description", new_text)
112 | self.emit_signal("changed_story")
113 |
114 |
115 | func _on_Dialog_checked(dialog):
116 | self._checked_dialogs.push_front(dialog)
117 |
118 |
119 | func _on_Dialog_edit_pressed(did : int):
120 | self.emit_signal("dialog_edit_pressed", self, did)
121 |
122 |
123 | func _on_Dialog_unchecked(dialog):
124 | self._checked_dialogs.erase(dialog)
125 |
126 |
127 | func _on_Filter_Menu_index_pressed(idx):
128 | var checked = self._Filter_Menu.get_popup().is_item_checked(idx)
129 | if not checked:
130 | self._Filter_Menu.get_popup().set_item_checked(idx, true)
131 | else:
132 | self._Filter_Menu.get_popup().set_item_checked(idx, false)
133 | self._update_filter()
134 |
135 |
136 | func _on_Group_Manager_BTN_toggled(button_pressed : bool):
137 | if button_pressed:
138 | self._Group_Manager_Panel.visible = true
139 | else:
140 | self._Group_Manager_Panel.visible = false
141 |
142 |
143 | func _on_Group_Selector_BTN_pressed():
144 | self._populate_group_selector()
145 |
146 |
147 | func _on_Load_CSV_BTN_pressed():
148 | self._Load_CSV.popup_centered_ratio(0.7)
149 |
150 |
151 | func _on_Load_CSV_file_selected(filepath : String):
152 | var csv_file = File.new()
153 | var status = csv_file.open(filepath, File.READ)
154 |
155 | if not status == OK:
156 | print_debug("EXP_Story_Editor: Error loading file \"" + filepath + "\".")
157 | return
158 |
159 | csv_file.get_csv_line()
160 |
161 | while not csv_file.eof_reached():
162 | var line = csv_file.get_csv_line()
163 |
164 | if line.empty():
165 | continue
166 |
167 | var did = int(line[0])
168 | var nid = int(line[1])
169 | var dialog = String(line[2])
170 |
171 | if not self._story.has(did):
172 | continue
173 | if not self._story[did]["nodes"].has(nid):
174 | continue
175 |
176 | self._story[did]["nodes"][nid]["text"] = dialog
177 |
178 | csv_file.close()
179 |
180 |
181 | func _on_Load_Story_BTN_pressed():
182 | self._Load_Story.popup_centered_ratio(0.7)
183 |
184 |
185 | func _on_Load_Story_file_selected(filename : String):
186 | var file_data = load(filename)
187 | if not file_data.TYPE == "EXP_Story_editor":
188 | return
189 |
190 | self._clear_story()
191 | self._load_data_from(file_data)
192 | self._Filename_LBL.text = filename.get_file()
193 |
194 | for group in self._groups:
195 | self._Group_List.add_item(group)
196 | self._populate_filter_menu()
197 |
198 | for did in self.get_dids():
199 | var new_dialog_record = _Dialog_Record.instance()
200 | self._Dialog_Record_Root.add_child(new_dialog_record)
201 | new_dialog_record.set_story_editor(self)
202 | new_dialog_record.connect("checked", self, "_on_Dialog_checked")
203 | new_dialog_record.connect("unchecked", self, "_on_Dialog_unchecked")
204 | new_dialog_record.connect("changed_human_readable_text", self,
205 | "_on_Dialog_changed_human_readable_text")
206 | new_dialog_record.connect("edit_pressed", self, "_on_Dialog_edit_pressed")
207 | new_dialog_record.connect("rename_pressed", self, "_on_Record_Rename_pressed")
208 |
209 | new_dialog_record.set_did(did)
210 | var human_readable_description = self.get_dialog_property(did, "human_readable_description")
211 | new_dialog_record.update_human_readable_description(human_readable_description)
212 |
213 | if self._story[did].has("name"):
214 | var record_name = self._story[did]["name"]
215 | new_dialog_record.set_record_name(record_name)
216 |
217 |
218 | func _on_New_Story_BTN_pressed():
219 | self._clear_story()
220 |
221 |
222 | func _on_Record_Rename_pressed(record):
223 | self._Record_Rename_Box.set_target_record(record)
224 | self._Record_Rename_Box.visible = true
225 |
226 |
227 | func _on_Remove_Group_BTN_pressed():
228 | var id = self._Group_Selector.get_selected_id()
229 | if id == -1:
230 | return
231 |
232 | var idx = self._Group_Selector.get_item_index(id)
233 | var group = self._Group_Selector.get_popup().get_item_text(idx)
234 | for record in self._checked_dialogs.duplicate():
235 | var did = record.get_did()
236 | self._dialog_remove_group(did, group)
237 | record.uncheck()
238 | self.emit_signal("changed_story")
239 |
240 |
241 | func _on_Rename_Box_Rename(rename : String):
242 | var record = self._Record_Rename_Box.get_target_record()
243 | var old_name = record.get_record_name()
244 | var record_did = record.get_did()
245 |
246 | if rename.empty() or rename == "NAME":
247 | record.set_record_name("NAME")
248 | self._story[record_did].erase("name")
249 | self._record_names.erase(old_name)
250 | return
251 |
252 | if self._record_names.has(rename):
253 | return
254 |
255 | self._record_names.erase(old_name)
256 |
257 | self._record_names[rename] = record_did
258 | self._story[record_did]["name"] = rename
259 | record.set_record_name(rename)
260 |
261 |
262 | func _on_Save_CSV_BTN_pressed():
263 | self._Save_CSV_As.popup_centered_ratio(0.7)
264 |
265 |
266 | func _on_Save_CVS_As_file_selected(filepath : String):
267 | var csv_file = File.new()
268 | var status = csv_file.open(filepath, File.WRITE)
269 |
270 | if not status == OK:
271 | print_debug("EXP_Story_Editor: Error saving csv file \"" + filepath + "\".")
272 | return
273 |
274 | csv_file.store_csv_line(["DID", "NID", "Dialog"], ",")
275 |
276 | for did in self._story.keys():
277 | for nid in self._story[did]["nodes"].keys():
278 | var dialog = self._story[did]["nodes"][nid]["text"]
279 | csv_file.store_csv_line([did, nid, dialog], ",")
280 |
281 | csv_file.close()
282 |
283 |
284 | func _on_Save_Story_As_file_selected(filename : String):
285 | self._save_data_to(filename)
286 | self._Filename_LBL.text = filename.get_file()
287 |
288 |
289 | func _on_Save_Story_BTN_pressed():
290 | self._Save_Story_As.popup_centered_ratio(0.7)
291 |
292 |
293 | func _on_Search_LineEdit_text_changed(new_text : String):
294 | self._update_filter()
295 |
296 |
297 | func _on_Search_OptionButton_item_selected(id):
298 | self._update_filter()
299 |
300 |
301 | func _on_story_menu_option_pressed(id):
302 | match id:
303 | 0:
304 | self._on_New_Story_BTN_pressed()
305 | 1:
306 | self._on_Load_Story_BTN_pressed()
307 | 2:
308 | self._on_Save_Story_BTN_pressed()
309 | 3:
310 | self._on_Bake_Story_BTN_pressed()
311 | 4:
312 | self._on_Save_CSV_BTN_pressed()
313 | 5:
314 | self._on_Load_CSV_BTN_pressed()
315 |
316 |
317 | func _on_Uncheck_All_BTN_pressed():
318 | var records = self._Dialog_Record_Root.get_children()
319 | for record in records:
320 | if record.visible:
321 | record.uncheck()
322 |
323 | #Public Methods
324 |
325 | func create_node(did : int, type : String) -> int:
326 | var new_nid = self._generate_nid(did)
327 | var node_data = {"type": type, "text": "", "graph_offset": Vector2(40, 40),
328 | "rect_size": Vector2(0,0) ,"links": {}, "slot_amount": 1}
329 | self._story[did]["nodes"][new_nid] = node_data
330 | return new_nid
331 |
332 | func dialog_get_groups(did : int):
333 | return self._story[did]["groups"]
334 |
335 |
336 | func erase_all_links(did: int, nid : int):
337 | self._story[did]["nodes"][nid]["links"].clear()
338 |
339 |
340 | func erase_dialog(did : int):
341 | self._story.erase(did)
342 | self._make_did_available(did)
343 |
344 |
345 | func erase_link(did : int, nid : int, slot : int):
346 | self._story[did]["nodes"][nid]["links"].erase(slot)
347 |
348 |
349 | func erase_node(did :int, nid :int):
350 | self._story[did]["nodes"].erase(nid)
351 | self._make_nid_available(did, nid)
352 |
353 |
354 | func get_dialog_property(did : int, property: String):
355 | return self._story[did][property]
356 |
357 |
358 | func get_dids():
359 | return self._story.keys()
360 |
361 |
362 | func get_link_slots(did : int, nid : int):
363 | return self._story[did]["nodes"][nid]["links"].keys()
364 |
365 |
366 | func get_nid_link_from(did : int, nid: int, slot : int):
367 | return self._story[did]["nodes"][nid]["links"][slot]
368 |
369 |
370 | func get_nids(did : int):
371 | return self._story[did]["nodes"].keys()
372 |
373 |
374 | func get_node_property(did : int, nid : int, property: String):
375 | return self._story[did]["nodes"][nid][property]
376 |
377 |
378 | func set_dialog_property(did : int, property : String , data):
379 | self._story[did][property] = data
380 |
381 |
382 | func set_link(did : int, this_nid : int, slot : int, that_nid : int):
383 | self._story[did]["nodes"][this_nid]["links"][slot] = that_nid
384 |
385 |
386 | func set_node_property(did : int, nid : int, property : String , data):
387 | self._story[did]["nodes"][nid][property] = data
388 |
389 |
390 | func set_node_slot_count(did : int, nid : int, amount : int):
391 | self._story[did]["nodes"][nid]["slot_amount"] = amount
392 |
393 | #Private Methods
394 |
395 | func _add_group():
396 | var new_group_name = self._New_Group_LineEdit.text
397 | if new_group_name == "" or self._groups.has(new_group_name):
398 | return
399 |
400 | self._groups.push_back(new_group_name)
401 | self._New_Group_LineEdit.text = ""
402 | self._Group_List.add_item(new_group_name)
403 | self._populate_filter_menu()
404 |
405 | var sort_list : Array
406 | for idx in range(self._Group_List.get_item_count()):
407 | var group = self._Group_List.get_item_text(idx)
408 | sort_list.push_back(group)
409 | sort_list.sort()
410 | self._Group_List.clear()
411 | for group in sort_list:
412 | self._Group_List.add_item(group)
413 |
414 |
415 | func _bake_data() :
416 | var baked_story = self._story.duplicate(true)
417 | for did in baked_story.keys():
418 | baked_story[did].erase("name")
419 | baked_story[did].erase("groups")
420 | baked_story[did].erase("available_nid")
421 | baked_story[did].erase("human_readable_description")
422 | for nid in baked_story[did]["nodes"].keys():
423 | baked_story[did]["nodes"][nid].erase("type")
424 | baked_story[did]["nodes"][nid].erase("graph_offset")
425 | baked_story[did]["nodes"][nid].erase("rect_size")
426 | baked_story[did]["nodes"][nid].erase("slot_amount")
427 | return baked_story.duplicate(true)
428 |
429 |
430 | func _bake_data_to(filename):
431 | var file_data
432 | if self._Dir.file_exists(filename):
433 | file_data = load(filename)
434 | if file_data.TYPE == "EXP_Baked_Story":
435 | file_data.story = self._bake_data()
436 | file_data.names = self._record_names.duplicate(true)
437 | ResourceSaver.save(filename, file_data)
438 | else:
439 | file_data = _EXP_Baked_Story.new()
440 | file_data.story = self._bake_data()
441 | file_data.names = self._record_names.duplicate(true)
442 | ResourceSaver.save(filename, file_data)
443 |
444 |
445 | func _clear_group_manager():
446 | self._groups.clear()
447 | for idx in range(self._Group_List.get_item_count()):
448 | self._Group_List.remove_item(0)
449 | self._populate_group_selector()
450 | self._Filter_Menu.get_popup().clear()
451 |
452 |
453 | func _clear_story():
454 | self._remove_all_records()
455 | self._clear_group_manager()
456 | self._populate_filter_menu()
457 | self._story.clear()
458 | self._available_dids.clear()
459 | self._checked_dialogs.clear()
460 | self._record_names.clear()
461 | self._Filename_LBL.text = "Unsaved Story"
462 | self.emit_signal("changed_story")
463 |
464 |
465 | func _create_dialog() -> int:
466 | var new_did = self._generate_did()
467 | var dialog_data = {"human_readable_description":
468 | "New Dialog - Enter Human Readable Description",
469 | "groups": [],
470 | "available_nid": [],
471 | "nodes": {}}
472 | self._story[new_did] = dialog_data
473 | return new_did
474 |
475 |
476 | func _create_dialog_record():
477 | var new_did = self._create_dialog()
478 |
479 | var new_dialog_record = _Dialog_Record.instance()
480 | self._Dialog_Record_Root.add_child(new_dialog_record)
481 | new_dialog_record.set_story_editor(self)
482 |
483 | new_dialog_record.connect("checked", self, "_on_Dialog_checked")
484 | new_dialog_record.connect("unchecked", self, "_on_Dialog_unchecked")
485 | new_dialog_record.connect("changed_human_readable_text", self,
486 | "_on_Dialog_changed_human_readable_text")
487 | new_dialog_record.connect("edit_pressed", self, "_on_Dialog_edit_pressed")
488 | new_dialog_record.connect("rename_pressed", self, "_on_Record_Rename_pressed")
489 |
490 | new_dialog_record.set_did(new_did)
491 | new_dialog_record.update_human_readable_description(
492 | "New Dialog - Enter Human Readable Description.")
493 |
494 |
495 | func _create_rename_box():
496 | self._Record_Rename_Box = _Record_Rename_Box_TSCN.instance()
497 | self._Record_Rename_Box.connect("rename_BTN_pressed", self, "_on_Rename_Box_Rename")
498 | self.add_child(self._Record_Rename_Box)
499 |
500 |
501 | func _delete_checked_dialogs():
502 | for dialog in self._checked_dialogs:
503 | self._delete_dialog(dialog)
504 | self._checked_dialogs.clear()
505 | self.emit_signal("changed_story")
506 |
507 |
508 | func _delete_dialog(dialog):
509 | var did = dialog.get_did()
510 | self.erase_dialog(did)
511 | self._remove_record(dialog)
512 |
513 |
514 | func _delete_group(group):
515 | self._groups.erase(group)
516 | self._remove_group_from_story(group)
517 |
518 |
519 | func _dialog_apply_group(did : int, group : String):
520 | if not self._story[did]["groups"].has(group):
521 | self._story[did]["groups"].push_back(group)
522 |
523 |
524 | func _dialog_remove_group(did : int, group : String):
525 | if self._story[did]["groups"].has(group):
526 | self._story[did]["groups"].erase(group)
527 |
528 |
529 | func _generate_did() -> int:
530 | if not self._available_dids.empty():
531 | return self._available_dids.pop_front()
532 | else:
533 | return self._story.size() + 1
534 |
535 |
536 | func _generate_nid(did : int) -> int:
537 | if not self._story[did]["available_nid"].empty():
538 | return self._story[did]["available_nid"].pop_front()
539 | else:
540 | return self._story[did]["nodes"].size() + 1
541 |
542 |
543 | func _load_data_from(new_story):
544 | self._story = new_story.story.duplicate(true)
545 | self._available_dids = new_story.available_dids.duplicate(true)
546 | self._groups = new_story.groups.duplicate(true)
547 | self._record_names = new_story.names.duplicate(true)
548 |
549 |
550 |
551 | func _make_did_available(did : int):
552 | self._available_dids.push_front(did)
553 | self._available_dids.sort()
554 |
555 |
556 | func _make_nid_available(did : int, nid : int):
557 | self._story[did]["available_nid"].push_front(nid)
558 | self._story[did]["available_nid"].sort()
559 |
560 |
561 | func _make_records_visible():
562 | var children = self._Dialog_Record_Root.get_children()
563 | for child in children:
564 | child.visible = true
565 |
566 |
567 | func _populate_filter_menu():
568 | self._Filter_Menu.get_popup().clear()
569 | self._Filter_Menu.get_popup().add_check_item("-No Tags-")
570 | for group in self._groups:
571 | self._Filter_Menu.get_popup().add_check_item(group)
572 | for idx in range(self._Filter_Menu.get_popup().get_item_count()):
573 | self._Filter_Menu.get_popup().set_item_checked(idx, true)
574 |
575 |
576 | func _populate_group_selector():
577 | self._Group_Selector.clear()
578 | self._Group_Selector.text = "Tags"
579 | for group in self._groups:
580 | self._Group_Selector.get_popup().add_item(group)
581 |
582 |
583 | func _populate_searchby_menu():
584 | self._Search_Option_BTN.clear()
585 | self._Search_Option_BTN.get_popup().add_item("Human Readable LBL", 0)
586 | self._Search_Option_BTN.get_popup().add_item("DID", 1)
587 | self._Search_Option_BTN.get_popup().add_item("Record Name", 2)
588 | self._Search_Option_BTN.select(0)
589 |
590 | func _populate_story_menu():
591 | self._Story_Menu.get_popup().clear()
592 | self._Story_Menu.get_popup().add_item("New Story", 0)
593 | self._Story_Menu.get_popup().add_item("Load Story", 1)
594 | self._Story_Menu.get_popup().add_item("Save Story As", 2)
595 | self._Story_Menu.get_popup().add_item("Bake Story As", 3)
596 | self._Story_Menu.get_popup().add_item("Save CSV As", 4)
597 | self._Story_Menu.get_popup().add_item("Load CSV", 5)
598 | self._Story_Menu.get_popup().connect("id_pressed", self, "_on_story_menu_option_pressed")
599 |
600 |
601 | func _remove_all_records():
602 | var dialog_records = self._Dialog_Record_Root.get_children()
603 | for record in dialog_records:
604 | self._remove_record(record)
605 |
606 |
607 | func _remove_group_from_story(group : String):
608 | for did in self._story:
609 | if self._story[did]["groups"].has(group):
610 | self._story[did]["groups"].erase(group)
611 |
612 |
613 | func _remove_record(dialog_record):
614 | dialog_record.disconnect("checked", self, "_on_Dialog_checked")
615 | dialog_record.disconnect("unchecked", self, "_on_Dialog_unchecked")
616 | dialog_record.disconnect("changed_human_readable_text", self,
617 | "_on_Dialog_changed_human_readable_text")
618 | dialog_record.disconnect("rename_pressed", self, "_on_Record_Rename_pressed")
619 | var record_name = dialog_record.get_record_name()
620 | if not record_name == "NAME":
621 | self._record_names.erase(record_name)
622 |
623 | dialog_record.free()
624 |
625 |
626 | func _save_data_to(filename):
627 | var file_data
628 | if self._Dir.file_exists(filename):
629 | file_data = load(filename)
630 | if file_data.TYPE == "EXP_Story_editor":
631 | file_data.names = self._record_names.duplicate(true)
632 | file_data.story = self._story.duplicate(true)
633 | file_data.available_dids = self._available_dids.duplicate(true)
634 | file_data.groups = self._groups.duplicate(true)
635 | ResourceSaver.save(filename, file_data)
636 | else:
637 | file_data = _EXP_Story.new()
638 | file_data.names = self._record_names.duplicate(true)
639 | file_data.story = self._story.duplicate(true)
640 | file_data.available_dids = self._available_dids.duplicate(true)
641 | file_data.groups = self._groups.duplicate(true)
642 | ResourceSaver.save(filename, file_data)
643 |
644 |
645 | func _setup_dialogs():
646 | self._Load_Story = EditorFileDialog.new()
647 | self._Load_Story.mode = EditorFileDialog.MODE_OPEN_FILE
648 | self._Load_Story.add_filter("*.tres ; Story files")
649 | self._Load_Story.resizable = true
650 | self._Load_Story.access = EditorFileDialog.ACCESS_RESOURCES
651 | self._Load_Story.current_dir = "res://"
652 | self._Load_Story.connect("file_selected", self, "_on_Load_Story_file_selected")
653 | self.add_child(self._Load_Story)
654 |
655 | self._Save_Story_As = EditorFileDialog.new()
656 | self._Save_Story_As.mode = EditorFileDialog.MODE_SAVE_FILE
657 | self._Save_Story_As.add_filter("*.tres ; Story files")
658 | self._Save_Story_As.resizable = true
659 | self._Save_Story_As.access = EditorFileDialog.ACCESS_RESOURCES
660 | self._Save_Story_As.current_dir = "res://"
661 | self._Save_Story_As.connect("file_selected", self, "_on_Save_Story_As_file_selected")
662 | self.add_child(self._Save_Story_As)
663 |
664 | self._Bake_Story_As = EditorFileDialog.new()
665 | self._Bake_Story_As.mode = EditorFileDialog.MODE_SAVE_FILE
666 | self._Bake_Story_As.add_filter("*.tres ; Baked Story files")
667 | self._Bake_Story_As.resizable = true
668 | self._Bake_Story_As.access = EditorFileDialog.ACCESS_RESOURCES
669 | self._Bake_Story_As.current_dir = "res://"
670 | self._Bake_Story_As.connect("file_selected", self, "_on_Bake_Story_As_file_selected")
671 | self.add_child(self._Bake_Story_As)
672 |
673 | self._Save_CSV_As = EditorFileDialog.new()
674 | self._Save_CSV_As.mode = EditorFileDialog.MODE_SAVE_FILE
675 | self._Save_CSV_As.add_filter("*.csv ; CSV files")
676 | self._Save_CSV_As.resizable = true
677 | self._Save_CSV_As.access = EditorFileDialog.ACCESS_FILESYSTEM
678 | self._Save_CSV_As.current_dir = "res://"
679 | self._Save_CSV_As.connect("file_selected", self, "_on_Save_CVS_As_file_selected")
680 | self.add_child(self._Save_CSV_As)
681 |
682 | self._Load_CSV = EditorFileDialog.new()
683 | self._Load_CSV .mode = EditorFileDialog.MODE_OPEN_FILE
684 | self._Load_CSV .add_filter("*.csv ; CSV files")
685 | self._Load_CSV .resizable = true
686 | self._Load_CSV .access = EditorFileDialog.ACCESS_FILESYSTEM
687 | self._Load_CSV .current_dir = "res://"
688 | self._Load_CSV .connect("file_selected", self, "_on_Load_CSV_file_selected")
689 | self.add_child(self._Load_CSV)
690 |
691 |
692 | func _update_filter():
693 | var new_text = self._Search_LineEdit.text
694 | self._make_records_visible()
695 |
696 | var filter_groups : Array
697 | for idx in range(self._Filter_Menu.get_popup().get_item_count()):
698 | if self._Filter_Menu.get_popup().is_item_checked(idx):
699 | var group = self._Filter_Menu.get_popup().get_item_text(idx)
700 | filter_groups.push_back(group)
701 |
702 | var children = self._Dialog_Record_Root.get_children()
703 |
704 | var search_option = self._Search_Option_BTN.selected
705 |
706 | match search_option:
707 | 0: #Human Readable Search
708 | for child in children:
709 | var did = child.get_did()
710 | var human_readable_description = self.get_dialog_property(did, "human_readable_description")
711 | if human_readable_description.find(new_text) == -1 and not new_text.empty():
712 | child.visible = false
713 | else:
714 | child.visible = false
715 | if self._Filter_Menu.get_popup().get_item_count() == 0:
716 | child.visible = true
717 | var dialog_groups = self.dialog_get_groups(did)
718 | if dialog_groups.empty() and filter_groups.has("-No Tags-"):
719 | child.visible = true
720 | for group in dialog_groups:
721 | if filter_groups.has(group):
722 | child.visible = true
723 | 1: #DID Search
724 | for child in children:
725 | var did = child.get_did()
726 | if not new_text == str(did) and not new_text.empty():
727 | child.visible = false
728 | else:
729 | child.visible = false
730 | if self._Filter_Menu.get_popup().get_item_count() == 0:
731 | child.visible = true
732 | var dialog_groups = self.dialog_get_groups(did)
733 | if dialog_groups.empty() and filter_groups.has("-No Tags-"):
734 | child.visible = true
735 | for group in dialog_groups:
736 | if filter_groups.has(group):
737 | child.visible = true
738 | 2: #Record Name Search
739 | for child in children:
740 | var did = child.get_did()
741 | var record_name = child.get_record_name()
742 | if record_name.find(new_text) == -1 and not new_text.empty():
743 | child.visible = false
744 | else:
745 | child.visible = false
746 | if self._Filter_Menu.get_popup().get_item_count() == 0:
747 | child.visible = true
748 | var dialog_groups = self.dialog_get_groups(did)
749 | if dialog_groups.empty() and filter_groups.has("-No Tags-"):
750 | child.visible = true
751 | for group in dialog_groups:
752 | if filter_groups.has(group):
753 | child.visible = true
754 |
--------------------------------------------------------------------------------