diff --git a/scenes/main.gd b/scenes/main.gd index 9f1cda6..9c7de4f 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -28,6 +28,7 @@ func _ready(): ui_manager.setup_action_buttons(_set_action_state_callback) ui_manager.setup_playerboard_ui() ui_manager.setup_timer_labels(self) + ui_manager.setup_playerboard_label(self) # NEW ui_manager.setup_leaderboard_ui(self) ui_manager.setup_powerup_bar_ui(self) # GlobalMatchTimer is now static in main.tscn - no setup needed @@ -43,6 +44,11 @@ func _ready(): if multiplayer.is_server(): randomize_game_grid() _setup_tile_respawn_timer() + +func _on_goal_count_updated(peer_id: int, count: int): + # Only update for local player + if peer_id == multiplayer.get_unique_id(): + ui_manager.update_goal_count_label(count) func _setup_tile_respawn_timer(): # Configure respawn rate based on Scarcity Mode from Lobby @@ -127,6 +133,7 @@ func _init_managers(): # Connect signals for UI updates goals_cycle_manager.timer_updated.connect(_on_timer_updated) goals_cycle_manager.score_updated.connect(_on_score_updated) + goals_cycle_manager.goal_count_updated.connect(_on_goal_count_updated) # NEW goals_cycle_manager.leaderboard_updated.connect(_on_leaderboard_updated) goals_cycle_manager.global_timer_updated.connect(_on_global_timer_updated) goals_cycle_manager.match_ended.connect(_on_match_ended) diff --git a/scenes/main.tscn b/scenes/main.tscn index 051f738..9e17e61 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -961,6 +961,18 @@ grow_horizontal = 2 grow_vertical = 2 texture = ExtResource("9_6gcb6") +[node name="PlayerBoardLabel" type="Label" parent="." unique_id=341385584] +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 217.0 +offset_top = -20.0 +offset_right = 257.0 +offset_bottom = 3.0 +grow_vertical = 2 +theme_override_font_sizes/font_size = 32 +text = "X0" + [node name="PowerUpBar" type="PanelContainer" parent="." unique_id=1775378146] anchors_preset = 4 anchor_top = 0.5 @@ -9415,6 +9427,7 @@ offset_bottom = 24.0 texture = ExtResource("10_my1qp") [node name="MessageBar" type="PanelContainer" parent="." unique_id=142729129] +visible = false anchors_preset = 2 anchor_top = 1.0 anchor_bottom = 1.0 diff --git a/scripts/managers/goals_cycle_manager.gd b/scripts/managers/goals_cycle_manager.gd index 8325a5e..581d615 100644 --- a/scripts/managers/goals_cycle_manager.gd +++ b/scripts/managers/goals_cycle_manager.gd @@ -4,8 +4,8 @@ extends Node # Also handles global match timer that ends the game const CYCLE_DURATION: float = 30.0 -const BASE_SCORE: int = 100 -const TIME_BONUS_MULTIPLIER: float = 2.0 +const BASE_SCORE: int = 1000 +const TIME_BONUS_MULTIPLIER: float = 0.0 # User implied flat 1000, setting bonus to 0 effectively or I can just ignore it in calc. # Cycle timer state (30-second cycles) var current_cycle_timer: float = 0.0 @@ -18,6 +18,7 @@ var is_match_active: bool = false # Score tracking: peer_id -> score var player_scores: Dictionary = {} +var player_goal_counts: Dictionary = {} # peer_id -> count # Reference to main scene var main_scene: Node = null @@ -26,6 +27,7 @@ signal cycle_started() signal cycle_ended() signal timer_updated(time_remaining: float) signal score_updated(peer_id: int, new_score: int) +signal goal_count_updated(peer_id: int, count: int) signal leaderboard_updated(sorted_scores: Array) # Global match signals @@ -225,11 +227,18 @@ func on_goal_completed(player: Node, time_remaining: float): player_scores[peer_id] = 0 player_scores[peer_id] += score_earned + # Update goal count + if not player_goal_counts.has(peer_id): + player_goal_counts[peer_id] = 0 + player_goal_counts[peer_id] += 1 + emit_signal("score_updated", peer_id, player_scores[peer_id]) + emit_signal("goal_count_updated", peer_id, player_goal_counts[peer_id]) _update_leaderboard() # Sync score to all clients rpc("sync_player_score", peer_id, player_scores[peer_id]) + rpc("sync_goal_count", peer_id, player_goal_counts[peer_id]) # Clear playerboard tiles (they convert to powerup bar reward) player.playerboard.fill(-1) @@ -251,6 +260,38 @@ func sync_player_score(peer_id: int, total_score: int): emit_signal("score_updated", peer_id, total_score) _update_leaderboard() +@rpc("authority", "call_local", "reliable") +func sync_goal_count(peer_id: int, count: int): + player_goal_counts[peer_id] = count + emit_signal("goal_count_updated", peer_id, count) + +@rpc("any_peer", "call_local", "reliable") +func request_add_score(amount: int): + """RPC for clients to request score addition (trusted).""" + if not multiplayer.is_server(): + return + + var sender_id = multiplayer.get_remote_sender_id() + # If called locally by server, sender_id might be 0 or 1. + if sender_id == 0: sender_id = 1 + + add_score(sender_id, amount) + +func add_score(peer_id: int, amount: int): + """Add points to a specific player (Server only).""" + if not multiplayer.is_server(): + return + + if not player_scores.has(peer_id): + player_scores[peer_id] = 0 + + player_scores[peer_id] += amount + + # Sync + rpc("sync_player_score", peer_id, player_scores[peer_id]) + print("[GoalsCycle] Added %d points to Player %d. Total: %d" % [amount, peer_id, player_scores[peer_id]]) + + func _update_leaderboard(): # Sort players by score (descending) var sorted_scores = [] diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index ccffa55..e8031d5 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -142,6 +142,15 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: # Consume all available boost to force a full recharge cycle player.powerup_manager.consume_boost(100.0) + # SCORING: 200 Points for successful attack + if player.is_multiplayer_authority(): + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + var gcm = main.get_node_or_null("GoalsCycleManager") + if gcm: + gcm.rpc("request_add_score", 200) + NotificationManager.send_message(player, "Successful Attack! +200 Pts", NotificationManager.MessageType.GOAL) + # 5. Attack Mode Persistence # logic moved to consume_boost: checks if <= 0 then disables. # So we do NOT force disable here. diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index 4d79b46..ed5fdb7 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -294,6 +294,8 @@ func _execute_area_freeze(center_pos: Vector2i = Vector2i.ZERO): # Initial Check (Instant Feedback) var all_players = player.get_tree().get_nodes_in_group("Players") + var hit_count = 0 + for p in all_players: # Check distance (Chebyshev distance for square area) var dx = abs(p.current_position.x - center_pos.x) @@ -303,6 +305,18 @@ func _execute_area_freeze(center_pos: Vector2i = Vector2i.ZERO): if dx <= radius and dy <= radius: p.rpc("apply_slow_effect", FREEZE_SLOW_DURATION) NotificationManager.send_message(p, "Caught in Freeze Zone!", NotificationManager.MessageType.WARNING) + if p != player: # Don't score for freezing self (unless desired?) - Assuming enemies + hit_count += 1 + + if hit_count > 0 and player.is_multiplayer_authority(): + var points = hit_count * 50 + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + var gcm = main.get_node_or_null("GoalsCycleManager") + if gcm: + gcm.add_score(player.name.to_int(), points) + + NotificationManager.send_message(player, "Hit %d Players! +%d Pts" % [hit_count, points], NotificationManager.MessageType.GOAL) # Visual Feedback (Turn Floor Blue - Item 12 on Layer 0) if player.is_multiplayer_authority(): diff --git a/scripts/managers/ui_manager.gd b/scripts/managers/ui_manager.gd index 8748131..5bf51a4 100644 --- a/scripts/managers/ui_manager.gd +++ b/scripts/managers/ui_manager.gd @@ -27,6 +27,7 @@ var playerboard_ui var action_menu_instance var powerup_inventory_ui var timer_label: Label +var playerboard_label: Label # Shows (xN) goal completions var local_player_character var _previous_playerboard_state: Array = [] @@ -390,9 +391,32 @@ func setup_timer_labels(main_node): if t_label: timer_label = t_label # Store reference t_label.add_theme_color_override("font_color", Color(1.0, 0.85, 0.2)) - print("[UIManager] SUCCESS: Found and stored TimerLabel reference.") else: print("[UIManager] ERROR: GoalsTimer found but TimerLabel NOT found at node/TimerLabel") + var suffix_label = goals_timer.get_node_or_null("SuffixLabel") + if suffix_label: + suffix_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7)) + +func setup_playerboard_label(main_node): + var lbl = main_node.get_node_or_null("PlayerBoardLabel") + if lbl: + playerboard_label = lbl + playerboard_label.text = "" # Hidden initially + print("[UIManager] Found PlayerBoardLabel") + else: + print("[UIManager] PlayerBoardLabel not found") + +func update_goal_count_label(count: int): + if playerboard_label: + if count > 0: + playerboard_label.text = "x%d" % count + + # Pop effect + var tween = playerboard_label.create_tween() + playerboard_label.scale = Vector2(1.5, 1.5) + tween.tween_property(playerboard_label, "scale", Vector2(1.0, 1.0), 0.3).set_trans(Tween.TRANS_BOUNCE) + else: + playerboard_label.text = "" # Method to update leaderboard with all players in match func initialize_leaderboard_with_players(players: Array):