320 lines
11 KiB
GDScript
320 lines
11 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_银元"] as PackedStringArray,
|
|
"player_x": 30.0,
|
|
}
|
|
@export var debug_ground_data: Dictionary[String, Variant] = {}
|
|
|
|
@export var auto_reset_on_debug_restarting := false
|
|
@export_tool_button("auto reference") var auto_ref = _auto_setup_node_reference
|
|
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(_setup_node_reference)
|
|
ground.ready.connect(_on_ground_ready)
|
|
else:
|
|
_setup_node_reference()
|
|
_on_ground_ready()
|
|
ready.connect(_on_ready)
|
|
|
|
|
|
func _setup_node_reference() -> void:
|
|
pass
|
|
|
|
|
|
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.get_string(1)] = true
|
|
for getter_match in getter_regx.search_all(code):
|
|
properties[getter_match.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(&"xxx", 2)
|
|
# EventManager.set_stage_if_greater(&"xxx", 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.get_string(1)] = true
|
|
for event_match in event_set_greater_regx.search_all(code):
|
|
events[event_match.get_string(1)] = true
|
|
for event_match in event_getter_regx.search_all(code):
|
|
events[event_match.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, 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)
|
|
|
|
|
|
var func_line_id := -1
|
|
var region_start_id := -1
|
|
var region_end_id := -1
|
|
# #region node_reference
|
|
# #endregion
|
|
# # 读取设置变量名
|
|
# func _setup_node_reference() -> void:
|
|
# pass
|
|
func _auto_setup_node_reference():
|
|
var script = get_script() as GDScript
|
|
if not script:
|
|
printerr("script not found")
|
|
return
|
|
var code_lines := script.source_code.split("\n") as PackedStringArray
|
|
func_line_id = -1
|
|
region_start_id = -1
|
|
region_end_id = -1
|
|
for i in len(code_lines):
|
|
var line = code_lines[i]
|
|
if line.begins_with("func _setup_node_reference()"):
|
|
func_line_id = i
|
|
elif line.begins_with("#region node_reference"):
|
|
region_start_id = i
|
|
elif region_start_id != -1 and region_end_id == -1 and line.begins_with("#endregion"):
|
|
region_end_id = i
|
|
if func_line_id == -1:
|
|
printerr("func _setup_node_reference() not found")
|
|
return
|
|
if region_start_id == -1:
|
|
print("#region node_reference not found, creat region upon func_line..")
|
|
region_start_id = func_line_id - 2
|
|
func_line_id += 1
|
|
code_lines.insert(region_start_id, "#region node_reference")
|
|
if region_end_id == -1:
|
|
region_end_id = region_start_id + 1
|
|
code_lines.insert(region_end_id, "#endregion")
|
|
func_line_id += 1
|
|
print("auto reference start, region_start_id=", region_start_id, " region_end_id=", region_end_id)
|
|
var existing_vars = _read_existing_vars(code_lines)
|
|
var created_vars = []
|
|
_traverse_nodes(get_node(".."), get_node(".."), existing_vars, code_lines, created_vars)
|
|
script.source_code = "\n".join(code_lines)
|
|
ResourceSaver.save(script)
|
|
print("auto reference done.")
|
|
print_rich("skipped existing_vars:[color=cyan]", existing_vars.size(), existing_vars)
|
|
print_rich("created_vars:[color=green]", created_vars.size(), created_vars)
|
|
|
|
|
|
func _read_existing_vars(code_lines: PackedStringArray) -> Dictionary:
|
|
var existing_vars = {}
|
|
for i in range(region_start_id + 1, region_end_id):
|
|
var line = code_lines[i]
|
|
if line.begins_with("var "):
|
|
var var_name = line.split(" ")[1].split(":")[0].strip_escapes()
|
|
if var_name:
|
|
existing_vars[var_name] = true
|
|
return existing_vars
|
|
|
|
|
|
func _traverse_nodes(ground_node, node: Node, existing_vars: Dictionary, code_lines: PackedStringArray, created_vars):
|
|
if node:
|
|
_parse_node(ground_node, node, existing_vars, code_lines, created_vars)
|
|
for child in node.get_children():
|
|
_traverse_nodes(ground_node, child, existing_vars, code_lines, created_vars)
|
|
|
|
|
|
func _parse_node(ground_node, node:Node, existing_vars:Dictionary, code_lines:PackedStringArray, created_vars):
|
|
# 0. filter unique mark
|
|
# 1. create `var snake_case_variable_name = $"../xx/yy/NodeName"`
|
|
# 2. create `var snake_case_variable_name = $"../xx/yy/NodeName"`
|
|
if not node.unique_name_in_owner or ground_node.get_node_or_null("%"+node.name) == null:
|
|
# 只读取 unique 标记过的节点; node.unique_name_in_owner 参数不可靠,需要实际检查
|
|
return
|
|
var var_name = node.name.to_snake_case()
|
|
if existing_vars.has(var_name):
|
|
return
|
|
created_vars.append(var_name)
|
|
code_lines.insert(region_start_id + 1, "var " + var_name + ": "+ str(node.get_class()))
|
|
func_line_id += 1
|
|
var path = self.get_path_to(node).get_concatenated_names()
|
|
# closeup花名册 = $"../DeployLayer/Closeup花名册"
|
|
code_lines.insert(func_line_id + 1, "\t" + var_name +" = $\"" + path +"\"")
|
|
|
|
|
|
|