xiandie/manager/event_manager/event_binder.gd
2025-08-06 01:37:15 +08:00

242 lines
7.3 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 EventBinder extends Node
@export_group("Updater", "updater")
@export var updater_event := &"":
set(val):
if updater_event != val:
updater_event = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export_enum("enabler", "unfreezer", "player", "shower", "opener") var updater_mode := "enabler":
set(val):
if updater_mode != val:
updater_mode = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
# 仅对 canvas item 生效; <=0 时无 ease
@export var updater_ease_duration := 1.0:
set(val):
if updater_ease_duration != val:
updater_ease_duration = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export_enum("include", "exclude") var updater_stage_mode := "include":
set(val):
if updater_stage_mode != val:
updater_stage_mode = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export var updater_stages: Array[int] = [1]:
set(val):
if updater_stages != val:
updater_stages = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export_group("Trigger", "trigger")
@export var trigger_event := &"":
set(val):
if trigger_event != val:
trigger_event = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export_tool_button("AutoDetectMode") var trigger_auto_detect_mode := _auto_detect_mode
@export_enum("none", "interacted", "triggered", "picked", "played") var trigger_mode := "none":
set(val):
if trigger_mode != val:
trigger_mode = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export_range(0, 10000, 1) var trigger_stage := 1:
set(val):
if trigger_stage != val:
trigger_stage = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
@export var trigger_set_stage_if_greater := true:
set(val):
if trigger_set_stage_if_greater != val:
trigger_set_stage_if_greater = val
if is_node_ready() and Engine.is_editor_hint():
_auto_memo()
var memo := ""
@export_tool_button("auto memo") var auto_memo = _auto_memo
func _ready() -> void:
if Engine.is_editor_hint():
_auto_memo()
return
var parent = get_parent()
if not updater_event.is_empty():
if not parent:
printerr("[EventBinder] bind_enable is true but parent is null. path: %s" % get_path())
return
EventManager.stage_updated.connect(_on_global_stage_updated)
# 等待 parent ready再执行 update 操作
parent.ready.connect(_update_parent, CONNECT_ONE_SHOT)
if not trigger_event.is_empty():
# connect trigger_mode signal
if trigger_mode != "none":
parent.connect(trigger_mode, _on_happened)
func _update_parent() -> void:
if GlobalConfig.DEBUG:
prints(get_parent().name, updater_event, "updater stage:", EventManager.get_stage(updater_event))
_update_binding_state(EventManager.get_stage(updater_event))
func _on_happened(_arg1 = null, _arg2 = null) -> void:
if trigger_set_stage_if_greater:
var updated = EventManager.set_stage_if_greater(trigger_event, trigger_stage)
if not updated:
print(
(
"[EventBinder] _on_happened not updated, event: %s, stage: %s"
% [trigger_event, EventManager.get_stage(trigger_event)]
)
)
else:
EventManager.set_stage(trigger_event, trigger_stage)
func _on_global_stage_updated(e: StringName, s: int):
if e == updater_event:
_update_binding_state(s)
func _update_binding_state(stage: int):
var parent = get_parent()
if not parent:
printerr("[EventBinder] _update_updater_visibility parent is null. path: %s" % get_path())
return
# check stage in updater_stages
var updater_status = false
if updater_stage_mode == "exclude":
updater_status = not stage in updater_stages
elif updater_stage_mode == "include":
updater_status = stage in updater_stages
# check updater_mode
if updater_mode == "enabler":
parent.enabled = updater_status
elif updater_mode == "unfreezer":
parent.set("freezing", not updater_status)
elif updater_mode == "player":
if updater_status:
parent.play()
else:
if parent is Sfx2D:
parent.easing_kill()
else:
parent.stop()
elif updater_mode == "opener":
# open portal
parent.status = "opened" if updater_status else "locked"
elif updater_mode == "shower":
_toggle_parent_visibility_with_ease(parent, updater_status)
func _toggle_parent_visibility_with_ease(parent, show: bool):
if parent is CanvasItem:
if updater_ease_duration > 0.0:
if show:
parent.show()
var original_a = parent.modulate.a
var target_a = 1.0 if show else 0.0
var tween = create_tween()
tween.tween_property(parent, "modulate:a", target_a, updater_ease_duration)
if not show:
tween.tween_callback(parent.hide)
tween.tween_callback(func(): parent.modulate.a = original_a)
else:
parent.visible = show
func _auto_detect_mode():
var parent = get_parent()
if not parent:
printerr("[EventBinder] _auto_detect_mode parent is null. path: %s" % get_path())
if parent.has_signal("interacted"):
trigger_mode = "interacted"
elif parent.has_signal("triggered"):
trigger_mode = "triggered"
elif parent.has_signal("picked"):
trigger_mode = "picked"
elif parent.has_signal("played"):
trigger_mode = "played"
else:
printerr("[EventBinder] _auto_detect_mode cannot detect trigger mode. path:", get_path())
##### memo getter and setter
func _get(property: StringName) -> Variant:
if property == "memo":
if Engine.is_editor_hint():
return null
if memo == "":
_auto_memo()
return memo
return null
func _get_property_list() -> Array[Dictionary]:
return [
{
"name": "memo",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_MULTILINE_TEXT,
"usage": PROPERTY_USAGE_EDITOR
}
]
#### TOOL BUTTON
func _auto_memo() -> void:
# updater release_stage[(!)=0|1] 时[显示|启用][parent.name.replace("Closeup", "")]
# trigger [parent] [picked|played...] 时 release_stage=1(greater|any)
var parent_name = get_parent().name.to_lower()
# replace english letters
var matcher = RegEx.create_from_string("^[a-z]*(.*)$")
var parent_name_match = matcher.search(parent_name).get_string(1)
if parent_name_match != "":
parent_name = parent_name_match
var memo_str = ""
if updater_event != "":
memo_str = updater_event
memo_str += "=" if updater_stage_mode == "include" else "!="
var stages = await EventManager.map_event_stages(updater_event, updater_stages)
memo_str += "[" + "|".join(stages) + "] 时,"
if updater_mode == "shower":
memo_str += "显示"
elif updater_mode == "enabler":
memo_str += "启用"
elif updater_mode == "unfreezer":
memo_str += "解冻"
elif updater_mode == "player":
memo_str += "播放"
elif updater_mode == "opener":
memo_str += "打开"
else:
printerr("[EventBinder] _auto_memo updater_mode not recognized:", updater_mode)
memo_str += ": [" + parent_name + "]\n"
if trigger_event != "" and trigger_mode != "none":
memo_str += "[" + parent_name + "] " + trigger_mode + " 时: "
var stages = await EventManager.map_event_stages(trigger_event, [trigger_stage])
memo_str += trigger_event + "=" + str(stages[0])
memo_str += "(greater)" if trigger_set_stage_if_greater else "(any)"
var existing_lines = memo.split("\n")
# 保留 # 开头的 line
var kept_lines = []
for line in existing_lines:
if line.begins_with("#"):
kept_lines.append(line)
memo = "\n".join(kept_lines)
memo += "\n" if kept_lines else ""
memo += memo_str
notify_property_list_changed()