Quest dialog functional.
parent
f9ced395ce
commit
eeff59a413
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=40 format=3 uid="uid://bugqatylloxkl"]
|
||||
[gd_scene load_steps=41 format=3 uid="uid://bugqatylloxkl"]
|
||||
|
||||
[ext_resource type="Script" path="res://Player.gd" id="1_3lyis"]
|
||||
[ext_resource type="Script" path="res://World.gd" id="1_e417k"]
|
||||
|
@ -6,6 +6,7 @@
|
|||
[ext_resource type="Script" path="res://RootUI.gd" id="2_gg3gt"]
|
||||
[ext_resource type="PackedScene" uid="uid://dk2wv1vem0ubw" path="res://assets/scene_props/RockA.tscn" id="3_owduk"]
|
||||
[ext_resource type="PackedScene" uid="uid://dy8vjf760prhq" path="res://assets/characters/Rogue.tscn" id="4_6mbva"]
|
||||
[ext_resource type="Script" path="res://model/quest_state.gd" id="4_fmwrw"]
|
||||
[ext_resource type="Texture2D" uid="uid://cq8ypeagpedq" path="res://assets/kenney/ui-pack-rpg-expansion/PNG/cursorSword_bronze.png" id="4_pcyi0"]
|
||||
[ext_resource type="Texture2D" uid="uid://c7fu3paj3b4e8" path="res://assets/kenney/ui-pack-rpg-expansion/PNG/cursorSword_silver.png" id="5_24tqj"]
|
||||
[ext_resource type="Texture2D" uid="uid://drpl0ql1p3pfk" path="res://assets/kenney/ui-pack-rpg-expansion/PNG/cursorSword_gold.png" id="6_uid86"]
|
||||
|
@ -97,6 +98,10 @@ environment = SubResource("Environment_v85yo")
|
|||
unique_name_in_owner = true
|
||||
script = ExtResource("1_3lyis")
|
||||
|
||||
[node name="QuestState" type="Node" parent="World/Player"]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("4_fmwrw")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/Player"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
|
||||
visible = false
|
||||
|
@ -684,7 +689,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.921204, 0, -3.30199)
|
|||
[node name="mushrooms2" parent="." instance=ExtResource("25_tea3k")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.851, 0, 0.798104)
|
||||
|
||||
[connection signal="trigger_message" from="World/Player" to="RootUI/GameUI" method="_on_player_trigger_message"]
|
||||
[connection signal="pressed" from="RootUI/MainMenuUI/MarginContainer/VBoxContainer/MarginContainer/NewGameButton" to="RootUI" method="_on_new_game_button_pressed"]
|
||||
[connection signal="pressed" from="RootUI/MainMenuUI/MarginContainer/VBoxContainer/MarginContainer3/QuitButton" to="RootUI" method="_on_quit_button_pressed"]
|
||||
[connection signal="pressed" from="RootUI/NewGameUI/MarginContainer/VBoxContainer/MarginContainer3/HBoxContainer/BackButton" to="RootUI" method="_to_main_menu_button_pressed"]
|
||||
|
|
14
Player.gd
14
Player.gd
|
@ -5,6 +5,7 @@ const JUMP_VELOCITY = 2.5
|
|||
|
||||
@onready var geometry = %Geometry
|
||||
@onready var actionable_detector = %ActionableDetector
|
||||
@onready var quest_state = %QuestState
|
||||
|
||||
# Get the gravity from the project settings to be synced with RigidBody nodes.
|
||||
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
@ -38,14 +39,23 @@ func _physics_process(delta):
|
|||
last_nonzero_velocity = Vector3(ground_velocity.x, 0, ground_velocity.y).normalized()
|
||||
|
||||
geometry.look_at(position - last_nonzero_velocity, Vector3.UP)
|
||||
|
||||
|
||||
func update_quest_state() -> void:
|
||||
for item:Item in inventory.get_items():
|
||||
if item.name == "Wrench":
|
||||
quest_state.has_wrench = true
|
||||
|
||||
|
||||
|
||||
func on_item_picked_up(item:Item):
|
||||
inventory.add_item(item)
|
||||
emit_signal("trigger_message", "Picked up a " + item.name)
|
||||
inventory.add_item(item)
|
||||
update_quest_state()
|
||||
|
||||
|
||||
func _unhandled_input(_event: InputEvent) -> void:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
var actionables = actionable_detector.get_overlapping_areas()
|
||||
if actionables.size() > 0:
|
||||
actionables[0].action()
|
||||
actionables[0].action(quest_state)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,8 +2,14 @@
|
|||
|
||||
Chloe: Mist! Ohne meinen Hammer kann ich die Brücke nicht fertig bauen!
|
||||
Chloe: Hast Du meinen Hammer gesehen?
|
||||
|
||||
- Ja!
|
||||
Chloe: Du scheinst keinen Hammer zu haben. Oder weißt nicht was ein Hammer ist.
|
||||
if has_wrench == false:
|
||||
Chloe: Du scheinst keinen Hammer zu haben. Oder weißt nicht was ein Hammer ist.
|
||||
else:av
|
||||
Chloe: Danke!
|
||||
|
||||
set bridge_built_by_engineer = true
|
||||
- Lieber nicht.
|
||||
Chloe: Schade!
|
||||
- Wo kann er denn sein?
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class_name QuestState
|
||||
extends Node
|
||||
|
||||
@export var has_wrench:bool = false
|
||||
@export var bridge_built_by_engineer:bool = false
|
|
@ -3,5 +3,7 @@ extends Area3D
|
|||
@export var dialogue_resource: DialogueResource
|
||||
@export var dialogue_start: String = "start"
|
||||
|
||||
func action() -> void:
|
||||
DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)
|
||||
func action(game_state: QuestState) -> void:
|
||||
var balloon = load("res://ui/dialogue/balloon.tscn").instantiate()
|
||||
get_tree().current_scene.add_child(balloon)
|
||||
balloon.start(dialogue_resource, dialogue_start, [game_state])
|
||||
|
|
|
@ -19,6 +19,10 @@ config/icon="res://icon.svg"
|
|||
|
||||
DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd"
|
||||
|
||||
[dialogue_manager]
|
||||
|
||||
general/balloon_path="res://ui/dialogue/balloon.tscn"
|
||||
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="UIAndInteractionTests"
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
extends CanvasLayer
|
||||
|
||||
## The action to use for advancing the dialogue
|
||||
@export var next_action: StringName = &"ui_accept"
|
||||
|
||||
## The action to use to skip typing the dialogue
|
||||
@export var skip_action: StringName = &"ui_cancel"
|
||||
|
||||
@onready var balloon: Control = %Balloon
|
||||
@onready var character_label: RichTextLabel = %CharacterLabel
|
||||
@onready var dialogue_label: DialogueLabel = %DialogueLabel
|
||||
@onready var responses_menu: DialogueResponsesMenu = %ResponsesMenu
|
||||
|
||||
## The dialogue resource
|
||||
var resource: DialogueResource
|
||||
|
||||
## Temporary game states
|
||||
var temporary_game_states: Array = []
|
||||
|
||||
## See if we are waiting for the player
|
||||
var is_waiting_for_input: bool = false
|
||||
|
||||
## See if we are running a long mutation and should hide the balloon
|
||||
var will_hide_balloon: bool = false
|
||||
|
||||
## The current line
|
||||
var dialogue_line: DialogueLine:
|
||||
set(next_dialogue_line):
|
||||
is_waiting_for_input = false
|
||||
balloon.focus_mode = Control.FOCUS_ALL
|
||||
balloon.grab_focus()
|
||||
|
||||
# The dialogue has finished so close the balloon
|
||||
if not next_dialogue_line:
|
||||
queue_free()
|
||||
return
|
||||
|
||||
# If the node isn't ready yet then none of the labels will be ready yet either
|
||||
if not is_node_ready():
|
||||
await ready
|
||||
|
||||
dialogue_line = next_dialogue_line
|
||||
|
||||
character_label.visible = not dialogue_line.character.is_empty()
|
||||
character_label.text = tr(dialogue_line.character, "dialogue")
|
||||
|
||||
dialogue_label.hide()
|
||||
dialogue_label.dialogue_line = dialogue_line
|
||||
|
||||
responses_menu.hide()
|
||||
responses_menu.set_responses(dialogue_line.responses)
|
||||
|
||||
# Show our balloon
|
||||
balloon.show()
|
||||
will_hide_balloon = false
|
||||
|
||||
dialogue_label.show()
|
||||
if not dialogue_line.text.is_empty():
|
||||
dialogue_label.type_out()
|
||||
await dialogue_label.finished_typing
|
||||
|
||||
# Wait for input
|
||||
if dialogue_line.responses.size() > 0:
|
||||
balloon.focus_mode = Control.FOCUS_NONE
|
||||
responses_menu.show()
|
||||
elif dialogue_line.time != "":
|
||||
var time = dialogue_line.text.length() * 0.02 if dialogue_line.time == "auto" else dialogue_line.time.to_float()
|
||||
await get_tree().create_timer(time).timeout
|
||||
next(dialogue_line.next_id)
|
||||
else:
|
||||
is_waiting_for_input = true
|
||||
balloon.focus_mode = Control.FOCUS_ALL
|
||||
balloon.grab_focus()
|
||||
get:
|
||||
return dialogue_line
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
balloon.hide()
|
||||
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
||||
|
||||
# If the responses menu doesn't have a next action set, use this one
|
||||
if responses_menu.next_action.is_empty():
|
||||
responses_menu.next_action = next_action
|
||||
|
||||
|
||||
func _unhandled_input(_event: InputEvent) -> void:
|
||||
# Only the balloon is allowed to handle input while it's showing
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
# Detect a change of locale and update the current dialogue line to show the new language
|
||||
if what == NOTIFICATION_TRANSLATION_CHANGED and is_instance_valid(dialogue_label):
|
||||
var visible_ratio = dialogue_label.visible_ratio
|
||||
self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id)
|
||||
if visible_ratio < 1:
|
||||
dialogue_label.skip_typing()
|
||||
|
||||
|
||||
## Start some dialogue
|
||||
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
||||
temporary_game_states = [self] + extra_game_states
|
||||
is_waiting_for_input = false
|
||||
resource = dialogue_resource
|
||||
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
||||
|
||||
|
||||
## Go to the next line
|
||||
func next(next_id: String) -> void:
|
||||
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
||||
|
||||
|
||||
#region Signals
|
||||
|
||||
|
||||
func _on_mutated(_mutation: Dictionary) -> void:
|
||||
is_waiting_for_input = false
|
||||
will_hide_balloon = true
|
||||
get_tree().create_timer(0.1).timeout.connect(func():
|
||||
if will_hide_balloon:
|
||||
will_hide_balloon = false
|
||||
balloon.hide()
|
||||
)
|
||||
|
||||
|
||||
func _on_balloon_gui_input(event: InputEvent) -> void:
|
||||
# See if we need to skip typing of the dialogue
|
||||
if dialogue_label.is_typing:
|
||||
var mouse_was_clicked: bool = event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed()
|
||||
var skip_button_was_pressed: bool = event.is_action_pressed(skip_action)
|
||||
if mouse_was_clicked or skip_button_was_pressed:
|
||||
get_viewport().set_input_as_handled()
|
||||
dialogue_label.skip_typing()
|
||||
return
|
||||
|
||||
if not is_waiting_for_input: return
|
||||
if dialogue_line.responses.size() > 0: return
|
||||
|
||||
# When there are no response options the balloon itself is the clickable thing
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
next(dialogue_line.next_id)
|
||||
elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon:
|
||||
next(dialogue_line.next_id)
|
||||
|
||||
|
||||
func _on_responses_menu_response_selected(response: DialogueResponse) -> void:
|
||||
next(response.next_id)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1,91 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://73jm5qjy52vq"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/dialogue/balloon.gd" id="1_36de5"]
|
||||
[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_a8ve6"]
|
||||
[ext_resource type="Theme" uid="uid://dmk7hc81l8gbw" path="res://ui/ui_theme.tres" id="2_uj536"]
|
||||
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_reponses_menu.gd" id="3_72ixx"]
|
||||
|
||||
[node name="ExampleBalloon" type="CanvasLayer"]
|
||||
layer = 100
|
||||
script = ExtResource("1_36de5")
|
||||
|
||||
[node name="Balloon" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("2_uj536")
|
||||
|
||||
[node name="Panel" type="Panel" parent="Balloon"]
|
||||
clip_children = 2
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 21.0
|
||||
offset_top = -183.0
|
||||
offset_right = -19.0
|
||||
offset_bottom = -19.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Balloon/Panel/Dialogue"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CharacterLabel" type="RichTextLabel" parent="Balloon/Panel/Dialogue/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
modulate = Color(1, 1, 1, 0.501961)
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
bbcode_enabled = true
|
||||
text = "Character"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="DialogueLabel" parent="Balloon/Panel/Dialogue/VBoxContainer" instance=ExtResource("2_a8ve6")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
text = "Dialogue..."
|
||||
|
||||
[node name="Responses" type="MarginContainer" parent="Balloon"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -147.0
|
||||
offset_top = -558.0
|
||||
offset_right = 494.0
|
||||
offset_bottom = -154.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="ResponsesMenu" type="VBoxContainer" parent="Balloon/Responses" node_paths=PackedStringArray("response_template")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
theme_override_constants/separation = 2
|
||||
script = ExtResource("3_72ixx")
|
||||
response_template = NodePath("ResponseExample")
|
||||
|
||||
[node name="ResponseExample" type="Button" parent="Balloon/Responses/ResponsesMenu"]
|
||||
layout_mode = 2
|
||||
text = "Response example"
|
||||
|
||||
[connection signal="gui_input" from="Balloon" to="." method="_on_balloon_gui_input"]
|
||||
[connection signal="response_selected" from="Balloon/Responses/ResponsesMenu" to="." method="_on_responses_menu_response_selected"]
|
Loading…
Reference in New Issue