extends Node var player: Node3D var movement_manager: Node var race_manager: Node @onready var SettingsManager = get_node_or_null("/root/SettingsManager") # Analog stick repeat throttle (prevents firing a new move every frame while held) var _analog_move_timer: float = 0.0 const ANALOG_MOVE_RATE: float = 0.18 # seconds between grid steps when holding stick func initialize(p_player: Node3D, p_movement_manager: Node, p_race_manager: Node): player = p_player movement_manager = p_movement_manager race_manager = p_race_manager func _process(delta): # Early return conditions if not is_instance_valid(player) or not multiplayer.has_multiplayer_peer() or not player.is_multiplayer_authority() or player.is_bot or player.is_in_group("Bots") or player.is_frozen or player.is_stop_frozen: return if TurnManager.turn_based_mode: return var move_vec = Vector2i.ZERO # 1. Left Analog Stick — always active (controller plugged in, any mode) var joystick_vec = Input.get_vector("move_west", "move_east", "move_north", "move_south", 0.25) if joystick_vec.length() > 0.25: _analog_move_timer -= delta if _analog_move_timer <= 0.0: if abs(joystick_vec.x) > abs(joystick_vec.y): move_vec.x = 1 if joystick_vec.x > 0 else -1 else: move_vec.y = 1 if joystick_vec.y > 0 else -1 _analog_move_timer = ANALOG_MOVE_RATE else: _analog_move_timer = 0.0 # Reset so next touch fires immediately # 2. Keyboard / D-pad Movement (fires via action pressed, handled by event) if move_vec == Vector2i.ZERO: if Input.is_action_pressed("move_north"): move_vec.y -= 1 if Input.is_action_pressed("move_south"): move_vec.y += 1 if Input.is_action_pressed("move_east"): move_vec.x += 1 if Input.is_action_pressed("move_west"): move_vec.x -= 1 # Fallback for explicit diagonal actions (if mapped) if move_vec == Vector2i.ZERO: if Input.is_action_pressed("move_northeast"): move_vec = Vector2i(1, -1) elif Input.is_action_pressed("move_southeast"): move_vec = Vector2i(1, 1) elif Input.is_action_pressed("move_southwest"): move_vec = Vector2i(-1, 1) elif Input.is_action_pressed("move_northwest"): move_vec = Vector2i(-1, -1) # 3. Action inputs (momentary) if Input.is_action_just_pressed("action_grab"): player.grab_item(player.current_position) if move_vec != Vector2i.ZERO: var base_pos = player.current_position if movement_manager.is_moving and player.target_position != Vector2i(-1, -1): base_pos = player.target_position var target_position = base_pos + move_vec movement_manager.simple_move_to(target_position) else: if movement_manager.is_moving: movement_manager.movement_queue.clear() func handle_unhandled_input(event): # Early return if not authorized human player if not multiplayer.has_multiplayer_peer() or not player.is_multiplayer_authority() or player.is_bot or player.is_in_group("Bots"): if multiplayer.has_multiplayer_peer(): player.set_process_unhandled_input(false) return var main = player.get_node("/root/Main") if not main: return # Turn-based mouse input (handled in unhandled_input) if not player.is_multiplayer_authority() or player.is_frozen or player.is_stop_frozen or (TurnManager.turn_based_mode and (not player.is_my_turn or movement_manager.is_moving)): return # --- Action Shortcuts (Keyboard OR Controller Button) --- var is_action_event = (event is InputEventKey and event.pressed and not event.echo) \ or (event is InputEventJoypadButton and event.pressed) if is_action_event: # Safety check for SettingsManager if not SettingsManager: return # 1. Unified check for POWER-UP Activation if event.is_action_pressed("use_powerup"): player.activate_held_powerup() get_viewport().set_input_as_handled() return # 2. Action Buttons (Remappable via InputMap) if event.is_action_pressed("action_knock_tekton"): if player.powerup_manager: player.powerup_manager.use_special_effect() if player.get("is_attack_mode") and player.has_method("enter_attack_mode"): player.enter_attack_mode() get_viewport().set_input_as_handled() elif event.is_action_pressed("action_grab_tekton"): if not player.is_carrying_tekton: if player.powerup_manager and player.powerup_manager.has_method("can_use_special"): player.grab_tekton() get_viewport().set_input_as_handled() # Handle spawn point selection if not yet selected if not player.spawn_point_selected and player.highlighted_spawn_points.size() > 0: if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: var camera = player.get_viewport().get_camera_3d() var from = camera.project_ray_origin(event.position) var to = from + camera.project_ray_normal(event.position) * 1000 var click_position = player.raycast_to_grid(from, to) if click_position in player.highlighted_spawn_points: if player.select_spawn_point(click_position): return # Turn-based mouse input if not player.is_multiplayer_authority() or (TurnManager.turn_based_mode and (not player.is_my_turn or movement_manager.is_moving)): return if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: if player.is_bot == true or player.is_in_group("Bots"): player.set_process_unhandled_input(false) player.set_process_input(false) return var camera = player.get_viewport().get_camera_3d() var from = camera.project_ray_origin(event.position) var to = from + camera.project_ray_normal(event.position) * 1000 var click_position = player.raycast_to_grid(from, to) if click_position != Vector2i(-1, -1): handle_grid_click(click_position) func handle_grid_click(grid_position: Vector2i): if player.is_frozen or player.is_stop_frozen or player.is_bot == true or player.is_in_group("Bots"): return var main = player.get_node("/root/Main") if not main: return # Action logic previously here has been removed along with ActionMenu. # Standard movement/interaction is now handled via keyboard/joystick. func handle_slot_gui_input(event, slot_index, slot_ui) -> int: # Arrangement mode has been removed along with ActionMenu. return -1