diff --git a/project.godot b/project.godot index deaacc3..f8dc165 100644 --- a/project.godot +++ b/project.godot @@ -15,7 +15,7 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="tekton-local" -run/main_scene="uid://dxn87yj8qnfpp" +run/main_scene="uid://b7nxt2hc4kqp8" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://icon.svg" diff --git a/scenes/main.tscn b/scenes/main.tscn index 689dd09..7ec8ed8 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -9787,7 +9787,7 @@ offset_right = -73.0 offset_bottom = -171.0 grow_horizontal = 0 grow_vertical = 0 -text = "⚡" +text = "🚀" [node name="SettingsBtn" type="Button" parent="TouchControls/TouchControls" unique_id=1964422444] layout_mode = 1 diff --git a/scripts/managers/playerboard_manager.gd b/scripts/managers/playerboard_manager.gd index 37224c7..ad2ce03 100644 --- a/scripts/managers/playerboard_manager.gd +++ b/scripts/managers/playerboard_manager.gd @@ -119,11 +119,13 @@ func _execute_grab(grid_pos: Vector2i, cell: Vector3i, item_id: int): # Check if item is still there if server_item != item_id: print("Server: Item mismatch or already taken. Server has ", server_item) + _force_sync_to_client(cell, server_item) return false # Check action points if player.action_points <= 0: print("Server: Player has no action points.") + _force_sync_to_client(cell, server_item) return false # Check adjacency @@ -131,12 +133,14 @@ func _execute_grab(grid_pos: Vector2i, cell: Vector3i, item_id: int): var neighbors = server_gridmap.get_neighbors(player.current_position, 0) if not neighbors.any(func(n): return n.position == grid_pos): print("Server: Player is not adjacent to item.") + _force_sync_to_client(cell, server_item) return false # 2. Server-side Auto-Arrange var target_slot = find_best_goal_slot_for_item(item_id) if target_slot == -1: print("Server: Player has no valid slot for item.") + _force_sync_to_client(cell, server_item) return false # 3. Server Executes the Action @@ -197,6 +201,32 @@ func _check_and_refill_grid_if_needed(server_gridmap: Node): var item = server_gridmap.get_cell_item(Vector3i(x, 1, z)) main.rpc("sync_grid_item", x, 1, z, item) + + +func _force_sync_to_client(cell: Vector3i, server_item: int): + """Force a sync of the specific cell and playerboard to the client who initiated the failed action.""" + # Only meaningful if we are server + if not multiplayer.is_server(): + return + + var main = player.get_tree().get_root().get_node_or_null("Main") + if not main: return + + # Determine client peer ID from player name (standard convention) + # Note: Bots are ID 1, so we don't need to sync special for them (local function calls) + # But for Clients... + var peer_id = player.name.to_int() + if peer_id == 1: # Server/Bot + return + + # Sync the Grid Item (which they thought they took) + main.rpc_id(peer_id, "sync_grid_item", cell.x, cell.y, cell.z, server_item) + + # Sync their Playerboard (which they thought they updated) + main.rpc_id(peer_id, "sync_playerboard", peer_id, player.playerboard) + + print("Server: Forced sync to client %d due to action failure." % peer_id) + func bot_try_grab_item() -> bool: if not enhanced_gridmap or player.action_points <= 0: return false diff --git a/scripts/managers/powerup_manager.gd b/scripts/managers/powerup_manager.gd index 4156838..2b7eb10 100644 --- a/scripts/managers/powerup_manager.gd +++ b/scripts/managers/powerup_manager.gd @@ -72,7 +72,7 @@ func _on_boost_full(): # player.is_attack_mode = true # Removed auto-activate emit_signal("bar_filled") NotificationManager.send_message(player, NotificationManager.MESSAGES.ATTACK_MODE_READY, NotificationManager.MessageType.POWERUP) - print("[PowerUp] Player %s Boost Full! Entering Attack Mode." % player.name) + print("[PowerUp] Player %s Boost Full! Ready for Attack Mode." % player.name) if player.is_multiplayer_authority(): rpc("sync_boost", current_boost) @@ -100,6 +100,14 @@ func sync_boost(value: float): # Could trigger visual effect here pass +@rpc("authority", "call_local", "reliable") +func sync_boost_level(level: int): + current_level = level + var level_idx = clamp(current_level - 1, 0, FILL_TIMES.size() - 1) + print("[PowerUp] Difficulty synced: Level %d (Fill Time: %.1fs)" % [current_level, FILL_TIMES[level_idx]]) + + + # ============================================================================= # Getters # ============================================================================= @@ -113,6 +121,11 @@ func get_max_points() -> int: func get_fill_percentage() -> float: return current_boost / MAX_BOOST +func get_bars() -> int: + """Returns the number of filled segments (0-4).""" + # Each bar is 25 points (100 / 4) + return int(current_boost / 25.0) + func can_use_special() -> bool: # Use small epsilon for float comparison to avoid "99.999" issues return current_boost >= (MAX_BOOST - 0.1) @@ -177,3 +190,7 @@ func add_goal_completion_reward(): print("[PowerUp] Player %s Completed Goal. Boost Level Up! Now: %d (Fill Time: %.1fs)" % [player.name, current_level, FILL_TIMES[level_idx]]) # Optional: Notify user of difficulty increase? + + if multiplayer.is_server(): + rpc("sync_boost_level", current_level) + diff --git a/scripts/managers/touch_controls.gd b/scripts/managers/touch_controls.gd index 3ef7a66..bbfadd1 100644 --- a/scripts/managers/touch_controls.gd +++ b/scripts/managers/touch_controls.gd @@ -190,6 +190,9 @@ func _style_button(btn: Button, opacity: float): 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 func _on_joystick_direction(direction: Vector2i): if local_player and local_player.has_method("simple_move_to"):