feat: update
This commit is contained in:
@@ -55,6 +55,11 @@ var hud_points_label: Label
|
||||
var hud_delivery_indicator: Label
|
||||
var _candy_survival_hud_scene: PackedScene = preload("res://scenes/candy_survival_hud.tscn")
|
||||
|
||||
# Cache for local client HUD resync (avoids local dictionary/timer desyncs)
|
||||
var _last_badge_count: int = 0
|
||||
var _last_badge_mult: float = 1.0
|
||||
var _last_badge_face_match: bool = false
|
||||
|
||||
func _ready():
|
||||
set_process(false)
|
||||
_setup_hud()
|
||||
@@ -87,7 +92,14 @@ func activate_client_side() -> void:
|
||||
|
||||
# Per-player state
|
||||
var player_candies: Dictionary = {} # pid -> int (stack count)
|
||||
var player_candy_color: Dictionary = {} # pid -> CandyColor (-1 if none)
|
||||
var player_candy_colors: Dictionary = {} # pid -> Array of CandyColor
|
||||
var player_candy_color: Dictionary:
|
||||
get:
|
||||
var d = {}
|
||||
for pid in player_candy_colors:
|
||||
var colors = player_candy_colors[pid]
|
||||
d[pid] = colors.back() if colors.size() > 0 else -1
|
||||
return d
|
||||
var player_knocks: Dictionary = {} # pid -> int (charges left)
|
||||
var player_ghosts: Dictionary = {} # pid -> int (charges left)
|
||||
var player_ghost_active: Dictionary = {} # pid -> bool
|
||||
@@ -132,7 +144,7 @@ func start_game_mode() -> void:
|
||||
face_timer = 0.0
|
||||
game_elapsed = 0.0
|
||||
player_candies.clear()
|
||||
player_candy_color.clear()
|
||||
player_candy_colors.clear()
|
||||
player_knocks.clear()
|
||||
player_ghosts.clear()
|
||||
player_ghost_active.clear()
|
||||
@@ -146,7 +158,7 @@ func start_game_mode() -> void:
|
||||
var pids = _get_player_ids()
|
||||
for pid in pids:
|
||||
player_candies[pid] = 0
|
||||
player_candy_color[pid] = -1
|
||||
player_candy_colors[pid] = []
|
||||
player_knocks[pid] = START_KNOCK
|
||||
player_ghosts[pid] = START_GHOST
|
||||
player_ghost_active[pid] = false
|
||||
@@ -164,6 +176,8 @@ func _enter_tree() -> void:
|
||||
func _process(delta: float) -> void:
|
||||
if not active:
|
||||
return
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
game_elapsed += delta
|
||||
|
||||
# Mekton face color cycle
|
||||
@@ -227,18 +241,45 @@ func _process(delta: float) -> void:
|
||||
# ── Blueprint ──
|
||||
|
||||
func on_blueprint_completed(pid: int, primary_tile_id: int, off_color: bool) -> void:
|
||||
var base_points = 1000
|
||||
var points = base_points if not off_color else int(base_points * OFF_COLOR_PENALTY)
|
||||
_add_score(pid, points)
|
||||
player_blueprints[pid] = player_blueprints.get(pid, 0) + 1
|
||||
|
||||
var target_tile_id = primary_tile_id
|
||||
var player_node: Node = null
|
||||
if main_scene:
|
||||
player_node = main_scene.get_node_or_null(str(pid))
|
||||
if player_node and player_node.goals.size() > 0:
|
||||
target_tile_id = player_node.goals[0]
|
||||
|
||||
var candy_color = CandyColor.HEART
|
||||
for key in TILE_IDS:
|
||||
if TILE_IDS[key] == primary_tile_id:
|
||||
if TILE_IDS[key] == target_tile_id:
|
||||
candy_color = key
|
||||
break
|
||||
|
||||
_give_candy(pid, candy_color)
|
||||
|
||||
# Clear the board now that it's matched and converted to a candy
|
||||
if main_scene and player_node:
|
||||
if player_node.playerboard_manager:
|
||||
# Visual wipe
|
||||
player_node.playerboard.fill(-1)
|
||||
main_scene.rpc("sync_playerboard", pid, player_node.playerboard)
|
||||
|
||||
# Record goal completion stats in GoalsCycleManager
|
||||
var goals_cycle = main_scene.get_node_or_null("GoalsCycleManager")
|
||||
if goals_cycle:
|
||||
# Increment goal count (but DO NOT trigger the general score points
|
||||
# because in Candy Survival, points are awarded during Mekton deposit)
|
||||
if not goals_cycle.player_goal_counts.has(pid):
|
||||
goals_cycle.player_goal_counts[pid] = 0
|
||||
goals_cycle.player_goal_counts[pid] += 1
|
||||
goals_cycle.emit_signal("goal_count_updated", pid, goals_cycle.player_goal_counts[pid])
|
||||
goals_cycle.rpc("sync_goal_count", pid, goals_cycle.player_goal_counts[pid])
|
||||
|
||||
# Generate new goals immediately to keep player active
|
||||
goals_cycle.regenerate_goals_for_player(player_node)
|
||||
# Need to randomize tiles around player to refresh pickups for the new goal
|
||||
goals_cycle._randomize_tiles_around_player(player_node)
|
||||
|
||||
func can_finish_with_off_color(pid: int, primary_tile_id: int) -> bool:
|
||||
return not _grid_has_color_tiles(primary_tile_id)
|
||||
@@ -257,13 +298,16 @@ func _grid_has_color_tiles(target_tile_id: int) -> bool:
|
||||
|
||||
func _give_candy(pid: int, color: int) -> void:
|
||||
player_candies[pid] = player_candies.get(pid, 0) + 1
|
||||
player_candy_color[pid] = color
|
||||
if not player_candy_colors.has(pid):
|
||||
player_candy_colors[pid] = []
|
||||
player_candy_colors[pid].append(color)
|
||||
_update_candy_badge(pid)
|
||||
|
||||
func _update_candy_badge(pid: int) -> void:
|
||||
var count = player_candies.get(pid, 0)
|
||||
var mult = 1.0 + count * MULTI_STEP
|
||||
var color = player_candy_color.get(pid, -1)
|
||||
var colors = player_candy_colors.get(pid, [])
|
||||
var color = colors.back() if colors.size() > 0 else -1
|
||||
var face_match = (color != -1 and color == current_face)
|
||||
rpc("sync_candy_badge", pid, count, mult, face_match)
|
||||
|
||||
@@ -273,7 +317,7 @@ func _update_candy_badge(pid: int) -> void:
|
||||
var curr_pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
|
||||
if curr_pid == pid:
|
||||
if player.has_method("sync_candy_stack"):
|
||||
player.rpc("sync_candy_stack", count, color)
|
||||
player.rpc("sync_candy_stack", colors)
|
||||
break
|
||||
|
||||
func get_multiplier(pid: int) -> float:
|
||||
@@ -287,21 +331,28 @@ func try_deliver(pid: int) -> bool:
|
||||
if not multiplayer.is_server():
|
||||
return false
|
||||
|
||||
var color = player_candy_color.get(pid, -1)
|
||||
if color == -1 or player_candies.get(pid, 0) == 0:
|
||||
var colors = player_candy_colors.get(pid, [])
|
||||
var color = colors.back() if colors.size() > 0 else -1
|
||||
if color == -1 or colors.size() == 0:
|
||||
rpc("sync_delivery_result", pid, false, "No candy")
|
||||
return false
|
||||
if color != current_face:
|
||||
rpc("sync_delivery_result", pid, false, "Wrong color")
|
||||
return false
|
||||
|
||||
var count = player_candies[pid]
|
||||
var count = colors.size()
|
||||
var mult = get_multiplier(pid)
|
||||
_add_score(pid, count * 500)
|
||||
|
||||
var base_points = 1000 * count
|
||||
_add_score(pid, int(base_points * mult))
|
||||
|
||||
player_candies[pid] = 0
|
||||
player_candy_color[pid] = -1
|
||||
player_candy_colors[pid] = []
|
||||
_update_candy_badge(pid)
|
||||
|
||||
|
||||
_trigger_sugar_rush(pid, count, mult)
|
||||
|
||||
rpc("sync_delivery_result", pid, true, "Delivered!")
|
||||
return true
|
||||
|
||||
func _trigger_sugar_rush(pid: int, candies: int, mult: float) -> void:
|
||||
@@ -330,7 +381,8 @@ func try_knock(attacker: int, target: int) -> bool:
|
||||
if player_knocks.get(attacker, 0) <= 0:
|
||||
return false
|
||||
|
||||
var target_candies = player_candies.get(target, 0)
|
||||
var target_colors = player_candy_colors.get(target, [])
|
||||
var target_candies = target_colors.size()
|
||||
|
||||
if target_candies == 0:
|
||||
# Backfire: both lose a charge
|
||||
@@ -340,10 +392,13 @@ func try_knock(attacker: int, target: int) -> bool:
|
||||
return false
|
||||
|
||||
# Steal all candies
|
||||
player_candies[attacker] = player_candies.get(attacker, 0) + target_candies
|
||||
if not player_candy_colors.has(attacker):
|
||||
player_candy_colors[attacker] = []
|
||||
player_candy_colors[attacker].append_array(target_colors)
|
||||
player_candy_colors[target] = []
|
||||
|
||||
player_candies[attacker] = player_candy_colors[attacker].size()
|
||||
player_candies[target] = 0
|
||||
player_candy_color[target] = -1
|
||||
player_candy_color[attacker] = player_candy_color.get(target, -1)
|
||||
|
||||
player_knocks[attacker] = player_knocks.get(attacker, 0) - 1
|
||||
player_last_knocked_by[target] = attacker
|
||||
@@ -355,6 +410,7 @@ func try_knock(attacker: int, target: int) -> bool:
|
||||
rpc("sync_knock_result", attacker, target, target_candies)
|
||||
return true
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func try_activate_ghost(pid: int) -> bool:
|
||||
if player_ghost_active.get(pid, false):
|
||||
return false
|
||||
@@ -491,6 +547,10 @@ func sync_candy_badge(pid: int, count: int, mult: float, face_match: bool) -> vo
|
||||
if not active:
|
||||
activate_client_side()
|
||||
if pid == multiplayer.get_unique_id():
|
||||
_last_badge_count = count
|
||||
_last_badge_mult = mult
|
||||
_last_badge_face_match = face_match
|
||||
|
||||
if hud_stack_badge:
|
||||
hud_stack_badge.text = "Stack: %d (x%.1f)" % [count, mult]
|
||||
if hud_delivery_indicator:
|
||||
@@ -504,6 +564,39 @@ func sync_candy_badge(pid: int, count: int, mult: float, face_match: bool) -> vo
|
||||
else:
|
||||
hud_delivery_indicator.text = ""
|
||||
|
||||
@rpc("authority", "call_remote", "unreliable")
|
||||
func sync_delivery_result(pid: int, success: bool, msg: String) -> void:
|
||||
if not active:
|
||||
activate_client_side()
|
||||
if pid != multiplayer.get_unique_id():
|
||||
return
|
||||
if hud_delivery_indicator:
|
||||
if success:
|
||||
hud_delivery_indicator.text = "✓ Delivered! +Sugar Rush!"
|
||||
hud_delivery_indicator.add_theme_color_override("font_color", Color(0.3, 1.0, 0.3))
|
||||
else:
|
||||
hud_delivery_indicator.text = "✗ " + msg
|
||||
hud_delivery_indicator.add_theme_color_override("font_color", Color(1.0, 0.3, 0.3))
|
||||
# Flash and auto-clear after 1.5s
|
||||
var tween = create_tween()
|
||||
tween.tween_interval(1.5)
|
||||
tween.tween_callback(_restore_delivery_indicator)
|
||||
|
||||
func _restore_delivery_indicator() -> void:
|
||||
if not active or not hud_delivery_indicator:
|
||||
return
|
||||
var count = _last_badge_count
|
||||
var face_match = _last_badge_face_match
|
||||
if count > 0:
|
||||
if face_match:
|
||||
hud_delivery_indicator.text = "READY TO DELIVER!"
|
||||
hud_delivery_indicator.add_theme_color_override("font_color", Color(0.4, 1.0, 0.4))
|
||||
else:
|
||||
hud_delivery_indicator.text = "Waiting for match..."
|
||||
hud_delivery_indicator.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4))
|
||||
else:
|
||||
hud_delivery_indicator.text = ""
|
||||
|
||||
@rpc("authority", "call_remote", "unreliable")
|
||||
func sync_knock_result(attacker: int, target: int, candies: int) -> void:
|
||||
if not active:
|
||||
|
||||
@@ -215,6 +215,9 @@ func sync_cycle_end():
|
||||
func on_goal_completed(player: Node, time_remaining: float):
|
||||
"""Called when a player completes their goal pattern."""
|
||||
|
||||
if LobbyManager.is_game_mode(GameMode.Mode.CANDY_SURVIVAL):
|
||||
return # Candy Survival goal generation / completion handled separately by mekton deposit
|
||||
|
||||
# CLIENT PATH: clear board immediately for visual responsiveness,
|
||||
# then let server send back the single authoritative new goals.
|
||||
# Do NOT generate goals locally — that caused rollback/blinking.
|
||||
|
||||
@@ -127,11 +127,24 @@ func handle_unhandled_input(event):
|
||||
player.enter_attack_mode()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
elif event.is_action_pressed("action_interact"):
|
||||
if LobbyManager.game_mode == "Candy Survival":
|
||||
var gm = player.get_node_or_null("/root/Main/CandySurvivalManager")
|
||||
if gm and gm.active:
|
||||
var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
|
||||
if multiplayer.is_server():
|
||||
gm.try_deliver(pid)
|
||||
else:
|
||||
gm.rpc_id(1, "try_deliver", pid)
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
||||
elif event.is_action_pressed("action_grab_tekton"):
|
||||
if LobbyManager.game_mode == "Candy Survival":
|
||||
var gm = player.get_node_or_null("/root/Main/CandySurvivalManager")
|
||||
if gm and gm.active:
|
||||
var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
|
||||
# In Candy Survival, action_grab_tekton activates Ghost mode
|
||||
if multiplayer.is_server():
|
||||
gm.try_activate_ghost(pid)
|
||||
else:
|
||||
|
||||
@@ -168,12 +168,8 @@ func simple_move_to(grid_position: Vector2i) -> bool:
|
||||
if gm and gm.active and gm.has_method("try_deliver"):
|
||||
var dist = abs(grid_position.x - 8) + abs(grid_position.y - 8)
|
||||
if dist <= 2:
|
||||
var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
|
||||
if multiplayer.is_server():
|
||||
gm.try_deliver(pid)
|
||||
else:
|
||||
gm.rpc_id(1, "try_deliver", pid)
|
||||
|
||||
pass # Delivery must be triggered by 'action_interact' input
|
||||
|
||||
rotate_towards_target(grid_position)
|
||||
|
||||
rotate_towards_target(grid_position)
|
||||
@@ -214,7 +210,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
||||
# === INVULNERABILITY CHECK ===
|
||||
if other_player.get("is_carrying_tekton"):
|
||||
print("[Move] Push blocked: Target is carrying a Tekton and is invulnerable.")
|
||||
Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Target is Immune!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING)
|
||||
NotificationManager.send_message(player, "Target is Immune!", NotificationManager.MessageType.WARNING)
|
||||
return false
|
||||
|
||||
# Candy Survival: check if attacker has knock charges
|
||||
@@ -241,13 +237,13 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
||||
# 1. Prevent attacker from attacking IF THEY ARE in a Safe Zone
|
||||
if player.current_position.x in safe_columns:
|
||||
print(" - Attack BLOCKED: Attacker is in Safe Zone!")
|
||||
Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Cannot Attack while in Safe Zone!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING)
|
||||
NotificationManager.send_message(player, "Cannot Attack while in Safe Zone!", NotificationManager.MessageType.WARNING)
|
||||
return false
|
||||
|
||||
# 2. Prevent attacking players WHO ARE in a Safe Zone (existing logic)
|
||||
if target_pos.x in safe_columns:
|
||||
print(" - Attack BLOCKED: Target is in Safe Zone!")
|
||||
Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Target is in Safe Zone!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING)
|
||||
NotificationManager.send_message(player, "Target is in Safe Zone!", NotificationManager.MessageType.WARNING)
|
||||
return false
|
||||
|
||||
|
||||
@@ -359,9 +355,9 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
||||
else:
|
||||
# Client: Request score add (sender ID used)
|
||||
gcm.rpc("request_add_score", 200)
|
||||
Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Successful Attack! +200 Pts", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.GOAL)
|
||||
NotificationManager.send_message(player, "Successful Attack! +200 Pts", NotificationManager.MessageType.GOAL)
|
||||
else:
|
||||
Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Successful Attack!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.GOAL)
|
||||
NotificationManager.send_message(player, "Successful Attack!", NotificationManager.MessageType.GOAL)
|
||||
|
||||
# 5. Block the attacker from moving into the victim's space to prevent overlapping
|
||||
return false
|
||||
@@ -382,8 +378,20 @@ func _on_movement_finished():
|
||||
if Engine.get_main_loop().root.get_node_or_null("LobbyManager").game_mode == "Candy Survival":
|
||||
if player.has_method("grab_item"):
|
||||
# Check if there is an item at current_position and if the board has space
|
||||
if player.playerboard_manager and player.playerboard_manager.has_empty_slot():
|
||||
player.grab_item(player.current_position)
|
||||
var current_cell = Vector3i(player.current_position.x, 1, player.current_position.y)
|
||||
var item = player.playerboard_manager.enhanced_gridmap.get_cell_item(current_cell) if player.playerboard_manager and player.playerboard_manager.enhanced_gridmap else -1
|
||||
|
||||
if item != -1:
|
||||
# Normalize item to match goals logic
|
||||
var normalized_item = player.playerboard_manager._normalize_tile(item)
|
||||
if normalized_item in player.goals:
|
||||
var empty_slot = -1
|
||||
for i in range(player.playerboard.size()):
|
||||
if player.playerboard[i] == -1 and not (i in player.playerboard_manager.HIDDEN_SLOTS):
|
||||
empty_slot = i
|
||||
break
|
||||
if player.playerboard_manager and empty_slot != -1:
|
||||
player.grab_item(player.current_position)
|
||||
|
||||
if not movement_queue.is_empty():
|
||||
var next_target = movement_queue.pop_front()
|
||||
|
||||
@@ -526,6 +526,8 @@ func find_best_goal_slot_for_item(item: int) -> int:
|
||||
# Goal slots are row 2-3, col 1-3 (indices: 11,12,13, 16,17,18) matching User request
|
||||
# Rows 1 (6,7,8) are now storage slots
|
||||
var reserved_goal_slots = [11, 12, 13, 16, 17, 18]
|
||||
if LobbyManager.game_mode == "Candy Survival":
|
||||
reserved_goal_slots = []
|
||||
|
||||
for i in range(player.playerboard.size()):
|
||||
if player.playerboard[i] == -1:
|
||||
@@ -733,10 +735,12 @@ func _check_goal_completion():
|
||||
if not player.race_manager:
|
||||
return
|
||||
|
||||
# Note: We still trigger blueprint completion (giving Candies to the player stack)
|
||||
var is_match = false
|
||||
var is_off_color = false
|
||||
var primary_color = -1
|
||||
|
||||
var original_is_match = false
|
||||
|
||||
if LobbyManager.game_mode == "Candy Survival":
|
||||
# Custom Candy Survival blueprint check (can be off-color)
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
@@ -766,22 +770,27 @@ func _check_goal_completion():
|
||||
primary_color = c
|
||||
|
||||
is_off_color = (max_count < 9)
|
||||
if is_off_color and gm and gm.has_method("can_finish_with_off_color"):
|
||||
if not gm.can_finish_with_off_color(player.name.to_int(), primary_color):
|
||||
is_match = false # Reject if primary color is still available on grid
|
||||
|
||||
if is_match:
|
||||
break
|
||||
if is_match:
|
||||
original_is_match = true
|
||||
break
|
||||
|
||||
if is_match and gm and gm.has_method("on_blueprint_completed"):
|
||||
# In Candy Survival, the visual goal check does not trigger completion.
|
||||
# We only give candies into the stack. Goals are cleared & scored
|
||||
# when delivered to the Mekton.
|
||||
gm.on_blueprint_completed(player.name.to_int(), primary_color, is_off_color)
|
||||
is_match = false # Force false so we don't trigger the regular free-mode goal completion
|
||||
else:
|
||||
is_match = player.race_manager.check_pattern_match()
|
||||
|
||||
if is_match:
|
||||
print("[PlayerboardManager] Goal completed for player %s!" % player.name)
|
||||
if is_match or (original_is_match and LobbyManager.game_mode == "Candy Survival"):
|
||||
if not (LobbyManager.game_mode == "Candy Survival"):
|
||||
print("[PlayerboardManager] Goal completed for player %s!" % player.name)
|
||||
else:
|
||||
print("[PlayerboardManager] Blueprint completed for player %s! Generating candy." % player.name)
|
||||
|
||||
var powerup_manager = player.get_node_or_null("PowerUpManager")
|
||||
if powerup_manager:
|
||||
@@ -791,12 +800,13 @@ func _check_goal_completion():
|
||||
if player.is_multiplayer_authority() and player.has_method("trigger_screen_shake"):
|
||||
player.trigger_screen_shake("goal")
|
||||
|
||||
# Notify GoalsCycleManager for scoring
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
if main:
|
||||
var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager")
|
||||
if goals_cycle_manager:
|
||||
goals_cycle_manager.on_goal_completed(player, goals_cycle_manager.get_time_remaining())
|
||||
# Notify GoalsCycleManager for scoring (only if not Candy Survival)
|
||||
if not (LobbyManager.game_mode == "Candy Survival"):
|
||||
var main = player.get_tree().get_root().get_node_or_null("Main")
|
||||
if main:
|
||||
var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager")
|
||||
if goals_cycle_manager:
|
||||
goals_cycle_manager.on_goal_completed(player, goals_cycle_manager.get_time_remaining())
|
||||
else:
|
||||
# Fallback if manager not initialized yet
|
||||
NotificationManager.send_message(player, NotificationManager.MESSAGES.GOAL_COMPLETED, NotificationManager.MessageType.GOAL)
|
||||
|
||||
+163
-129
@@ -28,15 +28,15 @@ var local_player_character
|
||||
var _previous_playerboard_state: Array = []
|
||||
|
||||
|
||||
func initialize(player_node):
|
||||
func initialize(player_node):
|
||||
# Get PowerUp Inventory UI from scene
|
||||
powerup_inventory_ui = player_node.get_node_or_null("TouchLayer/TouchControls/PowerUpInventoryUI")
|
||||
|
||||
|
||||
# Get node references from main scene
|
||||
playerboard_ui = player_node.get_node_or_null("PlayerBoardUI/PlayerboardUI")
|
||||
if not playerboard_ui:
|
||||
playerboard_ui = player_node.get_node_or_null("PlayerboardUI")
|
||||
|
||||
|
||||
# Connect PlayerName label — now lives under PlayerBoardUI
|
||||
player_name_label = player_node.get_node_or_null("PlayerBoardUI/PlayerName")
|
||||
if not player_name_label:
|
||||
@@ -45,10 +45,10 @@ func initialize(player_node):
|
||||
|
||||
func set_local_player(player):
|
||||
local_player_character = player
|
||||
|
||||
|
||||
if powerup_inventory_ui:
|
||||
powerup_inventory_ui.setup(player)
|
||||
|
||||
|
||||
# Connect to powerup signals with deferred call (manager needs time to initialize)
|
||||
_connect_powerup_manager_deferred(player)
|
||||
|
||||
@@ -64,37 +64,37 @@ func setup_playerboard_ui():
|
||||
return
|
||||
for child in playerboard_ui.get_children():
|
||||
child.queue_free()
|
||||
|
||||
|
||||
playerboard_ui.columns = 5
|
||||
|
||||
|
||||
for i in range(25):
|
||||
var slot = TextureRect.new()
|
||||
|
||||
|
||||
var highlight_rect = TextureRect.new()
|
||||
var hr_tex = load("res://assets/models/pboard/HighlightRect.tres")
|
||||
|
||||
|
||||
var select_rect = TextureRect.new()
|
||||
var sr_tex = load("res://assets/models/pboard/SelectRect.tres")
|
||||
|
||||
|
||||
var adjacent_rect = TextureRect.new()
|
||||
var ar_tex = load("res://assets/models/pboard/AdjacentRect.tres")
|
||||
|
||||
|
||||
|
||||
slot.custom_minimum_size = Vector2(36, 36)
|
||||
slot.texture = item_tex[0]
|
||||
|
||||
# 0-based indices corresponding to User's 1-based request: 1,5,6,10,11,15,16,20,21,22,23,24,25
|
||||
slot.set_meta("slot_idx", i)
|
||||
|
||||
var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24]
|
||||
|
||||
|
||||
if i in hidden_slots:
|
||||
slot.modulate = Color(1, 1, 1, 0)
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
else:
|
||||
slot.modulate = Color.WHITE
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
|
||||
|
||||
playerboard_ui.add_child(slot, true)
|
||||
|
||||
|
||||
highlight_rect.texture = hr_tex
|
||||
highlight_rect.size = Vector2(36, 36)
|
||||
select_rect.texture = sr_tex
|
||||
@@ -105,7 +105,7 @@ func setup_playerboard_ui():
|
||||
slot.add_child(highlight_rect)
|
||||
slot.add_child(select_rect)
|
||||
slot.add_child(adjacent_rect)
|
||||
|
||||
|
||||
slot.get_child(0).hide()
|
||||
slot.get_child(1).hide()
|
||||
slot.get_child(2).hide()
|
||||
@@ -113,109 +113,143 @@ func setup_playerboard_ui():
|
||||
func update_playerboard_ui():
|
||||
if not local_player_character or not playerboard_ui:
|
||||
return
|
||||
|
||||
# Center 3x3 slot indices in a 5x5 grid (0-indexed)
|
||||
# Row 1: 6, 7, 8
|
||||
# Row 2: 11, 12, 13
|
||||
# Center 3x3 slot indices in a 5x5 grid (0-indexed)
|
||||
# Row 1: 6, 7, 8 (Now Storage - but kept in index map for goals[0-2])
|
||||
# Row 2: 11, 12, 13 (Goals[3-5])
|
||||
# Row 3: 16, 17, 18 (Goals[6-8])
|
||||
|
||||
# Candy for CandySurvival, not a tile
|
||||
var is_candy = LobbyManager and LobbyManager.is_game_mode(GameMode.Mode.CANDY_SURVIVAL)
|
||||
|
||||
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):
|
||||
|
||||
var count = playerboard_ui.get_child_count()
|
||||
for i in range(count):
|
||||
var slot = playerboard_ui.get_child(i)
|
||||
|
||||
|
||||
# Get actual playerboard index from slot metadata
|
||||
var slot_idx = i
|
||||
if slot.has_meta("slot_idx"):
|
||||
slot_idx = slot.get_meta("slot_idx")
|
||||
|
||||
# Safety check: Ensure playerboard has enough items
|
||||
if i >= local_player_character.playerboard.size():
|
||||
if slot_idx >= local_player_character.playerboard.size():
|
||||
continue
|
||||
|
||||
var item = local_player_character.playerboard[i]
|
||||
|
||||
# 0-based indices corresponding to User's 1-based request: 1,5,6,10,11,15,16,20,21,22,23,24,25
|
||||
var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24]
|
||||
|
||||
|
||||
var item = local_player_character.playerboard[slot_idx]
|
||||
|
||||
# Default texture (empty)
|
||||
slot.texture = item_tex[0]
|
||||
|
||||
if i in hidden_slots:
|
||||
slot.modulate = Color(1, 1, 1, 0)
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
if is_candy:
|
||||
var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24]
|
||||
if slot_idx in hidden_slots:
|
||||
slot.modulate = Color(1, 1, 1, 0)
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
else:
|
||||
slot.modulate = Color.WHITE
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
# All 9 cells show goal ghost or placed tile
|
||||
var center_index = center_slots.find(slot_idx)
|
||||
if center_index != -1 and center_index < goals.size():
|
||||
var goal_value = goals[center_index]
|
||||
if item != -1:
|
||||
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:
|
||||
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]
|
||||
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]
|
||||
else:
|
||||
slot.modulate = Color.WHITE
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
|
||||
# Check if this is a center slot that should show a goal
|
||||
# BUT only show ghost goals for rows 2 & 3 (indices 11+)
|
||||
var center_index = center_slots.find(i)
|
||||
if center_index != -1 and center_index < goals.size() and i > 8:
|
||||
var goal_value = goals[center_index]
|
||||
|
||||
if item != -1:
|
||||
# Player has a tile in this slot - show it at full brightness
|
||||
# Original 5x5 logic
|
||||
var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24]
|
||||
|
||||
if slot_idx in hidden_slots:
|
||||
slot.modulate = Color(1, 1, 1, 0)
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
else:
|
||||
slot.modulate = Color.WHITE
|
||||
slot.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
|
||||
# Check if this is a center slot that should show a goal
|
||||
# BUT only show ghost goals for rows 2 & 3 (indices 11+)
|
||||
var center_index = center_slots.find(slot_idx)
|
||||
if center_index != -1 and center_index < goals.size() and slot_idx > 8:
|
||||
var goal_value = goals[center_index]
|
||||
|
||||
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]
|
||||
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]
|
||||
# Non-center slots always full brightness (UNLESS HIDDEN)
|
||||
if not (i in hidden_slots):
|
||||
slot.modulate = Color.WHITE
|
||||
|
||||
# Non-center slots always full brightness (UNLESS HIDDEN)
|
||||
if not (slot_idx in hidden_slots):
|
||||
slot.modulate = Color.WHITE
|
||||
|
||||
# Check for new special tile placement to trigger effect
|
||||
if i < _previous_playerboard_state.size():
|
||||
var prev_item = _previous_playerboard_state[i]
|
||||
if slot_idx < _previous_playerboard_state.size():
|
||||
var prev_item = _previous_playerboard_state[slot_idx]
|
||||
# If slot was empty or different, and now has a special tile (7-10)
|
||||
if item != prev_item and item >= 7 and item <= 10:
|
||||
_pulse_slot_effect(slot)
|
||||
|
||||
|
||||
# Update cache
|
||||
_previous_playerboard_state = local_player_character.playerboard.duplicate()
|
||||
|
||||
func _pulse_slot_effect(slot: Control):
|
||||
"""Visual feedback when a special tile is placed."""
|
||||
var tween = create_tween()
|
||||
|
||||
|
||||
# Reset scale first to be safe
|
||||
slot.scale = Vector2.ONE
|
||||
slot.pivot_offset = slot.size / 2 # Center pivot
|
||||
|
||||
|
||||
# Pop effect
|
||||
tween.tween_property(slot, "scale", Vector2(1.4, 1.4), 0.15).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||
tween.tween_property(slot, "scale", Vector2(1.0, 1.0), 0.2).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
||||
|
||||
|
||||
# Flash effect
|
||||
var original_modulate = slot.modulate
|
||||
slot.modulate = Color(1.5, 1.5, 1.5) # Overbright
|
||||
tween.parallel().tween_property(slot, "modulate", original_modulate, 0.3)
|
||||
|
||||
|
||||
|
||||
|
||||
func _connect_powerup_manager_deferred(player):
|
||||
"""Wait for PowerUpManager to be initialized before connecting."""
|
||||
# player._ready waits 0.5s before creating managers, so wait longer
|
||||
await player.get_tree().create_timer(0.8).timeout
|
||||
|
||||
|
||||
var powerup_manager = player.get_node_or_null("PowerUpManager")
|
||||
if powerup_manager:
|
||||
if not powerup_manager.points_changed.is_connected(_on_powerup_points_changed):
|
||||
@@ -243,9 +277,9 @@ func setup_powerup_bar_ui(main_node):
|
||||
if not powerup_bar:
|
||||
push_warning("PowerUpBar node not found in scene")
|
||||
return
|
||||
|
||||
|
||||
powerup_ready_rect = main_node.get_node_or_null("PowerBar/PowerUpReady")
|
||||
|
||||
|
||||
# Get segment references from scene
|
||||
powerup_segments.clear()
|
||||
var hbox = powerup_bar.get_node_or_null("HBox")
|
||||
@@ -266,24 +300,24 @@ func update_powerup_bar(current_points: int, _max_points: int):
|
||||
# 3 Segments total. Max Boost is 100. So each segment represents 33.33 points.
|
||||
var points_per_segment = _max_points / 3.0
|
||||
var bars_filled = int(current_points / points_per_segment)
|
||||
|
||||
|
||||
for i in range(powerup_segments.size()):
|
||||
var segment = powerup_segments[i]
|
||||
var style = StyleBoxTexture.new()
|
||||
var tex_path = ""
|
||||
|
||||
|
||||
if i < bars_filled:
|
||||
# Filled segment
|
||||
tex_path = "res://assets/graphics/gui/gauge/Segment%d_filled.png" % i
|
||||
else:
|
||||
# Empty segment
|
||||
tex_path = "res://assets/graphics/gui/gauge/Segment%d_empty.png" % i
|
||||
|
||||
|
||||
if ResourceLoader.exists(tex_path):
|
||||
style.texture = load(tex_path)
|
||||
|
||||
|
||||
segment.add_theme_stylebox_override("panel", style)
|
||||
|
||||
|
||||
if powerup_ready_rect:
|
||||
if current_points >= _max_points and _max_points > 0:
|
||||
powerup_ready_rect.visible = true
|
||||
@@ -296,7 +330,7 @@ func _on_powerup_points_changed(current: int, max_points: int):
|
||||
if current % 10 == 0: print("[UIManager] Points changed: ", current)
|
||||
# Calculate based on max points (100) / 3 segments = 33.33 points per segment
|
||||
var new_bars = int(current / (max_points / 3.0))
|
||||
|
||||
|
||||
# Detect if a new bar was filled
|
||||
if new_bars > _previous_bars and powerup_bar:
|
||||
# Pulse effect on newly filled segment
|
||||
@@ -304,10 +338,10 @@ func _on_powerup_points_changed(current: int, max_points: int):
|
||||
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 Safety: Check if timer_label is valid
|
||||
if timer_label and local_player_character and local_player_character.powerup_manager:
|
||||
var time_left = local_player_character.powerup_manager.get_time_until_full()
|
||||
@@ -358,7 +392,7 @@ func setup_timer_labels(main_node):
|
||||
if not goals_timer:
|
||||
push_warning("GoalsTimer node not found in scene")
|
||||
return
|
||||
|
||||
|
||||
# Apply dark background style
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.1, 0.1, 0.15, 0.9)
|
||||
@@ -369,7 +403,7 @@ func setup_timer_labels(main_node):
|
||||
style.corner_radius_bottom_left = 8
|
||||
style.corner_radius_bottom_right = 8
|
||||
goals_timer.add_theme_stylebox_override("panel", style)
|
||||
|
||||
|
||||
# Style the timer label
|
||||
var t_label = goals_timer.get_node_or_null("TimerLabel")
|
||||
if t_label:
|
||||
@@ -396,7 +430,7 @@ func setup_playerboard_label(main_node):
|
||||
func update_goal_count_label(count: int):
|
||||
if playerboard_label:
|
||||
playerboard_label.text = "x%d" % count
|
||||
|
||||
|
||||
# Pop effect only for progress
|
||||
if count > 0:
|
||||
var tween = playerboard_label.create_tween()
|
||||
@@ -408,22 +442,22 @@ func initialize_leaderboard_with_players(players: Array):
|
||||
"""Initialize leaderboard showing all players with score 0."""
|
||||
if not leaderboard_panel:
|
||||
return
|
||||
|
||||
|
||||
var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox")
|
||||
if not vbox:
|
||||
vbox = leaderboard_panel.get_node_or_null("VBox")
|
||||
if not vbox:
|
||||
return
|
||||
|
||||
|
||||
for i in range(4):
|
||||
var entry_root = vbox.get_node_or_null("Entry" + str(i + 1))
|
||||
if not entry_root:
|
||||
continue
|
||||
|
||||
|
||||
var entry = entry_root.get_node_or_null("HBox")
|
||||
if not entry:
|
||||
entry = entry_root
|
||||
|
||||
|
||||
if i < players.size():
|
||||
var player = players[i]
|
||||
var name_label = entry.get_node_or_null("SplitterContainer/SectionA/NameLabel")
|
||||
@@ -431,21 +465,21 @@ func initialize_leaderboard_with_players(players: Array):
|
||||
var portrait_rect = entry.get_node_or_null("PortraitRect")
|
||||
var ghost_icon = entry.get_node_or_null("SplitterContainer/SectionA/GhostIcon")
|
||||
var mini_powerup_bar = entry.get_node_or_null("SplitterContainer/SectionB/MiniPowerUpBar")
|
||||
|
||||
|
||||
if name_label:
|
||||
# Use display_name if available, otherwise fallback to node name
|
||||
var player_display_name = player.display_name if player and player.get("display_name") else ""
|
||||
if player_display_name.is_empty():
|
||||
player_display_name = str(player.name) if player else "Player " + str(i + 1)
|
||||
name_label.text = player_display_name
|
||||
|
||||
|
||||
if score_label:
|
||||
score_label.text = str(player.score) if player and player.get("score") else "0"
|
||||
|
||||
|
||||
if portrait_rect:
|
||||
var character_name = "Pip" # Default fallback
|
||||
var peer_id = player.name.to_int() if player else 0
|
||||
|
||||
|
||||
var lobby_manager = get_node_or_null("/root/LobbyManager")
|
||||
if lobby_manager:
|
||||
var lobby_players = lobby_manager.get_players()
|
||||
@@ -453,15 +487,15 @@ func initialize_leaderboard_with_players(players: Array):
|
||||
if p.get("id") == peer_id:
|
||||
character_name = p.get("character", "Pip")
|
||||
break
|
||||
|
||||
|
||||
var avatar_url = "res://assets/graphics/character_potrait/sc_%s.png" % character_name.to_lower()
|
||||
if ResourceLoader.exists(avatar_url):
|
||||
portrait_rect.texture = load(avatar_url)
|
||||
|
||||
|
||||
if ghost_icon:
|
||||
# Hidden by default. The live update loop will populate the correct texture.
|
||||
ghost_icon.modulate = Color(1, 1, 1, 0)
|
||||
|
||||
|
||||
if mini_powerup_bar:
|
||||
# Initialize to empty segments
|
||||
for j in range(3):
|
||||
@@ -472,7 +506,7 @@ func initialize_leaderboard_with_players(players: Array):
|
||||
style.border_color = Color(0.3, 0.7, 0.3, 1.0)
|
||||
style.set_border_width_all(2)
|
||||
seg.add_theme_stylebox_override("panel", style)
|
||||
|
||||
|
||||
entry.visible = true
|
||||
else:
|
||||
entry.visible = false
|
||||
@@ -483,48 +517,48 @@ func update_live_leaderboard(players: Array):
|
||||
var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox")
|
||||
if not vbox: vbox = leaderboard_panel.get_node_or_null("VBox")
|
||||
if not vbox: return
|
||||
|
||||
|
||||
var sorted_players = players.duplicate()
|
||||
sorted_players.sort_custom(func(a, b):
|
||||
var score_a = a.score if "score" in a else 0
|
||||
var score_b = b.score if "score" in b else 0
|
||||
return score_a > score_b
|
||||
)
|
||||
|
||||
|
||||
var my_id = -1
|
||||
if leaderboard_panel.is_inside_tree() and leaderboard_panel.get_tree().get_multiplayer():
|
||||
my_id = leaderboard_panel.get_tree().get_multiplayer().get_unique_id()
|
||||
|
||||
|
||||
var my_index = -1
|
||||
for i in range(sorted_players.size()):
|
||||
if sorted_players[i] and sorted_players[i].name == str(my_id):
|
||||
my_index = i
|
||||
break
|
||||
|
||||
|
||||
var items_to_display = []
|
||||
for i in range(min(3, sorted_players.size())):
|
||||
items_to_display.append({"player": sorted_players[i], "rank": i + 1})
|
||||
|
||||
|
||||
if sorted_players.size() >= 4:
|
||||
if my_index > 3:
|
||||
items_to_display.append({"player": sorted_players[my_index], "rank": my_index + 1})
|
||||
else:
|
||||
items_to_display.append({"player": sorted_players[3], "rank": 4})
|
||||
|
||||
|
||||
for i in range(4):
|
||||
var entry_root = vbox.get_node_or_null("Entry" + str(i + 1))
|
||||
if not entry_root or i >= items_to_display.size():
|
||||
if entry_root: entry_root.visible = false
|
||||
continue
|
||||
|
||||
|
||||
entry_root.visible = true
|
||||
var entry = entry_root.get_node_or_null("HBox")
|
||||
if not entry: entry = entry_root
|
||||
|
||||
|
||||
var item = items_to_display[i]
|
||||
var player = item.player
|
||||
var rank = item.rank
|
||||
|
||||
|
||||
var rank_label = entry.get_node_or_null("RankLabel")
|
||||
if rank_label:
|
||||
match rank:
|
||||
@@ -532,22 +566,22 @@ func update_live_leaderboard(players: Array):
|
||||
2: rank_label.text = "2nd"
|
||||
3: rank_label.text = "3rd"
|
||||
_: rank_label.text = str(rank) + "th"
|
||||
|
||||
|
||||
if player and player.name == str(my_id):
|
||||
entry_root.modulate = Color(0.3, 0.7, 1.0) # Blue highlight for local player
|
||||
else:
|
||||
entry_root.modulate = Color.WHITE
|
||||
|
||||
|
||||
var score_label = entry.get_node_or_null("SplitterContainer/SectionB/ScoreLabel")
|
||||
var ghost_icon = entry.get_node_or_null("SplitterContainer/SectionA/GhostIcon")
|
||||
var mini_powerup_bar = entry.get_node_or_null("SplitterContainer/SectionB/MiniPowerUpBar")
|
||||
var portrait_rect = entry.get_node_or_null("PortraitRect")
|
||||
var name_label = entry.get_node_or_null("SplitterContainer/SectionA/NameLabel")
|
||||
|
||||
|
||||
if name_label:
|
||||
var default_name = player.name if player else "Unknown"
|
||||
name_label.text = player.get("display_name") if (player and player.get("display_name")) else default_name
|
||||
|
||||
|
||||
if portrait_rect:
|
||||
var character_name = "Pip" # Default fallback
|
||||
if player and player.get("selected_character"):
|
||||
@@ -558,18 +592,18 @@ func update_live_leaderboard(players: Array):
|
||||
"Gatot": character_name = "Gatot"
|
||||
"Oldpop": character_name = "Copper"
|
||||
_: character_name = sc
|
||||
|
||||
|
||||
var avatar_url = "res://assets/graphics/character_potrait/sc_%s.png" % character_name.to_lower()
|
||||
if ResourceLoader.exists(avatar_url):
|
||||
portrait_rect.texture = load(avatar_url)
|
||||
|
||||
|
||||
if score_label:
|
||||
score_label.text = str(player.score) if player and player.get("score") else "0"
|
||||
|
||||
|
||||
if ghost_icon:
|
||||
var active_skill_id = -1
|
||||
var is_blinking = false
|
||||
|
||||
|
||||
if player.get("special_tiles_manager"):
|
||||
var stm = player.special_tiles_manager
|
||||
# Check if any skill is CURRENTLY active (User requesting blinking state)
|
||||
@@ -592,7 +626,7 @@ func update_live_leaderboard(players: Array):
|
||||
if inv[effect_idx]:
|
||||
active_skill_id = effect_idx
|
||||
break
|
||||
|
||||
|
||||
if active_skill_id != -1:
|
||||
var tex_path = "res://assets/textures/player_board_and_blue_print/tile_null.tres"
|
||||
match int(active_skill_id):
|
||||
@@ -600,10 +634,10 @@ func update_live_leaderboard(players: Array):
|
||||
1: tex_path = "res://assets/graphics/touch_control/freeze_area.png"
|
||||
2: tex_path = "res://assets/graphics/touch_control/wall.png"
|
||||
3: tex_path = "res://assets/graphics/touch_control/ghost.png"
|
||||
|
||||
|
||||
if ResourceLoader.exists(tex_path):
|
||||
ghost_icon.texture = load(tex_path)
|
||||
|
||||
|
||||
if is_blinking:
|
||||
var alpha = 1.0 if (Time.get_ticks_msec() % 500) > 250 else 0.3
|
||||
ghost_icon.modulate = Color(1, 1, 1, alpha)
|
||||
@@ -611,7 +645,7 @@ func update_live_leaderboard(players: Array):
|
||||
ghost_icon.modulate = Color(1, 1, 1, 1)
|
||||
else:
|
||||
ghost_icon.modulate = Color(1, 1, 1, 0)
|
||||
|
||||
|
||||
if mini_powerup_bar and player.get("powerup_manager"):
|
||||
var p_mgr = player.powerup_manager
|
||||
if p_mgr:
|
||||
@@ -619,7 +653,7 @@ func update_live_leaderboard(players: Array):
|
||||
var current_pts = p_mgr.get_points()
|
||||
var points_per_segment = max_pts / 3.0
|
||||
var bars_filled = int(current_pts / points_per_segment)
|
||||
|
||||
|
||||
for j in range(3):
|
||||
var seg = mini_powerup_bar.get_node_or_null("Segment" + str(j))
|
||||
if seg:
|
||||
|
||||
Reference in New Issue
Block a user