class_name Door extends StaticBody3D signal opened signal closed signal locked signal unlocked @export var is_open: bool = false @export var is_locked: bool = false @export var key: Item var requires_key: bool: get: return key != null class UnopenableDoorError: static var name: get: return "UnopenableDoorError" class NodeNoInventoryError: static var name: get: return "NodeNoInventoryError" class MissingKeyError: static var name: get: return "MissingKeyError" class LockedError: static var name: get: return "LockedError" func check_requirements(interactor: Node) -> Result: if not is_locked: return Result.Unit elif not requires_key: return Result.err(UnopenableDoorError) match NodeExt.find_child_variant(interactor, ItemContainer): var opt when opt.is_none(): return Result.err(NodeNoInventoryError) var opt: match opt.unwrap(): var inv when inv.has_item(key): return Result.Unit _: return Result.err(MissingKeyError) func open() -> Result: if is_locked: return Result.err(LockedError) if not is_open: is_open = true opened.emit() return Result.Unit func close() -> Result: if is_open: is_open = false closed.emit() return Result.Unit func unlock() -> Result: if is_locked: is_locked = false unlocked.emit() return Result.Unit func lock() -> Result: if not is_locked: is_locked = true locked.emit() return Result.Unit func try_unlock(node: Node) -> Result: match check_requirements(node): var result when result.is_ok(): return unlock() var err: return err func try_open(node: Node) -> Result: if is_locked: return try_unlock(node) else: return open() func try_interact(node: Node) -> Result: if is_open: return close() else: return try_open(node) func _on_interact(interactor: Interactor): match try_interact(interactor.root_node): var ok when ok.is_ok(): pass var err: push_error(err.unwrap_err().name)