243 lines
6.7 KiB
GDScript
243 lines
6.7 KiB
GDScript
@tool
|
|
class_name Ambush2D extends Node2D
|
|
|
|
signal triggered
|
|
|
|
@export var enabled := true:
|
|
set(val):
|
|
enabled = val
|
|
if is_node_ready():
|
|
# sign_mark.enabled = val
|
|
_check_sign_display()
|
|
@export_enum("enter", "area_enter", "interact") var trigger_mode := "enter":
|
|
set(val):
|
|
trigger_mode = val
|
|
if is_node_ready():
|
|
_check_sign_display()
|
|
@export var one_shot := true
|
|
# 首次进入 tree 就直接启用
|
|
@export var on_first_enter_tree := false
|
|
@export var freeze_time := 5.0
|
|
var hook_animation = ""
|
|
@export var lock_player_on_playing_dialogue = true
|
|
@export var hook_os_key := ""
|
|
@export_enum("c01", "c02", "c03", "c04", "c05") var hook_dialogue_res = "c01":
|
|
set(val):
|
|
hook_dialogue_res = val
|
|
match val:
|
|
"c01":
|
|
dialogue_res = dialogue_c01
|
|
"c02":
|
|
dialogue_res = dialogue_c02
|
|
"c03":
|
|
dialogue_res = dialogue_c03
|
|
"c04":
|
|
dialogue_res = dialogue_c04
|
|
"c05":
|
|
dialogue_res = dialogue_c05
|
|
if is_node_ready() and Engine.is_editor_hint():
|
|
notify_property_list_changed()
|
|
var hook_dialogue_title := ""
|
|
var hook_method := ""
|
|
|
|
var dialogue_c01 = preload("res://asset/dialogue/c01.dialogue")
|
|
var dialogue_c02 = preload("res://asset/dialogue/c02.dialogue")
|
|
var dialogue_c03 = preload("res://asset/dialogue/c03.dialogue")
|
|
var dialogue_c04 = preload("res://asset/dialogue/c04.dialogue")
|
|
var dialogue_c05 = preload("res://asset/dialogue/c05.dialogue")
|
|
var dialogue_res = dialogue_c01
|
|
|
|
var played_time := 0.0
|
|
# var played := false:
|
|
# set(val):
|
|
# if played != val and ground_archive:
|
|
# ground_archive.set_pair(name, "played", played)
|
|
# played = val
|
|
|
|
@onready var sign_mark := %Sign as Sign
|
|
@onready var area := %Area2D as Area2D
|
|
var ground_archive: GroundArchive
|
|
var played: bool:
|
|
set(val):
|
|
played = val
|
|
if not Engine.is_editor_hint() and ground_archive:
|
|
ground_archive.set_pair(name, "played", played)
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
if Engine.is_editor_hint():
|
|
var animation_player = _get_animation_player()
|
|
# 更新 hook_animation 的可选项
|
|
if animation_player:
|
|
animation_player.animation_libraries_updated.connect(notify_property_list_changed)
|
|
return
|
|
# setup default value
|
|
ground_archive = ArchiveManager.archive.ground_archive()
|
|
played = ground_archive.get_value(name, "played", false)
|
|
played_time = 0.0
|
|
sign_mark.enabled = enabled
|
|
_check_sign_display()
|
|
if played:
|
|
if GlobalConfig.DEBUG:
|
|
print("Ambush has played, name=", name, " one_shot=", one_shot)
|
|
if one_shot:
|
|
return
|
|
sign_mark.interacted.connect(_interacted)
|
|
area.body_entered.connect(_entered)
|
|
area.area_entered.connect(_area_entered)
|
|
if on_first_enter_tree:
|
|
call_deferred("_do_trigger")
|
|
|
|
|
|
func _check_sign_display():
|
|
sign_mark.enabled = enabled and (not one_shot or not played)
|
|
sign_mark.display_sign = trigger_mode == "interact"
|
|
|
|
func _get_animation_player() -> AnimationPlayer:
|
|
var node = get_parent()
|
|
while node and not node is Ground2D:
|
|
node = node.get_parent()
|
|
if node is Ground2D:
|
|
return node.get_node("AnimationPlayer") as AnimationPlayer
|
|
return null
|
|
|
|
|
|
var trigger_mutex = Mutex.new()
|
|
|
|
|
|
func _interacted():
|
|
if enabled and trigger_mode == "interact":
|
|
_do_trigger()
|
|
|
|
|
|
func _entered(_body = null):
|
|
if enabled and trigger_mode == "enter":
|
|
_do_trigger()
|
|
|
|
|
|
func _area_entered(_area = null):
|
|
if enabled and trigger_mode == "area_enter":
|
|
_do_trigger()
|
|
|
|
|
|
func _do_trigger():
|
|
var time = Time.get_ticks_msec()
|
|
# 确保只有一个线程进入该逻辑,因为有时 player 碰撞和首次进入 tree 都会触发该方法
|
|
if not trigger_mutex.try_lock():
|
|
print("Ambush trigger mutex lock fail, name=", name)
|
|
return
|
|
print("Ambush trigger mutex locked, name=", name)
|
|
if not one_shot and freeze_time > 0:
|
|
var time_left = freeze_time - (time - played_time) * 0.001
|
|
if time_left > 0:
|
|
if GlobalConfig.DEBUG:
|
|
print("Ambush freeze time not reached, time left=", time_left)
|
|
trigger_mutex.unlock()
|
|
return
|
|
if one_shot and played:
|
|
trigger_mutex.unlock()
|
|
return
|
|
played_time = time
|
|
played = true
|
|
trigger_mutex.unlock()
|
|
# hook_animation
|
|
if hook_animation:
|
|
var animation_player = _get_animation_player()
|
|
if animation_player:
|
|
animation_player.play(hook_animation)
|
|
# hook_dialogue
|
|
if hook_dialogue_title:
|
|
if lock_player_on_playing_dialogue:
|
|
SceneManager.freeze_player(0.0)
|
|
DialogueManager.show_dialogue_balloon(dialogue_res, hook_dialogue_title)
|
|
DialogueManager.dialogue_ended.connect(_on_dialogue_ended, CONNECT_ONE_SHOT)
|
|
# hook_os
|
|
if hook_os_key:
|
|
SceneManager.pop_os_with_str(hook_os_key)
|
|
if hook_method:
|
|
var animation_player = _get_animation_player()
|
|
if animation_player:
|
|
animation_player.call(hook_method)
|
|
triggered.emit()
|
|
if GlobalConfig.DEBUG:
|
|
print("ambush triggered! name=", name)
|
|
_check_sign_display()
|
|
|
|
|
|
func _on_dialogue_ended(_res):
|
|
if GlobalConfig.DEBUG:
|
|
print("Ambush dialogue ended")
|
|
if lock_player_on_playing_dialogue:
|
|
SceneManager.release_player()
|
|
|
|
|
|
func _get(property: StringName) -> Variant:
|
|
if property == "hook_dialogue_title":
|
|
return hook_dialogue_title
|
|
elif property == "hook_animation":
|
|
return hook_animation
|
|
elif property == "hook_method":
|
|
return hook_method
|
|
return null
|
|
|
|
|
|
func _set(property: StringName, value: Variant) -> bool:
|
|
if property == "hook_dialogue_title":
|
|
hook_dialogue_title = value
|
|
return true
|
|
elif property == "hook_animation":
|
|
hook_animation = value
|
|
return true
|
|
elif property == "hook_method":
|
|
hook_method = value
|
|
return true
|
|
return false
|
|
|
|
|
|
func _get_property_list() -> Array[Dictionary]:
|
|
var hint_methods = ""
|
|
var hint_animation = ""
|
|
if Engine.is_editor_hint():
|
|
var animation_player = _get_animation_player()
|
|
if animation_player:
|
|
# 更新 hook_animation 的可选项
|
|
var animation_list = animation_player.get_animation_list()
|
|
hint_animation = ",".join(animation_list)
|
|
# 更新 hook_method 的可选项
|
|
var method_list = animation_player.get_method_list()
|
|
var methods = []
|
|
for method in method_list:
|
|
if (
|
|
# bit operation
|
|
(method.flags == METHOD_FLAG_NORMAL)
|
|
# and method.args.size() == method.default_args.size()
|
|
and method.args.is_empty()
|
|
and not method.name.begins_with("_")
|
|
):
|
|
methods.append(method.name)
|
|
hint_methods = ",".join(methods)
|
|
var hint_dialogue_title = ""
|
|
if Engine.is_editor_hint():
|
|
hint_dialogue_title = ",".join(dialogue_res.get_ordered_titles())
|
|
return [
|
|
{
|
|
"name": "hook_dialogue_title",
|
|
"type": TYPE_STRING,
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
"hint_string": hint_dialogue_title
|
|
},
|
|
{
|
|
"name": "hook_animation",
|
|
"type": TYPE_STRING,
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
"hint_string": hint_animation
|
|
},
|
|
{
|
|
"name": "hook_method",
|
|
"type": TYPE_STRING,
|
|
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
|
|
"hint_string": hint_methods
|
|
}
|
|
]
|