rename gauntlet→candy_survival + rewrite Candy Survival per boss design

- Rename enum GAUNTLET→CANDY_SURVIVAL, all gauntlet_*→candy_survival_*
- Rename files: gauntlet_manager→candy_survival_manager, candy_cannon→candy_survival_npc, gauntlet.tscn→candy_survival.tscn
- Rewrite candy_survival_manager.gd: blueprints, candy stack, Mekton delivery, Sugar Rush, knock/ghost charges, sticky-as-wall
- Update player_movement_manager.gd: smack→knock system, ghost integration
- All Candy Survival issues (#54-57, #65-70) retitled per boss design
- Shared managers (goals_cycle, goal, player_race, playerboard, turn) untouched
This commit is contained in:
god
2026-07-06 01:28:39 +08:00
parent 114748a54f
commit 528e22875d
33 changed files with 731 additions and 2181 deletions
+29 -55
View File
@@ -112,19 +112,19 @@ func simple_move_to(grid_position: Vector2i) -> bool:
print("[MovementManager] Hard block at %s. Ghost pass denied." % grid_position)
var gm = null
var main_gauntlet = player.get_tree().root.get_node_or_null("Main")
if main_gauntlet and main_gauntlet.get("gauntlet_manager"):
gm = main_gauntlet.gauntlet_manager
var main_candy_survival = player.get_tree().root.get_node_or_null("Main")
if main_candy_survival and main_candy_survival.get("candy_survival_manager"):
gm = main_candy_survival.candy_survival_manager
# Check Floor 0 (Basic Walkability/Void)
if (cell_floor == -1 or cell_floor in enhanced_gridmap.non_walkable_items) and not is_wall_passable:
print("[Move] Failed: Floor Item %d is non-walkable" % cell_floor)
return false
# Gauntlet Mode explicit wall overrides (since we visually removed the wall blocks)
# Candy Survival explicit wall overrides (since we visually removed the wall blocks)
if gm and gm.is_active:
if gm._is_npc_zone(grid_position):
print("[Move] Failed: Blocked by Gauntlet NPC center at %s" % grid_position)
print("[Move] Failed: Blocked by Candy Survival NPC center at %s" % grid_position)
return false
# Check Floor 1 (Obstacles/Walls)
@@ -156,13 +156,13 @@ func simple_move_to(grid_position: Vector2i) -> bool:
return false # Don't move into the tile, just knock
# If moving into a sticky cell: block movement unless player is in ghost
# mode (is_invisible), which lets them bypass sticky tiles in gauntlet.
# mode (is_invisible), which lets them bypass sticky tiles in Candy Survival
if gm and gm.is_active and gm.is_sticky_cell(grid_position):
if player.get("is_invisible"):
# Ghost mode: walk through sticky tile freely
print("[Move] Ghost mode bypassed sticky cell at %s" % grid_position)
else:
print("[Move] Failed: Blocked by Gauntlet Sticky cell at %s" % grid_position)
print("[Move] Failed: Blocked by Candy Survival Sticky cell at %s" % grid_position)
return false
rotate_towards_target(grid_position)
@@ -208,15 +208,16 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
NotificationManager.send_message(player, "Target is Immune!", NotificationManager.MessageType.WARNING)
return false
# === NEW LOGIC: Only allow push if in ATTACK MODE and NOT GHOST ===
var has_smack = false
var main_for_smack = player.get_tree().root.get_node_or_null("Main")
var gm_for_smack = main_for_smack.get("gauntlet_manager") if main_for_smack else null
if gm_for_smack and gm_for_smack.is_active:
# Candy Survival: check if attacker has knock charges
var has_knock = false
var main_for_knock = player.get_tree().root.get_node_or_null("Main")
var gm_for_knock = main_for_knock.get("candy_survival_manager") if main_for_knock else null
if gm_for_knock and gm_for_knock.is_active:
var att_pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
has_smack = gm_for_smack.has_smack_charged(att_pid)
var charges = gm_for_knock.player_knocks.get(att_pid, 0) if gm_for_knock.player_knocks else 0
has_knock = charges > 0
if (not player.get("is_charged_strike") and not has_smack) or player.get("is_invisible"):
if (not player.get("is_charged_strike") and not has_knock) or player.get("is_invisible"):
# Standard bumping effect (Visual only)
print("[Move] Push blocked: Not charged or is Ghost (%s trying to push %s)" % [player.name, other_player.name])
if _can_rpc():
@@ -243,47 +244,20 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
var gm = null
var main_push_check = player.get_tree().root.get_node_or_null("Main")
if main_push_check and main_push_check.get("gauntlet_manager"):
gm = main_push_check.gauntlet_manager
if main_push_check and main_push_check.get("candy_survival_manager"):
gm = main_push_check.candy_survival_manager
# IF Gauntlet Mode is active, handle special Gauntlet Smacks
if gm and gm.is_active:
# Candy Survival: knock mechanic (candy-steal or backfire)
if gm and gm.is_active and gm.has_method("try_knock"):
var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int()
var other_pid = other_player.get("peer_id") if "peer_id" in other_player else other_player.name.to_int()
# Check if attacker has smack
if not gm.has_smack_charged(pid):
# bump visuals
if _can_rpc():
player.rpc("sync_bump", target_pos, true)
elif player.has_method("sync_bump"):
player.sync_bump(target_pos, true)
return false
# Smack Clash: Both charged
if gm.has_smack_charged(other_pid):
print("[Move] SMACK CLASH! Both %s and %s consumed." % [player.name, other_player.name])
if multiplayer.is_server():
gm.consume_smack(pid)
gm.consume_smack(other_pid)
elif _can_rpc():
gm.rpc("consume_smack", pid) # Assuming consume_smack is @rpc
gm.rpc("consume_smack", other_pid)
if _can_rpc():
player.rpc("apply_stagger", 1.0)
other_player.rpc("apply_stagger", 1.0)
else:
player.apply_stagger(1.0)
other_player.apply_stagger(1.0)
return false
# Else standard push
if multiplayer.is_server():
gm.consume_smack(pid)
elif _can_rpc():
gm.rpc("consume_smack", pid)
if gm.try_knock(pid, other_pid):
print("[CandySurvival] Player %d knocked %d, stole candies!" % [pid, other_pid])
else:
print("[CandySurvival] Player %d tried to knock %d (backfire or no charges)" % [pid, other_pid])
if _can_rpc():
player.rpc("sync_bump", target_pos, false)
return false
# === SUPER PUSH (Attack Mode) ===
print("Player %s SUPER PUSHING %s!" % [player.name, other_player.name])
@@ -300,7 +274,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
var push_direction = Vector2i(-1, 0) # Default back (Stop N Go)
var main_push = player.get_tree().root.get_node_or_null("Main")
var gm_push = main_push.gauntlet_manager if main_push and main_push.has_node("GauntletManager") else (main_push.get("gauntlet_manager") if main_push else null)
var gm_push = main_push.candy_survival_manager if main_push and main_push.has_node("CandySurvivalManager") else (main_push.get("candy_survival_manager") if main_push else null)
if gm_push and gm_push.is_active:
push_direction = direction # Use the direction of the attack
var pushed_to_pos = target_pos
@@ -335,8 +309,8 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
# Check if landing spot is sticky
var main_sticky = player.get_tree().root.get_node_or_null("Main")
if main_sticky and main_sticky.get("gauntlet_manager"):
var gm_sticky = main_sticky.gauntlet_manager
if main_sticky and main_sticky.get("candy_survival_manager"):
var gm_sticky = main_sticky.candy_survival_manager
if gm_sticky.is_active and gm_sticky.is_sticky_cell(pushed_to_pos):
if other_player.get("is_invisible"):
# Ghost mode: pushed player bypasses sticky