616 lines
23 KiB
GDScript
616 lines
23 KiB
GDScript
extends Control
|
|
|
|
# TouchControlsManager - Manages mobile touch controls including virtual joystick and action buttons
|
|
|
|
signal grab_pressed
|
|
signal put_pressed
|
|
signal attack_mode_pressed
|
|
|
|
# Touch control nodes
|
|
var virtual_joystick: Control
|
|
var power_bar_container: Control # Renamed from actions_container
|
|
var interaction_container: Control # New container for Interaction
|
|
var grab_button: Button
|
|
var put_button: Button
|
|
var attack_mode_button: Button # Renamed from special_button
|
|
var spawn_boost_button: Button
|
|
var settings_button: Button
|
|
var tekton_grab_button: Button
|
|
|
|
@onready var SettingsManager = get_node_or_null("/root/SettingsManager")
|
|
|
|
# Settings - persisted to config file
|
|
const CONFIG_PATH = "user://touch_controls_settings.cfg"
|
|
var button_size: float = 70.0
|
|
var button_opacity: float = 0.7
|
|
var joystick_enabled: bool = true
|
|
var touch_buttons_enabled: bool = true # Master toggle for action buttons
|
|
var joystick_position: Vector2 = Vector2(120, -120) # Relative to bottom-left
|
|
var button_positions: Dictionary = {
|
|
"grab": Vector2(-200, -240), # Relative to bottom-right
|
|
"put": Vector2(-120, -160),
|
|
"attack_mode": Vector2(-200, -80), # Renamed
|
|
"spawn_boost": Vector2(-120, -80)
|
|
}
|
|
|
|
var button_scale: float = 1.0
|
|
|
|
# Reference to main scene and player
|
|
var main_scene: Node
|
|
var local_player: Node
|
|
|
|
func initialize(p_main: Node):
|
|
main_scene = p_main
|
|
_create_touch_ui()
|
|
_load_settings()
|
|
|
|
# Connect to remapping signals
|
|
if SettingsManager and not SettingsManager.control_remapped.is_connected(_on_control_remapped):
|
|
SettingsManager.control_remapped.connect(_on_control_remapped)
|
|
|
|
func _on_control_remapped(_action: String, _key: int):
|
|
print("[TouchControls] Control remapped: %s. Refreshing labels." % _action)
|
|
|
|
# Refresh primary assigned buttons
|
|
if grab_button: _ensure_shortcut_label(grab_button, "Grab")
|
|
if put_button: _ensure_shortcut_label(put_button, "Put")
|
|
if attack_mode_button: _ensure_shortcut_label(attack_mode_button, "AttackMode")
|
|
if spawn_boost_button: _ensure_shortcut_label(spawn_boost_button, "SpawnBoost")
|
|
if tekton_grab_button: _ensure_shortcut_label(tekton_grab_button, "TektonGrab")
|
|
|
|
# Also check all direct children of containers just in case
|
|
if power_bar_container:
|
|
for child in power_bar_container.get_children():
|
|
if child is Button:
|
|
var b_name = child.name.replace("Btn", "")
|
|
_ensure_shortcut_label(child, b_name)
|
|
if interaction_container:
|
|
for child in interaction_container.get_children():
|
|
if child is Button:
|
|
var b_name = child.name.replace("Btn", "")
|
|
_ensure_shortcut_label(child, b_name)
|
|
|
|
func set_player(p_player: Node):
|
|
local_player = p_player
|
|
|
|
# Connect to Tekton status updates
|
|
if not local_player.tekton_carried_changed.is_connected(_on_tekton_carried_changed):
|
|
local_player.tekton_carried_changed.connect(_on_tekton_carried_changed)
|
|
|
|
# Connect to PowerUpManager if it exists (for boost updates)
|
|
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
|
if powerup_mgr:
|
|
if not powerup_mgr.points_changed.is_connected(_on_boost_points_changed):
|
|
powerup_mgr.points_changed.connect(_on_boost_points_changed)
|
|
# Initialize state
|
|
_on_boost_points_changed(powerup_mgr.current_boost, powerup_mgr.MAX_BOOST)
|
|
else:
|
|
# Retry connection if manager appears later?
|
|
if not local_player.child_entered_tree.is_connected(_on_player_child_to_find_powerup):
|
|
local_player.child_entered_tree.connect(_on_player_child_to_find_powerup)
|
|
|
|
func _on_player_child_to_find_powerup(node):
|
|
if node.name == "PowerUpManager":
|
|
var powerup_mgr = node
|
|
if not powerup_mgr.points_changed.is_connected(_on_boost_points_changed):
|
|
powerup_mgr.points_changed.connect(_on_boost_points_changed)
|
|
_on_boost_points_changed(powerup_mgr.current_boost, powerup_mgr.MAX_BOOST)
|
|
|
|
if local_player.child_entered_tree.is_connected(_on_player_child_to_find_powerup):
|
|
local_player.child_entered_tree.disconnect(_on_player_child_to_find_powerup)
|
|
|
|
func _create_touch_ui():
|
|
print("[TouchControls] Creating/Finding touch UI...")
|
|
# Use layer 10 - above regular UI but below pause menu
|
|
|
|
# Check if container already exists (added in scene)
|
|
var container = self
|
|
|
|
# Helper to find or create control
|
|
var find_or_create_joystick = func():
|
|
var joy = container.get_node_or_null("VirtualJoystick")
|
|
if joy:
|
|
print("[TouchControls] Found existing VirtualJoystick")
|
|
return joy
|
|
|
|
var joystick_script = load("res://scripts/ui/virtual_joystick.gd")
|
|
joy = Control.new()
|
|
joy.set_script(joystick_script)
|
|
joy.name = "VirtualJoystick"
|
|
joy.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
|
|
|
# Use standard size from joystick script defaults (radius 60 -> size 160)
|
|
var joy_size = Vector2(160, 160)
|
|
joy.custom_minimum_size = joy_size
|
|
joy.size = joy_size
|
|
|
|
joy.offset_left = 120
|
|
joy.offset_top = -280
|
|
joy.offset_right = 280
|
|
joy.offset_bottom = -120
|
|
|
|
container.add_child(joy)
|
|
return joy
|
|
|
|
virtual_joystick = find_or_create_joystick.call()
|
|
if not virtual_joystick.direction_changed.is_connected(_on_joystick_direction):
|
|
virtual_joystick.direction_changed.connect(_on_joystick_direction)
|
|
|
|
# --- Actions Containers ---
|
|
power_bar_container = container.get_node_or_null("PowerBarBtn")
|
|
interaction_container = container.get_node_or_null("InteractionBtn")
|
|
|
|
# Create containers if missing (runtime dynamic creation)
|
|
#if not power_bar_container:
|
|
#power_bar_container = Control.new()
|
|
#power_bar_container.name = "PowerBarBtn"
|
|
#power_bar_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
#power_bar_container.mouse_filter = Control.MOUSE_FILTER_PASS
|
|
#container.add_child(power_bar_container)
|
|
#else:
|
|
#print("[TouchControls] Found existing PowerBarBtn container")
|
|
#
|
|
#if not interaction_container:
|
|
#interaction_container = Control.new()
|
|
#interaction_container.name = "InteractionBtn"
|
|
#interaction_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
#interaction_container.mouse_filter = Control.MOUSE_FILTER_PASS
|
|
#container.add_child(interaction_container)
|
|
#else:
|
|
#print("[TouchControls] Found existing InteractionBtn container")
|
|
|
|
# Style/Align Containers
|
|
if power_bar_container is BoxContainer:
|
|
power_bar_container.alignment = BoxContainer.ALIGNMENT_CENTER
|
|
if interaction_container is BoxContainer:
|
|
interaction_container.alignment = BoxContainer.ALIGNMENT_CENTER
|
|
|
|
# Create action buttons
|
|
attack_mode_button = _find_or_create_action_button(power_bar_container, "AttackMode", "⚡", button_positions.attack_mode)
|
|
tekton_grab_button = _find_or_create_action_button(power_bar_container, "TektonGrab", "👋", Vector2(-280, -80))
|
|
|
|
grab_button = _find_or_create_action_button(interaction_container, "Grab", "👋", button_positions.grab)
|
|
put_button = _find_or_create_action_button(interaction_container, "Put", "📦", button_positions.put)
|
|
|
|
# Order: AttackMode, SpawnBoost, Grab, TektonGrab
|
|
# Order / Icons
|
|
if attack_mode_button:
|
|
attack_mode_button.icon = load("res://assets/graphics/touch_control/attack_mode.png")
|
|
attack_mode_button.expand_icon = true
|
|
|
|
if grab_button:
|
|
grab_button.icon = load("res://assets/graphics/touch_control/take_tile.png")
|
|
grab_button.expand_icon = true
|
|
|
|
if tekton_grab_button:
|
|
tekton_grab_button.icon = load("res://assets/graphics/touch_control/grab_tekton.png")
|
|
tekton_grab_button.expand_icon = true
|
|
|
|
|
|
# Hide Put Button
|
|
if put_button:
|
|
put_button.visible = true
|
|
|
|
# SettingsBtn signal is handled by main.gd (_toggle_pause_menu).
|
|
# We only grab the reference here in case touch_controls needs it in future,
|
|
# but we do NOT connect a second handler to avoid double-toggle.
|
|
settings_button = main_scene.get_node_or_null("TopMenuUI/SettingsBtn")
|
|
if not settings_button:
|
|
settings_button = main_scene.get_node_or_null("SettingsBtn")
|
|
if not settings_button:
|
|
settings_button = container.get_node_or_null("SettingsBtn")
|
|
|
|
if settings_button:
|
|
print("[TouchControls] Found existing SettingsBtn on main scene")
|
|
settings_button.visible = true
|
|
# Note: do NOT connect pressed here — main.gd already connects _toggle_pause_menu
|
|
|
|
# Always visible now - controlled by settings toggle
|
|
# Can be hidden via settings if user doesn't want touch controls on desktop
|
|
visible = true
|
|
|
|
func _find_or_create_action_button(container: Control, button_name: String, icon: String, pos: Vector2) -> Button:
|
|
var btn = container.get_node_or_null(button_name + "Btn")
|
|
if btn:
|
|
print("[TouchControls] Found existing %s button" % button_name)
|
|
# Style it and connect
|
|
_style_button(btn, button_opacity)
|
|
# Avoid duplicate signal connections
|
|
if not btn.button_down.is_connected(_on_button_pressed): # Wait, cannot check lambda easily
|
|
# Disconnect all to be safe if previously connected
|
|
for conn in btn.button_down.get_connections():
|
|
if conn["callable"].get_object() == self:
|
|
btn.button_down.disconnect(conn["callable"])
|
|
for conn in btn.button_up.get_connections():
|
|
if conn["callable"].get_object() == self:
|
|
btn.button_up.disconnect(conn["callable"])
|
|
|
|
btn.button_down.connect(func(): _on_button_pressed(button_name))
|
|
btn.button_up.connect(func(): _on_button_released(button_name))
|
|
|
|
_ensure_shortcut_label(btn, button_name)
|
|
return btn
|
|
|
|
# Create new
|
|
var new_btn = _create_action_button(button_name, icon, pos)
|
|
container.add_child(new_btn)
|
|
return new_btn
|
|
|
|
func _create_action_button(button_name: String, icon: String, pos: Vector2) -> Button:
|
|
var btn = Button.new()
|
|
btn.name = button_name + "Btn"
|
|
btn.text = icon
|
|
btn.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
|
# Use offsets strictly for anchored positioning
|
|
# pos.x and pos.y are negative offsets from bottom-right (e.g. -200, -240)
|
|
btn.offset_left = pos.x
|
|
btn.offset_top = pos.y
|
|
btn.offset_right = pos.x + button_size
|
|
btn.offset_bottom = pos.y + button_size
|
|
|
|
btn.custom_minimum_size = Vector2(button_size, button_size)
|
|
btn.pivot_offset = Vector2(button_size / 2, button_size / 2) # Center pivot for scaling
|
|
|
|
# Connect signals
|
|
btn.button_down.connect(func(): _on_button_pressed(button_name))
|
|
btn.button_up.connect(func(): _on_button_released(button_name))
|
|
|
|
# Helper to ensure label exists
|
|
_ensure_shortcut_label(btn, button_name)
|
|
|
|
return btn
|
|
|
|
func _style_button(btn: Button, opacity: float):
|
|
var style = StyleBoxFlat.new()
|
|
style.bg_color = Color(0.2, 0.2, 0.25, opacity)
|
|
style.border_width_left = 2
|
|
style.border_width_top = 2
|
|
style.border_width_right = 2
|
|
style.border_width_bottom = 2
|
|
style.border_color = Color(0.647, 0.996, 0.224, 0.8)
|
|
style.corner_radius_top_left = 15
|
|
style.corner_radius_top_right = 15
|
|
style.corner_radius_bottom_right = 15
|
|
style.corner_radius_bottom_left = 15
|
|
btn.add_theme_stylebox_override("normal", style)
|
|
|
|
var pressed_style = style.duplicate()
|
|
pressed_style.bg_color = Color(0.4, 0.8, 0.4, opacity + 0.2)
|
|
btn.add_theme_stylebox_override("pressed", pressed_style)
|
|
|
|
var hover_style = style.duplicate()
|
|
hover_style.bg_color = Color(0.3, 0.3, 0.35, opacity)
|
|
btn.add_theme_stylebox_override("hover", hover_style)
|
|
|
|
btn.add_theme_font_size_override("font_size", 28)
|
|
|
|
# Prevent buttons from stealing focus (fixes Spacebar activation)
|
|
btn.focus_mode = Control.FOCUS_NONE
|
|
|
|
# Fix "Floating" issue: don't expand button to fill whole container height
|
|
# This keeps the button (and its labels) centered near the icon/text
|
|
btn.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
|
btn.custom_minimum_size.y = 70 # Consistent height
|
|
|
|
func _ensure_shortcut_label(btn: Button, button_name: String):
|
|
if btn.has_node("ShortcutLabel"):
|
|
var existing_lbl = btn.get_node("ShortcutLabel")
|
|
if not SettingsManager: return
|
|
|
|
match button_name:
|
|
"Grab": existing_lbl.text = SettingsManager.get_control_text("grab")
|
|
"Put": existing_lbl.text = SettingsManager.get_control_text("put")
|
|
"AttackMode": existing_lbl.text = SettingsManager.get_control_text("attack_mode")
|
|
"TektonGrab": existing_lbl.text = SettingsManager.get_control_text("tekton_grab")
|
|
|
|
|
|
print("[TouchControls] Updated %s shortcut label to: %s" % [button_name, existing_lbl.text])
|
|
|
|
# Ensure correct placement (Top Right)
|
|
existing_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
existing_lbl.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
|
existing_lbl.offset_top = -8 # Lowered slightly from -18
|
|
existing_lbl.offset_right = 0 # Aligned with right edge
|
|
|
|
# Ensure Outline
|
|
existing_lbl.add_theme_color_override("font_outline_color", Color.BLACK)
|
|
existing_lbl.add_theme_constant_override("outline_size", 4)
|
|
return
|
|
|
|
# Add Keyboard Shortcut Label
|
|
var shortcut_lbl = Label.new()
|
|
shortcut_lbl.name = "ShortcutLabel"
|
|
shortcut_lbl.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
shortcut_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
shortcut_lbl.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
|
shortcut_lbl.offset_top = -8 # Lowered slightly from -18
|
|
shortcut_lbl.offset_right = 0 # Aligned with right edge
|
|
shortcut_lbl.add_theme_font_size_override("font_size", 16)
|
|
shortcut_lbl.add_theme_color_override("font_outline_color", Color.BLACK)
|
|
shortcut_lbl.add_theme_constant_override("outline_size", 4)
|
|
|
|
match button_name:
|
|
"Grab": shortcut_lbl.text = SettingsManager.get_control_text("grab") if SettingsManager else "Space"
|
|
"Put": shortcut_lbl.text = SettingsManager.get_control_text("put") if SettingsManager else "R"
|
|
"AttackMode": shortcut_lbl.text = SettingsManager.get_control_text("attack_mode") if SettingsManager else "Q"
|
|
"TektonGrab": shortcut_lbl.text = SettingsManager.get_control_text("tekton_grab") if SettingsManager else "G"
|
|
|
|
|
|
btn.add_child(shortcut_lbl)
|
|
|
|
func _on_joystick_direction(direction: Vector2i):
|
|
if local_player and local_player.movement_manager:
|
|
var target_pos = local_player.current_position + direction
|
|
local_player.movement_manager.simple_move_to(target_pos)
|
|
|
|
func _on_button_pressed(button_name: String):
|
|
if not local_player:
|
|
return
|
|
|
|
# Visual feedback - scale up
|
|
var btn: Button
|
|
match button_name:
|
|
"Grab": btn = grab_button
|
|
"Put": btn = put_button
|
|
"AttackMode": btn = attack_mode_button
|
|
"SpawnBoost": btn = spawn_boost_button
|
|
"TektonGrab": btn = tekton_grab_button
|
|
|
|
|
|
if btn:
|
|
var tween = create_tween()
|
|
tween.tween_property(btn, "scale", Vector2(1.15, 1.15), 0.1)
|
|
|
|
# Trigger action
|
|
match button_name:
|
|
"Grab":
|
|
emit_signal("grab_pressed")
|
|
if local_player.has_method("grab_item"):
|
|
local_player.grab_item(local_player.current_position)
|
|
"Put":
|
|
emit_signal("put_pressed")
|
|
if local_player.has_method("auto_put_item"):
|
|
local_player.auto_put_item()
|
|
"AttackMode":
|
|
emit_signal("attack_mode_pressed")
|
|
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
|
if powerup_mgr:
|
|
var can_use = powerup_mgr.can_use_special()
|
|
var boost_val = powerup_mgr.current_boost
|
|
print("[TouchControls] AttackMode Pressed. Boost: %s, CanUse: %s" % [boost_val, can_use])
|
|
|
|
if can_use:
|
|
powerup_mgr.use_special_effect() # Sets is_attack_mode=true
|
|
else:
|
|
pass
|
|
else:
|
|
print("[TouchControls] PowerUpManager missing on player")
|
|
# "SpawnBoost":
|
|
# if local_player and local_player.is_carrying_tekton:
|
|
# if local_player.powerup_manager and local_player.powerup_manager.has_method("spawn_boost_reward"):
|
|
# local_player.powerup_manager.spawn_boost_reward()
|
|
"TektonGrab":
|
|
if not local_player.is_carrying_tekton and local_player.has_method("grab_tekton"):
|
|
local_player.grab_tekton()
|
|
|
|
|
|
func _on_button_released(button_name: String):
|
|
var btn: Button
|
|
match button_name:
|
|
"Grab": btn = grab_button
|
|
"Put": btn = put_button
|
|
"AttackMode": btn = attack_mode_button
|
|
"SpawnBoost": btn = spawn_boost_button
|
|
"TektonGrab": btn = tekton_grab_button
|
|
|
|
|
|
if btn:
|
|
var tween = create_tween()
|
|
tween.tween_property(btn, "scale", Vector2(1.0, 1.0), 0.1)
|
|
|
|
func _on_settings_pressed():
|
|
# Toggle pause menu on main scene
|
|
if main_scene and main_scene.has_method("_toggle_pause_menu"):
|
|
main_scene._toggle_pause_menu()
|
|
print("[TouchControls] Toggling pause menu")
|
|
else:
|
|
print("[TouchControls] Main scene _toggle_pause_menu method not found")
|
|
|
|
func _is_touch_device() -> bool:
|
|
# Check if running on mobile
|
|
return OS.has_feature("android") or OS.has_feature("ios") or OS.has_feature("web_android") or OS.has_feature("web_ios")
|
|
|
|
func _load_settings():
|
|
"""Load touch control settings from config file."""
|
|
var config = ConfigFile.new()
|
|
var err = config.load(CONFIG_PATH)
|
|
if err != OK:
|
|
print("[TouchControls] No saved settings found, using defaults")
|
|
_apply_settings() # Apply default settings immediately
|
|
return
|
|
|
|
# Load settings values
|
|
button_size = config.get_value("touch_controls", "button_size", 70.0)
|
|
button_opacity = config.get_value("touch_controls", "button_opacity", 0.7)
|
|
button_scale = config.get_value("touch_controls", "button_scale", 1.0)
|
|
joystick_enabled = config.get_value("touch_controls", "joystick_enabled", true)
|
|
touch_buttons_enabled = config.get_value("touch_controls", "touch_buttons_enabled", true)
|
|
|
|
# Load button positions
|
|
var grab_pos = config.get_value("touch_controls", "grab_position", Vector2(-200, -240))
|
|
var put_pos = config.get_value("touch_controls", "put_position", Vector2(-120, -160))
|
|
var attack_mode_pos = config.get_value("touch_controls", "attack_mode_position", Vector2(-200, -80)) # Changed key from special
|
|
if config.has_section_key("touch_controls", "special_position"): # Migrate legacy
|
|
attack_mode_pos = config.get_value("touch_controls", "special_position", Vector2(-200, -80))
|
|
|
|
var spawn_boost_pos = config.get_value("touch_controls", "spawn_boost_position", Vector2(-120, -80))
|
|
button_positions = {
|
|
"grab": grab_pos,
|
|
"put": put_pos,
|
|
"attack_mode": attack_mode_pos,
|
|
"spawn_boost": spawn_boost_pos
|
|
}
|
|
|
|
# Apply loaded settings
|
|
_apply_settings()
|
|
print("[TouchControls] Settings loaded")
|
|
|
|
func _save_settings():
|
|
"""Save touch control settings to config file."""
|
|
var config = ConfigFile.new()
|
|
|
|
config.set_value("touch_controls", "button_size", button_size)
|
|
config.set_value("touch_controls", "button_opacity", button_opacity)
|
|
config.set_value("touch_controls", "button_scale", button_scale)
|
|
config.set_value("touch_controls", "joystick_enabled", joystick_enabled)
|
|
config.set_value("touch_controls", "touch_buttons_enabled", touch_buttons_enabled)
|
|
config.set_value("touch_controls", "grab_position", button_positions.grab)
|
|
config.set_value("touch_controls", "put_position", button_positions.put)
|
|
config.set_value("touch_controls", "attack_mode_position", button_positions.attack_mode) # Renamed
|
|
config.set_value("touch_controls", "spawn_boost_position", button_positions.spawn_boost)
|
|
|
|
var err = config.save(CONFIG_PATH)
|
|
if err != OK:
|
|
push_error("[TouchControls] Failed to save settings: %s" % err)
|
|
else:
|
|
print("[TouchControls] Settings saved")
|
|
|
|
func _apply_settings():
|
|
"""Apply current settings to UI elements."""
|
|
# Apply joystick visibility
|
|
if virtual_joystick:
|
|
virtual_joystick.visible = joystick_enabled and _is_touch_device()
|
|
|
|
# Apply touch buttons visibility - FORCED ON per request to "just show them"
|
|
# Apply touch buttons visibility
|
|
if power_bar_container:
|
|
power_bar_container.visible = true
|
|
if interaction_container:
|
|
interaction_container.visible = true
|
|
|
|
var buttons_visible = true
|
|
|
|
print("[TouchControls] Applying settings: ButtonsVisible=", buttons_visible)
|
|
|
|
if grab_button:
|
|
grab_button.visible = true
|
|
grab_button.scale = Vector2(button_scale, button_scale)
|
|
grab_button.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
|
grab_button.offset_left = button_positions.grab.x
|
|
grab_button.offset_top = button_positions.grab.y
|
|
grab_button.offset_right = button_positions.grab.x + button_size
|
|
grab_button.offset_bottom = button_positions.grab.y + button_size
|
|
|
|
if put_button:
|
|
put_button.visible = true
|
|
|
|
if attack_mode_button:
|
|
attack_mode_button.visible = true
|
|
attack_mode_button.scale = Vector2(button_scale, button_scale)
|
|
attack_mode_button.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
|
attack_mode_button.offset_left = button_positions.attack_mode.x
|
|
attack_mode_button.offset_top = button_positions.attack_mode.y
|
|
attack_mode_button.offset_right = button_positions.attack_mode.x + button_size
|
|
attack_mode_button.offset_bottom = button_positions.attack_mode.y + button_size
|
|
|
|
if spawn_boost_button:
|
|
# spawn_boost_button.visible = true
|
|
# spawn_boost_button.scale = Vector2(button_scale, button_scale)
|
|
# spawn_boost_button.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
|
# spawn_boost_button.offset_left = button_positions.spawn_boost.x
|
|
# spawn_boost_button.offset_top = button_positions.spawn_boost.y
|
|
# spawn_boost_button.offset_right = button_positions.spawn_boost.x + button_size
|
|
# spawn_boost_button.offset_bottom = button_positions.spawn_boost.y + button_size
|
|
spawn_boost_button.visible = false
|
|
|
|
if tekton_grab_button:
|
|
tekton_grab_button.visible = true
|
|
tekton_grab_button.scale = Vector2(button_scale, button_scale)
|
|
tekton_grab_button.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
|
tekton_grab_button.offset_left = -280
|
|
tekton_grab_button.offset_top = -80
|
|
tekton_grab_button.offset_right = -280 + button_size
|
|
tekton_grab_button.offset_bottom = -80 + button_size
|
|
|
|
# Force layer update
|
|
visible = true
|
|
|
|
# =============================================================================
|
|
# Public Settings API
|
|
# =============================================================================
|
|
|
|
func set_touch_buttons_enabled(enabled: bool):
|
|
"""Enable or disable all action buttons (grab, put, attack_mode)."""
|
|
touch_buttons_enabled = enabled
|
|
_apply_settings()
|
|
|
|
func set_joystick_enabled(enabled: bool):
|
|
"""Enable or disable the virtual joystick (and all touch controls)."""
|
|
joystick_enabled = enabled
|
|
_apply_settings()
|
|
|
|
func set_button_scale(p_scale: float):
|
|
"""Set scale for all action buttons."""
|
|
button_scale = p_scale
|
|
_apply_settings()
|
|
|
|
func set_button_position(button_name: String, new_position: Vector2):
|
|
"""Update position of a specific button."""
|
|
button_positions[button_name] = new_position
|
|
# Logic to update offsets removed per request (Scene Controlled)
|
|
# Only saving the data for persistence if user eventually wants it back
|
|
|
|
func get_button_positions() -> Dictionary:
|
|
"""Get current button positions for settings UI."""
|
|
return button_positions.duplicate()
|
|
|
|
func get_settings() -> Dictionary:
|
|
"""Get all current settings as a dictionary."""
|
|
return {
|
|
"button_size": button_size,
|
|
"button_opacity": button_opacity,
|
|
"button_scale": button_scale,
|
|
"joystick_enabled": joystick_enabled,
|
|
"touch_buttons_enabled": touch_buttons_enabled,
|
|
"button_positions": button_positions.duplicate()
|
|
}
|
|
|
|
func show_controls():
|
|
visible = true
|
|
|
|
func hide_controls():
|
|
visible = false
|
|
|
|
func _on_boost_points_changed(current_points: int, max_points: int):
|
|
# User Request: Disable Special & SpawnBoost if < 100%
|
|
var is_full = current_points >= (max_points - 1) # Tolerance
|
|
|
|
# Attack Mode (⚡) is only enabled if full AND not carrying a Tekton
|
|
var can_attack = is_full and not (local_player and local_player.is_carrying_tekton)
|
|
_update_boost_button_state(attack_mode_button, can_attack)
|
|
|
|
# SpawnBoost depends on carrying a Tekton, not boost points
|
|
# var can_spawn = local_player and local_player.is_carrying_tekton
|
|
# _update_boost_button_state(spawn_boost_button, can_spawn)
|
|
|
|
# Tekton Grab is only enabled if full AND not already carrying one
|
|
var can_grab = not (local_player and local_player.is_carrying_tekton) and (is_full)
|
|
_update_boost_button_state(tekton_grab_button, can_grab)
|
|
|
|
func _on_tekton_carried_changed(_is_carrying: bool):
|
|
# Refresh button states
|
|
var powerup_mgr = local_player.get_node_or_null("PowerUpManager")
|
|
if powerup_mgr:
|
|
_on_boost_points_changed(powerup_mgr.current_boost, powerup_mgr.MAX_BOOST)
|
|
|
|
func _update_boost_button_state(btn: Button, is_enabled: bool):
|
|
if not btn: return
|
|
|
|
btn.disabled = !is_enabled
|
|
# Visual feedback
|
|
if is_enabled:
|
|
btn.modulate = Color.WHITE
|
|
# Optional: Pulse effect?
|
|
else:
|
|
btn.modulate = Color(0.5, 0.5, 0.5, 0.5) # Dimmed
|