xiandie/scene/entity/portal.gd
2025-06-17 13:01:19 +08:00

264 lines
7.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@tool
class_name Portal2D extends Sprite2D
signal before_pre_transport_wait
signal sign_mark_offset_updated
# sign_mark 节点在 ready 时会直接读取
@export var sign_mark_offset := Vector2.ZERO:
set(val):
sign_mark_offset = val
sign_mark_offset_updated.emit(val)
@export_multiline var debug_note: String = ""
@export var enabled := true:
set(val):
enabled = val
_check_sign_mark_and_texture()
@export var immediately := false
@export_enum("left", "right", "1", "2", "3", "4", "5", "6", "7", "8", "9") var portal_name := "left":
set(value):
#if portal_name:
#remove_from_group("portal_"+portal_name)
portal_name = value
#add_to_group("portal_"+value)
name = "portal_" + value
@export var target_scene := "c02_s00"
@export_enum("none", "left", "right", "1", "2", "3", "4", "5", "6", "7", "8", "9")
var target_portal := "none":
set(value):
target_portal = value
_check_sign_mark_and_texture()
@export_enum("default", "locked", "opened") var status := "default":
set(value):
status = value
# 只在编辑器模式下检查
if is_node_ready() and Engine.is_editor_hint():
_check_sign_mark_and_texture()
@export var pre_transport_wait_time := 0.0
# holding 意味着当前门禁用,不可传送,需要先完成某些条件
@export var holding := false
@export var holding_reason_key := ""
@export var default_texture: Texture2D
@export var opened_texture: Texture2D
@export var unrevealed_sign_texture: Texture2D
@export var default_sign_texture: Texture2D
@export var opened_sign_texture: Texture2D
@export var matched_sign_texture: Texture2D
@export var locked_sign_texture: Texture2D
var prop_key := ""
@export var disable_key_after_used := true
# 穿过通道的音效
@onready var sfx_default = %SfxDefault as Sfx
# 开锁的音效
@onready var sfx_open = %SfxOpen as Sfx
# 进门的音效
@onready var sfx_enter = %SfxEnter as Sfx
# 门被锁定,无法打开的音效
@onready var sfx_locked = %SfxLocked as Sfx
@onready var sign_mark = %Sign as Sign
@onready var area2d = %Area2D as Area2D
var activated := false
static var item_config_res = preload("res://asset/dialogue/item_description.dialogue")
var items: PackedStringArray
var ground_archive: GroundArchive
# 尝试互动的次数
var tried_times: int:
set(val):
tried_times = val
ground_archive.set_pair(name, "tried_times", val)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
name = "portal_" + portal_name
if Engine.is_editor_hint():
_check_sign_mark_and_texture()
_reload_items()
return
ground_archive = ArchiveManager.archive.ground_archive()
tried_times = ground_archive.get_value(name, "tried_times", 0)
status = ground_archive.get_value(name, "status", status)
if GlobalConfig.DEBUG:
print("Portal read status [", name, "] status=", status)
_check_sign_mark_and_texture()
area2d.body_entered.connect(_reset)
area2d.body_exited.connect(_on_cancel)
sign_mark.interacted.connect(_on_interacted)
sign_mark.cancel.connect(_on_cancel)
func _reload_items() -> void:
items.clear()
var id = item_config_res.titles["PropItems"]
var current_line = item_config_res.lines[id]
while current_line:
if current_line.has("translation_key"):
items.append(current_line.translation_key)
if not current_line.has("next_id") or current_line.next_id == "end":
break
current_line = item_config_res.lines[current_line.next_id]
notify_property_list_changed()
func _check_sign_mark_and_texture():
if not is_node_ready():
return
if status == "opened" and opened_texture:
texture = opened_texture
elif default_texture:
texture = default_texture
if status == "default":
sign_mark.sprite2d.texture = default_sign_texture
else:
# holding 状态下,同样显示 unrevealed_sign_texture
if tried_times == 0 or holding:
sign_mark.sprite2d.texture = unrevealed_sign_texture
else:
match status:
"locked":
sign_mark.sprite2d.texture = locked_sign_texture
"opened":
sign_mark.sprite2d.texture = opened_sign_texture
if target_portal == "none" or not enabled:
sign_mark.enabled = false
else:
sign_mark.enabled = true
var interact_mutex = Mutex.new()
func _on_interacted() -> void:
tried_times += 1
if holding:
if holding_reason_key:
SceneManager.pop_os_with_str(holding_reason_key)
return
if target_portal == "none" or not enabled:
return
interact_mutex.lock()
if status == "locked":
# 检查是否有钥匙,尝试打开
var key = SceneManager.get_current_prop(false)
if prop_key and key == prop_key:
sfx_open.global_play()
status = "opened"
ground_archive.set_pair(name, "status", status)
if disable_key_after_used:
SceneManager.disable_prop_item(key)
else:
sfx_locked.global_play()
sign_mark.invalid_shake(locked_sign_texture, locked_sign_texture)
_check_sign_mark_and_texture()
interact_mutex.unlock()
# 开锁尝试后,哪怕开锁成功,也需要下次操作再进入,而不是立即传送
return
# 传送queue free 导致 sfx 无法播放,使用全局声源
if status == "default":
sfx_default.global_play()
elif status == "opened":
sfx_enter.global_play()
else:
sfx_locked.global_play()
interact_mutex.unlock()
return
interact_mutex.unlock()
before_pre_transport_wait.emit()
if pre_transport_wait_time > 0.0:
await get_tree().create_timer(pre_transport_wait_time).timeout
if GlobalConfig.DEBUG:
print("传送前往", target_scene, target_portal, " immediately=", immediately)
var ground_loader = SceneManager.get_ground_loader() as GroundLoader
if ground_loader:
if immediately:
ground_loader.transition_to_scene(target_scene, target_portal, 0.0)
else:
ground_loader.transition_to_scene(target_scene, target_portal)
func _on_cancel(_body = null):
activated = false
# disconnect signal
var prop_hud = SceneManager.get_prop_hud() as PropHud
if prop_hud and prop_hud.current_item_changed.is_connected(_set_sign_texture_to_prop):
prop_hud.current_item_changed.disconnect(_set_sign_texture_to_prop)
func _reset(_body):
_check_sign_mark_and_texture()
activated = true
if status == "locked":
var key = SceneManager.get_current_prop(false)
if key:
_set_sign_texture_to_prop(key)
var prop_hud = SceneManager.get_prop_hud() as PropHud
if not prop_hud.current_item_changed.is_connected(_set_sign_texture_to_prop):
prop_hud.current_item_changed.connect(_set_sign_texture_to_prop)
# 根据当前 prop调整 sign 所显示的 texture
func _set_sign_texture_to_prop(_key):
# if not prop_key or prop_key == key:
# sign_mark.sprite2d.texture = matched_sign_texture
# else:
if tried_times:
sign_mark.sprite2d.texture = locked_sign_texture
else:
sign_mark.sprite2d.texture = unrevealed_sign_texture
# 暂时不启用自动传送
# var action_times := 0
# func _input(event: InputEvent) -> void:
# # 长按自动传送
# if activated:
# if portal_name == "left" and target_portal == "right":
# if event.is_action("left"):
# action_times += 1
# elif event.is_action("right"):
# action_times = 0
# if action_times >= 7:
# activated = false
# action_times = 0
# %Sfx.play()
# _on_interacted()
# if portal_name == "right" and target_portal == "left":
# if event.is_action("right"):
# action_times += 1
# elif event.is_action("left"):
# action_times = 0
# if action_times >= 7:
# activated = false
# action_times = 0
# %Sfx.play()
# _on_interacted()
func _get_property_list() -> Array[Dictionary]:
return [
{
"name": "prop_key",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
"hint_string": ",".join(items),
}
]
func _get(property: StringName) -> Variant:
if property == "prop_key":
return prop_key
return null
func _set(property: StringName, value: Variant) -> bool:
if property == "prop_key":
prop_key = value
return true
return false