xiandie/scene/ground/scene/animation_root.gd
2025-07-02 15:32:37 +08:00

228 lines
7.6 KiB
GDScript

@tool
class_name AnimationRoot extends AnimationPlayer
# 在继承 AnimationRoot 的各场景内的脚本中,可以直接调用 DialogueResource
var dialogue_c01 := preload("res://asset/dialogue/c01.dialogue") as DialogueResource
var dialogue_c02 := preload("res://asset/dialogue/c02.dialogue") as DialogueResource
var dialogue_c03 := preload("res://asset/dialogue/c03.dialogue") as DialogueResource
var dialogue_c04 := preload("res://asset/dialogue/c04.dialogue") as DialogueResource
var dialogue_c05 := preload("res://asset/dialogue/c05.dialogue") as DialogueResource
var dialogue_c06 := preload("res://asset/dialogue/c06.dialogue") as DialogueResource
@export var data = {
# 首次进入场景时触发
"oneshot_animation_played": false
}
@export_tool_button("reset 存档") var reset_archive = _reset_archive
# event 也混合其中
@export var debug_global_data: Dictionary[String, Variant] = {
"enabled_items": ["prop_火柴", "prop_院长的信", "prop_银元"],
"player_x": 30.0,
}
@export var debug_ground_data: Dictionary[String, Variant] = {}
@export var auto_reset_on_debug_restarting := false
var oneshot_animation := ""
var ground_archive: GroundArchive
var ground: Ground2D
# 继承覆盖该方法
func _default_data() -> Dictionary:
print("read default data from root")
return {}
func _ready() -> void:
ground = get_node("..")
if not ground:
printerr("ground not found")
return
if ground.restarting:
print("auto reset archive=", auto_reset_on_debug_restarting)
if auto_reset_on_debug_restarting:
_reset_archive()
print("restarting: skip animation root _ready()")
return
data.merge(_default_data(), true)
if Engine.is_editor_hint():
# notify_property_list_changed()
# 更新 oneshot_animation 的可选项
animation_libraries_updated.connect(notify_property_list_changed)
return
ground_archive = ArchiveManager.archive.ground_archive() as GroundArchive
var archive_data = ground_archive.get_data(name)
# merge data
for key in archive_data.keys():
if data.has(key):
data[key] = archive_data[key]
# 等待 DeployLayer 先加载完成
if not ground.is_node_ready():
ground.ready.connect(_on_ground_ready)
else:
_on_ground_ready()
ready.connect(_on_ready)
func _on_ground_ready() -> void:
pass
func _on_ready() -> void:
if Engine.is_editor_hint():
return
# 仅在首次进入场景时触发
if oneshot_animation:
if not data["oneshot_animation_played"]:
play(oneshot_animation)
animation_finished.connect(_oneshot_animation_finished, CONNECT_ONE_SHOT)
else:
if GlobalConfig.DEBUG:
print("oneshot_animation_played:", oneshot_animation)
func _oneshot_animation_finished(animation_name) -> void:
if GlobalConfig.DEBUG:
print("oneshot_animation_finished:", animation_name)
set_data("oneshot_animation_played", true)
func set_data(property: StringName, value: Variant) -> bool:
if data.has(property):
ground_archive.set_pair(name, property, value)
data[property] = value
return true
return false
func set_global_entry(property: StringName, value: Variant) -> void:
ArchiveManager.set_global_entry(property, value)
func get_global_value(property: StringName, default_value = null) -> Variant:
var val = ArchiveManager.get_global_value(property)
if val == null:
return default_value
return val
func _get(property: StringName) -> Variant:
if property == "oneshot_animation":
return oneshot_animation
return null
func _set(property: StringName, value: Variant) -> bool:
if property == "oneshot_animation":
oneshot_animation = value
return true
return false
func _get_property_list() -> Array[Dictionary]:
return [
{
"name": "oneshot_animation",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
"hint_string": ",".join(get_animation_list()),
}
]
###### TOOL BUTTON
func _reset_archive() -> void:
var archive = (
ResourceLoader.load("user://data/archives/save000.tres", "AssembledArchive")
as AssembledArchive
)
archive.player_global_position_x = debug_global_data.get_or_add("player_x", 30.0)
# 重置全局变量
var prop_arr = archive.prop_inventory.default_enabled_items
if get_node("../MainPlayer").character.begins_with("吕萍"):
prop_arr = archive.prop_inventory.xdie_enabled_items
elif get_node("../MainPlayer").character.begins_with("小小蝶"):
prop_arr = archive.prop_inventory.xxdie_enabled_items
elif get_node("../MainPlayer").character.begins_with("小小小蝶"):
prop_arr = archive.prop_inventory.xxxdie_enabled_items
for prop in debug_global_data["enabled_items"]:
if not prop_arr.has(prop):
prop_arr.append(prop)
# 从 code 中找到 set_global_entry/get_global_value 方法中第一个 property
var code = get_script().source_code
# set_global_entry(property: StringName, value)
# get_global_value(property: StringName, default = null)
var setter_regx = RegEx.create_from_string(r'set_global_entry\(.?"(.+)"') as RegEx
var getter_regx = RegEx.create_from_string(r'get_global_value\(.?"(.+)"') as RegEx
var properties = {}
for setter_match in setter_regx.search_all(code):
properties[(setter_match as RegExMatch).get_string(1)] = true
for getter_match in getter_regx.search_all(code):
properties[(getter_match as RegExMatch).get_string(1)] = true
for p in properties.keys():
if debug_global_data.get(p) == null:
debug_global_data[p] = false
# archive.set_global_entry(p, new_data[p])
archive.global_data_dict[p] = debug_global_data[p]
# 重置 ground_archive
if not archive.ground_archives.has(ground.scene_name):
archive.ground_archives[ground.scene_name] = GroundArchive.new()
archive.ground_archives[ground.scene_name].scene_name = ground.scene_name
# setup
_setup_ground_data(debug_ground_data, ground)
archive.ground_archives[ground.scene_name].data = debug_ground_data
# 重置 event 状态
# EventManager.set_stage(&"c02_burning_end_stage", 2)
# EventManager.set_stage_if_greater("handnote_stage", 5)
var event_setter_regx = RegEx.create_from_string(r'EventManager.set_stage\(.?"(.+)"') as RegEx
var event_getter_regx = RegEx.create_from_string(r'EventManager.get_stage\(.?"(.+)"') as RegEx
var event_set_greater_regx = (
RegEx.create_from_string(r'set_stage_if_greater\(.?"(.+)"') as RegEx
)
var events = {}
for event_match in event_setter_regx.search_all(code):
events[(event_match as RegExMatch).get_string(1)] = true
for event_match in event_set_greater_regx.search_all(code):
events[(event_match as RegExMatch).get_string(1)] = true
for event_match in event_getter_regx.search_all(code):
events[(event_match as RegExMatch).get_string(1)] = true
# 遍历 ".." 下所有节点,找到属于 Event2D 的节点
_find_event2d(events, ground)
for e in events.keys():
if debug_global_data.get(e) == null:
debug_global_data[e] = 0
archive.event_stage[e] = debug_global_data[e]
print("reset archive data success")
ResourceSaver.save(archive)
notify_property_list_changed()
func _setup_ground_data(g_data: Dictionary[String, Variant], node: Node):
if not node:
return
for child in node.get_children():
if child is Ambush2D and not g_data.has(child.name):
g_data[child.name] = {"played": false}
elif child is Interactable2D and not g_data.has(child.name):
g_data[child.name] = {"interacted_times": 0}
elif child is Pickable2D and not g_data.has(child.name):
g_data[child.name] = {"picked": false}
elif child is AnimationRoot and not g_data.has(child.name):
g_data[child.name] = child.data
_setup_ground_data(g_data, child)
func _find_event2d(events: Dictionary, node: Node) -> void:
if not node:
return
for child in node.get_children():
if child is Event2D:
if child.event != &"":
events[child.event] = true
if child.pre_event != &"":
events[child.pre_event] = true
_find_event2d(events, child)