更新序章;dialogue manager升级适配
This commit is contained in:
parent
86edef7f0b
commit
594cf599f9
@ -444,7 +444,8 @@ func show_dialogue_balloon_scene(balloon_scene, resource: DialogueResource, titl
|
|||||||
|
|
||||||
# Call "start" on the given balloon.
|
# Call "start" on the given balloon.
|
||||||
func _start_balloon(balloon: Node, resource: DialogueResource, title: String, extra_game_states: Array) -> void:
|
func _start_balloon(balloon: Node, resource: DialogueResource, title: String, extra_game_states: Array) -> void:
|
||||||
get_current_scene.call().add_child(balloon)
|
if balloon.get_parent() == null:
|
||||||
|
get_current_scene.call().add_child(balloon)
|
||||||
|
|
||||||
if balloon.has_method(&"start"):
|
if balloon.has_method(&"start"):
|
||||||
balloon.start(resource, title, extra_game_states)
|
balloon.start(resource, title, extra_game_states)
|
||||||
|
@ -10,22 +10,22 @@
|
|||||||
[resource]
|
[resource]
|
||||||
animations = [{
|
animations = [{
|
||||||
"frames": [{
|
"frames": [{
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("1_i8sqx")
|
"texture": ExtResource("1_i8sqx")
|
||||||
}, {
|
}, {
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("2_q01yr")
|
"texture": ExtResource("2_q01yr")
|
||||||
}, {
|
}, {
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("3_l6n6k")
|
"texture": ExtResource("3_l6n6k")
|
||||||
}, {
|
}, {
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("4_0y176")
|
"texture": ExtResource("4_0y176")
|
||||||
}, {
|
}, {
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("5_bmxf1")
|
"texture": ExtResource("5_bmxf1")
|
||||||
}, {
|
}, {
|
||||||
"duration": 3.0,
|
"duration": 6.0,
|
||||||
"texture": ExtResource("6_jlcn1")
|
"texture": ExtResource("6_jlcn1")
|
||||||
}],
|
}],
|
||||||
"loop": true,
|
"loop": true,
|
||||||
|
@ -135,24 +135,39 @@ func _check_dirs_and_archives() -> bool:
|
|||||||
# id = -1 means create a new archive, otherwise create an archive with the given id
|
# id = -1 means create a new archive, otherwise create an archive with the given id
|
||||||
func create_and_use_new_archive(id := -1) -> void:
|
func create_and_use_new_archive(id := -1) -> void:
|
||||||
_check_dirs_and_archives()
|
_check_dirs_and_archives()
|
||||||
archive = AssembledArchive.new()
|
|
||||||
var archive_path = _get_archive_path(id)
|
var archive_path = _get_archive_path(id)
|
||||||
if id < 0:
|
if id < 0:
|
||||||
|
# 如果 id 小于 0,找到一个新的 id,创建新存档
|
||||||
id = 0
|
id = 0
|
||||||
# find a new id
|
# find a new id
|
||||||
archive_path = _get_archive_path(id)
|
archive_path = _get_archive_path(id)
|
||||||
while FileAccess.file_exists(archive_path):
|
while FileAccess.file_exists(archive_path):
|
||||||
id += 1
|
id += 1
|
||||||
archive_path = _get_archive_path(id)
|
archive_path = _get_archive_path(id)
|
||||||
archive.resource_path = archive_path
|
_create_and_save_new_archive_resoure(id)
|
||||||
archive.archive_id = id
|
else:
|
||||||
archive.created_time = Time.get_datetime_string_from_system(false, true)
|
# 如果 id 大于等于 0,创建指定 id 的存档
|
||||||
ResourceSaver.save(archive, archive_path)
|
if FileAccess.file_exists(archive_path):
|
||||||
|
_create_and_save_new_archive_resoure(id, true)
|
||||||
|
else:
|
||||||
|
_create_and_save_new_archive_resoure(id)
|
||||||
archives[id] = archive
|
archives[id] = archive
|
||||||
# this will auto trigger signal and load the new archive
|
# this will auto trigger signal and load the new archive
|
||||||
GlobalConfigManager.config.current_selected_archive_id = id
|
GlobalConfigManager.config.current_selected_archive_id = id
|
||||||
|
|
||||||
|
|
||||||
|
func _create_and_save_new_archive_resoure(id, take_over_path = false) -> void:
|
||||||
|
var archive_path = _get_archive_path(id)
|
||||||
|
archive = AssembledArchive.new() as Resource
|
||||||
|
if take_over_path:
|
||||||
|
archive.take_over_path(archive_path)
|
||||||
|
else:
|
||||||
|
archive.resource_path = archive_path
|
||||||
|
archive.archive_id = id
|
||||||
|
archive.created_time = Time.get_datetime_string_from_system(false, true)
|
||||||
|
ResourceSaver.save(archive, archive_path)
|
||||||
|
|
||||||
|
|
||||||
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
|
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
|
||||||
func _get_archive_path(id: int) -> String:
|
func _get_archive_path(id: int) -> String:
|
||||||
var id_str := ""
|
var id_str := ""
|
||||||
|
@ -2,6 +2,7 @@ extends Node
|
|||||||
|
|
||||||
var sfx_players = [] as Array[AudioStreamPlayer]
|
var sfx_players = [] as Array[AudioStreamPlayer]
|
||||||
var idx = 0
|
var idx = 0
|
||||||
|
var bgm_dict = {}
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@ -12,13 +13,37 @@ func _ready() -> void:
|
|||||||
add_child(sfx_player)
|
add_child(sfx_player)
|
||||||
|
|
||||||
|
|
||||||
func play_animation_sound(animation):
|
|
||||||
#TODO
|
|
||||||
print("Playing sound for animation: ", animation)
|
|
||||||
|
|
||||||
|
|
||||||
func play_sfx(sfx: AudioStream, db := 1.0) -> void:
|
func play_sfx(sfx: AudioStream, db := 1.0) -> void:
|
||||||
sfx_players[idx].stream = sfx
|
sfx_players[idx].stream = sfx
|
||||||
sfx_players[idx].volume_db = db
|
sfx_players[idx].volume_db = db
|
||||||
sfx_players[idx].play()
|
sfx_players[idx].play()
|
||||||
idx = wrapi(idx + 1, 0, 5)
|
idx = wrapi(idx + 1, 0, 5)
|
||||||
|
|
||||||
|
|
||||||
|
# 挂载并循环播放 bgm 音效
|
||||||
|
func loop_bgm_music(music_name: StringName, stream: AudioStream, db := 0.0, loop := true) -> void:
|
||||||
|
var audio_player = bgm_dict.get(music_name) as AudioStreamPlayer
|
||||||
|
if audio_player:
|
||||||
|
bgm_dict.erase(music_name)
|
||||||
|
audio_player.queue_free()
|
||||||
|
audio_player.stop()
|
||||||
|
audio_player = AudioStreamPlayer.new()
|
||||||
|
add_child(audio_player)
|
||||||
|
bgm_dict[music_name] = audio_player
|
||||||
|
audio_player.stream = stream
|
||||||
|
audio_player.volume_db = db
|
||||||
|
audio_player.bus = "game_music"
|
||||||
|
audio_player.play()
|
||||||
|
if loop and stream.get_length() > 0:
|
||||||
|
audio_player.finished.connect(audio_player.play)
|
||||||
|
|
||||||
|
|
||||||
|
func stop_bgm_music(music_name: StringName) -> void:
|
||||||
|
var audio_player = bgm_dict.get(music_name) as AudioStreamPlayer
|
||||||
|
if audio_player:
|
||||||
|
audio_player.stop()
|
||||||
|
remove_child(audio_player)
|
||||||
|
audio_player.queue_free()
|
||||||
|
bgm_dict.erase(music_name)
|
||||||
|
else:
|
||||||
|
print("music bgm not found: ", music_name)
|
||||||
|
@ -116,7 +116,6 @@ func set_player_boundary(rect: Rect2) -> void:
|
|||||||
|
|
||||||
var balloon_node
|
var balloon_node
|
||||||
|
|
||||||
|
|
||||||
func pop_debug_dialog_info(character: String, content: String):
|
func pop_debug_dialog_info(character: String, content: String):
|
||||||
if GlobalConfig.DEBUG:
|
if GlobalConfig.DEBUG:
|
||||||
if not is_instance_valid(balloon_node):
|
if not is_instance_valid(balloon_node):
|
||||||
|
@ -15,12 +15,6 @@ signal manually_skipped_line
|
|||||||
|
|
||||||
## The action to use to skip typing the dialogue
|
## The action to use to skip typing the dialogue
|
||||||
@export var skip_action: StringName = &""
|
@export var skip_action: StringName = &""
|
||||||
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
|
||||||
|
|
||||||
@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
|
## The dialogue resource
|
||||||
var resource: DialogueResource
|
var resource: DialogueResource
|
||||||
@ -34,110 +28,51 @@ var is_waiting_for_input: bool = false
|
|||||||
## See if we are running a long mutation and should hide the balloon
|
## See if we are running a long mutation and should hide the balloon
|
||||||
var will_hide_balloon: bool = false
|
var will_hide_balloon: bool = false
|
||||||
|
|
||||||
|
## A dictionary to store any ephemeral variables
|
||||||
|
var locals: Dictionary = {}
|
||||||
|
|
||||||
|
var _locale: String = TranslationServer.get_locale()
|
||||||
|
|
||||||
## The current line
|
## The current line
|
||||||
var dialogue_line: DialogueLine:
|
var dialogue_line: DialogueLine:
|
||||||
set(next_dialogue_line):
|
set(value):
|
||||||
is_waiting_for_input = false
|
if value:
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
dialogue_line = value
|
||||||
balloon.grab_focus()
|
apply_dialogue_line()
|
||||||
# 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")
|
|
||||||
#主要角色颜色
|
|
||||||
var color = dialogue_line.get_tag_value("color")
|
|
||||||
if color:
|
|
||||||
character_label.modulate = Color.WHITE
|
|
||||||
character_label.text = "[color=" + color + "]" + character_label.text + "[/color]"
|
|
||||||
elif GlobalConfig.CHARACTER_COLOR_MAP.has(dialogue_line.character):
|
|
||||||
character_label.modulate = GlobalConfig.CHARACTER_COLOR_MAP[dialogue_line.character]
|
|
||||||
else:
|
else:
|
||||||
character_label.modulate = GlobalConfig.CHARACTER_COLOR_MAP["default"]
|
# The dialogue has finished so close the balloon
|
||||||
# 配色结束后匿名化处理
|
|
||||||
if dialogue_line.tags.has("anonymous"):
|
|
||||||
character_label.text = tr("???", "dialogue")
|
|
||||||
|
|
||||||
#print(dialogue_line.character,character_label.modulate)
|
|
||||||
|
|
||||||
dialogue_label.hide()
|
|
||||||
_setup_content_text()
|
|
||||||
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()
|
|
||||||
|
|
||||||
#sound
|
|
||||||
var audio_time_len := 0.0
|
|
||||||
# 因为版权问题,有些 mp3 文件打不开,所以使用 ogg 格式
|
|
||||||
var audio_path = "res://asset/audio/peiyin/ogg/%s.ogg" % dialogue_line.translation_key
|
|
||||||
# var audio_path = "res://asset/audio/peiyin/%s.mp3" % dialogue_line.translation_key
|
|
||||||
if FileAccess.file_exists(audio_path):
|
|
||||||
var stream = load(audio_path)
|
|
||||||
audio_time_len = stream.get_length()
|
|
||||||
if audio_stream_player.stream != stream or not audio_stream_player.playing:
|
|
||||||
audio_stream_player.stream = stream
|
|
||||||
audio_stream_player.play()
|
|
||||||
elif audio_stream_player.playing:
|
|
||||||
audio_stream_player.stop()
|
|
||||||
|
|
||||||
if dialogue_label.is_typing:
|
|
||||||
await dialogue_label.finished_typing
|
|
||||||
if audio_stream_player.playing:
|
|
||||||
await audio_stream_player.finished
|
|
||||||
# # Wait for input
|
|
||||||
# if dialogue_line.responses.size() > 0:
|
|
||||||
# balloon.focus_mode = Control.FOCUS_NONE
|
|
||||||
# responses_menu.show()
|
|
||||||
else:
|
|
||||||
is_waiting_for_input = true
|
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
|
||||||
balloon.grab_focus()
|
|
||||||
# if dialogue_line.time != "":
|
|
||||||
# auto_play: 不管是否配置等待时长,都执行自动播放
|
|
||||||
if auto_play or next_dialogue_line.time:
|
|
||||||
# 如果音频时长为 0,则等待玩家读完文本;否则音频播放结束后等待 0.5 秒
|
|
||||||
var wait_time = 0.5
|
|
||||||
if not audio_time_len:
|
|
||||||
# 0.2 秒每个字符,最小 3 秒,最大 5 秒
|
|
||||||
wait_time = max(min(0.2 * dialogue_line.text.length(), 5.0), 3.0)
|
|
||||||
# 从 tags 中获取备注参数,覆盖默认等待时长
|
|
||||||
var wait = next_dialogue_line.get_tag_value("wait")
|
|
||||||
# eg. [#wait=2.5]
|
|
||||||
if wait:
|
|
||||||
wait_time = wait.to_float()
|
|
||||||
await get_tree().create_timer(wait_time).timeout
|
|
||||||
# 如果不再是当前 line,则不跳转
|
|
||||||
if dialogue_line.translation_key == next_dialogue_line.translation_key:
|
|
||||||
next(next_dialogue_line.next_id)
|
|
||||||
# var time = next_dialogue_line.text.length() * 0.2 if next_dialogue_line.time == "auto" else next_dialogue_line.time.to_float()
|
|
||||||
# await get_tree().create_timer(time).timeout
|
|
||||||
# 如果当前 line 运行结束,则 queue free 释放资源
|
|
||||||
if dialogue_line == next_dialogue_line:
|
|
||||||
queue_free()
|
queue_free()
|
||||||
|
get:
|
||||||
|
return dialogue_line
|
||||||
|
|
||||||
|
## A cooldown timer for delaying the balloon hide when encountering a mutation.
|
||||||
|
var mutation_cooldown: Timer = Timer.new()
|
||||||
|
|
||||||
|
## The base balloon anchor
|
||||||
|
@onready var balloon: Control = %Balloon
|
||||||
|
|
||||||
|
## The label showing the name of the currently speaking character
|
||||||
|
@onready var character_label: RichTextLabel = %CharacterLabel
|
||||||
|
|
||||||
|
## The label showing the currently spoken dialogue
|
||||||
|
@onready var dialogue_label: DialogueLabel = %DialogueLabel
|
||||||
|
|
||||||
|
## The menu of responses
|
||||||
|
@onready var responses_menu: DialogueResponsesMenu = %ResponsesMenu
|
||||||
|
|
||||||
|
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
layer = GlobalConfig.CANVAS_LAYER_DIALOG
|
|
||||||
balloon.hide()
|
balloon.hide()
|
||||||
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
||||||
|
|
||||||
# # If the responses menu doesn't have a next action set, use this one
|
# If the responses menu doesn't have a next action set, use this one
|
||||||
# if responses_menu.next_action.is_empty():
|
if responses_menu.next_action.is_empty():
|
||||||
# responses_menu.next_action = next_action
|
responses_menu.next_action = next_action
|
||||||
|
|
||||||
|
mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout)
|
||||||
|
add_child(mutation_cooldown)
|
||||||
|
|
||||||
# 自定义获得文本,从 tags 中获取备注参数
|
# 自定义获得文本,从 tags 中获取备注参数
|
||||||
func _setup_content_text() -> void:
|
func _setup_content_text() -> void:
|
||||||
@ -162,43 +97,147 @@ func _setup_content_text() -> void:
|
|||||||
text = "[color=orange]" + text + "[/color]"
|
text = "[color=orange]" + text + "[/color]"
|
||||||
dialogue_line.text = text
|
dialogue_line.text = text
|
||||||
|
|
||||||
|
# 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:
|
func _notification(what: int) -> void:
|
||||||
# Detect a change of locale and update the current dialogue line to show the new language
|
## 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):
|
if what == NOTIFICATION_TRANSLATION_CHANGED and _locale != TranslationServer.get_locale() and is_instance_valid(dialogue_label):
|
||||||
|
_locale = TranslationServer.get_locale()
|
||||||
var visible_ratio = dialogue_label.visible_ratio
|
var visible_ratio = dialogue_label.visible_ratio
|
||||||
self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id)
|
self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id)
|
||||||
if visible_ratio < 1:
|
if visible_ratio < 1:
|
||||||
dialogue_label.skip_typing()
|
dialogue_label.skip_typing()
|
||||||
|
|
||||||
|
|
||||||
## Start some dialogue
|
## Start some dialogue
|
||||||
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
||||||
if not is_node_ready():
|
temporary_game_states = [self] + extra_game_states
|
||||||
await ready
|
|
||||||
temporary_game_states = [self] + extra_game_states
|
|
||||||
is_waiting_for_input = false
|
is_waiting_for_input = false
|
||||||
resource = dialogue_resource
|
resource = dialogue_resource
|
||||||
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
||||||
|
|
||||||
|
|
||||||
|
## Apply any changes to the balloon given a new [DialogueLine].
|
||||||
|
func apply_dialogue_line() -> void:
|
||||||
|
mutation_cooldown.stop()
|
||||||
|
|
||||||
|
is_waiting_for_input = false
|
||||||
|
balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
balloon.grab_focus()
|
||||||
|
|
||||||
|
character_label.visible = not dialogue_line.character.is_empty()
|
||||||
|
character_label.text = tr(dialogue_line.character, "dialogue")
|
||||||
|
#主要角色颜色
|
||||||
|
var color = dialogue_line.get_tag_value("color")
|
||||||
|
if color:
|
||||||
|
character_label.modulate = Color.WHITE
|
||||||
|
character_label.text = "[color=" + color + "]" + character_label.text + "[/color]"
|
||||||
|
elif GlobalConfig.CHARACTER_COLOR_MAP.has(dialogue_line.character):
|
||||||
|
character_label.modulate = GlobalConfig.CHARACTER_COLOR_MAP[dialogue_line.character]
|
||||||
|
else:
|
||||||
|
character_label.modulate = GlobalConfig.CHARACTER_COLOR_MAP["default"]
|
||||||
|
# 配色结束后匿名化处理
|
||||||
|
if dialogue_line.tags.has("anonymous"):
|
||||||
|
character_label.text = tr("???", "dialogue")
|
||||||
|
|
||||||
|
dialogue_label.hide()
|
||||||
|
_setup_content_text()
|
||||||
|
dialogue_label.dialogue_line = dialogue_line
|
||||||
|
|
||||||
|
responses_menu.hide()
|
||||||
|
responses_menu.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
|
||||||
|
|
||||||
|
#sound
|
||||||
|
var initial_translation_key = dialogue_line.translation_key
|
||||||
|
var audio_time_len := 0.0
|
||||||
|
# 因为版权问题,有些 mp3 文件打不开,所以使用 ogg 格式
|
||||||
|
var audio_path = "res://asset/audio/peiyin/ogg/%s.ogg" % initial_translation_key
|
||||||
|
# var audio_path = "res://asset/audio/peiyin/%s.mp3" % initial_translation_key
|
||||||
|
if FileAccess.file_exists(audio_path):
|
||||||
|
var stream = load(audio_path)
|
||||||
|
audio_time_len = stream.get_length()
|
||||||
|
if audio_stream_player.stream != stream or not audio_stream_player.playing:
|
||||||
|
audio_stream_player.stream = stream
|
||||||
|
audio_stream_player.play()
|
||||||
|
elif audio_stream_player.playing:
|
||||||
|
audio_stream_player.stop()
|
||||||
|
|
||||||
|
if dialogue_label.is_typing:
|
||||||
|
await dialogue_label.finished_typing
|
||||||
|
if audio_stream_player.playing:
|
||||||
|
await audio_stream_player.finished
|
||||||
|
# # Wait for input
|
||||||
|
# if dialogue_line.responses.size() > 0:
|
||||||
|
# balloon.focus_mode = Control.FOCUS_NONE
|
||||||
|
# responses_menu.show()
|
||||||
|
# else:
|
||||||
|
is_waiting_for_input = true
|
||||||
|
balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
balloon.grab_focus()
|
||||||
|
# if dialogue_line.time != "":
|
||||||
|
# auto_play: 不管是否配置等待时长,都执行自动播放
|
||||||
|
if auto_play or dialogue_line.time:
|
||||||
|
var wait_time = 0.5
|
||||||
|
# 如果音频时长为 0,则等待玩家读完文本;否则音频播放结束后等待 0.5 秒
|
||||||
|
if not audio_time_len:
|
||||||
|
# 0.2 秒每个字符,最小 3 秒,最大 5 秒
|
||||||
|
wait_time = max(min(0.2 * dialogue_line.text.length(), 5.0), 3.0)
|
||||||
|
# 从 tags 中获取备注参数,覆盖默认等待时长
|
||||||
|
var wait = dialogue_line.get_tag_value("wait")
|
||||||
|
# eg. [#wait=2.5]
|
||||||
|
if wait:
|
||||||
|
wait_time = wait.to_float()
|
||||||
|
await get_tree().create_timer(wait_time).timeout
|
||||||
|
# 在 translation key 仍旧是当前 line 时跳转;如果不再是当前 line,则不跳转
|
||||||
|
if dialogue_line.translation_key == initial_translation_key:
|
||||||
|
next(dialogue_line.next_id)
|
||||||
|
# var time = next_dialogue_line.text.length() * 0.2 if next_dialogue_line.time == "auto" else next_dialogue_line.time.to_float()
|
||||||
|
# await get_tree().create_timer(time).timeout
|
||||||
|
|
||||||
|
# # 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()
|
||||||
|
|
||||||
|
|
||||||
## Go to the next line
|
## Go to the next line
|
||||||
func next(next_id: String) -> void:
|
func next(next_id: String) -> void:
|
||||||
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
||||||
|
|
||||||
|
|
||||||
#region Signals
|
#region Signals
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mutation_cooldown_timeout() -> void:
|
||||||
|
if will_hide_balloon:
|
||||||
|
will_hide_balloon = false
|
||||||
|
balloon.hide()
|
||||||
|
|
||||||
|
|
||||||
func _on_mutated(_mutation: Dictionary) -> void:
|
func _on_mutated(_mutation: Dictionary) -> void:
|
||||||
is_waiting_for_input = false
|
is_waiting_for_input = false
|
||||||
will_hide_balloon = true
|
will_hide_balloon = true
|
||||||
get_tree().create_timer(0.1).timeout.connect(func():
|
mutation_cooldown.start(0.1)
|
||||||
if will_hide_balloon:
|
|
||||||
will_hide_balloon = false
|
|
||||||
balloon.hide()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# 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 _on_balloon_gui_input(event: InputEvent) -> void:
|
func _on_balloon_gui_input(event: InputEvent) -> void:
|
||||||
@ -212,25 +251,31 @@ func _on_balloon_gui_input(event: InputEvent) -> void:
|
|||||||
dialogue_label.skip_typing()
|
dialogue_label.skip_typing()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
return
|
return
|
||||||
|
# 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 not is_waiting_for_input: return
|
||||||
# if dialogue_line.responses.size() > 0: return
|
# if dialogue_line.responses.size() > 0: return
|
||||||
|
|
||||||
# When there are no response options the balloon itself is the clickable thing
|
# When there are no response options the balloon itself is the clickable thing
|
||||||
# get_viewport().set_input_as_handled()
|
# get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
#if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
|
# if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
#next(dialogue_line.next_id)
|
# next(dialogue_line.next_id)
|
||||||
#elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon:
|
# elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon:
|
||||||
#next(dialogue_line.next_id)
|
|
||||||
|
|
||||||
if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
||||||
# if event.is_action_pressed("interact") and get_viewport().gui_get_focus_owner() == balloon:
|
# if event.is_action_pressed("interact") and get_viewport().gui_get_focus_owner() == balloon:
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
manually_skipped_line.emit()
|
manually_skipped_line.emit()
|
||||||
next(dialogue_line.next_id)
|
next(dialogue_line.next_id)
|
||||||
|
|
||||||
# func _on_responses_menu_response_selected(response: DialogueResponse) -> void:
|
|
||||||
# next(response.next_id)
|
func _on_responses_menu_response_selected(response: DialogueResponse) -> void:
|
||||||
|
next(response.next_id)
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -13,11 +13,6 @@ extends CanvasLayer
|
|||||||
|
|
||||||
## The action to use to skip typing the dialogue
|
## The action to use to skip typing the dialogue
|
||||||
@export var skip_action: StringName = &""
|
@export var skip_action: StringName = &""
|
||||||
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
|
||||||
|
|
||||||
@onready var balloon: Control = %Balloon
|
|
||||||
@onready var character_label: RichTextLabel = %CharacterLabel
|
|
||||||
@onready var dialogue_label: DialogueLabel = %DialogueLabel
|
|
||||||
|
|
||||||
## The dialogue resource
|
## The dialogue resource
|
||||||
var resource: DialogueResource
|
var resource: DialogueResource
|
||||||
@ -31,65 +26,39 @@ var is_waiting_for_input: bool = false
|
|||||||
## See if we are running a long mutation and should hide the balloon
|
## See if we are running a long mutation and should hide the balloon
|
||||||
var will_hide_balloon: bool = false
|
var will_hide_balloon: bool = false
|
||||||
|
|
||||||
const CHARACTER_COLOR_MAP = {
|
|
||||||
"音效": Color.DARK_VIOLET,
|
|
||||||
"美术": Color.WHITE_SMOKE,
|
|
||||||
"default": Color.LIGHT_SALMON,
|
|
||||||
}
|
|
||||||
|
|
||||||
## The current line
|
## The current line
|
||||||
var dialogue_line: DialogueLine:
|
var dialogue_line: DialogueLine:
|
||||||
set(next_dialogue_line):
|
set(value):
|
||||||
is_waiting_for_input = false
|
if value:
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
dialogue_line = value
|
||||||
balloon.grab_focus()
|
apply_dialogue_line()
|
||||||
# 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")
|
|
||||||
#主要角色颜色
|
|
||||||
if CHARACTER_COLOR_MAP.has(dialogue_line.character):
|
|
||||||
character_label.modulate = CHARACTER_COLOR_MAP[dialogue_line.character]
|
|
||||||
else:
|
else:
|
||||||
character_label.modulate = CHARACTER_COLOR_MAP["default"]
|
# The dialogue has finished so close the balloon
|
||||||
|
queue_free()
|
||||||
|
get:
|
||||||
|
return dialogue_line
|
||||||
|
|
||||||
#print(dialogue_line.character,character_label.modulate)
|
## A cooldown timer for delaying the balloon hide when encountering a mutation.
|
||||||
|
var mutation_cooldown: Timer = Timer.new()
|
||||||
|
|
||||||
dialogue_label.hide()
|
## The base balloon anchor
|
||||||
_setup_content_text()
|
@onready var balloon: Control = %Balloon
|
||||||
dialogue_label.dialogue_line = dialogue_line
|
|
||||||
|
|
||||||
# Show our balloons
|
## The label showing the name of the currently speaking character
|
||||||
balloon.show()
|
@onready var character_label: RichTextLabel = %CharacterLabel
|
||||||
will_hide_balloon = false
|
|
||||||
|
|
||||||
dialogue_label.show()
|
## The label showing the currently spoken dialogue
|
||||||
if not dialogue_line.text.is_empty():
|
@onready var dialogue_label: DialogueLabel = %DialogueLabel
|
||||||
dialogue_label.type_out()
|
|
||||||
|
|
||||||
is_waiting_for_input = true
|
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
|
||||||
balloon.grab_focus()
|
|
||||||
await get_tree().create_timer(2.0).timeout
|
|
||||||
# debug line 不需要下一行,直接释放(避免触发 dialogue_ended 信号)
|
|
||||||
queue_free()
|
|
||||||
# next(next_dialogue_line.next_id)
|
|
||||||
|
|
||||||
# # 如果当前 line 运行结束,则 queue free 释放资源
|
|
||||||
# if dialogue_line == next_dialogue_line:
|
|
||||||
# queue_free()
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
layer = GlobalConfig.CANVAS_LAYER_DIALOG
|
|
||||||
balloon.hide()
|
balloon.hide()
|
||||||
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
||||||
|
|
||||||
|
mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout)
|
||||||
|
add_child(mutation_cooldown)
|
||||||
|
|
||||||
# 自定义获得文本,从 tags 中获取备注参数
|
# 自定义获得文本,从 tags 中获取备注参数
|
||||||
func _setup_content_text() -> void:
|
func _setup_content_text() -> void:
|
||||||
@ -114,62 +83,125 @@ func _setup_content_text() -> void:
|
|||||||
text = "[color=orange]" + text + "[/color]"
|
text = "[color=orange]" + text + "[/color]"
|
||||||
dialogue_line.text = text
|
dialogue_line.text = text
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
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()
|
|
||||||
)
|
|
||||||
|
|
||||||
# debug balloon 无需接管输入事件
|
|
||||||
|
|
||||||
# func _unhandled_input(_event: InputEvent) -> void:
|
# func _unhandled_input(_event: InputEvent) -> void:
|
||||||
# # Only the balloon is allowed to handle input while it's showing
|
# # Only the balloon is allowed to handle input while it's showing
|
||||||
# get_viewport().set_input_as_handled()
|
# get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
# func _on_balloon_gui_input(event: InputEvent) -> void:
|
# func _notification(what: int) -> void:
|
||||||
# # See if we need to skip typing of the dialogue
|
# ## Detect a change of locale and update the current dialogue line to show the new language
|
||||||
# if dialogue_label.is_typing:
|
# if what == NOTIFICATION_TRANSLATION_CHANGED and _locale != TranslationServer.get_locale() and is_instance_valid(dialogue_label):
|
||||||
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
# _locale = TranslationServer.get_locale()
|
||||||
|
# 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()
|
# dialogue_label.skip_typing()
|
||||||
# get_viewport().set_input_as_handled()
|
|
||||||
# return
|
|
||||||
|
|
||||||
# #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)
|
|
||||||
|
|
||||||
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
## Start some dialogue
|
||||||
# # if event.is_action_pressed("interact") and get_viewport().gui_get_focus_owner() == balloon:
|
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
||||||
# get_viewport().set_input_as_handled()
|
temporary_game_states = [self] + extra_game_states
|
||||||
# manually_skipped_line.emit()
|
is_waiting_for_input = false
|
||||||
# next(dialogue_line.next_id)
|
resource = dialogue_resource
|
||||||
#endregion
|
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
||||||
|
|
||||||
|
|
||||||
|
const CHARACTER_COLOR_MAP = {
|
||||||
|
"音效": Color.DARK_VIOLET,
|
||||||
|
"美术": Color.WHITE_SMOKE,
|
||||||
|
"default": Color.LIGHT_SALMON,
|
||||||
|
}
|
||||||
|
|
||||||
|
## Apply any changes to the balloon given a new [DialogueLine].
|
||||||
|
func apply_dialogue_line() -> void:
|
||||||
|
mutation_cooldown.stop()
|
||||||
|
|
||||||
|
is_waiting_for_input = false
|
||||||
|
balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
balloon.grab_focus()
|
||||||
|
|
||||||
|
character_label.visible = not dialogue_line.character.is_empty()
|
||||||
|
character_label.text = tr(dialogue_line.character, "dialogue")
|
||||||
|
#主要角色颜色
|
||||||
|
if CHARACTER_COLOR_MAP.has(dialogue_line.character):
|
||||||
|
character_label.modulate = CHARACTER_COLOR_MAP[dialogue_line.character]
|
||||||
|
else:
|
||||||
|
character_label.modulate = CHARACTER_COLOR_MAP["default"]
|
||||||
|
|
||||||
|
dialogue_label.hide()
|
||||||
|
_setup_content_text()
|
||||||
|
dialogue_label.dialogue_line = dialogue_line
|
||||||
|
|
||||||
|
# Show our balloon
|
||||||
|
balloon.show()
|
||||||
|
will_hide_balloon = false
|
||||||
|
|
||||||
|
dialogue_label.show()
|
||||||
|
if not dialogue_line.text.is_empty():
|
||||||
|
dialogue_label.type_out()
|
||||||
|
|
||||||
|
# # Wait for input
|
||||||
|
# if dialogue_line.responses.size() > 0:
|
||||||
|
# balloon.focus_mode = Control.FOCUS_NONE
|
||||||
|
# responses_menu.show()
|
||||||
|
# else:
|
||||||
|
is_waiting_for_input = true
|
||||||
|
# balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
# balloon.grab_focus()
|
||||||
|
await get_tree().create_timer(2.0).timeout
|
||||||
|
# debug line 不需要下一行,直接释放(避免触发 dialogue_ended 信号)
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
## 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_mutation_cooldown_timeout() -> void:
|
||||||
|
if will_hide_balloon:
|
||||||
|
will_hide_balloon = false
|
||||||
|
balloon.hide()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mutated(_mutation: Dictionary) -> void:
|
||||||
|
is_waiting_for_input = false
|
||||||
|
will_hide_balloon = true
|
||||||
|
mutation_cooldown.start(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
# func _on_balloon_gui_input(event: InputEvent) -> void:
|
||||||
|
# # 根据全局配置,是否允许忽略输入
|
||||||
|
# if temporary_game_states.has(GlobalConfig.DIALOG_IGNORE_INPUT):
|
||||||
|
# return
|
||||||
|
|
||||||
|
# # See if we need to skip typing of the dialogue
|
||||||
|
# if dialogue_label.is_typing:
|
||||||
|
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
||||||
|
# dialogue_label.skip_typing()
|
||||||
|
# get_viewport().set_input_as_handled()
|
||||||
|
# return
|
||||||
|
# # 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:
|
||||||
|
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
||||||
|
# # if event.is_action_pressed("interact") and get_viewport().gui_get_focus_owner() == balloon:
|
||||||
|
# get_viewport().set_input_as_handled()
|
||||||
|
# manually_skipped_line.emit()
|
||||||
|
# next(dialogue_line.next_id)
|
||||||
|
@ -61,8 +61,7 @@ func _reset(_body = null) -> void:
|
|||||||
|
|
||||||
func _reset_sign_testure_to_prop():
|
func _reset_sign_testure_to_prop():
|
||||||
var key = SceneManager.get_current_prop(false)
|
var key = SceneManager.get_current_prop(false)
|
||||||
if key:
|
_set_sign_texture_to_prop(key)
|
||||||
_set_sign_texture_to_prop(key)
|
|
||||||
|
|
||||||
|
|
||||||
# 根据当前 prop,调整 sign 所显示的 texture
|
# 根据当前 prop,调整 sign 所显示的 texture
|
||||||
|
@ -574,7 +574,7 @@ scene_name = "c01_s05"
|
|||||||
|
|
||||||
[node name="AnimationPlayer" parent="Ground" index="0"]
|
[node name="AnimationPlayer" parent="Ground" index="0"]
|
||||||
libraries = {
|
libraries = {
|
||||||
"": SubResource("AnimationLibrary_ifimj")
|
&"": SubResource("AnimationLibrary_ifimj")
|
||||||
}
|
}
|
||||||
script = ExtResource("2_j5oim")
|
script = ExtResource("2_j5oim")
|
||||||
data = {
|
data = {
|
||||||
@ -595,7 +595,6 @@ position = Vector2(27, 3)
|
|||||||
position = Vector2(503, 11)
|
position = Vector2(503, 11)
|
||||||
texture = ExtResource("4_gdhoy")
|
texture = ExtResource("4_gdhoy")
|
||||||
enabled = false
|
enabled = false
|
||||||
immediately = false
|
|
||||||
target_scene = "c01_s06"
|
target_scene = "c01_s06"
|
||||||
target_portal = "left"
|
target_portal = "left"
|
||||||
default_texture = ExtResource("4_gdhoy")
|
default_texture = ExtResource("4_gdhoy")
|
||||||
@ -855,10 +854,10 @@ bus = &"game_sfx"
|
|||||||
script = ExtResource("23_o1482")
|
script = ExtResource("23_o1482")
|
||||||
file = "门锁互动.mp3"
|
file = "门锁互动.mp3"
|
||||||
|
|
||||||
[node name="参考" type="Sprite2D" parent="Ground"]
|
[node name="参考" type="Sprite2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
modulate = Color(1, 1, 1, 0.219608)
|
modulate = Color(1, 1, 1, 0.219608)
|
||||||
position = Vector2(281.5, 3.3)
|
position = Vector2(282.5, 3.3)
|
||||||
scale = Vector2(0.333, 0.333)
|
scale = Vector2(0.333, 0.333)
|
||||||
texture = ExtResource("3_7u4bh")
|
texture = ExtResource("3_7u4bh")
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=15 format=3 uid="uid://c777lv8mjojcw"]
|
[gd_scene load_steps=16 format=3 uid="uid://c777lv8mjojcw"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://dayyx4jerj7io" path="res://scene/ground/ground.tscn" id="1_mrwu5"]
|
[ext_resource type="PackedScene" uid="uid://dayyx4jerj7io" path="res://scene/ground/ground.tscn" id="1_mrwu5"]
|
||||||
[ext_resource type="Script" uid="uid://c7rvvsuf18ykn" path="res://scene/ground/scene/c01/s09_animation.gd" id="2_u053j"]
|
[ext_resource type="Script" uid="uid://c7rvvsuf18ykn" path="res://scene/ground/scene/c01/s09_animation.gd" id="2_u053j"]
|
||||||
@ -9,6 +9,7 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://bicuc35kbn8hd" path="res://scene/shading/fog.tscn" id="7_wrhtk"]
|
[ext_resource type="PackedScene" uid="uid://bicuc35kbn8hd" path="res://scene/shading/fog.tscn" id="7_wrhtk"]
|
||||||
[ext_resource type="Shader" uid="uid://bcfnbll451i2r" path="res://asset/shader/fog.gdshader" id="8_0ohlv"]
|
[ext_resource type="Shader" uid="uid://bcfnbll451i2r" path="res://asset/shader/fog.gdshader" id="8_0ohlv"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cs56isj1je50a" path="res://asset/art/scene/c01/s07_书店外/fog_mask.png" id="9_j34rt"]
|
[ext_resource type="Texture2D" uid="uid://cs56isj1je50a" path="res://asset/art/scene/c01/s07_书店外/fog_mask.png" id="9_j34rt"]
|
||||||
|
[ext_resource type="SpriteFrames" uid="uid://c2peyi2l65h47" path="res://asset/art/gif/c01_公寓外街道/frames.tres" id="10_0ohlv"]
|
||||||
|
|
||||||
[sub_resource type="LabelSettings" id="LabelSettings_7adaf"]
|
[sub_resource type="LabelSettings" id="LabelSettings_7adaf"]
|
||||||
font_size = 20
|
font_size = 20
|
||||||
@ -78,7 +79,14 @@ one_shot = false
|
|||||||
freeze_time = 0.1
|
freeze_time = 0.1
|
||||||
hook_method = "player_been_catched"
|
hook_method = "player_been_catched"
|
||||||
|
|
||||||
[node name="Fog" parent="Ground/DeployLayer" index="6" instance=ExtResource("7_wrhtk")]
|
[node name="书店老板害怕" type="AnimatedSprite2D" parent="Ground/DeployLayer" index="6"]
|
||||||
|
position = Vector2(507, 0)
|
||||||
|
sprite_frames = ExtResource("10_0ohlv")
|
||||||
|
animation = &"书店老板害怕"
|
||||||
|
autoplay = "书店老板害怕"
|
||||||
|
|
||||||
|
[node name="Fog" parent="Ground/DeployLayer" index="7" instance=ExtResource("7_wrhtk")]
|
||||||
|
visible = false
|
||||||
z_index = 10
|
z_index = 10
|
||||||
material = SubResource("ShaderMaterial_828bq")
|
material = SubResource("ShaderMaterial_828bq")
|
||||||
position = Vector2(399, -49)
|
position = Vector2(399, -49)
|
||||||
|
@ -12,7 +12,6 @@ func _ready() -> void:
|
|||||||
if Engine.is_editor_hint():
|
if Engine.is_editor_hint():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
func _on_ground_ready() -> void:
|
func _on_ground_ready() -> void:
|
||||||
# 不显示玩家,锁定玩家移动
|
# 不显示玩家,锁定玩家移动
|
||||||
SceneManager.freeze_player(0)
|
SceneManager.freeze_player(0)
|
||||||
|
@ -33,7 +33,8 @@ func _on_ground_ready() -> void:
|
|||||||
chapter_sfx = $chapter_sfx
|
chapter_sfx = $chapter_sfx
|
||||||
SceneManager.get_camera_marker().limit_bottom = 158
|
SceneManager.get_camera_marker().limit_bottom = 158
|
||||||
SceneManager.get_camera_marker().limit_top = -158
|
SceneManager.get_camera_marker().limit_top = -158
|
||||||
# play("intro")
|
|
||||||
|
play("intro")
|
||||||
|
|
||||||
# ## test 测试最后运镜 ##
|
# ## test 测试最后运镜 ##
|
||||||
# main_character.global_position.x = target_x
|
# main_character.global_position.x = target_x
|
||||||
|
@ -126,7 +126,7 @@ player_y = 40
|
|||||||
|
|
||||||
[node name="AnimationPlayer" parent="Ground" index="0"]
|
[node name="AnimationPlayer" parent="Ground" index="0"]
|
||||||
libraries = {
|
libraries = {
|
||||||
"": SubResource("AnimationLibrary_6ojod")
|
&"": SubResource("AnimationLibrary_6ojod")
|
||||||
}
|
}
|
||||||
script = ExtResource("2_espm6")
|
script = ExtResource("2_espm6")
|
||||||
oneshot_animation = ""
|
oneshot_animation = ""
|
||||||
@ -146,9 +146,9 @@ dir = "c01"
|
|||||||
file = "序章标题出现.wav"
|
file = "序章标题出现.wav"
|
||||||
|
|
||||||
[node name="BGSprite2D" parent="Ground" index="2"]
|
[node name="BGSprite2D" parent="Ground" index="2"]
|
||||||
position = Vector2(0, 6)
|
position = Vector2(-12, 2)
|
||||||
texture = ExtResource("6_vjki6")
|
texture = ExtResource("6_vjki6")
|
||||||
offset = Vector2(0, -640)
|
offset = Vector2(0, -646)
|
||||||
|
|
||||||
[node name="portal_left" parent="Ground/DeployLayer" index="0"]
|
[node name="portal_left" parent="Ground/DeployLayer" index="0"]
|
||||||
position = Vector2(95, 40)
|
position = Vector2(95, 40)
|
||||||
@ -157,7 +157,7 @@ position = Vector2(95, 40)
|
|||||||
target_scene = "c02_s01"
|
target_scene = "c02_s01"
|
||||||
|
|
||||||
[node name="车夫与吕萍" type="AnimatedSprite2D" parent="Ground/DeployLayer" index="2"]
|
[node name="车夫与吕萍" type="AnimatedSprite2D" parent="Ground/DeployLayer" index="2"]
|
||||||
position = Vector2(136, 30)
|
position = Vector2(157, 68)
|
||||||
sprite_frames = ExtResource("7_wo6md")
|
sprite_frames = ExtResource("7_wo6md")
|
||||||
animation = &"车夫静止"
|
animation = &"车夫静止"
|
||||||
autoplay = "车夫静止"
|
autoplay = "车夫静止"
|
||||||
@ -171,7 +171,6 @@ shape = SubResource("RectangleShape2D_b0oon")
|
|||||||
position = Vector2(461, 80)
|
position = Vector2(461, 80)
|
||||||
sprite_frames = ExtResource("3_hnnuc")
|
sprite_frames = ExtResource("3_hnnuc")
|
||||||
animation = &"c01_捡球男孩_关键帧"
|
animation = &"c01_捡球男孩_关键帧"
|
||||||
autoplay = "c01_捡球男孩_关键帧"
|
|
||||||
|
|
||||||
[node name="ambush_title1" parent="Ground/DeployLayer" index="4" instance=ExtResource("6_3k8jj")]
|
[node name="ambush_title1" parent="Ground/DeployLayer" index="4" instance=ExtResource("6_3k8jj")]
|
||||||
position = Vector2(1569, 21)
|
position = Vector2(1569, 21)
|
||||||
|
@ -4,4 +4,17 @@ extends Node2D
|
|||||||
func play():
|
func play():
|
||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
tween.tween_property(self, "modulate:a", 1.0, 0.5)
|
tween.tween_property(self, "modulate:a", 1.0, 0.5)
|
||||||
$AnimationPlayer.play("鬼差探头")
|
$Sfx.play()
|
||||||
|
var animation_player = $AnimationPlayer as AnimationPlayer
|
||||||
|
animation_player.play("鬼差探头")
|
||||||
|
animation_player.animation_finished.connect(_on_animation_finished)
|
||||||
|
|
||||||
|
func play_bgm():
|
||||||
|
# TODO 音效
|
||||||
|
# var stream = preload("res://asset/sfx/c01/s10_bgm.mp3")
|
||||||
|
# AudioManager.loop_bgm_music("黄包车背景音效", stream)
|
||||||
|
SceneManager.pop_debug_dialog_info("音效", "播放黄包车背景音乐过渡")
|
||||||
|
|
||||||
|
func _on_animation_finished(_name):
|
||||||
|
# 跳转场景
|
||||||
|
SceneManager.get_ground_loader().transition_to_scene("c01_s11", "left")
|
||||||
|
@ -1,9 +1,26 @@
|
|||||||
[gd_scene load_steps=8 format=3 uid="uid://bf6oxxe2e6vxj"]
|
[gd_scene load_steps=10 format=3 uid="uid://bf6oxxe2e6vxj"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bgffnekpvw8pu" path="res://scene/ground/script/c01/s10_鬼差探头.gd" id="1_pvnth"]
|
[ext_resource type="Script" uid="uid://bgffnekpvw8pu" path="res://scene/ground/script/c01/s10_鬼差探头.gd" id="1_pvnth"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dsyb81xacsbc8" path="res://asset/art/scene/c01/s03_旧版序章/ux_背景gaise.png" id="2_r4vbv"]
|
[ext_resource type="Texture2D" uid="uid://dsyb81xacsbc8" path="res://asset/art/scene/c01/s03_旧版序章/ux_背景gaise.png" id="2_r4vbv"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bqawq75la061h" path="res://asset/art/scene/c01/s03_旧版序章/ux_鬼差脸gaise.png" id="3_dxjux"]
|
[ext_resource type="Texture2D" uid="uid://bqawq75la061h" path="res://asset/art/scene/c01/s03_旧版序章/ux_鬼差脸gaise.png" id="3_dxjux"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bvxa5gejmlk4o" path="res://asset/art/scene/c01/s03_旧版序章/e_柱子有红纹gaise.png" id="4_beln2"]
|
[ext_resource type="Texture2D" uid="uid://bvxa5gejmlk4o" path="res://asset/art/scene/c01/s03_旧版序章/e_柱子有红纹gaise.png" id="4_beln2"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://ctdoent5ye3us" path="res://asset/audio/sfx/c01/黑雾说话.wav" id="5_c13qq"]
|
||||||
|
[ext_resource type="Script" uid="uid://rq6w1vuhuq1m" path="res://scene/entity/general/sfx.gd" id="5_ug335"]
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_ug335"]
|
||||||
|
length = 0.001
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("脸与眼:position")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Vector2(-67, -10)]
|
||||||
|
}
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_ptmad"]
|
[sub_resource type="Animation" id="Animation_ptmad"]
|
||||||
resource_name = "鬼差探头"
|
resource_name = "鬼差探头"
|
||||||
@ -20,20 +37,19 @@ tracks/0/keys = {
|
|||||||
"update": 0,
|
"update": 0,
|
||||||
"values": [Vector2(-87, -5), Vector2(-78, 3), Vector2(-66, 2), Vector2(-56, 5), Vector2(-45, 5), Vector2(-41, 10), Vector2(-40, 19), Vector2(-30, 24), Vector2(-27, 28)]
|
"values": [Vector2(-87, -5), Vector2(-78, 3), Vector2(-66, 2), Vector2(-56, 5), Vector2(-45, 5), Vector2(-41, 10), Vector2(-40, 19), Vector2(-30, 24), Vector2(-27, 28)]
|
||||||
}
|
}
|
||||||
|
tracks/1/type = "method"
|
||||||
[sub_resource type="Animation" id="Animation_ug335"]
|
tracks/1/imported = false
|
||||||
length = 0.001
|
tracks/1/enabled = true
|
||||||
tracks/0/type = "value"
|
tracks/1/path = NodePath(".")
|
||||||
tracks/0/imported = false
|
tracks/1/interp = 1
|
||||||
tracks/0/enabled = true
|
tracks/1/loop_wrap = true
|
||||||
tracks/0/path = NodePath("脸与眼:position")
|
tracks/1/keys = {
|
||||||
tracks/0/interp = 1
|
"times": PackedFloat32Array(3.7),
|
||||||
tracks/0/loop_wrap = true
|
|
||||||
tracks/0/keys = {
|
|
||||||
"times": PackedFloat32Array(0),
|
|
||||||
"transitions": PackedFloat32Array(1),
|
"transitions": PackedFloat32Array(1),
|
||||||
"update": 0,
|
"values": [{
|
||||||
"values": [Vector2(-67, -10)]
|
"args": [],
|
||||||
|
"method": &"play_bgm"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ug335"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ug335"]
|
||||||
@ -66,3 +82,11 @@ position = Vector2(22, -3)
|
|||||||
scale = Vector2(1.0601, 1.38482)
|
scale = Vector2(1.0601, 1.38482)
|
||||||
texture = ExtResource("4_beln2")
|
texture = ExtResource("4_beln2")
|
||||||
centered = false
|
centered = false
|
||||||
|
|
||||||
|
[node name="Sfx" type="AudioStreamPlayer" parent="."]
|
||||||
|
stream = ExtResource("5_c13qq")
|
||||||
|
bus = &"game_sfx"
|
||||||
|
script = ExtResource("5_ug335")
|
||||||
|
dir = "c01"
|
||||||
|
file = "黑雾说话.wav"
|
||||||
|
metadata/_custom_type_script = "uid://rq6w1vuhuq1m"
|
||||||
|
@ -169,7 +169,7 @@ func _update_prop_display_with_texture():
|
|||||||
if not inventory:
|
if not inventory:
|
||||||
return
|
return
|
||||||
# 在没有道具时,展示空手 placeholder
|
# 在没有道具时,展示空手 placeholder
|
||||||
if inventory.enabled_items.size() == 0:
|
if inventory.enabled_items.is_empty():
|
||||||
display_prop.texture_normal = preload("res://asset/art/ui/hud/placeholder.png")
|
display_prop.texture_normal = preload("res://asset/art/ui/hud/placeholder.png")
|
||||||
display_prop.size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X)
|
display_prop.size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X)
|
||||||
display_prop.scale = Vector2(1.0, 1.0)
|
display_prop.scale = Vector2(1.0, 1.0)
|
||||||
@ -188,7 +188,7 @@ func _update_prop_display_with_texture():
|
|||||||
select_mark.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X)
|
select_mark.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X)
|
||||||
select_mark.texture = preload("res://asset/art/ui/hud/select_mark.png")
|
select_mark.texture = preload("res://asset/art/ui/hud/select_mark.png")
|
||||||
# bag
|
# bag
|
||||||
for i in range(prop_containers.size()):
|
for i in range(inventory.enabled_items.size()):
|
||||||
var id = wrapi(i, 0, inventory.enabled_items.size())
|
var id = wrapi(i, 0, inventory.enabled_items.size())
|
||||||
var key = inventory.enabled_items[id]
|
var key = inventory.enabled_items[id]
|
||||||
var button = prop_containers[i].get_child(0).get_child(0) as TextureButton
|
var button = prop_containers[i].get_child(0).get_child(0) as TextureButton
|
||||||
|
@ -14,9 +14,9 @@ signal current_item_changed(prop_key: String)
|
|||||||
current_item_changed.emit("")
|
current_item_changed.emit("")
|
||||||
|
|
||||||
|
|
||||||
func current_item_key():
|
func current_item_key() -> String:
|
||||||
if enabled_items.size() == 0:
|
if enabled_items.size() == 0:
|
||||||
return null
|
return ""
|
||||||
return enabled_items[current_index]
|
return enabled_items[current_index]
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user