godot-plateformer/addons/reedscene/act/ActManager.gd

270 lines
6.9 KiB
GDScript
Raw Permalink 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.

#@icon("")
@tool
class_name ActManager extends Node
##ActManager里记录了一系列的ActID和对应的Act主要用来同步场景的Act比如我们现在使用ActID = 0的Act那么就会让所有的Act的状态重置回到ActID = 0的Act的状态
@export var prop_state_map: Dictionary[int, Act] = {}
##用于生成一个基础的Act的工具按钮
@export_tool_button("Generate Default Act")
var _gen_default_act: Callable = Callable(self, "_editor_generate_default_act")
##用于生成一个Empty的工具按钮
@export_tool_button("Generate Empty Act")
var _gen_empty_act: Callable = Callable(self, "_editor_generate_empty_act")
##是否要输出报错信息
@export var debug_log: bool = false
##关卡的初始Act默认Clamp到Act的数量防止出现错误此处的-1是用来占位的如果是 -1意味着不修改使用const的Id即0
@export var init_act_id: int = -1:
set(value):
var max_index := max(prop_state_map.size() - 1, 0)
init_act_id = clamp(value, 0, max_index)
## ====================
## Private Field
## ====================
var _current_act: Act = null
var _current_act_id: int = -1
## 我们认为act无论在一帧里做多少次切换实际可以被应用的只有最后一次。
var _pending_act_id: int = -1
var _pending_trans_overwrite: int = 0
var _commit_scheduled: bool = false
const DEFAULT_ACT_ID:int = 0
signal act_changed(from_act_id: int, to_act_id: int)
func _ready() -> void:
pass
## 缓存act的change
func _commit_pending_act() -> void:
_commit_scheduled = false
if _pending_act_id == -1:
return
if _pending_act_id == _current_act_id:
return
var act := prop_state_map.get(_pending_act_id)
if act == null:
return
_switch_act_internal(act, _pending_act_id, _pending_trans_overwrite)
##通过ID切换act
## 如果trans overwrite = 0则使用act上自己配置的use trans
## 如果trans overwrite = 1则全部禁用use trans
## 如果trans overwrite = 2则全部开启use trans注意如果没有trans则还是正常的切换
func switch_act_with_id(act_id: int,trans_overwrite: int = 0) -> void:
if not prop_state_map.has(act_id):
push_warning("[ActManager] Act id not found: %d" % act_id)
return
# 只记录请求,不立刻执行
_pending_act_id = act_id
_pending_trans_overwrite = trans_overwrite
if not _commit_scheduled:
_commit_scheduled = true
call_deferred("_commit_pending_act")
#if not prop_state_map.has(act_id):
#push_warning("[ActManager] Act id not found: %d" % act_id)
#return
#
#var act := prop_state_map[act_id]
#_switch_act_internal(act, act_id,trans_overwrite)
##内部通过ID和Act来切换状态
func _switch_act_internal(act: Act, act_id: int, trans_overwrite: int = 0) -> void:
if act == null:
return
var from_id := _current_act_id
_current_act = act
_current_act_id = act_id
for pid in act.prop_state_map.keys():
var single : SingleAct = act.prop_state_map[pid]
if single == null:
continue
var prop := _find_prop_by_id(pid)
if prop == null:
continue
var use_trans: bool = false
match trans_overwrite:
0: use_trans = single.use_trans
1: use_trans = false
2: use_trans = true
prop.change_state(single.state_id, use_trans)
emit_signal("act_changed", from_id, act_id)
##用来扫描prop
func _scan_scene_for_props(scene_root: Node) -> Dictionary:
var result := {}
if scene_root == null:
return result
_scan_node_recursive(scene_root, result)
return result
##递归扫描
func _scan_node_recursive(node: Node, out: Dictionary) -> void:
if node is PropComponent:
var prop := node as PropComponent
if out.has(prop.prop_id):
push_warning(
"[ActManagerInspector] Duplicate prop_id: %d (%s)"
% [prop.prop_id, node.get_path()]
)
else:
out[prop.prop_id] = {
"node": prop,
"path": node.get_path()
}
for child in node.get_children():
_scan_node_recursive(child, out)
##生成一个默认的Act
func _editor_generate_default_act() -> void:
if not Engine.is_editor_hint():
return
var scene := _get_owner_scene()
if scene == null:
_editor_popup(
"ActManager must be a child of ReedScene.",
"Generate Default Act - Failed"
)
return
var props_root := scene.get_props_root()
if props_root == null:
_editor_popup(
"Props root not found:\n%s" % scene.props_root_path,
"Generate Default Act - Failed"
)
return
var act := Act.new()
act.prop_state_map.clear()
var count := 0
for prop_node in props_root.get_children():
if not prop_node is Node:
continue
var prop_comp := _find_prop_component_top(prop_node)
if prop_comp == null:
continue
var pid := prop_comp.prop_id
var init_state := prop_comp.initial_state_id
var single := SingleAct.new()
single.state_id = init_state
act.prop_state_map[pid] = single
count += 1
prop_state_map[0] = act
notify_property_list_changed()
_editor_popup(
"Default Act generated successfully.\n\nProp count: %d\nAct ID: 0" % count,
"Generate Default Act - Success"
)
if debug_log:
print("[ActManager][Editor] Generated Default Act with", count, "props.")
##生成一个空的Act
func _editor_generate_empty_act() -> void:
if not Engine.is_editor_hint():
return
var act := Act.new()
act.prop_state_map.clear() # 明确强调“空 Act”
var act_id := _find_next_free_act_id()
prop_state_map[act_id] = act
notify_property_list_changed()
_editor_popup(
"Empty Act generated successfully.\n\nAct ID: %d\nProp count: 0" % act_id,
"Generate Empty Act - Success"
)
if debug_log:
print("[ActManager][Editor] Generated EMPTY Act with id =", act_id)
## ==============================
## 工具函数
## ==============================
func _editor_popup(message: String, title: String = "ActManager") -> void:
if not Engine.is_editor_hint():
return
var dialog := AcceptDialog.new()
dialog.title = title
dialog.dialog_text = message
get_tree().root.add_child(dialog)
dialog.popup_centered()
##找到场景scene节点
func _get_owner_scene() -> ReedScene:
var p := get_parent()
if p is ReedScene:
return p as ReedScene
return null
##找到PropComp
func _find_prop_component_top(prop: Node) -> PropComponent:
for child in prop.get_children():
if child is PropComponent:
return child as PropComponent
return null
##找到最近的空位ActID
func _find_next_free_act_id() -> int:
var id := 0
while prop_state_map.has(id):
id += 1
return id
##通过ID寻找Prop
func _find_prop_by_id(prop_id: int) -> PropComponent:
var scene := _get_owner_scene()
if scene == null:
return null
var props_root := scene.get_props_root()
if props_root == null:
return null
return _find_prop_recursive(props_root, prop_id)
##递归的寻找Prop
func _find_prop_recursive(node: Node, prop_id: int) -> PropComponent:
if node is PropComponent and node.prop_id == prop_id:
return node as PropComponent
for child in node.get_children():
var r := _find_prop_recursive(child, prop_id)
if r != null:
return r
return null