feat: Implement core game managers for power-ups, UI, goals, and player actions, and establish initial game scene and player.
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
[ ADT's Daily Report - 2025-12-17 ]
|
||||||
|
|
||||||
|
✅ Action Points Non-Turn-Based Mode - Removed auto-reset behavior for action points in non-turn-based mode. Action points are now unlimited in real-time fast-paced mode, meaning `player_action_manager.gd` no longer deducts points when `TurnManager.turn_based_mode` is false.
|
||||||
|
|
||||||
|
✅ Player Goals on Playerboard - Moved the 3x3 player goals visual to the center of the playerboard. Goals now display in slots 6,7,8,11,12,13,16,17,18 of the 5x5 grid. Hidden the `AllPlayerGoals` UI in `main.tscn` since goals are now shown on the playerboard.
|
||||||
|
|
||||||
|
✅ Goal Dimming for Uncollected Tiles - Goals that aren't collected yet appear dimmed (dark gray `Color(0.3, 0.3, 0.3, 1.0)`) in the playerboard center. Collected tiles display at full brightness.
|
||||||
|
|
||||||
|
✅ Goal Completion Rewards - After completing goals, all tiles on the playerboard are now cleared (`player.playerboard.fill(-1)`) and converted into one powerup bar. New random goals are regenerated via `regenerate_goals_for_player()`.
|
||||||
|
|
||||||
|
✅ Enhanced Message Bar - Upgraded `add_message_to_bar()` in `main.gd` with animations and styling. Added color-coded messages with emojis: ⚡ Powerup (green), 🎯 Goal (gold), ⏱️ Cycle (blue), ⚠️ Warning (orange), 💬 Normal (gray). Messages now have fade-in/out animations, slide-in entrance effects, and pulse animation for powerup messages.
|
||||||
|
|
||||||
|
✅ Powerup Bar Visual Effects - Added pulse animation to `ui_manager.gd` when powerup bar segments fill. The `_pulse_segment()` function scales the segment to 1.3x with a bounce effect.
|
||||||
|
|
||||||
|
✅ Powerup Bar Repositioning - Moved `PowerUpBar` to be directly above the playerboard in `main.tscn`. Changed label text from "POWER" to "⚡" emoji. Reduced segment size from 36x20 to 20x16 pixels for a more compact look.
|
||||||
|
|
||||||
|
✅ Playerboard Background Panel - Added `PlayerboardPanel` as a dark semi-transparent background (rgba 0.08, 0.08, 0.12, 0.9) with 8px rounded corners and subtle border. Created `StyleBoxFlat_playerboard` subresource for consistent styling.
|
||||||
|
|
||||||
|
✅ GUI Margin Consistency - Aligned all left-side UI elements with 10px margin from screen edge. PowerUpBar and PlayerboardUI are now vertically aligned within the background panel.
|
||||||
|
|
||||||
|
🚧 Cycle Duration - User manually changed `CYCLE_DURATION` from 60.0 to 30.0 seconds in `goals_cycle_manager.gd`.
|
||||||
+69
-11
@@ -62,41 +62,99 @@ func _init_managers():
|
|||||||
|
|
||||||
# Message Bar Configuration
|
# Message Bar Configuration
|
||||||
const MAX_MESSAGES := 5
|
const MAX_MESSAGES := 5
|
||||||
const MESSAGE_DURATION := 3.0
|
const MESSAGE_DURATION := 4.0
|
||||||
|
|
||||||
@onready var message_bar: PanelContainer = $MessageBar
|
@onready var message_bar: PanelContainer = $MessageBar
|
||||||
@onready var message_container: VBoxContainer = $MessageBar/MarginContainer/MessageContainer
|
@onready var message_container: VBoxContainer = $MessageBar/MarginContainer/MessageContainer
|
||||||
|
|
||||||
func add_message_to_bar(player_name: String, message: String):
|
# Message types for different styling
|
||||||
|
enum MessageType {NORMAL, POWERUP, GOAL, CYCLE, WARNING}
|
||||||
|
|
||||||
|
func add_message_to_bar(player_name: String, message: String, type: int = MessageType.NORMAL):
|
||||||
if not message_container:
|
if not message_container:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create message label
|
# Create message label with rich styling
|
||||||
var label = Label.new()
|
var label = Label.new()
|
||||||
label.text = "[%s] %s" % [player_name, message]
|
|
||||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
label.add_theme_color_override("font_color", Color.WHITE)
|
label.add_theme_font_size_override("font_size", 16)
|
||||||
label.add_theme_font_size_override("font_size", 14)
|
label.modulate.a = 0.0 # Start invisible for fade-in
|
||||||
|
|
||||||
|
# Style based on message type
|
||||||
|
var icon = ""
|
||||||
|
var color = Color.WHITE
|
||||||
|
match type:
|
||||||
|
MessageType.POWERUP:
|
||||||
|
icon = "⚡ "
|
||||||
|
color = Color(0.4, 1.0, 0.4) # Bright green
|
||||||
|
MessageType.GOAL:
|
||||||
|
icon = "🎯 "
|
||||||
|
color = Color(1.0, 0.85, 0.2) # Gold
|
||||||
|
MessageType.CYCLE:
|
||||||
|
icon = "⏱️ "
|
||||||
|
color = Color(0.4, 0.8, 1.0) # Light blue
|
||||||
|
MessageType.WARNING:
|
||||||
|
icon = "⚠️ "
|
||||||
|
color = Color(1.0, 0.5, 0.3) # Orange
|
||||||
|
_:
|
||||||
|
icon = "💬 "
|
||||||
|
color = Color(0.9, 0.9, 0.9) # Light gray
|
||||||
|
|
||||||
|
label.text = "%s%s" % [icon, message]
|
||||||
|
label.add_theme_color_override("font_color", color)
|
||||||
|
|
||||||
|
# Add shadow for better visibility
|
||||||
|
label.add_theme_constant_override("shadow_offset_x", 2)
|
||||||
|
label.add_theme_constant_override("shadow_offset_y", 2)
|
||||||
|
label.add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.7))
|
||||||
|
|
||||||
# Add to container
|
# Add to container
|
||||||
message_container.add_child(label)
|
message_container.add_child(label)
|
||||||
|
|
||||||
# Show the message bar
|
# Show the message bar with fade
|
||||||
message_bar.visible = true
|
if not message_bar.visible:
|
||||||
|
message_bar.visible = true
|
||||||
|
message_bar.modulate.a = 0.0
|
||||||
|
var bar_tween = create_tween()
|
||||||
|
bar_tween.tween_property(message_bar, "modulate:a", 1.0, 0.2)
|
||||||
|
|
||||||
|
# Animate label entrance (slide in + fade)
|
||||||
|
label.position.x = -50
|
||||||
|
var entrance_tween = create_tween()
|
||||||
|
entrance_tween.set_parallel(true)
|
||||||
|
entrance_tween.tween_property(label, "modulate:a", 1.0, 0.3)
|
||||||
|
entrance_tween.tween_property(label, "position:x", 0.0, 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||||
|
|
||||||
|
# Powerup gets extra pulse effect
|
||||||
|
if type == MessageType.POWERUP:
|
||||||
|
await entrance_tween.finished
|
||||||
|
var pulse_tween = create_tween()
|
||||||
|
pulse_tween.set_loops(2)
|
||||||
|
pulse_tween.tween_property(label, "scale", Vector2(1.1, 1.1), 0.15).set_trans(Tween.TRANS_SINE)
|
||||||
|
pulse_tween.tween_property(label, "scale", Vector2(1.0, 1.0), 0.15).set_trans(Tween.TRANS_SINE)
|
||||||
|
|
||||||
# Remove oldest messages if over limit
|
# Remove oldest messages if over limit
|
||||||
while message_container.get_child_count() > MAX_MESSAGES:
|
while message_container.get_child_count() > MAX_MESSAGES:
|
||||||
var oldest = message_container.get_child(0)
|
var oldest = message_container.get_child(0)
|
||||||
oldest.queue_free()
|
oldest.queue_free()
|
||||||
|
|
||||||
# Auto-remove after duration
|
# Auto-remove after duration with fade-out
|
||||||
await get_tree().create_timer(MESSAGE_DURATION).timeout
|
await get_tree().create_timer(MESSAGE_DURATION).timeout
|
||||||
if is_instance_valid(label):
|
if is_instance_valid(label):
|
||||||
label.queue_free()
|
var exit_tween = create_tween()
|
||||||
|
exit_tween.set_parallel(true)
|
||||||
|
exit_tween.tween_property(label, "modulate:a", 0.0, 0.3)
|
||||||
|
exit_tween.tween_property(label, "position:x", 50.0, 0.3)
|
||||||
|
await exit_tween.finished
|
||||||
|
if is_instance_valid(label):
|
||||||
|
label.queue_free()
|
||||||
|
|
||||||
# Hide bar when empty
|
# Hide bar when empty with fade
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
if message_container.get_child_count() == 0:
|
if message_container.get_child_count() == 0:
|
||||||
|
var hide_tween = create_tween()
|
||||||
|
hide_tween.tween_property(message_bar, "modulate:a", 0.0, 0.3)
|
||||||
|
await hide_tween.finished
|
||||||
message_bar.visible = false
|
message_bar.visible = false
|
||||||
|
|
||||||
@rpc("any_peer", "call_local")
|
@rpc("any_peer", "call_local")
|
||||||
|
|||||||
+46
-18
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=26 format=3 uid="uid://dxn87yj8qnfpp"]
|
[gd_scene load_steps=27 format=3 uid="uid://dxn87yj8qnfpp"]
|
||||||
|
|
||||||
[ext_resource type="MeshLibrary" uid="uid://kcv6ans86ug7" path="res://addons/enhanced_gridmap/meshlibrary/default.tres" id="1_110wo"]
|
[ext_resource type="MeshLibrary" uid="uid://kcv6ans86ug7" path="res://addons/enhanced_gridmap/meshlibrary/default.tres" id="1_110wo"]
|
||||||
[ext_resource type="Script" uid="uid://co1ads72by6na" path="res://scenes/main.gd" id="1_xcpe3"]
|
[ext_resource type="Script" uid="uid://co1ads72by6na" path="res://scenes/main.gd" id="1_xcpe3"]
|
||||||
@@ -30,6 +30,22 @@ texture = ExtResource("13_ahjgs")
|
|||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s1l63"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s1l63"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerboard"]
|
||||||
|
bg_color = Color(0.08, 0.08, 0.12, 0.9)
|
||||||
|
border_color = Color(0.3, 0.3, 0.4, 0.8)
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
corner_radius_top_left = 8
|
||||||
|
corner_radius_top_right = 8
|
||||||
|
corner_radius_bottom_right = 8
|
||||||
|
corner_radius_bottom_left = 8
|
||||||
|
content_margin_left = 8.0
|
||||||
|
content_margin_top = 8.0
|
||||||
|
content_margin_right = 8.0
|
||||||
|
content_margin_bottom = 8.0
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
script = ExtResource("1_xcpe3")
|
script = ExtResource("1_xcpe3")
|
||||||
|
|
||||||
@@ -88,15 +104,26 @@ text = "Unique Peer ID"
|
|||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="PlayerboardPanel" type="PanelContainer" parent="."]
|
||||||
|
anchors_preset = 4
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 10.0
|
||||||
|
offset_top = -120.0
|
||||||
|
offset_right = 216.0
|
||||||
|
offset_bottom = 120.0
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_playerboard")
|
||||||
|
|
||||||
[node name="PlayerboardUI" type="GridContainer" parent="."]
|
[node name="PlayerboardUI" type="GridContainer" parent="."]
|
||||||
clip_contents = true
|
clip_contents = true
|
||||||
anchors_preset = 4
|
anchors_preset = 4
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
anchor_bottom = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = 32.0
|
offset_left = 18.0
|
||||||
offset_top = -104.0
|
offset_top = -88.0
|
||||||
offset_right = 228.0
|
offset_right = 208.0
|
||||||
offset_bottom = 92.0
|
offset_bottom = 102.0
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
columns = 5
|
columns = 5
|
||||||
@@ -1065,6 +1092,7 @@ environment = ExtResource("4_ky38j")
|
|||||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||||
|
|
||||||
[node name="AllPlayerGoals" type="HBoxContainer" parent="."]
|
[node name="AllPlayerGoals" type="HBoxContainer" parent="."]
|
||||||
|
visible = false
|
||||||
y_sort_enabled = true
|
y_sort_enabled = true
|
||||||
clip_contents = true
|
clip_contents = true
|
||||||
layout_direction = 2
|
layout_direction = 2
|
||||||
@@ -9325,14 +9353,14 @@ layout_mode = 2
|
|||||||
theme_override_constants/separation = 4
|
theme_override_constants/separation = 4
|
||||||
|
|
||||||
[node name="PowerUpBar" type="PanelContainer" parent="."]
|
[node name="PowerUpBar" type="PanelContainer" parent="."]
|
||||||
anchors_preset = 5
|
anchors_preset = 4
|
||||||
anchor_left = 0.5
|
anchor_top = 0.5
|
||||||
anchor_right = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = -110.0
|
offset_left = 18.0
|
||||||
offset_top = 125.0
|
offset_top = -115.0
|
||||||
offset_right = 110.0
|
offset_right = 110.0
|
||||||
offset_bottom = 161.0
|
offset_bottom = -92.0
|
||||||
grow_horizontal = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="HBox" type="HBoxContainer" parent="PowerUpBar"]
|
[node name="HBox" type="HBoxContainer" parent="PowerUpBar"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -9340,23 +9368,23 @@ theme_override_constants/separation = 4
|
|||||||
|
|
||||||
[node name="PowerLabel" type="Label" parent="PowerUpBar/HBox"]
|
[node name="PowerLabel" type="Label" parent="PowerUpBar/HBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 14
|
theme_override_font_sizes/font_size = 18
|
||||||
text = "POWER "
|
text = "⚡"
|
||||||
|
|
||||||
[node name="Segment0" type="Panel" parent="PowerUpBar/HBox"]
|
[node name="Segment0" type="Panel" parent="PowerUpBar/HBox"]
|
||||||
custom_minimum_size = Vector2(36, 20)
|
custom_minimum_size = Vector2(20, 16)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Segment1" type="Panel" parent="PowerUpBar/HBox"]
|
[node name="Segment1" type="Panel" parent="PowerUpBar/HBox"]
|
||||||
custom_minimum_size = Vector2(36, 20)
|
custom_minimum_size = Vector2(20, 16)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Segment2" type="Panel" parent="PowerUpBar/HBox"]
|
[node name="Segment2" type="Panel" parent="PowerUpBar/HBox"]
|
||||||
custom_minimum_size = Vector2(36, 20)
|
custom_minimum_size = Vector2(20, 16)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Segment3" type="Panel" parent="PowerUpBar/HBox"]
|
[node name="Segment3" type="Panel" parent="PowerUpBar/HBox"]
|
||||||
custom_minimum_size = Vector2(36, 20)
|
custom_minimum_size = Vector2(20, 16)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="LeaderboardPanel" type="PanelContainer" parent="."]
|
[node name="LeaderboardPanel" type="PanelContainer" parent="."]
|
||||||
|
|||||||
+2
-2
@@ -675,12 +675,12 @@ func remote_set_position(authority_position):
|
|||||||
global_position = authority_position
|
global_position = authority_position
|
||||||
|
|
||||||
@rpc("any_peer", "call_local")
|
@rpc("any_peer", "call_local")
|
||||||
func display_message(message):
|
func display_message(message, type: int = 0):
|
||||||
# Send message to the main scene's message bar instead of player bubble
|
# Send message to the main scene's message bar instead of player bubble
|
||||||
var main = get_tree().get_root().get_node_or_null("Main")
|
var main = get_tree().get_root().get_node_or_null("Main")
|
||||||
if main and main.has_method("add_message_to_bar"):
|
if main and main.has_method("add_message_to_bar"):
|
||||||
var player_name = $Name.text.split("\n")[0] if $Name else str(name)
|
var player_name = $Name.text.split("\n")[0] if $Name else str(name)
|
||||||
main.add_message_to_bar(player_name, message)
|
main.add_message_to_bar(player_name, message, type)
|
||||||
|
|
||||||
func initialize_random_goals(_size: int, min_value: int, max_value: int, null_count: float) -> Array[int]:
|
func initialize_random_goals(_size: int, min_value: int, max_value: int, null_count: float) -> Array[int]:
|
||||||
goals.clear()
|
goals.clear()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ extends Node
|
|||||||
|
|
||||||
# GoalsCycleManager - Handles 60-second goal cycles, scoring, and goal regeneration
|
# GoalsCycleManager - Handles 60-second goal cycles, scoring, and goal regeneration
|
||||||
|
|
||||||
const CYCLE_DURATION: float = 60.0
|
const CYCLE_DURATION: float = 30.0
|
||||||
const BASE_SCORE: int = 100
|
const BASE_SCORE: int = 100
|
||||||
const TIME_BONUS_MULTIPLIER: float = 2.0
|
const TIME_BONUS_MULTIPLIER: float = 2.0
|
||||||
|
|
||||||
@@ -126,6 +126,10 @@ func on_goal_completed(player: Node, time_remaining: float):
|
|||||||
# Sync score to all clients
|
# Sync score to all clients
|
||||||
rpc("sync_player_score", peer_id, player_scores[peer_id])
|
rpc("sync_player_score", peer_id, player_scores[peer_id])
|
||||||
|
|
||||||
|
# Clear playerboard tiles (they convert to powerup bar reward)
|
||||||
|
player.playerboard.fill(-1)
|
||||||
|
player.rpc("sync_playerboard", player.playerboard)
|
||||||
|
|
||||||
# Regenerate goals for this player
|
# Regenerate goals for this player
|
||||||
regenerate_goals_for_player(player)
|
regenerate_goals_for_player(player)
|
||||||
|
|
||||||
|
|||||||
@@ -21,22 +21,17 @@ func consume_action_points(points: int):
|
|||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
if not main:
|
if not main:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Don't consume points for bots in non-turn-based mode
|
# Non-turn-based mode: unlimited action points for real-time fast-paced gameplay
|
||||||
if player.is_bot == true and not TurnManager.turn_based_mode:
|
if not TurnManager.turn_based_mode:
|
||||||
after_action_completed()
|
after_action_completed()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Turn-based mode: consume action points normally
|
||||||
player.action_points -= points
|
player.action_points -= points
|
||||||
|
|
||||||
if player.action_points <= 0:
|
if player.action_points <= 0:
|
||||||
if TurnManager.turn_based_mode:
|
main.request_next_turn()
|
||||||
main.request_next_turn()
|
|
||||||
else:
|
|
||||||
player.action_points = 2
|
|
||||||
player.has_performed_action = false
|
|
||||||
player.has_moved_this_turn = false
|
|
||||||
player.rpc("display_message", "Action Points Reset!")
|
|
||||||
|
|
||||||
after_action_completed()
|
after_action_completed()
|
||||||
|
|
||||||
@@ -55,12 +50,6 @@ func after_action_completed():
|
|||||||
if multiplayer.get_unique_id() == player.get_multiplayer_authority():
|
if multiplayer.get_unique_id() == player.get_multiplayer_authority():
|
||||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||||
if main:
|
if main:
|
||||||
# Add this condition for bots
|
|
||||||
if not TurnManager.turn_based_mode and (player.action_points <= 0 or player.is_bot):
|
|
||||||
player.action_points = 20 # For bots in non-turn-based mode, this will keep refreshing
|
|
||||||
player.has_performed_action = false
|
|
||||||
player.has_moved_this_turn = false
|
|
||||||
|
|
||||||
main.ui_manager.update_button_states()
|
main.ui_manager.update_button_states()
|
||||||
main.ui_manager.update_playerboard_ui()
|
main.ui_manager.update_playerboard_ui()
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ func _add_bar():
|
|||||||
emit_signal("bar_filled")
|
emit_signal("bar_filled")
|
||||||
emit_signal("points_changed", current_points, MAX_POINTS)
|
emit_signal("points_changed", current_points, MAX_POINTS)
|
||||||
|
|
||||||
player.rpc("display_message", "Power-up bar filled!")
|
# Type 1 = POWERUP message for special styling
|
||||||
|
player.rpc("display_message", "Power-up bar filled!", 1)
|
||||||
print("[PowerUp] Player %s gained 1 bar! Total: %d/%d points" % [player.name, current_points, MAX_POINTS])
|
print("[PowerUp] Player %s gained 1 bar! Total: %d/%d points" % [player.name, current_points, MAX_POINTS])
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -74,7 +75,8 @@ func get_bars() -> int:
|
|||||||
func use_special_effect():
|
func use_special_effect():
|
||||||
"""Consume 1 bar and trigger a random special effect."""
|
"""Consume 1 bar and trigger a random special effect."""
|
||||||
if not can_use_special():
|
if not can_use_special():
|
||||||
player.rpc("display_message", "Not enough power-up!")
|
# Type 3 = WARNING message
|
||||||
|
player.rpc("display_message", "Not enough power-up!", 3)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Consume 1 bar
|
# Consume 1 bar
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ func update_playerboard_ui():
|
|||||||
if not local_player_character:
|
if not local_player_character:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Center 3x3 slot indices in a 5x5 grid (0-indexed)
|
||||||
|
# Row 1: 6, 7, 8
|
||||||
|
# Row 2: 11, 12, 13
|
||||||
|
# Row 3: 16, 17, 18
|
||||||
|
var center_slots = [6, 7, 8, 11, 12, 13, 16, 17, 18]
|
||||||
|
var goals = local_player_character.goals if local_player_character.goals else []
|
||||||
|
|
||||||
for i in range(25):
|
for i in range(25):
|
||||||
var slot = playerboard_ui.get_child(i)
|
var slot = playerboard_ui.get_child(i)
|
||||||
|
|
||||||
@@ -106,13 +113,43 @@ func update_playerboard_ui():
|
|||||||
|
|
||||||
var item = local_player_character.playerboard[i]
|
var item = local_player_character.playerboard[i]
|
||||||
|
|
||||||
|
# Default texture (empty)
|
||||||
slot.texture = item_tex[0]
|
slot.texture = item_tex[0]
|
||||||
|
slot.modulate = Color.WHITE
|
||||||
|
|
||||||
match item:
|
# Check if this is a center slot that should show a goal
|
||||||
7: slot.texture = item_tex[1]
|
var center_index = center_slots.find(i)
|
||||||
8: slot.texture = item_tex[2]
|
if center_index != -1 and center_index < goals.size():
|
||||||
9: slot.texture = item_tex[3]
|
var goal_value = goals[center_index]
|
||||||
10: slot.texture = item_tex[4]
|
|
||||||
|
# Check if player has collected this tile
|
||||||
|
var _has_tile = item == goal_value
|
||||||
|
|
||||||
|
if item != -1:
|
||||||
|
# Player has a tile in this slot - show it at full brightness
|
||||||
|
match item:
|
||||||
|
7: slot.texture = item_tex[1]
|
||||||
|
8: slot.texture = item_tex[2]
|
||||||
|
9: slot.texture = item_tex[3]
|
||||||
|
10: slot.texture = item_tex[4]
|
||||||
|
slot.modulate = Color.WHITE
|
||||||
|
else:
|
||||||
|
# Show goal tile dimmed (not collected yet)
|
||||||
|
match goal_value:
|
||||||
|
7: slot.texture = item_tex[1]
|
||||||
|
8: slot.texture = item_tex[2]
|
||||||
|
9: slot.texture = item_tex[3]
|
||||||
|
10: slot.texture = item_tex[4]
|
||||||
|
_: slot.texture = item_tex[0]
|
||||||
|
# Dim uncollected goals with black overlay
|
||||||
|
slot.modulate = Color(0.3, 0.3, 0.3, 1.0)
|
||||||
|
else:
|
||||||
|
# Non-center slot - just show playerboard item normally
|
||||||
|
match item:
|
||||||
|
7: slot.texture = item_tex[1]
|
||||||
|
8: slot.texture = item_tex[2]
|
||||||
|
9: slot.texture = item_tex[3]
|
||||||
|
10: slot.texture = item_tex[4]
|
||||||
|
|
||||||
func update_button_states():
|
func update_button_states():
|
||||||
if not local_player_character or local_player_character.is_in_group("Bots"):
|
if not local_player_character or local_player_character.is_in_group("Bots"):
|
||||||
@@ -184,7 +221,7 @@ func setup_powerup_bar_ui(main_node):
|
|||||||
segment.add_theme_stylebox_override("panel", style)
|
segment.add_theme_stylebox_override("panel", style)
|
||||||
powerup_segments.append(segment)
|
powerup_segments.append(segment)
|
||||||
|
|
||||||
func update_powerup_bar(current_points: int, max_points: int):
|
func update_powerup_bar(current_points: int, _max_points: int):
|
||||||
"""Update battery segments based on current power-up points."""
|
"""Update battery segments based on current power-up points."""
|
||||||
var bars_filled = current_points / 4 # 4 points per bar
|
var bars_filled = current_points / 4 # 4 points per bar
|
||||||
|
|
||||||
@@ -201,9 +238,30 @@ func update_powerup_bar(current_points: int, max_points: int):
|
|||||||
|
|
||||||
segment.add_theme_stylebox_override("panel", style)
|
segment.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
var _previous_bars: int = 0
|
||||||
|
|
||||||
func _on_powerup_points_changed(current: int, max_points: int):
|
func _on_powerup_points_changed(current: int, max_points: int):
|
||||||
|
var new_bars = current / 4
|
||||||
|
|
||||||
|
# Detect if a new bar was filled
|
||||||
|
if new_bars > _previous_bars and powerup_bar:
|
||||||
|
# Pulse effect on newly filled segment
|
||||||
|
var segment_index = new_bars - 1
|
||||||
|
if segment_index >= 0 and segment_index < powerup_segments.size():
|
||||||
|
var segment = powerup_segments[segment_index]
|
||||||
|
_pulse_segment(segment)
|
||||||
|
|
||||||
|
_previous_bars = new_bars
|
||||||
update_powerup_bar(current, max_points)
|
update_powerup_bar(current, max_points)
|
||||||
|
|
||||||
|
func _pulse_segment(segment: Panel):
|
||||||
|
"""Create a visual pulse effect on a powerup segment."""
|
||||||
|
var original_scale = segment.scale
|
||||||
|
var tween = segment.create_tween()
|
||||||
|
tween.set_loops(2)
|
||||||
|
tween.tween_property(segment, "scale", Vector2(1.3, 1.3), 0.1).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||||
|
tween.tween_property(segment, "scale", original_scale, 0.15).set_trans(Tween.TRANS_SINE)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Leaderboard UI
|
# Leaderboard UI
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user