├── icon.png ├── default_env.tres ├── DialogAndQuestManager.tscn ├── DialogResponse.tscn ├── World.gd ├── project.godot ├── icon.png.import ├── World.tscn ├── LICENSE ├── README.md ├── DialogWindow.tscn ├── DialogWindow.gd └── DialogAndQuestManager.gd /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Miziziziz/GodotDialogAndQuestSystem/HEAD/icon.png -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /DialogAndQuestManager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://DialogAndQuestManager.gd" type="Script" id=1] 4 | 5 | [node name="DialogAndQuestManager" type="Node"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /DialogResponse.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="DialogResponse" type="Label"] 4 | editor/display_folded = true 5 | margin_right = 500.0 6 | margin_bottom = 14.0 7 | rect_min_size = Vector2( 500, 0 ) 8 | text = "asdasdasdasdasdasd aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaaa" 9 | valign = 1 10 | autowrap = true 11 | 12 | [node name="Button" type="Button" parent="."] 13 | show_behind_parent = true 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | margin_bottom = 6.0 17 | -------------------------------------------------------------------------------- /World.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | onready var people_to_talk_to = $PeopleToTalkTo 4 | onready var dialog_window = $DialogWindow 5 | 6 | func _ready(): 7 | for button in people_to_talk_to.get_children(): 8 | button.connect("button_up", self, "start_conversation", [button.text]) 9 | dialog_window.connect("closed", self, "end_conversation") 10 | end_conversation() 11 | dialog_window.hide() 12 | 13 | func end_conversation(): 14 | people_to_talk_to.show() 15 | 16 | func start_conversation(convo_id): 17 | people_to_talk_to.hide() 18 | dialog_window.load_conversation(convo_id) 19 | -------------------------------------------------------------------------------- /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="DialogAndQuestSystem" 19 | run/main_scene="res://World.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [autoload] 23 | 24 | DialogAndQuestManager="*res://DialogAndQuestManager.tscn" 25 | 26 | [rendering] 27 | 28 | environment/default_environment="res://default_env.tres" 29 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /World.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://World.gd" type="Script" id=1] 4 | [ext_resource path="res://DialogWindow.tscn" type="PackedScene" id=2] 5 | 6 | [node name="World" type="Control"] 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | script = ExtResource( 1 ) 10 | 11 | [node name="PeopleToTalkTo" type="Control" parent="."] 12 | editor/display_folded = true 13 | margin_right = 40.0 14 | margin_bottom = 40.0 15 | 16 | [node name="John" type="Button" parent="PeopleToTalkTo"] 17 | margin_right = 12.0 18 | margin_bottom = 20.0 19 | text = "john" 20 | 21 | [node name="Sara" type="Button" parent="PeopleToTalkTo"] 22 | margin_top = 29.45 23 | margin_right = 12.0 24 | margin_bottom = 49.45 25 | text = "sara" 26 | 27 | [node name="DialogWindow" parent="." instance=ExtResource( 2 )] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Miziziziz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | To write conversations, go to DialogAndQuestManager.gd and alter the CONVERSATIONS const 3 | To start a conversation, call load_conversation on the DialogWindow scene 4 | 5 | structure of conversation: 6 | conversation_id: { 7 | list of greetings [ 8 | { 9 | id: id of dialog window to show 10 | conditions: (optional) list of conditions that must be met to choose this greeting 11 | add_global_thing_that_has_happened: (optional) if this greeting is chosen, add this to the list of global things that have happened 12 | } 13 | ] 14 | dict of dialog windows { 15 | id of dialog : { 16 | dialog text to show, 17 | responses player can choose [ 18 | { 19 | text: text to show for this response option(can wrap to multiple lines, 20 | switch_to: id of dialog window to switch to if this option is chosen 21 | close_dialog: (optional) closes dialog window if chosen 22 | add_global_thing_that_has_happened: (optional), adds this to list of global things that have happened 23 | add_local_thing_that_has_happened: (optional), adds this to list of local things that have happened 24 | conditions: (optional) a list of conditions that must be met to show this response option 25 | } 26 | ] 27 | } 28 | } 29 | 30 | Conditions can be prepended with ! to mean that the condition must not be met. 31 | Conditions will be check against both local and global lists of things that have happened. 32 | The local_list_of_things_that_have_happened can be used for events local to a conversation, e.g. if you must ask 33 | questions in a specific order to get to a certain dialog window and exiting the conversation means starting over. 34 | 35 | To use this system for quests, just add things to the global_list_of_things_that_have_happened whenever something 36 | interesting happens, e.g. "killed_all_rats", and use the conditions_met or is_condition_met methods to check if 37 | the player should recieve a reward or have access to an area. 38 | ``` 39 | -------------------------------------------------------------------------------- /DialogWindow.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://DialogWindow.gd" type="Script" id=1] 4 | 5 | [node name="DialogWindow" type="Control"] 6 | margin_left = 198.0 7 | margin_top = 109.0 8 | margin_right = 720.0 9 | margin_bottom = 447.0 10 | script = ExtResource( 1 ) 11 | 12 | [node name="TextDisplay" type="Panel" parent="."] 13 | anchor_right = 1.0 14 | anchor_bottom = 1.0 15 | margin_top = -11.0 16 | margin_bottom = -87.0 17 | 18 | [node name="TextEdit" type="TextEdit" parent="TextDisplay"] 19 | anchor_right = 1.0 20 | anchor_bottom = 1.0 21 | margin_left = 5.0 22 | margin_top = 20.0 23 | margin_right = -5.0 24 | margin_bottom = -5.0 25 | text = " 26 | assssssssssd 27 | assssssssssd 28 | assssssssssd 29 | assssssssssd 30 | assssssssssd 31 | assssssssssd 32 | assssssssssd 33 | 34 | assssssssssd 35 | assssssssssd 36 | assssssssssd 37 | assssssssssd 38 | assssssssssd 39 | assssssssssd 40 | assssssssssd 41 | 42 | assssssssssd 43 | assssssssssd 44 | assssssssssd 45 | assssssssssd 46 | assssssssssd 47 | assssssssssd 48 | assssssssssd 49 | " 50 | readonly = true 51 | 52 | [node name="NameDisplay" type="Label" parent="TextDisplay"] 53 | margin_left = 5.67226 54 | margin_top = 2.80676 55 | margin_right = 45.6723 56 | margin_bottom = 16.8068 57 | text = "Name" 58 | 59 | [node name="ResponseOptions" type="Panel" parent="."] 60 | anchor_top = 1.0 61 | anchor_right = 1.0 62 | anchor_bottom = 1.0 63 | margin_top = -80.0 64 | margin_bottom = 107.0 65 | 66 | [node name="ScrollContainer" type="ScrollContainer" parent="ResponseOptions"] 67 | anchor_right = 1.0 68 | anchor_bottom = 1.0 69 | margin_left = 5.0 70 | margin_top = 26.0 71 | margin_right = -5.0 72 | margin_bottom = -5.0 73 | 74 | [node name="VBoxContainer" type="VBoxContainer" parent="ResponseOptions/ScrollContainer"] 75 | 76 | [node name="Label" type="Label" parent="ResponseOptions"] 77 | margin_left = 4.59932 78 | margin_top = 4.5993 79 | margin_right = 81.5993 80 | margin_bottom = 18.5993 81 | text = "Response Options" 82 | -------------------------------------------------------------------------------- /DialogWindow.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | var dialog_response = preload("res://DialogResponse.tscn") 4 | 5 | onready var name_display = $TextDisplay/NameDisplay 6 | onready var text_display = $TextDisplay/TextEdit 7 | onready var response_container = $ResponseOptions/ScrollContainer/VBoxContainer 8 | var cur_convo = "" 9 | 10 | signal closed 11 | 12 | func _ready(): 13 | load_conversation("john") 14 | 15 | func load_conversation(convo_id): 16 | show() 17 | DialogAndQuestManager.clear_local_list_of_things_that_have_happened() 18 | cur_convo = DialogAndQuestManager.get_conversation(convo_id) 19 | name_display.text = cur_convo.name 20 | var greeting_id = "" 21 | for greeting in cur_convo.greetings: 22 | if not "conditions" in greeting or DialogAndQuestManager.conditions_met(greeting.conditions): 23 | greeting_id = greeting.id 24 | if "add_global_thing_that_has_happened" in greeting: 25 | DialogAndQuestManager.add_global_thing_that_has_happened(greeting.add_global_thing_that_has_happened) 26 | break 27 | load_dialog_window(greeting_id) 28 | 29 | func load_dialog_window(dialog_id): 30 | clear_response_options() 31 | var dialog = cur_convo.dialogs[dialog_id] 32 | text_display.text = dialog.text 33 | for response in dialog.responses: 34 | if not "conditions" in response or DialogAndQuestManager.conditions_met(response.conditions): 35 | add_response_option(response) 36 | 37 | func close_dialog_display(): 38 | hide() 39 | emit_signal("closed") 40 | 41 | func add_response_option(response_info): 42 | var new_dialog_response = dialog_response.instance() 43 | new_dialog_response.text = response_info.text 44 | response_container.add_child(new_dialog_response) 45 | var dialog_response_button = new_dialog_response.get_node("Button") 46 | 47 | if "add_global_thing_that_has_happened" in response_info: 48 | dialog_response_button.connect("button_up", DialogAndQuestManager, "add_global_thing_that_has_happened", [response_info.add_global_thing_that_has_happened]) 49 | if "add_local_thing_that_has_happened" in response_info: 50 | dialog_response_button.connect("button_up", DialogAndQuestManager, "add_local_thing_that_has_happened", [response_info.add_local_thing_that_has_happened]) 51 | 52 | if "close_dialog" in response_info: 53 | dialog_response_button.connect("button_up", self, "close_dialog_display") 54 | else: 55 | dialog_response_button.connect("button_up", self, "load_dialog_window", [response_info.switch_to]) 56 | 57 | func clear_response_options(): 58 | for child in response_container.get_children(): 59 | child.queue_free() 60 | -------------------------------------------------------------------------------- /DialogAndQuestManager.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | # structure of conversation: 4 | # conversation_id: { 5 | # list of greetings [ 6 | # { 7 | # id: id of dialog window to show 8 | # conditions: (optional) list of conditions that must be met to choose this greeting 9 | # add_global_thing_that_has_happened: (optional) if this greeting is chosen, add this to the list of global things that have happened 10 | # } 11 | # ] 12 | # dict of dialog windows { 13 | # id of dialog : { 14 | # dialog text to show, 15 | # responses player can choose [ 16 | # { 17 | # text: text to show for this response option(can wrap to multiple lines, 18 | # switch_to: id of dialog window to switch to if this option is chosen 19 | # close_dialog: (optional) closes dialog window if chosen 20 | # add_global_thing_that_has_happened: (optional), adds this to list of global things that have happened 21 | # add_local_thing_that_has_happened: (optional), adds this to list of local things that have happened 22 | # conditions: (optional) a list of conditions that must be met to show this response option 23 | # } 24 | # ] 25 | # } 26 | # } 27 | 28 | # Conditions can be prepended with ! to mean that the condition must not be met. 29 | # Conditions will be check against both local and global lists of things that have happened. 30 | # The local_list_of_things_that_have_happened can be used for events local to a conversation, e.g. if you must ask 31 | # questions in a specific order to get to a certain dialog window and exiting the conversation means starting over. 32 | 33 | # To use this system for quests, just add things to the global_list_of_things_that_have_happened whenever something 34 | # interesting happens, e.g. "killed_all_rats", and use the conditions_met or is_condition_met methods to check if 35 | # the player should recieve a reward or have access to an area. 36 | 37 | var global_list_of_things_that_have_happened = [] 38 | var local_list_of_things_that_have_happened = [] 39 | 40 | func add_global_thing_that_has_happened(ev): 41 | global_list_of_things_that_have_happened.append(ev) 42 | 43 | func add_local_thing_that_has_happened(ev): 44 | local_list_of_things_that_have_happened.append(ev) 45 | 46 | func clear_local_list_of_things_that_have_happened(): 47 | local_list_of_things_that_have_happened = [] 48 | 49 | const CONVERSATIONS = { 50 | "john" : { 51 | "name": "Johnathold", 52 | "greetings" : [ 53 | {"id": "freak_about_sara", "conditions":["talked_to_sara"]}, 54 | {"id": "comment_on_weather", "conditions":["talked_to_john_about_weather"]}, 55 | {"id": "generic_hello"} 56 | ], 57 | "dialogs": { 58 | "generic_hello": { 59 | "text": "hi how are you?", 60 | "responses": [ 61 | {"text": "how is the weather?", "switch_to": "asked_about_weather", "conditions": ["!talked_to_sara"], "add_global_thing_that_has_happened": "talked_to_john_about_weather"}, 62 | {"text": "bye", "close_dialog":""} 63 | ], 64 | }, 65 | "asked_about_weather": { 66 | "text": "I think it's going to rain", 67 | "responses": [ 68 | {"text": "yeah I better get going", "close_dialog":""} 69 | ], 70 | }, 71 | "comment_on_weather": { 72 | "text": "Looking stormy out", 73 | "responses": [ 74 | {"text": "yeah it is I better get going", "close_dialog":""} 75 | ], 76 | }, 77 | "freak_about_sara": { 78 | "text": "Have you seen sara's new car?!", 79 | "responses": [ 80 | {"text": "yeah it's pretty sick", "switch_to": "generic_hello"} 81 | ], 82 | }, 83 | } 84 | }, 85 | "sara" : { 86 | "name": "Sarathony", 87 | "greetings" : [ 88 | {"id": "generic_hello", "add_global_thing_that_has_happened": "talked_to_sara"} 89 | ], 90 | "dialogs": { 91 | "generic_hello": { 92 | "text": "hi how are you?", 93 | "responses": [ 94 | {"text": "how's your new car?", "switch_to": "asked_about_car", "add_local_thing_that_has_happened": "talked_to_sara_about_car"}, 95 | {"text": "how's your new car's mpg?", "switch_to": "asked_about_car_mpg", "conditions":["talked_to_sara_about_car"]}, 96 | {"text": "bye", "close_dialog":""} 97 | ], 98 | }, 99 | "asked_about_car": { 100 | "text": "It's pretty nice", 101 | "responses": [ 102 | {"text": "what's the mpg?", "switch_to": "asked_about_car_mpg"} 103 | ], 104 | }, 105 | "asked_about_car_mpg": { 106 | "text": "60 mpg", 107 | "responses": [ 108 | {"text": "ok cool", "switch_to": "generic_hello"} 109 | ], 110 | } 111 | } 112 | } 113 | } 114 | 115 | func get_conversation(convo_id): 116 | return CONVERSATIONS[convo_id] 117 | 118 | func conditions_met(list_of_conditions): 119 | for condition in list_of_conditions: 120 | if !is_condition_met(condition): 121 | return false 122 | return true 123 | 124 | func is_condition_met(condition): 125 | if condition[0] == "!": 126 | return !is_condition_met(condition.substr(1, condition.length())) 127 | 128 | if condition in global_list_of_things_that_have_happened: 129 | return true 130 | if condition in local_list_of_things_that_have_happened: 131 | return true 132 | return false --------------------------------------------------------------------------------