import uuid from typing import Optional, List, Dict, Callable from base import Transform2D, Vector2D from pygame import Surface class Node: def __init__(self, name: str = ""): self._id = str(uuid.uuid4()) self.name = name or f"Node_{self._id[:8]}" self.parent: Optional[Node] = None self.children: List[Node] = [] self._is_in_tree = False self._ready_called = False self._process_enabled = False self._physics_process_enabled = False self.signals: Dict[str, List[Callable]] = {} def _enter_tree(self): self.enter_tree() self._is_in_tree = True for child in self.children: child._enter_tree() def _exit_tree(self): self.exit_tree() self._is_in_tree = False for child in self.children: child._exit_tree() def _ready(self): if not self._ready_called: self._ready_called = True self.ready() for child in self.children: child._ready() def _process(self, delta: float): if not self._process_enabled: return self.process(delta) for child in self.children: child._process(delta) def _physics_process(self, delta: float): if not self._process_enabled: return self.physics_process(delta) for child in self.children: child._physics_process(delta) def _unhandled_input(self, event):pass def _unhandled_key_input(self, event):pass """ def _draw(self, surface: Surface): if self._visible: self.draw(surface) for child in self.children: child._draw(surface) """ def enter_tree(self): pass def exit_tree(self): pass def ready(self): pass def process(self, delta: float): pass def physics_process(self, delta: float): pass # def draw(self, surface: Surface): pass def add_child(self, node: 'Node'): if node.parent: node.parent.remove_child(node) self.children.append(node) node.parent = self def remove_child(self, node: 'Node'): if node in self.children: self.children.remove(node) node.parent = None def get_node(self, path: str) -> Optional['Node']: if path == ".": return self parts = path.split("/") current = self for part in parts: if part == "..": current = current.parent if not current: return None else: found = False for child in current.children: if child.name == part: current = child found = True break if not found: return None return current return current def connect(self, signal: str, callback: Callable): if signal not in self.signals: self.signals[signal] = [] self.signals[signal].append(callback) def emit(self, signal: str, *args): if signal in self.signals: for callback in self.signals[signal]: callback(*args) def to_dict(self) -> dict: return { "type": self.__class__.__name__, "name": self.name, "children": [child.to_dict() for child in self.children], } @classmethod def from_dict(cls, data: dict) -> 'Node': node = cls(data["name"]) for child_data in data["children"]: child = Node.from_dict(child_data) node.add_child(child) return node