2025-01-12 12:15:18 +00:00
|
|
|
|
@tool
|
2025-01-20 13:45:47 +00:00
|
|
|
|
class_name Interactable2D extends Sprite2D
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
# interacted_times 增加后,再发出信号
|
2025-01-20 13:45:47 +00:00
|
|
|
|
signal interacted
|
2025-05-13 11:45:33 +00:00
|
|
|
|
signal interacted_with_key(key)
|
2025-06-15 05:11:41 +00:00
|
|
|
|
# 交互 prop 不匹配
|
|
|
|
|
signal interact_mismatch_failed
|
2025-01-12 12:15:18 +00:00
|
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
|
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)
|
2025-01-20 13:45:47 +00:00
|
|
|
|
@export var enabled := true:
|
|
|
|
|
set(val):
|
|
|
|
|
enabled = val
|
|
|
|
|
if is_node_ready():
|
2025-05-13 11:45:33 +00:00
|
|
|
|
_check_sign_display()
|
2025-06-04 11:46:27 +00:00
|
|
|
|
@export var unrevealed_sign_texture: Texture2D
|
2025-01-13 08:09:57 +00:00
|
|
|
|
@export var unmatched_sign_texture: Texture2D
|
|
|
|
|
@export var matched_sign_texture: Texture2D
|
2025-05-13 11:45:33 +00:00
|
|
|
|
@export var mute_when_interacted := false
|
|
|
|
|
@export var mute_when_invalid := false
|
2025-01-12 12:15:18 +00:00
|
|
|
|
@export var one_shot := true
|
2025-05-13 11:45:33 +00:00
|
|
|
|
@export var one_shot_max_times := 1
|
2025-02-18 13:21:19 +00:00
|
|
|
|
@export var disable_prop_after_interacted := false
|
2025-01-30 12:04:02 +00:00
|
|
|
|
@export var interacted_texture: Texture2D
|
2025-01-20 13:45:47 +00:00
|
|
|
|
|
2025-01-12 12:15:18 +00:00
|
|
|
|
var prop_key := ""
|
2025-05-13 11:45:33 +00:00
|
|
|
|
var prop_key2 := ""
|
|
|
|
|
var prop_key3 := ""
|
2025-01-03 08:07:35 +00:00
|
|
|
|
|
2025-01-13 08:09:57 +00:00
|
|
|
|
@onready var sfx_invalid = $SfxInvalid as Sfx
|
|
|
|
|
@onready var sfx_success = $SfxSuccess as Sfx
|
2025-01-08 00:51:09 +00:00
|
|
|
|
@onready var sign_mark = %Sign as Sign
|
2025-01-03 08:07:35 +00:00
|
|
|
|
@onready var area2d = %Area2D as Area2D
|
|
|
|
|
|
2025-01-20 13:45:47 +00:00
|
|
|
|
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)
|
|
|
|
|
# 成功互动的次数
|
2025-01-20 13:45:47 +00:00
|
|
|
|
var interacted_times: int:
|
|
|
|
|
set(val):
|
|
|
|
|
interacted_times = val
|
|
|
|
|
ground_archive.set_pair(name, "interacted_times", val)
|
|
|
|
|
|
2025-01-12 12:15:18 +00:00
|
|
|
|
static var item_config_res = preload("res://asset/dialogue/item_description.dialogue")
|
|
|
|
|
|
|
|
|
|
|
2025-01-03 08:07:35 +00:00
|
|
|
|
func _ready() -> void:
|
2025-01-12 12:15:18 +00:00
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
return
|
2025-01-03 08:07:35 +00:00
|
|
|
|
area2d.body_entered.connect(_reset)
|
|
|
|
|
area2d.body_exited.connect(_on_cancel)
|
|
|
|
|
sign_mark.interacted.connect(_on_interacted)
|
|
|
|
|
sign_mark.cancel.connect(_on_cancel)
|
2025-01-20 13:45:47 +00:00
|
|
|
|
sign_mark.enabled = enabled
|
|
|
|
|
# setup default value
|
|
|
|
|
ground_archive = ArchiveManager.archive.ground_archive()
|
2025-06-04 11:46:27 +00:00
|
|
|
|
tried_times = ground_archive.get_value(name, "tried_times", 0)
|
2025-01-20 13:45:47 +00:00
|
|
|
|
interacted_times = ground_archive.get_value(name, "interacted_times", 0)
|
2025-01-30 12:04:02 +00:00
|
|
|
|
if interacted_times and interacted_texture:
|
|
|
|
|
texture = interacted_texture
|
2025-01-24 14:19:17 +00:00
|
|
|
|
_check_sign_display()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _check_sign_display():
|
2025-05-13 11:45:33 +00:00
|
|
|
|
sign_mark.enabled = enabled and (not one_shot or interacted_times < one_shot_max_times)
|
2025-01-24 14:19:17 +00:00
|
|
|
|
|
2025-01-20 13:45:47 +00:00
|
|
|
|
|
2025-02-14 11:04:21 +00:00
|
|
|
|
func _reset(_body = null) -> void:
|
|
|
|
|
_reset_sign_testure_to_prop()
|
|
|
|
|
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)
|
2025-01-03 08:07:35 +00:00
|
|
|
|
|
|
|
|
|
|
2025-02-14 11:04:21 +00:00
|
|
|
|
func _reset_sign_testure_to_prop():
|
2025-01-13 08:09:57 +00:00
|
|
|
|
var key = SceneManager.get_current_prop(false)
|
2025-03-11 09:16:24 +00:00
|
|
|
|
_set_sign_texture_to_prop(key)
|
2025-01-13 08:09:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 根据当前 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 tried_times == 0:
|
|
|
|
|
# 首次交互前 unrevealed
|
|
|
|
|
sign_mark.sprite2d.texture = unrevealed_sign_texture
|
|
|
|
|
return
|
|
|
|
|
# 目前默认都显示 matched_sign_texture
|
|
|
|
|
sign_mark.sprite2d.texture = matched_sign_texture
|
|
|
|
|
# if is_key_matched(key):
|
|
|
|
|
# sign_mark.sprite2d.texture = matched_sign_texture
|
2025-01-13 08:09:57 +00:00
|
|
|
|
# else:
|
2025-06-04 11:46:27 +00:00
|
|
|
|
# sign_mark.sprite2d.texture = unmatched_sign_texture
|
2025-01-03 08:07:35 +00:00
|
|
|
|
|
|
|
|
|
func _on_cancel(_body = null) -> void:
|
2025-01-13 08:09:57 +00:00
|
|
|
|
# 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)
|
2025-01-03 08:07:35 +00:00
|
|
|
|
|
|
|
|
|
|
2025-01-20 13:45:47 +00:00
|
|
|
|
var interact_mutex = Mutex.new()
|
|
|
|
|
|
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
func is_key_matched(key) -> bool:
|
|
|
|
|
return (
|
|
|
|
|
# 第一个 prop_key 若空,则表示不需要匹配
|
|
|
|
|
not prop_key
|
|
|
|
|
or (
|
|
|
|
|
# 只要有一个 prop_key 匹配即可
|
|
|
|
|
(prop_key and key == prop_key)
|
|
|
|
|
or (prop_key2 and key == prop_key2)
|
|
|
|
|
or (prop_key3 and key == prop_key3)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-01-03 08:07:35 +00:00
|
|
|
|
func _on_interacted() -> void:
|
2025-01-20 13:45:47 +00:00
|
|
|
|
interact_mutex.lock()
|
2025-06-04 11:46:27 +00:00
|
|
|
|
tried_times += 1
|
2025-05-13 11:45:33 +00:00
|
|
|
|
if one_shot and interacted_times >= one_shot_max_times:
|
2025-01-20 13:45:47 +00:00
|
|
|
|
interact_mutex.unlock()
|
2025-01-12 12:15:18 +00:00
|
|
|
|
return
|
2025-01-13 08:09:57 +00:00
|
|
|
|
var key = SceneManager.get_current_prop(false)
|
2025-05-13 11:45:33 +00:00
|
|
|
|
if not is_key_matched(key):
|
|
|
|
|
if not mute_when_invalid:
|
|
|
|
|
sfx_invalid.play()
|
2025-06-04 11:46:27 +00:00
|
|
|
|
sign_mark.invalid_shake(matched_sign_texture, unmatched_sign_texture)
|
2025-01-14 10:20:31 +00:00
|
|
|
|
# SceneManager.on_toggle_invalid_prop()
|
2025-01-20 13:45:47 +00:00
|
|
|
|
interact_mutex.unlock()
|
2025-06-15 05:11:41 +00:00
|
|
|
|
interact_mismatch_failed.emit()
|
2025-01-12 12:15:18 +00:00
|
|
|
|
return
|
2025-05-13 11:45:33 +00:00
|
|
|
|
if not mute_when_interacted:
|
|
|
|
|
sfx_success.play()
|
|
|
|
|
if disable_prop_after_interacted and key:
|
|
|
|
|
SceneManager.disable_prop_item(key)
|
2025-01-12 12:15:18 +00:00
|
|
|
|
interacted_times += 1
|
2025-01-30 12:04:02 +00:00
|
|
|
|
if interacted_texture:
|
|
|
|
|
texture = interacted_texture
|
2025-05-13 11:45:33 +00:00
|
|
|
|
# interacted_times 增加后,再发出信号
|
2025-01-12 12:15:18 +00:00
|
|
|
|
interacted.emit()
|
2025-05-13 11:45:33 +00:00
|
|
|
|
interacted_with_key.emit(key)
|
|
|
|
|
EventManager.prop_interacted(name, key, interacted_times)
|
|
|
|
|
# print("%s interacted with %s. total times: %s" % [name, key, interacted_times])
|
2025-01-20 13:45:47 +00:00
|
|
|
|
interact_mutex.unlock()
|
2025-01-24 14:19:17 +00:00
|
|
|
|
_check_sign_display()
|
2025-02-14 11:04:21 +00:00
|
|
|
|
_reset_sign_testure_to_prop()
|
2025-01-12 12:15:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _get_property_list() -> Array[Dictionary]:
|
2025-02-14 11:04:21 +00:00
|
|
|
|
var items = []
|
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
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]
|
2025-01-12 12:15:18 +00:00
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"name": "prop_key",
|
|
|
|
|
"type": TYPE_STRING,
|
|
|
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
|
|
|
"hint_string": ",".join(items),
|
2025-05-13 11:45:33 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "prop_key2",
|
|
|
|
|
"type": TYPE_STRING,
|
|
|
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
|
|
|
"hint_string": ",".join(items),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "prop_key3",
|
|
|
|
|
"type": TYPE_STRING,
|
|
|
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
|
|
|
"hint_string": ",".join(items),
|
2025-01-12 12:15:18 +00:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _get(property: StringName) -> Variant:
|
|
|
|
|
if property == "prop_key":
|
|
|
|
|
return prop_key
|
2025-05-13 11:45:33 +00:00
|
|
|
|
elif property == "prop_key2":
|
|
|
|
|
return prop_key2
|
|
|
|
|
elif property == "prop_key3":
|
|
|
|
|
return prop_key3
|
2025-01-12 12:15:18 +00:00
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _set(property: StringName, value: Variant) -> bool:
|
|
|
|
|
if property == "prop_key":
|
|
|
|
|
prop_key = value
|
|
|
|
|
return true
|
2025-05-13 11:45:33 +00:00
|
|
|
|
elif property == "prop_key2":
|
|
|
|
|
prop_key2 = value
|
|
|
|
|
return true
|
|
|
|
|
elif property == "prop_key3":
|
|
|
|
|
prop_key3 = value
|
|
|
|
|
return true
|
2025-01-12 12:15:18 +00:00
|
|
|
|
return false
|