extends Control # PowerUpInventoryUI - Displays stored powerups and handles selection # UI References var icon_containers: Dictionary = {} # { EffectEnum: Button } # Local State var selected_effect: int = -1 var special_manager_ref: Node = null # Reference to SpecialTilesManager @onready var SettingsManager = get_node_or_null("/root/SettingsManager") signal effect_selected(effect: int) func _ready(): print("[PowerUpUI] _ready called") # Connect to SettingsManager to update labels if SettingsManager and not SettingsManager.control_remapped.is_connected(_on_control_remapped): SettingsManager.control_remapped.connect(_on_control_remapped) # Map Effect Enum to UI Nodes (Assumes specific names in main.tscn) # We try to get them immediately. If they are children, they should be accessible. var container = get_node_or_null("Container") if not container: print("[PowerUpUI] ERROR: Container not found in ", get_path()) return # Center buttons in the container instead of spreading them out container.alignment = BoxContainer.ALIGNMENT_CENTER # Mapping based on User Request # 11: FASTER_SPEED (0) -> SpeedBtn # 12: AREA_FREEZE (1) -> FreezeAreaBtn # 13: BLOCK_FLOOR (2) -> WallBtn # 14: INVISIBLE_MODE (3) -> GhostBtn # We use 0, 1, 2, 3 to match SpecialTilesManager.SpecialEffect enum var speed_btn = container.get_node_or_null("SpeedBtn") var freeze_btn = container.get_node_or_null("FreezeAreaBtn") var wall_btn = container.get_node_or_null("WallBtn") var ghost_btn = container.get_node_or_null("GhostBtn") var mode = LobbyManager.get_game_mode() var is_restricted = GameMode.is_restricted(mode) _setup_btn(0, speed_btn) _setup_btn(1, freeze_btn) _setup_btn(2, wall_btn) _setup_btn(3, ghost_btn) # Remove mode-based restrictions on visibility for now, as ownership controls visibility if wall_btn: wall_btn.visible = false if freeze_btn: freeze_btn.visible = false if speed_btn: speed_btn.visible = false if ghost_btn: ghost_btn.visible = false _update_shortcuts_for_mode(is_restricted) print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size()) func _on_control_remapped(_action: String, _key: int): # Refresh all labels _update_shortcuts_for_mode(LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO)) func _setup_btn(effect_id: int, btn: Button): if not btn: print("[PowerUpUI] Warning: Button for effect %d is null" % effect_id) return icon_containers[effect_id] = btn # Start DISABLED btn.disabled = true btn.modulate = Color(0.5, 0.5, 0.5, 0.5) # Grayed out btn.focus_mode = Control.FOCUS_NONE # Fix "Floating" issue: don't expand button to fill whole container height # This keeps the buttons grouped tightly together btn.size_flags_vertical = Control.SIZE_SHRINK_CENTER btn.custom_minimum_size.y = 80 # Consistent height # Add Level Label if not btn.has_node("LevelLabel"): var lvl_lbl = Label.new() lvl_lbl.name = "LevelLabel" lvl_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE lvl_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT lvl_lbl.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM lvl_lbl.set_anchors_preset(Control.PRESET_FULL_RECT) lvl_lbl.add_theme_font_size_override("font_size", 16) lvl_lbl.add_theme_color_override("font_outline_color", Color.BLACK) lvl_lbl.add_theme_constant_override("outline_size", 4) lvl_lbl.text = "" # Hidden initially btn.add_child(lvl_lbl) # Add Cooldown Label if not btn.has_node("CooldownLabel"): var cd_lbl = Label.new() cd_lbl.name = "CooldownLabel" cd_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE cd_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER cd_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER cd_lbl.set_anchors_preset(Control.PRESET_FULL_RECT) cd_lbl.add_theme_font_size_override("font_size", 20) cd_lbl.add_theme_color_override("font_outline_color", Color.BLACK) cd_lbl.add_theme_constant_override("outline_size", 4) cd_lbl.text = "" btn.add_child(cd_lbl) # Add Keyboard Shortcut Label if not btn.has_node("ShortcutLabel"): var sc_lbl = Label.new() sc_lbl.name = "ShortcutLabel" sc_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE sc_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT sc_lbl.vertical_alignment = VERTICAL_ALIGNMENT_TOP sc_lbl.set_anchors_preset(Control.PRESET_FULL_RECT) sc_lbl.offset_left = 5 sc_lbl.offset_top = -4 # Lowered slightly from -12 sc_lbl.add_theme_font_size_override("font_size", 16) sc_lbl.add_theme_color_override("font_outline_color", Color.BLACK) sc_lbl.add_theme_constant_override("outline_size", 4) sc_lbl.add_theme_color_override("font_color", Color(0.9, 0.9, 0.9)) btn.add_child(sc_lbl) _update_btn_shortcut(effect_id, btn) # Connect click if not btn.pressed.is_connected(_on_btn_pressed): btn.pressed.connect(_on_btn_pressed.bind(effect_id)) func _update_shortcuts_for_mode(is_restricted: bool): for effect_id in icon_containers: var btn = icon_containers[effect_id] _update_btn_shortcut(effect_id, btn) func _update_btn_shortcut(_effect_id: int, btn: Button): var sc_lbl = btn.get_node_or_null("ShortcutLabel") if not sc_lbl: return if not SettingsManager: sc_lbl.text = "" return # Show only Shortcut 1 as per single-slot request sc_lbl.text = "[%s]" % SettingsManager.get_control_text("powerup_1") func _on_btn_pressed(effect_id: int): print("[PowerUpUI] Clicked Button %d" % effect_id) if special_manager_ref: special_manager_ref.activate_effect(effect_id) else: print("[PowerUpUI] ERROR: special_manager_ref is null during click") func setup(player_node): print("[PowerUpUI] Setup called for player: ", player_node.name) var special_manager = player_node.get_node_or_null("SpecialTilesManager") if special_manager: _connect_special_manager(special_manager) else: print("[PowerUpUI] SpecialTilesManager not found on %s. Waiting for it..." % player_node.name) if not player_node.child_entered_tree.is_connected(_on_player_child_entered): player_node.child_entered_tree.connect(_on_player_child_entered.bind(player_node)) func _on_player_child_entered(node: Node, player_node: Node): if node.name == "SpecialTilesManager": print("[PowerUpUI] SpecialTilesManager appeared on %s!" % player_node.name) _connect_special_manager(node) # Disconnect to avoid checking every child forever if player_node.child_entered_tree.is_connected(_on_player_child_entered): player_node.child_entered_tree.disconnect(_on_player_child_entered) func _connect_special_manager(special_manager): special_manager_ref = special_manager print("[PowerUpUI] Connected to SpecialTilesManager") # Connect signals if not already connected if not special_manager.is_connected("powerup_unlocked", _on_powerup_unlocked): special_manager.connect("powerup_unlocked", _on_powerup_unlocked) if not special_manager.is_connected("cooldown_updated", _on_cooldown_updated): special_manager.connect("cooldown_updated", _on_cooldown_updated) if not special_manager.is_connected("inventory_updated", _on_inventory_updated): special_manager.connect("inventory_updated", _on_inventory_updated) # Initial State Sync if icon_containers.is_empty(): print("[PowerUpUI] Warning: Icon containers empty during setup. Attempting _ready logic now...") var container = get_node_or_null("Container") if container: # Fix: Use correct IDs 0-3 here too _setup_btn(0, container.get_node_or_null("SpeedBtn")) _setup_btn(1, container.get_node_or_null("FreezeAreaBtn")) _setup_btn(2, container.get_node_or_null("WallBtn")) _setup_btn(3, container.get_node_or_null("GhostBtn")) # Sync Inventory _on_inventory_updated(special_manager.inventory) # Sync Levels for owned items for effect in special_manager.inventory: if special_manager.inventory[effect]: var lvl = special_manager.powerup_levels.get(effect, 1) _on_powerup_unlocked(effect, lvl) func _on_powerup_unlocked(effect: int, level: int): # Enable button and set level if icon_containers.has(effect): var btn = icon_containers[effect] print("[PowerUpUI] Enabling button for Effect %d (Node: %s)" % [effect, btn.name]) btn.disabled = false btn.modulate = Color.WHITE # Restore color btn.visible = true # Ensure visible # Update Level var lvl_lbl = btn.get_node_or_null("LevelLabel") if lvl_lbl: lvl_lbl.text = "Lvl %d" % level # Enforce 1-slot rule by hiding others if special_manager_ref: _on_inventory_updated(special_manager_ref.inventory) else: print("[PowerUpUI] ERROR: Unlocked Effect %d but no UI button found! Keys: %s" % [effect, icon_containers.keys()]) func _on_cooldown_updated(effect: int, time_left: float, max_time: float): if icon_containers.has(effect): var btn = icon_containers[effect] var cd_lbl = btn.get_node_or_null("CooldownLabel") if cd_lbl: if time_left > 0: cd_lbl.text = "%d" % int(time_left) btn.disabled = true btn.modulate = Color(0.7, 0.7, 0.7, 0.8) else: cd_lbl.text = "" # Re-enable if we own it if special_manager_ref and special_manager_ref.inventory.get(effect, false): btn.disabled = false btn.modulate = Color.WHITE func _on_inventory_updated(inventory: Dictionary): # Update UI icons (Only show ONE active slot as per user request) print("[PowerUpUI] Inventory Updated Signal Received! Data: ", inventory) for effect in icon_containers: var has_item = inventory.get(effect, false) var btn = icon_containers[effect] # Single slot logic: Only the owned one is visible btn.visible = has_item if has_item: btn.disabled = false # Individual cooldown logic might disable it later btn.modulate = Color.WHITE _update_btn_shortcut(effect, btn)