Release 2.4.4: Candy Survival Knock and backfire fixes
Release / Build & Release (push) Successful in 7m54s

This commit is contained in:
2026-07-09 15:05:15 +08:00
parent 85cdd5e9d4
commit 6506e9f416
9 changed files with 116 additions and 35 deletions
+57 -22
View File
@@ -148,8 +148,6 @@ func _define_sticky_walls() -> void:
sticky_cells[Vector2i(NPC_CENTER.x + dx, NPC_CENTER.y + dy)] = true
func start_game_mode() -> void:
if multiplayer.is_server():
activate_client_side()
active = true
current_face = CandyColor.HEART
face_timer = 0.0
@@ -183,6 +181,9 @@ func start_game_mode() -> void:
rpc("sync_knock_charge_count", pid, START_KNOCK)
rpc("sync_ghost_charge_count", pid, START_GHOST)
if multiplayer.is_server():
activate_client_side()
print("[CandySurvival] Started with %d players" % pids.size())
func _enter_tree() -> void:
@@ -330,15 +331,6 @@ func _update_candy_badge(pid: int) -> void:
var face_match = (color != -1)
var score = player_score.get(pid, 0)
rpc("sync_candy_badge", pid, count, mult, face_match, score, colors)
# Update the 3D meshes on the player's head across all clients
var all_players = get_tree().get_nodes_in_group("Players")
for player in all_players:
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", colors)
break
func get_multiplier(pid: int) -> float:
var count = player_candies.get(pid, 0)
@@ -395,31 +387,49 @@ func try_knock(attacker: int, target: int) -> bool:
return false
if player_ghost_active.get(target, false):
return false
if player_knocks.get(attacker, 0) <= 0:
if not player_knocks.has(attacker):
player_knocks[attacker] = START_KNOCK
if player_knocks[attacker] <= 0:
return false
var target_colors = player_candy_colors.get(target, [])
var target_candies = target_colors.size()
var attacker_node = main_scene.get_node_or_null(str(attacker))
var target_node = main_scene.get_node_or_null(str(target))
var attacker_node = _find_player_node(attacker)
var target_node = _find_player_node(target)
if attacker_node and target_node:
attacker_node.rpc("sync_bump", target_node.current_position)
if target_candies == 0:
# Backfire: both lose a charge
player_knocks[attacker] = player_knocks.get(attacker, 0) - 1
# Backfire: attacker loses a charge & gets staggered
player_knocks[attacker] = player_knocks.get(attacker, START_KNOCK) - 1
rpc("sync_knock_charge_count", attacker, player_knocks[attacker])
# Attacker is also knocked down briefly (backfire)
# Attacker is knocked down briefly (backfire)
if attacker_node:
attacker_node.rpc("apply_stagger", 2.0)
# Backfire penalty: Attacker's candy stack transfers to Target!
var attacker_colors = player_candy_colors.get(attacker, [])
var attacker_candies = attacker_colors.size()
if attacker_candies > 0:
if not player_candy_colors.has(target):
player_candy_colors[target] = []
player_candy_colors[target].append_array(attacker_colors)
player_candy_colors[attacker] = []
player_candies[target] = player_candy_colors[target].size()
player_candies[attacker] = 0
_add_score(target, attacker_candies * 100)
_update_candy_badge(attacker)
_update_candy_badge(target)
rpc("sync_knock_result", attacker, target, 0)
return false
# Steal all candies
# Steal all candies (Successful knock)
if not player_candy_colors.has(attacker):
player_candy_colors[attacker] = []
player_candy_colors[attacker].append_array(target_colors)
@@ -428,7 +438,7 @@ func try_knock(attacker: int, target: int) -> bool:
player_candies[attacker] = player_candy_colors[attacker].size()
player_candies[target] = 0
player_knocks[attacker] = player_knocks.get(attacker, 0) - 1
player_knocks[attacker] = player_knocks.get(attacker, START_KNOCK) - 1
rpc("sync_knock_charge_count", attacker, player_knocks[attacker])
player_last_knocked_by[target] = attacker
@@ -567,7 +577,24 @@ func _add_score(pid: int, points: int) -> void:
func _get_player_ids() -> Array:
if main_scene and main_scene.has_method("get_player_ids"):
return main_scene.get_player_ids()
return []
var pids: Array = []
for p in get_tree().get_nodes_in_group("Players"):
if not is_instance_valid(p): continue
var pid = p.get("peer_id") if "peer_id" in p else p.name.to_int()
if pid != 0 and not pids.has(pid):
pids.append(pid)
return pids
func _find_player_node(pid: int) -> Node:
if main_scene:
var n = main_scene.get_node_or_null(str(pid))
if n: return n
for p in get_tree().get_nodes_in_group("Players"):
if not is_instance_valid(p): continue
var p_id = p.get("peer_id") if "peer_id" in p else p.name.to_int()
if p_id == pid:
return p
return null
func candy_survival_round_duration() -> int:
return 180
@@ -577,10 +604,18 @@ func get_score(pid: int) -> int:
# ── RPCs (stubs — HUD client handles visuals) ──
@rpc("authority", "call_remote", "unreliable")
@rpc("authority", "call_local", "reliable")
func sync_candy_badge(pid: int, count: int, mult: float, face_match: bool, score: int, colors: Array) -> void:
if not active:
activate_client_side()
player_candies[pid] = count
player_candy_colors[pid] = colors
player_score[pid] = score
var p_node = _find_player_node(pid)
if p_node and p_node.has_method("sync_candy_stack"):
p_node.sync_candy_stack(colors)
if pid == multiplayer.get_unique_id():
_last_badge_count = count
_last_badge_mult = mult
@@ -644,7 +679,7 @@ func _restore_delivery_indicator() -> void:
else:
hud_delivery_indicator.text = ""
@rpc("authority", "call_remote", "unreliable")
@rpc("authority", "call_local", "reliable")
func sync_knock_result(attacker: int, target: int, candies: int) -> void:
if not active:
activate_client_side()