@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