fix(candy-survival): implement stagger (knockdown), lunge visual bump, and target stealing/backfire animations for Knock (Q)

This commit is contained in:
2026-07-08 17:40:01 +08:00
parent 5cf08f249b
commit f060a2f4b8
2 changed files with 37 additions and 10 deletions
+15 -1
View File
@@ -395,11 +395,21 @@ func try_knock(attacker: int, target: int) -> bool:
var target_colors = player_candy_colors.get(target, []) var target_colors = player_candy_colors.get(target, [])
var target_candies = target_colors.size() 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))
if attacker_node and target_node:
attacker_node.rpc("sync_bump", target_node.current_position)
if target_candies == 0: if target_candies == 0:
# Backfire: both lose a charge # Backfire: both lose a charge
player_knocks[attacker] = player_knocks.get(attacker, 0) - 1 player_knocks[attacker] = player_knocks.get(attacker, 0) - 1
rpc("sync_knock_charge_count", attacker, player_knocks[attacker]) rpc("sync_knock_charge_count", attacker, player_knocks[attacker])
# Attacker is also knocked down briefly (visual handled by caller)
# Attacker is also knocked down briefly (backfire)
if attacker_node:
attacker_node.rpc("apply_stagger", 2.0)
rpc("sync_knock_result", attacker, target, 0) rpc("sync_knock_result", attacker, target, 0)
return false return false
@@ -416,6 +426,10 @@ func try_knock(attacker: int, target: int) -> bool:
rpc("sync_knock_charge_count", attacker, player_knocks[attacker]) rpc("sync_knock_charge_count", attacker, player_knocks[attacker])
player_last_knocked_by[target] = attacker player_last_knocked_by[target] = attacker
# Target is knocked down / staggered on successful knock
if target_node:
target_node.rpc("apply_stagger", 2.0)
_add_score(attacker, target_candies * 100) _add_score(attacker, target_candies * 100)
_update_candy_badge(attacker) _update_candy_badge(attacker)
_update_candy_badge(target) _update_candy_badge(target)
+22 -9
View File
@@ -119,22 +119,35 @@ func handle_unhandled_input(event):
if LobbyManager.game_mode == "Candy Survival": if LobbyManager.game_mode == "Candy Survival":
var gm = player.get_node_or_null("/root/Main/CandySurvivalManager") var gm = player.get_node_or_null("/root/Main/CandySurvivalManager")
if gm and gm.active: if gm and gm.active:
var att_id = player.get("peer_id") if "peer_id" in player else player.name.to_int()
if gm.player_knocks.get(att_id, 0) <= 0:
NotificationManager.send_message(player, "No Knock charges left!", NotificationManager.MessageType.WARNING)
get_viewport().set_input_as_handled()
return
# Find a target to knock (first adjacent player) # Find a target to knock (first adjacent player)
var p_pos = player.current_position var p_pos = player.current_position
var all_players = get_tree().get_nodes_in_group("Players") var all_players = get_tree().get_nodes_in_group("Players")
var target_found = null
for other in all_players: for other in all_players:
if other == player: continue if other == player: continue
var other_pos = other.current_position var other_pos = other.current_position
if abs(p_pos.x - other_pos.x) + abs(p_pos.y - other_pos.y) == 1: if abs(p_pos.x - other_pos.x) + abs(p_pos.y - other_pos.y) == 1:
# Adjacent target_found = other
var att_id = player.get("peer_id") if "peer_id" in player else player.name.to_int() break
var tgt_id = other.get("peer_id") if "peer_id" in other else other.name.to_int()
if multiplayer.is_server(): if target_found:
gm.try_knock(att_id, tgt_id) var tgt_id = target_found.get("peer_id") if "peer_id" in target_found else target_found.name.to_int()
else: if multiplayer.is_server():
gm.rpc_id(1, "try_knock", att_id, tgt_id) gm.try_knock(att_id, tgt_id)
get_viewport().set_input_as_handled() else:
return gm.rpc_id(1, "try_knock", att_id, tgt_id)
else:
# No target, but play the bump animation anyway in place
player.rpc("sync_bump", p_pos)
get_viewport().set_input_as_handled()
return
if player.powerup_manager: if player.powerup_manager:
player.powerup_manager.use_special_effect() player.powerup_manager.use_special_effect()