diff --git a/scripts/managers/camera_context_manager.gd b/scripts/managers/camera_context_manager.gd index ffd5221..9157870 100644 --- a/scripts/managers/camera_context_manager.gd +++ b/scripts/managers/camera_context_manager.gd @@ -1,194 +1,61 @@ extends Node -# CameraContextManager -# Handles camera position based on player's row on the board. +# CameraContextManager - Smoothly follows player and clamps to arena edges -# var screen_shake_manager: Node # Removed var camera: Camera3D var player: Node3D -# Configuration Map: Row Threshold -> Target Position -# We use a list of dictionaries to define ranges. -# Setup A: Rows 0-5 -# Setup B: Rows 6-9 -# Setup C: Rows 10+ -var camera_setups = [ - { "max_row": 5, "position": Vector3(7.0, 19.636, 15.0) }, - { "max_row": 9, "position": Vector3(7.0, 19.636, 19.0) }, - { "max_row": 999, "position": Vector3(7.0, 19.22636, 22.5) } # Default/Bottom -] +# Configuration +@export var smooth_speed: float = 5.0 +@export var z_offset: float = 12.0 +@export var default_y: float = 19.636 -var unique_id: int -var current_row_setup: int = -1 -var current_col_setup: int = -1 +# Bounds Definitions { min_x, max_x, min_z, max_z } +var bounds_freemode = { "min_x": 3.0, "max_x": 11.0, "min_z": 15.0, "max_z": 22.5 } +var bounds_stop_n_go = { "min_x": 3.0, "max_x": 18.5, "min_z": 15.0, "max_z": 17.5 } +var bounds_doors = { "min_x": 7.0, "max_x": 7.0, "min_z": 25.8, "max_z": 25.8 } # Static overlook func initialize(p_camera: Camera3D, _p_shake_manager: Node): camera = p_camera - # screen_shake_manager = p_shake_manager print("[CameraContextManager] Initialized with camera") func set_player(p_player: Node3D): player = p_player - unique_id = p_player.name.to_int() - - # Try to get movement manager (might be initializing child) - var movement_mgr = player.get("movement_manager") - if not movement_mgr and "movement_manager" in player: - movement_mgr = player.movement_manager - - var has_snap_signal = player.has_signal("position_changed") - - if movement_mgr and has_snap_signal: - # 1. Follow during movement finished - if not movement_mgr.movement_finished.is_connected(_on_player_moved): - movement_mgr.movement_finished.connect(_on_player_moved) - - # 2. Snap on spawn/teleport (Resets setups for immediate catch-up) - if not player.position_changed.is_connected(_on_player_snapped): - player.position_changed.connect(_on_player_snapped) - - print("[CameraContextManager] Successfully connected to all player signals") - else: - print("[CameraContextManager] Warning: movement_manager or signal missing. Retrying in 0.5s...") - await get_tree().create_timer(0.5).timeout - if player == p_player: - set_player(p_player) - return # Exit current (failed) initialization - - _update_camera_target() + print("[CameraContextManager] Player set: ", player.name) -func _on_player_moved(): - # Normal movement finish (keeps hysteresis) - _update_camera_target() - -func _on_player_snapped(): - # Sharp snap (spawn/teleport) - Reset setup indices to force immediate re-eval - current_row_setup = -1 - current_col_setup = -1 - # print("[CameraContextManager] Player snapped, resetting setups") - _update_camera_target() - -func _update_camera_target(): - if not player or not camera: +func _physics_process(delta): + if not player or not camera or not is_instance_valid(player): return - var current_row = player.current_position.y - var current_col = player.current_position.x + var target_pos = _calculate_target_position() + # Smoothly interpolate to target + camera.position = camera.position.lerp(target_pos, smooth_speed * delta) + +func _calculate_target_position() -> Vector3: + var player_pos = player.global_position + + # Initial target based on player position + offsets + var target_x = player_pos.x + var target_y = default_y + var target_z = player_pos.z + z_offset + + # Apply Mode-Specific Clamping var mode = LobbyManager.get_game_mode() + var bounds = bounds_freemode # Default + if mode == GameMode.Mode.STOP_N_GO: - _update_stop_n_go_camera(current_row, current_col) + bounds = bounds_stop_n_go elif mode == GameMode.Mode.TEKTON_DOORS: - _update_tekton_doors_camera() - else: - _update_freemode_camera(current_row, current_col) - -func _update_stop_n_go_camera(current_row: int, current_col: int): - # --- STOP N GO: 4 Columns (X), 2 Rows (Y/Z) --- - # Columns: 0-5, 6-10, 11-15, 16-21 - # Rows: 0-4, 5-9 - - # 1. Column Logic (4 Zones) - if current_col_setup == -1: - if current_col <= 5: current_col_setup = 0 - elif current_col <= 10: current_col_setup = 1 - elif current_col <= 15: current_col_setup = 2 - else: current_col_setup = 3 - - match current_col_setup: - 0: # Leftmost - if current_col >= 7: current_col_setup = 1 - 1: # Mid-Left - if current_col <= 4: current_col_setup = 0 - elif current_col >= 12: current_col_setup = 2 - 2: # Mid-Right - if current_col <= 9: current_col_setup = 1 - elif current_col >= 17: current_col_setup = 3 - 3: # Rightmost - if current_col <= 14: current_col_setup = 2 - - # 2. Row Logic (2 Zones) - if current_row_setup == -1: - if current_row <= 4: current_row_setup = 0 - else: current_row_setup = 1 + bounds = bounds_doors + target_y = 32.3 # Doors uses a higher overlook - match current_row_setup: - 0: # Top - if current_row >= 6: current_row_setup = 1 - 1: # Bottom - if current_row <= 3: current_row_setup = 0 - - # 3. Target Calculation - var target_x = 3.0 + (current_col_setup * 5.0) # approx centers for 4 zones in 22 cols - var target_y = 19.636 - var target_z = 15.0 if current_row_setup == 0 else 17.5 + # Clamp X and Z + target_x = clamp(target_x, bounds.min_x, bounds.max_x) + target_z = clamp(target_z, bounds.min_z, bounds.max_z) - # Adjust specific X centers for the 22-col field - match current_col_setup: - 0: target_x = 3.0 - 1: target_x = 8.5 - 2: target_x = 13.5 - 3: target_x = 18.5 - - _apply_camera_tween(Vector3(target_x, target_y, target_z)) - -func _update_tekton_doors_camera(): - # --- TEKTON DOORS: Static Overlook --- - # Grid is 14x14, center is approx (7, 7) - # User requested position: Vector3(7.0, 31.0, 25.5) - _apply_camera_tween(Vector3(7.0, 32.3, 25.8)) - -func _update_freemode_camera(current_row: int, current_col: int): - # --- FREEMODE: 3x3 Grid --- - # Zone thresholds 4, 9 - - # Row Hysteresis - if current_row_setup == -1: - if current_row <= 4: current_row_setup = 0 - elif current_row <= 9: current_row_setup = 1 - else: current_row_setup = 2 + # Special case for Setup C in Freemode (Lower Y at bottom edges) + if mode == GameMode.Mode.FREEMODE and target_z > 21.0: + target_y = 19.22636 - match current_row_setup: - 0: # Top - if current_row >= 6: current_row_setup = 1 - 1: # Mid - if current_row <= 3: current_row_setup = 0 - elif current_row >= 11: current_row_setup = 2 - 2: # Bot - if current_row <= 8: current_row_setup = 1 - - # Col Hysteresis - if current_col_setup == -1: - if current_col <= 4: current_col_setup = 0 - elif current_col <= 9: current_col_setup = 1 - else: current_col_setup = 2 - - match current_col_setup: - 0: # Left - if current_col >= 6: current_col_setup = 1 - 1: # Center - if current_col <= 3: current_col_setup = 0 - elif current_col >= 11: current_col_setup = 2 - 2: # Right - if current_col <= 8: current_col_setup = 1 - - var target_z = 15.0 - var target_y = 19.636 - var target_x = 7.0 - - match current_row_setup: - 0: target_z = 15.0 - 1: target_z = 19.0 - 2: target_z = 22.5; target_y = 19.22636 - - match current_col_setup: - 0: target_x = 3.0 # Left zone center - 1: target_x = 7.0 # Main center - 2: target_x = 11.0 # Right zone center - - _apply_camera_tween(Vector3(target_x, target_y, target_z)) - -func _apply_camera_tween(target_pos: Vector3): - if camera.position != target_pos: - var tween = create_tween() - tween.tween_property(camera, "position", target_pos, 0.4).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + return Vector3(target_x, target_y, target_z) diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index c023ce1..b7da430 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -97,22 +97,22 @@ func handle_unhandled_input(event): KEY_KP_1, KEY_1, KEY_KP_2, KEY_2, KEY_KP_3, KEY_3, KEY_KP_4, KEY_4: var mode = LobbyManager.get_game_mode() var is_restricted = GameMode.is_restricted(mode) - match event.keycode: - KEY_KP_1, KEY_1: - player.activate_powerup(0) # FASTER_SPEED - KEY_KP_2, KEY_2: - if is_restricted: - player.activate_powerup(1) # AREA_FREEZE (Restricted) - else: - player.activate_powerup(2) # BLOCK_FLOOR (Free) - KEY_KP_3, KEY_3: - if is_restricted: - player.activate_powerup(3) # INVISIBLE_MODE (Restricted) - else: - player.activate_powerup(1) # AREA_FREEZE (Free) - KEY_KP_4, KEY_4: - if not is_restricted: - player.activate_powerup(3) # INVISIBLE_MODE (Free) + if is_restricted: + match event.keycode: + KEY_KP_1, KEY_1: + player.activate_powerup(0) # FASTER_SPEED + KEY_KP_2, KEY_2: + player.activate_powerup(3) # INVISIBLE_MODE (Ghost is now 2) + else: + match event.keycode: + KEY_KP_1, KEY_1: + player.activate_powerup(0) # FASTER_SPEED + KEY_KP_2, KEY_2: + player.activate_powerup(2) # BLOCK_FLOOR + KEY_KP_3, KEY_3: + player.activate_powerup(1) # AREA_FREEZE + KEY_KP_4, KEY_4: + player.activate_powerup(3) # INVISIBLE_MODE # KEY_R: # player.auto_put_item() KEY_Q: diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index edeca88..c99cd06 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -138,10 +138,17 @@ func initialize(p_player: Node3D, p_gridmap: Node): # Helper: Item ID to Effect Enum # ============================================================================= func get_effect_from_item(item_id: int) -> int: + var mode = LobbyManager.get_game_mode() + var is_restricted = GameMode.is_restricted(mode) + match item_id: 11: return SpecialEffect.FASTER_SPEED - 12: return SpecialEffect.AREA_FREEZE - 13: return SpecialEffect.BLOCK_FLOOR + 12: + if is_restricted: return -1 + return SpecialEffect.AREA_FREEZE + 13: + if is_restricted: return -1 + return SpecialEffect.BLOCK_FLOOR 14: return SpecialEffect.INVISIBLE_MODE _: return -1 @@ -457,7 +464,7 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true): var mode = LobbyManager.get_game_mode() var is_restricted = GameMode.is_restricted(mode) if is_restricted: - item_id = [11, 12, 14].pick_random() + item_id = [11, 14].pick_random() else: item_id = rng.randi_range(11, 14) diff --git a/scripts/ui/powerup_inventory_ui.gd b/scripts/ui/powerup_inventory_ui.gd index b13e8fa..54e7572 100644 --- a/scripts/ui/powerup_inventory_ui.gd +++ b/scripts/ui/powerup_inventory_ui.gd @@ -27,16 +27,22 @@ func _ready(): # 14: INVISIBLE_MODE (3) -> GhostBtn # We use 0, 1, 2, 3 to match SpecialTilesManager.SpecialEffect enum - _setup_btn(0, container.get_node_or_null("SpeedBtn")) - _setup_btn(1, container.get_node_or_null("FreezeAreaBtn")) + 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") - _setup_btn(2, wall_btn) + var ghost_btn = container.get_node_or_null("GhostBtn") var mode = LobbyManager.get_game_mode() var is_restricted = GameMode.is_restricted(mode) - if wall_btn and is_restricted: - wall_btn.visible = false # Hide Wall Power-up in restricted modes - _setup_btn(3, container.get_node_or_null("GhostBtn")) + + _setup_btn(0, speed_btn) + _setup_btn(1, freeze_btn) + _setup_btn(2, wall_btn) + _setup_btn(3, ghost_btn) + + if is_restricted: + if wall_btn: wall_btn.visible = false + if freeze_btn: freeze_btn.visible = false print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size()) @@ -108,11 +114,11 @@ func _setup_btn(effect_id: int, btn: Button): var mode = LobbyManager.get_game_mode() var is_restricted = GameMode.is_restricted(mode) if is_restricted: - # Restricted Mapping: 1, 2, 3 (No Wall) + # Restricted Mapping: 1, 2 match effect_id: 0: key_text = "1" - 1: key_text = "2" # Freeze is now 2 - 3: key_text = "3" # Ghost is now 3 + 3: key_text = "2" # Ghost is now 2 + _: key_text = "" else: # Free Mode Mapping: 1, 2, 3, 4 (Original) match effect_id: