xiandie/scene/entity/portal.gd

264 lines
7.7 KiB
GDScript3
Raw Permalink Normal View History

@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
2025-06-04 11:46:27 +00:00
@export var unrevealed_sign_texture: Texture2D
@export var default_sign_texture: Texture2D
@export var opened_sign_texture: Texture2D
@export var matched_sign_texture: Texture2D
2025-06-04 11:46:27 +00:00
@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
2025-01-08 00:51:09 +00:00
@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
2025-06-04 11:46:27 +00:00
# 尝试互动的次数
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()
2025-06-04 11:46:27 +00:00
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
2025-06-04 11:46:27 +00:00
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:
2025-06-04 11:46:27 +00:00
tried_times += 1
if holding:
if holding_reason_key:
2025-06-07 09:04:08 +00:00
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()
2025-06-04 11:46:27 +00:00
sign_mark.invalid_shake(locked_sign_texture, locked_sign_texture)
_check_sign_mark_and_texture()
interact_mutex.unlock()
# 开锁尝试后,哪怕开锁成功,也需要下次操作再进入,而不是立即传送
return
2025-01-14 00:56:51 +00:00
# 传送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
2025-06-17 05:01:19 +00:00
func _set_sign_texture_to_prop(_key):
2025-06-04 11:46:27 +00:00
# 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:
2025-06-04 11:46:27 +00:00
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