extends Control class_name InventoryOverlay signal close_requested signal item_confirmed(item_id: String) signal combine_requested(item_a_id: String, item_b_id: String) signal inspect_requested(item_id: String) var input_active: bool = false var _is_visible: bool = false var _selected_slot: InventorySlot = null var _drag_start_time: float = 0.0 var _is_dragging: bool = false var _dragged_item: Control = null var _hovered_slot: InventorySlot = null var _fade_tween: Tween = null const FADE_DURATION: float = 0.2 const LONG_PRESS_THRESHOLD: float = 0.5 const SLOTS_PER_ROW: int = 8 const SLOT_SIZE: Vector2i = Vector2i(64, 64) @onready var background: ColorRect = $Background @onready var panel: Control = $InventoryPanel @onready var frame: ColorRect = $InventoryPanel/Frame @onready var grid: GridContainer = $InventoryPanel/ItemGrid @onready var hover_label: Label = $InventoryPanel/HoverLabel func _ready() -> void: hide() background.mouse_filter = Control.MOUSE_FILTER_STOP InventoryManager.inventory_changed.connect(_refresh_grid) InventoryManager.combination_attempted.connect(_on_combination_attempted) func show_overlay() -> void: print("[OVERLAY] show_overlay called, inventory has %d items" % InventoryManager.inventory.size()) _refresh_grid() if _fade_tween: _fade_tween.kill() _fade_tween = null _is_visible = true show() modulate = Color(1, 1, 1, 0) input_active = false var tween = create_tween().bind_node(self) _fade_tween = tween tween.tween_property(self, "modulate", Color(1, 1, 1, 1), FADE_DURATION) tween.tween_callback(_on_fade_in_complete) func hide_overlay() -> void: print("[OVERLAY] hide_overlay called") if _fade_tween: _fade_tween.kill() _fade_tween = null input_active = false _clear_selection() var tween = create_tween().bind_node(self) _fade_tween = tween tween.tween_property(self, "modulate", Color(1, 1, 1, 0), FADE_DURATION) tween.tween_callback(_on_fade_out_complete) func _on_fade_in_complete() -> void: input_active = true func _on_fade_out_complete() -> void: _is_visible = false hide() input_active = false func is_active() -> bool: return _is_visible and input_active func _refresh_grid() -> void: for child in grid.get_children(): child.queue_free() for item_id in InventoryManager.inventory: var def = InventoryManager.get_item_definition(item_id) if not def: def = ItemDefinition.new() def.id = item_id def.name = item_id var slot_scene = load("res://inventory/inventory_overlay/InventorySlot.tscn") var slot: InventorySlot = slot_scene.instantiate() grid.add_child(slot) slot.set_item(def) slot.clicked.connect(_on_slot_clicked) slot.right_clicked.connect(_on_slot_right_clicked) slot.hovered.connect(_on_slot_hovered) slot.unhovered.connect(_on_slot_unhovered) grid.columns = SLOTS_PER_ROW func _on_slot_clicked(item_id: String) -> void: print("[OVERLAY] _on_slot_clicked: '%s', input_active=%s" % [item_id, input_active]) if not input_active: return for child in grid.get_children(): if child is InventorySlot and child.item_id == item_id: if _selected_slot == null: InventoryManager.select_item(item_id) item_confirmed.emit(item_id) hide_overlay() elif _selected_slot.item_id == item_id: InventoryManager.select_item(item_id) item_confirmed.emit(item_id) hide_overlay() else: combine_requested.emit(_selected_slot.item_id, item_id) _clear_selection() break func _on_slot_right_clicked(item_id: String) -> void: if not input_active: return inspect_requested.emit(item_id) hide_overlay() func _on_slot_hovered(item_id: String) -> void: if not input_active: return _hovered_slot = null for child in grid.get_children(): if child is InventorySlot and child.item_id == item_id: _hovered_slot = child break _update_hover_label() func _on_slot_unhovered(item_id: String) -> void: if _hovered_slot and _hovered_slot.item_id == item_id: _hovered_slot = null _update_hover_label() func _create_drag_preview(slot: InventorySlot) -> void: var preview = ColorRect.new() preview.color = Color(1, 0.6, 0.2, 0.7) preview.size = Vector2(48, 48) preview.position = slot.global_position - global_position panel.add_child(preview) _dragged_item = preview func _handle_release_same_item() -> void: var elapsed = Time.get_ticks_msec() / 1000.0 - _drag_start_time if elapsed <= LONG_PRESS_THRESHOLD: item_confirmed.emit(_selected_slot.item_id) hide_overlay() else: _clear_selection() func _clear_selection() -> void: _selected_slot = null _is_dragging = false if _dragged_item: _dragged_item.queue_free() _dragged_item = null _update_hover_label() func _update_hover_label() -> void: if _selected_slot and _hovered_slot and _selected_slot.item_id != _hovered_slot.item_id: var def_a = InventoryManager.get_item_definition(_selected_slot.item_id) var def_b = InventoryManager.get_item_definition(_hovered_slot.item_id) var name_a = _selected_slot.item_id var name_b = _hovered_slot.item_id if def_a: name_a = def_a.name if def_b: name_b = def_b.name hover_label.text = "Use %s with %s" % [name_a, name_b] elif _hovered_slot: var def = InventoryManager.get_item_definition(_hovered_slot.item_id) if def: hover_label.text = def.name else: hover_label.text = _hovered_slot.item_id else: hover_label.text = "" func _on_background_gui_input(event: InputEvent) -> void: if event is InputEventMouseButton and event.button_index == 1 and event.pressed: hide_overlay() close_requested.emit() func _gui_input(event: InputEvent) -> void: if event is InputEventMouseButton and event.button_index == 1 and event.pressed: var panel_rect = panel.get_global_rect() if not panel_rect.has_point(get_global_mouse_position()): hide_overlay() close_requested.emit() func _on_combination_attempted(item_a_id: String, item_b_id: String) -> void: pass func _notification(what: int) -> void: if what == NOTIFICATION_PREDELETE: if _fade_tween: _fade_tween.kill() _fade_tween = null