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:
|
||||
|
||||
Reference in New Issue
Block a user