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:
@@ -1,6 +1,6 @@
|
||||
extends Node
|
||||
|
||||
# Minimal EnhancedGridMap stand-in for Gauntlet headless tests. Records
|
||||
# Minimal EnhancedGridMap stand-in for Candy Survival headless tests. Records
|
||||
# set_cell_item calls so lifecycle tests can run the local sync path without a
|
||||
# real GridMap. Only the surface the manager touches is implemented.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends Node
|
||||
|
||||
# Minimal "Main" stand-in for Gauntlet headless tests. Provides the RPC methods
|
||||
# the GauntletManager calls on its main_scene so calls resolve without the full
|
||||
# Minimal "Main" stand-in for Candy Survival headless tests. Provides the RPC methods
|
||||
# the CandySurvivalManager calls on its main_scene so calls resolve without the full
|
||||
# game scene. Methods are no-ops that just need to exist + be rpc-tagged.
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
|
||||
+50
-50
@@ -1,13 +1,13 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Bot AI — Sticky Avoidance & Pathfinding [Gauntlet #075]
|
||||
# Test: Bot AI — Sticky Avoidance & Pathfinding Candy Survival #075]
|
||||
# Verifies the bot's strategic planner correctly:
|
||||
# • Detects Gauntlet mode and exposes helpers.
|
||||
# • Detects Candy Survival mode and exposes helpers.
|
||||
# • Rejects sticky / telegraphed cells in _is_valid_move_target.
|
||||
# • Activates Cleanser when boxed in or standing on telegraphed ground.
|
||||
# • Honours an active Cleanser (treats sticky cells as passable).
|
||||
# • Calls rpc_activate_cleanser on the GauntletManager when triggered.
|
||||
# • Calls rpc_activate_cleanser on the CandySurvivalManager when triggered.
|
||||
# =============================================================================
|
||||
|
||||
const BotStrategicPlanner = preload("res://scripts/bot_strategic_planner.gd")
|
||||
@@ -26,8 +26,8 @@ class StubActor extends Node3D:
|
||||
func is_position_occupied(_p: Vector2i) -> bool:
|
||||
return false
|
||||
|
||||
class StubGauntletManager extends Node:
|
||||
# Mimics the slice of GauntletManager API the bot planner depends on.
|
||||
class StubCandySurvivalManager extends Node:
|
||||
# Mimics the slice of CandySurvivalManager API the bot planner depends on.
|
||||
var sticky_map: Dictionary = {} # Vector2i -> true
|
||||
var player_cleansers: Dictionary = {} # peer_id -> int
|
||||
var cleanser_active: Dictionary = {} # peer_id -> true
|
||||
@@ -46,7 +46,7 @@ class StubGauntletManager extends Node:
|
||||
|
||||
var main_node: Node
|
||||
var gridmap: Node
|
||||
var gauntlet_manager: StubGauntletManager
|
||||
var candy_survival_manager: StubCandySurvivalManager
|
||||
var actor: StubActor
|
||||
var planner: RefCounted
|
||||
|
||||
@@ -68,9 +68,9 @@ func before_each():
|
||||
gridmap.set_cell_item(Vector3i(x, 0, z), 1)
|
||||
main_node.add_child(gridmap)
|
||||
|
||||
gauntlet_manager = StubGauntletManager.new()
|
||||
gauntlet_manager.name = "GauntletManager"
|
||||
main_node.add_child(gauntlet_manager)
|
||||
candy_survival_manager = StubCandySurvivalManager.new()
|
||||
candy_survival_manager.name = "CandySurvivalManager"
|
||||
main_node.add_child(candy_survival_manager)
|
||||
|
||||
actor = StubActor.new()
|
||||
actor.enhanced_gridmap = gridmap
|
||||
@@ -78,17 +78,17 @@ func before_each():
|
||||
main_node.add_child(actor)
|
||||
|
||||
planner = BotStrategicPlanner.new(actor, gridmap)
|
||||
planner.gauntlet_manager_override = gauntlet_manager
|
||||
planner.candy_survival_manager_override = candy_survival_manager
|
||||
|
||||
# Default to Gauntlet mode for these tests.
|
||||
LobbyManager.game_mode = "Candy Pump Survival"
|
||||
# Default to Candy Survival mode for these tests.
|
||||
LobbyManager.game_mode = "Candy Survival"
|
||||
|
||||
func after_each():
|
||||
if is_instance_valid(main_node):
|
||||
main_node.queue_free()
|
||||
actor = null
|
||||
planner = null
|
||||
gauntlet_manager = null
|
||||
candy_survival_manager = null
|
||||
gridmap = null
|
||||
# Reset lobby mode so other tests aren't affected.
|
||||
LobbyManager.game_mode = "Freemode"
|
||||
@@ -97,19 +97,19 @@ func after_each():
|
||||
# Mode detection
|
||||
# =============================================================================
|
||||
|
||||
func test_is_gauntlet_mode_true_when_set():
|
||||
assert_true(planner.is_gauntlet_mode(), "Detects Gauntlet via LobbyManager")
|
||||
func test_is_candy_survival_mode_true_when_set():
|
||||
assert_true(planner.is_candy_survival_mode(), "Detects Candy Survival via LobbyManager")
|
||||
|
||||
func test_is_gauntlet_mode_false_in_other_modes():
|
||||
func test_is_candy_survival_mode_false_in_other_modes():
|
||||
LobbyManager.game_mode = "Stop n Go"
|
||||
assert_false(planner.is_gauntlet_mode(), "Stop n Go is not Gauntlet")
|
||||
assert_false(planner.is_candy_survival_mode(), "Stop n Go is not Candy Survival")
|
||||
LobbyManager.game_mode = "Freemode"
|
||||
assert_false(planner.is_gauntlet_mode(), "Freemode is not Gauntlet")
|
||||
assert_false(planner.is_candy_survival_mode(), "Freemode is not Candy Survival")
|
||||
|
||||
func test_get_gauntlet_manager_resolves_from_main():
|
||||
var gm = planner._get_gauntlet_manager()
|
||||
assert_not_null(gm, "Resolves GauntletManager under /root/Main")
|
||||
assert_eq(gm, gauntlet_manager, "Same instance as the one we added")
|
||||
func test_get_candy_survival_manager_resolves_from_main():
|
||||
var gm = planner._get_candy_survival_manager()
|
||||
assert_not_null(gm, "Resolves CandySurvivalManager under /root/Main")
|
||||
assert_eq(gm, candy_survival_manager, "Same instance as the one we added")
|
||||
|
||||
# =============================================================================
|
||||
# Overlay detection
|
||||
@@ -134,7 +134,7 @@ func test_overlay_unsafe_ignores_layer0_and_layer1():
|
||||
gridmap.set_cell_item(Vector3i(3, 0, 3), 17)
|
||||
gridmap.set_cell_item(Vector3i(3, 1, 3), 17)
|
||||
assert_false(planner._is_overlay_unsafe(Vector2i(3, 3)),
|
||||
"Only layer 2 overlay matters for Gauntlet safety")
|
||||
"Only layer 2 overlay matters for Candy Survival safety")
|
||||
|
||||
# =============================================================================
|
||||
# _is_valid_move_target integration
|
||||
@@ -144,12 +144,12 @@ func test_valid_move_target_rejects_sticky_in_gauntlet():
|
||||
# Make a sticky cell pass all other checks (valid position, walkable floor).
|
||||
gridmap.set_cell_item(Vector3i(3, 2, 3), 17)
|
||||
assert_false(planner._is_valid_move_target(Vector2i(3, 3)),
|
||||
"Sticky cell rejected in Gauntlet mode")
|
||||
"Sticky cell rejected in Candy Survival mode")
|
||||
|
||||
func test_valid_move_target_rejects_telegraphed_in_gauntlet():
|
||||
gridmap.set_cell_item(Vector3i(5, 2, 5), 18)
|
||||
assert_false(planner._is_valid_move_target(Vector2i(5, 5)),
|
||||
"Telegraphed cell rejected in Gauntlet mode")
|
||||
"Telegraphed cell rejected in Candy Survival mode")
|
||||
|
||||
func test_valid_move_target_accepts_clean_cells():
|
||||
assert_true(planner._is_valid_move_target(Vector2i(8, 8)),
|
||||
@@ -163,27 +163,27 @@ func test_valid_move_target_ignores_players_when_requested():
|
||||
assert_false(planner._is_valid_move_target(Vector2i(3, 3), true),
|
||||
"Safety check still active with ignore_players=true")
|
||||
|
||||
func test_valid_move_target_outside_gauntlet_allows_sticky():
|
||||
# Outside Gauntlet, layer-2 sticky overlays are not safety-relevant.
|
||||
func test_valid_move_target_outside_candy_survival_allows_sticky():
|
||||
# Outside Candy Survival, layer-2 sticky overlays are not safety-relevant.
|
||||
LobbyManager.game_mode = "Freemode"
|
||||
gridmap.set_cell_item(Vector3i(3, 2, 3), 17)
|
||||
assert_true(planner._is_valid_move_target(Vector2i(3, 3)),
|
||||
"Sticky overlay ignored in non-Gauntlet modes")
|
||||
"Sticky overlay ignored in non-Candy Survival modes")
|
||||
|
||||
func test_valid_move_target_allows_sticky_when_cleanser_active():
|
||||
gauntlet_manager.cleanser_active[actor.peer_id] = true
|
||||
candy_survival_manager.cleanser_active[actor.peer_id] = true
|
||||
gridmap.set_cell_item(Vector3i(3, 2, 3), 17)
|
||||
assert_true(planner._is_valid_move_target(Vector2i(3, 3)),
|
||||
"Active Cleanser grants temporary immunity")
|
||||
|
||||
# =============================================================================
|
||||
# Sticky-cell awareness via GauntletManager authority
|
||||
# Sticky-cell awareness via CandySurvivalManager authority
|
||||
# =============================================================================
|
||||
|
||||
func test_valid_move_target_uses_gauntlet_manager_sticky_map():
|
||||
func test_valid_move_target_uses_candy_survival_manager_sticky_map():
|
||||
# Even if the gridmap overlay hasn't landed yet (RPC in flight), the
|
||||
# GauntletManager's authoritative sticky_cells map must block the move.
|
||||
gauntlet_manager.sticky_map[Vector2i(7, 7)] = true
|
||||
# CandySurvivalManager's authoritative sticky_cells map must block the move.
|
||||
candy_survival_manager.sticky_map[Vector2i(7, 7)] = true
|
||||
assert_false(planner._is_valid_move_target(Vector2i(7, 7)),
|
||||
"Manager's sticky map blocks moves before overlay tiles land")
|
||||
|
||||
@@ -221,7 +221,7 @@ func test_should_activate_cleanser_false_without_charge():
|
||||
"Trapped but no charge → cannot activate")
|
||||
|
||||
func test_should_activate_cleanser_true_when_surrounded():
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]:
|
||||
var n = Vector2i(5, 5) + d
|
||||
@@ -230,7 +230,7 @@ func test_should_activate_cleanser_true_when_surrounded():
|
||||
"Trapped with charge → activate Cleanser")
|
||||
|
||||
func test_should_activate_cleanser_true_when_on_telegraphed():
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
gridmap.set_cell_item(Vector3i(5, 2, 5), 18) # bot standing on telegraph
|
||||
# 3+ unsafe neighbors required by spec; add three.
|
||||
@@ -241,8 +241,8 @@ func test_should_activate_cleanser_true_when_on_telegraphed():
|
||||
"Standing on telegraph with 3 sticky neighbors → activate Cleanser")
|
||||
|
||||
func test_should_activate_cleanser_false_when_already_active():
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
gauntlet_manager.cleanser_active[actor.peer_id] = true
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.cleanser_active[actor.peer_id] = true
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]:
|
||||
var n = Vector2i(5, 5) + d
|
||||
@@ -252,16 +252,16 @@ func test_should_activate_cleanser_false_when_already_active():
|
||||
|
||||
func test_should_activate_cleanser_false_outside_gauntlet():
|
||||
LobbyManager.game_mode = "Stop n Go"
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]:
|
||||
var n = Vector2i(5, 5) + d
|
||||
gridmap.set_cell_item(Vector3i(n.x, 2, n.y), 17)
|
||||
assert_false(planner.should_activate_cleanser_now(),
|
||||
"Outside Gauntlet → never auto-activate Cleanser")
|
||||
"Outside Candy Survival → never auto-activate Cleanser")
|
||||
|
||||
func test_should_activate_cleanser_false_when_not_trapped():
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
# No sticky neighbors, plenty of room.
|
||||
assert_false(planner.should_activate_cleanser_now(),
|
||||
@@ -275,16 +275,16 @@ func test_bot_has_cleanser_charge_false_when_empty():
|
||||
assert_false(planner._bot_has_cleanser_charge(), "Empty by default")
|
||||
|
||||
func test_bot_has_cleanser_charge_true_when_granted():
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
assert_true(planner._bot_has_cleanser_charge(), "Charge granted")
|
||||
|
||||
func test_is_bot_cleanser_active_reflects_manager():
|
||||
assert_false(planner._is_bot_cleanser_active(), "Inactive by default")
|
||||
gauntlet_manager.cleanser_active[actor.peer_id] = true
|
||||
candy_survival_manager.cleanser_active[actor.peer_id] = true
|
||||
assert_true(planner._is_bot_cleanser_active(), "Manager reports active")
|
||||
|
||||
# =============================================================================
|
||||
# BotController → GauntletManager RPC integration
|
||||
# BotController → CandySurvivalManager RPC integration
|
||||
# =============================================================================
|
||||
|
||||
func test_bot_controller_requests_cleanser_when_trapped():
|
||||
@@ -295,7 +295,7 @@ func test_bot_controller_requests_cleanser_when_trapped():
|
||||
ctrl.strategic_planner = planner
|
||||
ctrl.actor = actor
|
||||
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]:
|
||||
var n = Vector2i(5, 5) + d
|
||||
@@ -303,9 +303,9 @@ func test_bot_controller_requests_cleanser_when_trapped():
|
||||
|
||||
var requested = ctrl._try_activate_cleanser()
|
||||
assert_true(requested, "Controller requests Cleanser when trapped")
|
||||
assert_eq(gauntlet_manager.activate_rpc_calls.size(), 1,
|
||||
assert_eq(candy_survival_manager.activate_rpc_calls.size(), 1,
|
||||
"RPC called exactly once")
|
||||
assert_eq(gauntlet_manager.activate_rpc_calls[0], actor.peer_id,
|
||||
assert_eq(candy_survival_manager.activate_rpc_calls[0], actor.peer_id,
|
||||
"Correct peer id sent")
|
||||
|
||||
ctrl.queue_free()
|
||||
@@ -317,11 +317,11 @@ func test_bot_controller_does_not_request_when_safe():
|
||||
ctrl.strategic_planner = planner
|
||||
ctrl.actor = actor
|
||||
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
# No sticky anywhere.
|
||||
var requested = ctrl._try_activate_cleanser()
|
||||
assert_false(requested, "No request when not trapped")
|
||||
assert_eq(gauntlet_manager.activate_rpc_calls.size(), 0,
|
||||
assert_eq(candy_survival_manager.activate_rpc_calls.size(), 0,
|
||||
"No RPC fired")
|
||||
|
||||
ctrl.queue_free()
|
||||
@@ -334,13 +334,13 @@ func test_bot_controller_skips_cleanser_outside_gauntlet():
|
||||
ctrl.actor = actor
|
||||
|
||||
LobbyManager.game_mode = "Stop n Go"
|
||||
gauntlet_manager.player_cleansers[actor.peer_id] = 1
|
||||
candy_survival_manager.player_cleansers[actor.peer_id] = 1
|
||||
actor.current_position = Vector2i(5, 5)
|
||||
for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]:
|
||||
var n = Vector2i(5, 5) + d
|
||||
gridmap.set_cell_item(Vector3i(n.x, 2, n.y), 17)
|
||||
|
||||
var requested = ctrl._try_activate_cleanser()
|
||||
assert_false(requested, "No request outside Gauntlet")
|
||||
assert_false(requested, "No request outside Candy Survival")
|
||||
|
||||
ctrl.queue_free()
|
||||
@@ -1,14 +1,14 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Candy Bubble System (v2) [Gauntlet #082]
|
||||
# Test: Candy Survival Candy Bubble System (v2) Candy Survival #082]
|
||||
# Covers bubble-specific scoring components, phase budgets, anti-stacking,
|
||||
# the 3x3 blast footprint, and the grow→explode lifecycle.
|
||||
# Runs headless (no multiplayer peer): elapsed_time = 0 so the final-30s window
|
||||
# is inactive unless a test sets it directly.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
const GridMapMock = preload("res://tests/helpers/gridmap_mock.gd")
|
||||
var manager
|
||||
var main_mock: Node
|
||||
@@ -20,7 +20,7 @@ func before_each():
|
||||
gridmap_mock = GridMapMock.new()
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
manager = GauntletManager.new()
|
||||
manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(manager)
|
||||
manager.initialize(main_mock, gridmap_mock)
|
||||
manager.current_phase = 0
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Cleanser Power-Up (v2) [Gauntlet #072]
|
||||
# Test: Candy Survival Cleanser Power-Up (v2) Candy Survival #072]
|
||||
# Covers grant cadence (every 2 missions, max 1), 5-cell immunity lifecycle,
|
||||
# sticky clearing, stun-blocked activation, and the safe-stop early termination.
|
||||
# Runs headless; uses a GridMap mock so clear_sticky_cell can run locally.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
const GridMapMock = preload("res://tests/helpers/gridmap_mock.gd")
|
||||
const MainMock = preload("res://tests/helpers/main_mock.gd")
|
||||
var manager
|
||||
@@ -20,7 +20,7 @@ func before_each():
|
||||
gridmap_mock = GridMapMock.new()
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
manager = GauntletManager.new()
|
||||
manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(manager)
|
||||
manager.initialize(main_mock, gridmap_mock)
|
||||
manager.current_phase = 0
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Telegraph Floor Highlight [Gauntlet #081]
|
||||
# Test: Candy Survival Telegraph Floor Highlight Candy Survival #081]
|
||||
# Verifies the amber floor overlay placed under cells during the 1-second
|
||||
# telegraph window: amber color, two-stage alpha (build-up → flash), lifetime
|
||||
# bound to telegraph_duration, distinct from sticky pink, RPC broadcast.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
const GridMapMock = preload("res://tests/helpers/gridmap_mock.gd")
|
||||
|
||||
var main_mock: Node
|
||||
@@ -15,7 +15,7 @@ var gridmap_mock: Node
|
||||
var manager: Node
|
||||
|
||||
func before_all():
|
||||
gut.p("=== Feature Tests [Gauntlet #081 Telegraph Floor Highlight] ===")
|
||||
gut.p("=== Feature Tests Candy Survival #081 Telegraph Floor Highlight] ===")
|
||||
|
||||
func before_each():
|
||||
main_mock = Node.new()
|
||||
@@ -26,7 +26,7 @@ func before_each():
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
|
||||
manager = GauntletManager.new()
|
||||
manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(manager)
|
||||
manager.initialize(main_mock, gridmap_mock)
|
||||
|
||||
@@ -63,7 +63,7 @@ func test_telegraph_apply_converts_to_sticky():
|
||||
# Verify the tile ID conversion by inspecting state directly — invoking
|
||||
# sync_growth_apply triggers _check_all_players_trapped which needs an
|
||||
# active multiplayer peer. The conversion is exercised by the
|
||||
# test_gauntlet_growth_tick.gd suite; here we only confirm the
|
||||
# test_candy_survival_growth_tick.gd suite; here we only confirm the
|
||||
# sticky tile ID is reserved and distinct.
|
||||
_without_peer(func():
|
||||
manager.sync_growth_telegraph([Vector2i(7, 7)])
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Growth Tick System (v2) [Gauntlet #067]
|
||||
# Test: Candy Survival Growth Tick System (v2) Candy Survival #067]
|
||||
# Replaces the old cannon-timer test. Covers growth timing, phase configs,
|
||||
# candidate generation, cells-per-tick ranges, weighted selection, and
|
||||
# cleansed-cell exclusion.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
var gauntlet_manager: Node
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
var candy_survival_manager: Node
|
||||
var main_mock: Node
|
||||
var gridmap_mock: Node
|
||||
|
||||
func before_all():
|
||||
gut.p("=== Feature Tests [Gauntlet #067 Growth Tick] ===")
|
||||
gut.p("=== Feature Tests Candy Survival #067 Growth Tick] ===")
|
||||
|
||||
func before_each():
|
||||
main_mock = Node.new()
|
||||
@@ -22,9 +22,9 @@ func before_each():
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
|
||||
gauntlet_manager = GauntletManager.new()
|
||||
main_mock.add_child(gauntlet_manager)
|
||||
gauntlet_manager.initialize(main_mock, gridmap_mock)
|
||||
candy_survival_manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(candy_survival_manager)
|
||||
candy_survival_manager.initialize(main_mock, gridmap_mock)
|
||||
|
||||
func after_each():
|
||||
if main_mock:
|
||||
@@ -38,43 +38,43 @@ func after_all():
|
||||
# =============================================================================
|
||||
|
||||
func test_growth_timer_starts_zero():
|
||||
assert_eq(gauntlet_manager.growth_timer, 0.0, "Growth timer starts at 0.0")
|
||||
assert_eq(candy_survival_manager.growth_timer, 0.0, "Growth timer starts at 0.0")
|
||||
|
||||
func test_growth_interval_default():
|
||||
assert_eq(gauntlet_manager.growth_interval, 3.0, "Growth interval defaults to 3.0s")
|
||||
assert_eq(candy_survival_manager.growth_interval, 3.0, "Growth interval defaults to 3.0s")
|
||||
|
||||
func test_telegraph_duration_default():
|
||||
assert_eq(gauntlet_manager.telegraph_duration, 1.0, "Telegraph duration defaults to 1.0s")
|
||||
assert_eq(candy_survival_manager.telegraph_duration, 1.0, "Telegraph duration defaults to 1.0s")
|
||||
|
||||
# =============================================================================
|
||||
# Phase Growth Config
|
||||
# =============================================================================
|
||||
|
||||
func test_phase_growth_config_has_three_phases():
|
||||
assert_eq(gauntlet_manager.phase_growth_config.size(), 3, "Three phase growth configs")
|
||||
assert_eq(candy_survival_manager.phase_growth_config.size(), 3, "Three phase growth configs")
|
||||
|
||||
func test_phase1_cells_range():
|
||||
var cfg = gauntlet_manager.phase_growth_config[0]
|
||||
var cfg = candy_survival_manager.phase_growth_config[0]
|
||||
assert_eq(cfg["cells_min"], 4, "Phase 1 min 4 cells/tick")
|
||||
assert_eq(cfg["cells_max"], 6, "Phase 1 max 6 cells/tick")
|
||||
|
||||
func test_phase2_cells_range():
|
||||
var cfg = gauntlet_manager.phase_growth_config[1]
|
||||
var cfg = candy_survival_manager.phase_growth_config[1]
|
||||
assert_eq(cfg["cells_min"], 6, "Phase 2 min 6 cells/tick")
|
||||
assert_eq(cfg["cells_max"], 8, "Phase 2 max 8 cells/tick")
|
||||
|
||||
func test_phase3_cells_range():
|
||||
var cfg = gauntlet_manager.phase_growth_config[2]
|
||||
var cfg = candy_survival_manager.phase_growth_config[2]
|
||||
assert_eq(cfg["cells_min"], 8, "Phase 3 min 8 cells/tick")
|
||||
assert_eq(cfg["cells_max"], 10, "Phase 3 max 10 cells/tick")
|
||||
|
||||
func test_cells_this_tick_in_phase_range():
|
||||
for phase in range(3):
|
||||
gauntlet_manager.current_phase = phase
|
||||
var cfg = gauntlet_manager.phase_growth_config[phase]
|
||||
candy_survival_manager.current_phase = phase
|
||||
var cfg = candy_survival_manager.phase_growth_config[phase]
|
||||
# Sample several times since the count is randomized.
|
||||
for _i in range(20):
|
||||
var n = gauntlet_manager._cells_this_tick()
|
||||
var n = candy_survival_manager._cells_this_tick()
|
||||
assert_true(n >= cfg["cells_min"] and n <= cfg["cells_max"],
|
||||
"Phase %d cells/tick %d within [%d,%d]" % [phase, n, cfg["cells_min"], cfg["cells_max"]])
|
||||
|
||||
@@ -83,15 +83,15 @@ func test_cells_this_tick_in_phase_range():
|
||||
# =============================================================================
|
||||
|
||||
func test_candidates_are_all_safe_cells():
|
||||
gauntlet_manager.current_phase = 0
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
candy_survival_manager.current_phase = 0
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
# Fresh arena: every playable cell is SAFE.
|
||||
assert_eq(candidates.size(), gauntlet_manager.playable_cell_count(),
|
||||
assert_eq(candidates.size(), candy_survival_manager.playable_cell_count(),
|
||||
"All playable cells are candidates on a fresh arena")
|
||||
|
||||
func test_candidates_exclude_sticky():
|
||||
gauntlet_manager.sticky_cells[Vector2i(3, 3)] = true
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
candy_survival_manager.sticky_cells[Vector2i(3, 3)] = true
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
var found := false
|
||||
for c in candidates:
|
||||
if c["pos"] == Vector2i(3, 3):
|
||||
@@ -99,8 +99,8 @@ func test_candidates_exclude_sticky():
|
||||
assert_false(found, "Sticky cells are excluded from candidates")
|
||||
|
||||
func test_candidates_exclude_cleansed():
|
||||
gauntlet_manager.mark_cleansed(Vector2i(4, 4))
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
candy_survival_manager.mark_cleansed(Vector2i(4, 4))
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
var found := false
|
||||
for c in candidates:
|
||||
if c["pos"] == Vector2i(4, 4):
|
||||
@@ -108,14 +108,14 @@ func test_candidates_exclude_cleansed():
|
||||
assert_false(found, "Cleansed cells are excluded from candidates (regrowth protection)")
|
||||
|
||||
func test_candidates_exclude_npc_and_boundary():
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
for c in candidates:
|
||||
var p = c["pos"]
|
||||
assert_false(gauntlet_manager._is_npc_zone(p), "No NPC-zone candidates")
|
||||
assert_false(gauntlet_manager._is_boundary(p), "No boundary candidates")
|
||||
assert_false(candy_survival_manager._is_npc_zone(p), "No NPC-zone candidates")
|
||||
assert_false(candy_survival_manager._is_boundary(p), "No boundary candidates")
|
||||
|
||||
func test_candidates_have_scores():
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
assert_true(candidates.size() > 0, "Has candidates")
|
||||
assert_true(candidates[0].has("score"), "Candidate carries a score")
|
||||
|
||||
@@ -124,13 +124,13 @@ func test_candidates_have_scores():
|
||||
# =============================================================================
|
||||
|
||||
func test_select_count_respected():
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
var picked = gauntlet_manager._select_cells_weighted(candidates, 5)
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
var picked = candy_survival_manager._select_cells_weighted(candidates, 5)
|
||||
assert_eq(picked.size(), 5, "Selects exactly the requested count")
|
||||
|
||||
func test_select_no_duplicates():
|
||||
var candidates = gauntlet_manager._generate_candidates()
|
||||
var picked = gauntlet_manager._select_cells_weighted(candidates, 10)
|
||||
var candidates = candy_survival_manager._generate_candidates()
|
||||
var picked = candy_survival_manager._select_cells_weighted(candidates, 10)
|
||||
var seen := {}
|
||||
for p in picked:
|
||||
assert_false(seen.has(p), "No duplicate selections")
|
||||
@@ -138,7 +138,7 @@ func test_select_no_duplicates():
|
||||
|
||||
func test_select_capped_at_pool_size():
|
||||
var small = [{"pos": Vector2i(2, 2), "score": 1.0}, {"pos": Vector2i(2, 3), "score": 1.0}]
|
||||
var picked = gauntlet_manager._select_cells_weighted(small, 10)
|
||||
var picked = candy_survival_manager._select_cells_weighted(small, 10)
|
||||
assert_eq(picked.size(), 2, "Cannot select more than pool size")
|
||||
|
||||
# =============================================================================
|
||||
@@ -146,14 +146,14 @@ func test_select_capped_at_pool_size():
|
||||
# =============================================================================
|
||||
|
||||
func test_layer_classification():
|
||||
assert_eq(gauntlet_manager._layer_of(Vector2i(9, 9)), "inner", "Center is inner")
|
||||
assert_eq(gauntlet_manager._layer_of(Vector2i(1, 1)), "outer", "Corner is outer")
|
||||
assert_eq(candy_survival_manager._layer_of(Vector2i(9, 9)), "inner", "Center is inner")
|
||||
assert_eq(candy_survival_manager._layer_of(Vector2i(1, 1)), "outer", "Corner is outer")
|
||||
|
||||
func test_sticky_neighbor_count():
|
||||
gauntlet_manager.sticky_cells[Vector2i(5, 5)] = true
|
||||
gauntlet_manager.sticky_cells[Vector2i(5, 6)] = true
|
||||
assert_eq(gauntlet_manager._sticky_neighbor_count(Vector2i(6, 5)), 2,
|
||||
candy_survival_manager.sticky_cells[Vector2i(5, 5)] = true
|
||||
candy_survival_manager.sticky_cells[Vector2i(5, 6)] = true
|
||||
assert_eq(candy_survival_manager._sticky_neighbor_count(Vector2i(6, 5)), 2,
|
||||
"Counts 8-directional sticky neighbors")
|
||||
|
||||
func test_chebyshev():
|
||||
assert_eq(gauntlet_manager._chebyshev(Vector2i(0, 0), Vector2i(3, 1)), 3, "Chebyshev distance")
|
||||
assert_eq(candy_survival_manager._chebyshev(Vector2i(0, 0), Vector2i(3, 1)), 3, "Chebyshev distance")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Movement Buffer System (v2) [Gauntlet #083]
|
||||
# Test: Candy Survival Movement Buffer System (v2) Candy Survival #083]
|
||||
# Hidden, decaying safe-corridor penalties layered onto candidate scoring.
|
||||
# Runs headless; elapsed_time = 0 so the final-30s window is inactive unless a
|
||||
# test sets elapsed_time directly.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
var manager
|
||||
var main_mock: Node
|
||||
var gridmap_mock: Node
|
||||
@@ -18,7 +18,7 @@ func before_each():
|
||||
gridmap_mock = Node.new()
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
manager = GauntletManager.new()
|
||||
manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(manager)
|
||||
manager.initialize(main_mock, gridmap_mock)
|
||||
manager.current_phase = 0
|
||||
@@ -61,7 +61,7 @@ func test_buffer_penalty_far_is_zero():
|
||||
|
||||
func test_buffer_penalty_lifts_in_final_window():
|
||||
manager._register_buffer(Vector2i(6, 6), 40.0)
|
||||
manager.elapsed_time = manager.gauntlet_round_duration() - 5.0 # within final 30s
|
||||
manager.elapsed_time = manager.candy_survival_round_duration() - 5.0 # within final 30s
|
||||
assert_eq(manager._buffer_penalty_at(Vector2i(6, 6)), 0.0, "Final window lifts buffers")
|
||||
|
||||
func test_buffer_penalty_empty_is_zero():
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# tests/test_gauntlet_registration.gd
|
||||
# Tests for [Gauntlet] #1 Game Mode Registration
|
||||
# Validates GAUNTLET enum, string conversion, lobby integration, and arena setup
|
||||
# tests/test_candy_survival_registration.gd
|
||||
# Tests for Candy Survival] #1 Game Mode Registration
|
||||
# Validates CANDY_SURVIVAL enum, string conversion, lobby integration, and arena setup
|
||||
|
||||
extends GutTest
|
||||
|
||||
func before_all():
|
||||
gut.p("=== Gauntlet Registration Tests [Gauntlet #1] ===")
|
||||
gut.p("=== Candy Survival Registration Tests Candy Survival #1] ===")
|
||||
|
||||
func after_each():
|
||||
pass
|
||||
@@ -14,35 +14,35 @@ func after_each():
|
||||
# GameMode Enum Tests
|
||||
# =============================================================================
|
||||
|
||||
# Test 1: GAUNTLET enum value exists and equals 3
|
||||
func test_gauntlet_enum_exists():
|
||||
assert_eq(GameMode.Mode.GAUNTLET, 3, "GAUNTLET should be enum value 3")
|
||||
# Test 1: CANDY_SURVIVAL enum value exists and equals 3
|
||||
func test_candy_survival_enum_exists():
|
||||
assert_eq(GameMode.Mode.CANDY_SURVIVAL, 3, "CANDY_SURVIVAL should be enum value 3")
|
||||
|
||||
# Test 2: All 3 modes are present in enum
|
||||
func test_all_modes_in_enum():
|
||||
assert_eq(GameMode.Mode.FREEMODE, 0, "FREEMODE should be 0")
|
||||
assert_eq(GameMode.Mode.STOP_N_GO, 1, "STOP_N_GO should be 1")
|
||||
assert_eq(GameMode.Mode.GAUNTLET, 3, "GAUNTLET should be 3")
|
||||
assert_eq(GameMode.Mode.CANDY_SURVIVAL, 3, "CANDY_SURVIVAL should be 3")
|
||||
|
||||
# =============================================================================
|
||||
# String Conversion Tests
|
||||
# =============================================================================
|
||||
|
||||
# Test 3: from_string recognizes "Candy Pump Survival"
|
||||
func test_from_string_candy_cannon():
|
||||
var result = GameMode.from_string("Candy Pump Survival")
|
||||
assert_eq(result, GameMode.Mode.GAUNTLET, "from_string should parse 'Candy Pump Survival' as GAUNTLET")
|
||||
# Test 3: from_string recognizes "Candy Survival"
|
||||
func test_from_string_candy_survival():
|
||||
var result = GameMode.from_string("Candy Survival")
|
||||
assert_eq(result, GameMode.Mode.CANDY_SURVIVAL, "from_string should parse 'Candy Survival' as CANDY_SURVIVAL")
|
||||
|
||||
# Test 4: mode_to_string returns "Candy Pump Survival" for GAUNTLET
|
||||
# Test 4: mode_to_string returns "Candy Survival" for CANDY_SURVIVAL
|
||||
func test_mode_to_string_gauntlet():
|
||||
var result = GameMode.mode_to_string(GameMode.Mode.GAUNTLET)
|
||||
assert_eq(result, "Candy Pump Survival", "mode_to_string should return 'Candy Pump Survival'")
|
||||
var result = GameMode.mode_to_string(GameMode.Mode.CANDY_SURVIVAL)
|
||||
assert_eq(result, "Candy Survival", "mode_to_string should return 'Candy Survival'")
|
||||
|
||||
# Test 5: Round-trip conversion is lossless
|
||||
func test_round_trip_conversion():
|
||||
var mode_str = GameMode.mode_to_string(GameMode.Mode.GAUNTLET)
|
||||
var mode_str = GameMode.mode_to_string(GameMode.Mode.CANDY_SURVIVAL)
|
||||
var mode_enum = GameMode.from_string(mode_str)
|
||||
assert_eq(mode_enum, GameMode.Mode.GAUNTLET, "Round-trip should preserve GAUNTLET")
|
||||
assert_eq(mode_enum, GameMode.Mode.CANDY_SURVIVAL, "Round-trip should preserve CANDY_SURVIVAL")
|
||||
|
||||
# Test 6: All existing modes still round-trip correctly
|
||||
func test_existing_modes_round_trip():
|
||||
@@ -60,10 +60,10 @@ func test_unknown_string_defaults_freemode():
|
||||
# get_all_modes Tests
|
||||
# =============================================================================
|
||||
|
||||
# Test 8: get_all_modes includes "Candy Pump Survival"
|
||||
# Test 8: get_all_modes includes "Candy Survival"
|
||||
func test_get_all_modes_includes_gauntlet():
|
||||
var modes = GameMode.get_all_modes()
|
||||
assert_has(modes, "Candy Pump Survival", "get_all_modes should include 'Candy Pump Survival'")
|
||||
assert_has(modes, "Candy Survival", "get_all_modes should include 'Candy Survival'")
|
||||
|
||||
# Test 9: get_all_modes returns exactly 3 entries
|
||||
func test_get_all_modes_count():
|
||||
@@ -75,16 +75,16 @@ func test_get_all_modes_order():
|
||||
var modes = GameMode.get_all_modes()
|
||||
assert_eq(modes[0], "Freemode", "First mode should be Freemode")
|
||||
assert_eq(modes[1], "Stop n Go", "Second mode should be Stop n Go")
|
||||
assert_eq(modes[2], "Candy Pump Survival", "Third mode should be Candy Pump Survival")
|
||||
assert_eq(modes[2], "Candy Survival", "Third mode should be Candy Survival")
|
||||
|
||||
# =============================================================================
|
||||
# is_restricted Tests
|
||||
# =============================================================================
|
||||
|
||||
# Test 11: GAUNTLET is a restricted mode
|
||||
func test_gauntlet_is_restricted():
|
||||
var result = GameMode.is_restricted(GameMode.Mode.GAUNTLET)
|
||||
assert_true(result, "GAUNTLET should be restricted (dedicated arena)")
|
||||
# Test 11: CANDY_SURVIVAL is a restricted mode
|
||||
func test_candy_survival_is_restricted():
|
||||
var result = GameMode.is_restricted(GameMode.Mode.CANDY_SURVIVAL)
|
||||
assert_true(result, "CANDY_SURVIVAL should be restricted (dedicated arena)")
|
||||
|
||||
# Test 12: FREEMODE is NOT restricted
|
||||
func test_freemode_not_restricted():
|
||||
@@ -94,46 +94,46 @@ func test_freemode_not_restricted():
|
||||
# Test 13: All restricted modes are confirmed
|
||||
func test_all_restricted_modes():
|
||||
assert_true(GameMode.is_restricted(GameMode.Mode.STOP_N_GO), "STOP_N_GO should be restricted")
|
||||
assert_true(GameMode.is_restricted(GameMode.Mode.GAUNTLET), "GAUNTLET should be restricted")
|
||||
assert_true(GameMode.is_restricted(GameMode.Mode.CANDY_SURVIVAL), "CANDY_SURVIVAL should be restricted")
|
||||
|
||||
# =============================================================================
|
||||
# LobbyManager Integration Tests
|
||||
# =============================================================================
|
||||
|
||||
# Test 14: Lobby available_game_modes includes "Candy Pump Survival"
|
||||
# Test 14: Lobby available_game_modes includes "Candy Survival"
|
||||
func test_lobby_modes_includes_gauntlet():
|
||||
var modes = LobbyManager.available_game_modes
|
||||
assert_has(modes, "Candy Pump Survival", "LobbyManager.available_game_modes should include 'Candy Pump Survival'")
|
||||
assert_has(modes, "Candy Survival", "LobbyManager.available_game_modes should include 'Candy Survival'")
|
||||
|
||||
# Test 15: gauntlet_manager.gd script file exists
|
||||
func test_gauntlet_manager_script_exists():
|
||||
var script_exists = ResourceLoader.exists("res://scripts/managers/gauntlet_manager.gd")
|
||||
assert_true(script_exists, "gauntlet_manager.gd should exist")
|
||||
# Test 15: candy_survival_manager.gd script file exists
|
||||
func test_candy_survival_manager_script_exists():
|
||||
var script_exists = ResourceLoader.exists("res://scripts/managers/candy_survival_manager.gd")
|
||||
assert_true(script_exists, "candy_survival_manager.gd should exist")
|
||||
|
||||
# Test 16: GauntletManager class can be loaded
|
||||
func test_gauntlet_manager_loads():
|
||||
var script = load("res://scripts/managers/gauntlet_manager.gd")
|
||||
assert_not_null(script, "gauntlet_manager.gd should load without errors")
|
||||
# Test 16: CandySurvivalManager class can be loaded
|
||||
func test_candy_survival_manager_loads():
|
||||
var script = load("res://scripts/managers/candy_survival_manager.gd")
|
||||
assert_not_null(script, "candy_survival_manager.gd should load without errors")
|
||||
|
||||
# Test 17: GauntletManager has required methods
|
||||
func test_gauntlet_manager_has_methods():
|
||||
var manager = GauntletManager.new()
|
||||
assert_true(manager.has_method("_setup_arena"), "GauntletManager should have _setup_arena()")
|
||||
assert_true(manager.has_method("_apply_arena_setup"), "GauntletManager should have _apply_arena_setup()")
|
||||
assert_true(manager.has_method("start_game_mode"), "GauntletManager should have start_game_mode()")
|
||||
assert_true(manager.has_method("initialize"), "GauntletManager should have initialize()")
|
||||
# Test 17: CandySurvivalManager has required methods
|
||||
func test_candy_survival_manager_has_methods():
|
||||
var manager = CandySurvivalManager.new()
|
||||
assert_true(manager.has_method("_setup_arena"), "CandySurvivalManager should have _setup_arena()")
|
||||
assert_true(manager.has_method("_apply_arena_setup"), "CandySurvivalManager should have _apply_arena_setup()")
|
||||
assert_true(manager.has_method("start_game_mode"), "CandySurvivalManager should have start_game_mode()")
|
||||
assert_true(manager.has_method("initialize"), "CandySurvivalManager should have initialize()")
|
||||
manager.free()
|
||||
|
||||
# Test 18: GauntletManager arena constants are correct
|
||||
func test_gauntlet_arena_constants():
|
||||
assert_eq(GauntletManager.ARENA_COLUMNS, 20, "Arena should be 20 columns")
|
||||
assert_eq(GauntletManager.ARENA_ROWS, 20, "Arena should be 20 rows")
|
||||
assert_eq(GauntletManager.NPC_SIZE, 3, "NPC zone should be 3x3")
|
||||
assert_eq(GauntletManager.NPC_CENTER, Vector2i(9, 9), "NPC center should be at (9,9)")
|
||||
# Test 18: CandySurvivalManager arena constants are correct
|
||||
func test_candy_survival_arena_constants():
|
||||
assert_eq(CandySurvivalManager.ARENA_COLUMNS, 20, "Arena should be 20 columns")
|
||||
assert_eq(CandySurvivalManager.ARENA_ROWS, 20, "Arena should be 20 rows")
|
||||
assert_eq(CandySurvivalManager.NPC_SIZE, 3, "NPC zone should be 3x3")
|
||||
assert_eq(CandySurvivalManager.NPC_CENTER, Vector2i(9, 9), "NPC center should be at (9,9)")
|
||||
|
||||
# Test 19: NPC zone detection works
|
||||
func test_npc_zone_detection():
|
||||
var manager = GauntletManager.new()
|
||||
var manager = CandySurvivalManager.new()
|
||||
# Center of NPC zone
|
||||
assert_true(manager._is_npc_zone(Vector2i(9, 9)), "Center (9,9) should be NPC zone")
|
||||
# Edges of NPC zone
|
||||
@@ -146,10 +146,10 @@ func test_npc_zone_detection():
|
||||
manager.free()
|
||||
|
||||
# Test 20: Phase enum has 3 phases
|
||||
func test_gauntlet_phases():
|
||||
assert_eq(GauntletManager.Phase.OPEN_ARENA, 0, "OPEN_ARENA should be 0")
|
||||
assert_eq(GauntletManager.Phase.ROUTE_PRESSURE, 1, "ROUTE_PRESSURE should be 1")
|
||||
assert_eq(GauntletManager.Phase.SURVIVAL_ENDGAME, 2, "SURVIVAL_ENDGAME should be 2")
|
||||
func test_candy_survival_phases():
|
||||
assert_eq(CandySurvivalManager.Phase.OPEN_ARENA, 0, "OPEN_ARENA should be 0")
|
||||
assert_eq(CandySurvivalManager.Phase.ROUTE_PRESSURE, 1, "ROUTE_PRESSURE should be 1")
|
||||
assert_eq(CandySurvivalManager.Phase.SURVIVAL_ENDGAME, 2, "SURVIVAL_ENDGAME should be 2")
|
||||
|
||||
func after_all():
|
||||
gut.p("=== Gauntlet Registration Tests Complete ===")
|
||||
gut.p("=== Candy Survival Registration Tests Complete ===")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Candidate Scoring System (v2) [Gauntlet #073]
|
||||
# Test: Candy Survival Candidate Scoring System (v2) Candy Survival #073]
|
||||
# Covers each score component, camping accumulation, and full-formula
|
||||
# composition. Runs headless (no multiplayer peer), so elapsed_time = 0 and
|
||||
# the final-30s window is inactive unless a test sets elapsed_time directly.
|
||||
# =============================================================================
|
||||
|
||||
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
|
||||
const CandySurvivalManager = preload("res://scripts/managers/candy_survival_manager.gd")
|
||||
var manager
|
||||
var main_mock: Node
|
||||
var gridmap_mock: Node
|
||||
@@ -18,7 +18,7 @@ func before_each():
|
||||
gridmap_mock = Node.new()
|
||||
gridmap_mock.name = "EnhancedGridMap"
|
||||
main_mock.add_child(gridmap_mock)
|
||||
manager = GauntletManager.new()
|
||||
manager = CandySurvivalManager.new()
|
||||
main_mock.add_child(manager)
|
||||
manager.initialize(main_mock, gridmap_mock)
|
||||
manager.current_phase = 0
|
||||
@@ -94,7 +94,7 @@ func test_player_pressure_under_player_penalized():
|
||||
assert_eq(manager._score_player_pressure(Vector2i(5, 5), players), -50.0, "Under player (early) = -50")
|
||||
|
||||
func test_player_pressure_under_player_final_window():
|
||||
manager.elapsed_time = manager.gauntlet_round_duration() - 5.0 # within final 30s
|
||||
manager.elapsed_time = manager.candy_survival_round_duration() - 5.0 # within final 30s
|
||||
var players = [Vector2i(5, 5)]
|
||||
assert_eq(manager._score_player_pressure(Vector2i(5, 5), players), 10.0, "Under player (final) = +10")
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Sticky Cell System (v2) [Gauntlet #068]
|
||||
# Test: Candy Survival Sticky Cell System (v2) Candy Survival #068]
|
||||
# Covers cell states, CLEANSED protection, coverage helpers, and the
|
||||
# path-safety reachability (BFS) check.
|
||||
# =============================================================================
|
||||
|
||||
var GauntletManagerScript = load("res://scripts/managers/gauntlet_manager.gd")
|
||||
var manager: GauntletManager
|
||||
var CandySurvivalManagerScript = load("res://scripts/managers/candy_survival_manager.gd")
|
||||
var manager: CandySurvivalManager
|
||||
|
||||
func before_each():
|
||||
manager = GauntletManagerScript.new()
|
||||
manager = CandySurvivalManagerScript.new()
|
||||
add_child(manager)
|
||||
|
||||
func after_each():
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
extends GutTest
|
||||
|
||||
# =============================================================================
|
||||
# Test: Gauntlet Tile Spawning & Mission System (Task #3)
|
||||
# Test: Candy Survival Tile Spawning & Mission System (Task #3)
|
||||
# =============================================================================
|
||||
|
||||
var GauntletManagerScript = load("res://scripts/managers/gauntlet_manager.gd")
|
||||
var manager: GauntletManager
|
||||
var CandySurvivalManagerScript = load("res://scripts/managers/candy_survival_manager.gd")
|
||||
var manager: CandySurvivalManager
|
||||
|
||||
func before_each():
|
||||
manager = GauntletManagerScript.new()
|
||||
manager = CandySurvivalManagerScript.new()
|
||||
add_child(manager)
|
||||
|
||||
func after_each():
|
||||
@@ -123,22 +123,22 @@ func test_clear_sticky_cell():
|
||||
# =============================================================================
|
||||
|
||||
func test_initial_phase_is_open_arena():
|
||||
assert_eq(manager.current_phase, GauntletManager.Phase.OPEN_ARENA, "Should start in Open Arena")
|
||||
assert_eq(manager.current_phase, CandySurvivalManager.Phase.OPEN_ARENA, "Should start in Open Arena")
|
||||
|
||||
func test_phase_to_string_open_arena():
|
||||
assert_eq(manager._phase_to_string(GauntletManager.Phase.OPEN_ARENA), "Outer Pressure")
|
||||
assert_eq(manager._phase_to_string(CandySurvivalManager.Phase.OPEN_ARENA), "Outer Pressure")
|
||||
|
||||
func test_phase_to_string_route_pressure():
|
||||
assert_eq(manager._phase_to_string(GauntletManager.Phase.ROUTE_PRESSURE), "Middle Pressure")
|
||||
assert_eq(manager._phase_to_string(CandySurvivalManager.Phase.ROUTE_PRESSURE), "Middle Pressure")
|
||||
|
||||
func test_phase_to_string_survival():
|
||||
assert_eq(manager._phase_to_string(GauntletManager.Phase.SURVIVAL_ENDGAME), "Inner Survival")
|
||||
assert_eq(manager._phase_to_string(CandySurvivalManager.Phase.SURVIVAL_ENDGAME), "Inner Survival")
|
||||
|
||||
# =============================================================================
|
||||
# Match Timer Integration
|
||||
# =============================================================================
|
||||
|
||||
func test_match_duration_180s():
|
||||
# Gauntlet uses 180s match (3 phases: 0-60, 60-120, 120-180)
|
||||
# Candy Survival uses 180s match (3 phases: 0-60, 60-120, 120-180)
|
||||
var total = manager.PHASE_3_START + 60.0 # Phase 3 starts at 120, runs 60s
|
||||
assert_eq(total, 180.0, "Total match should be 180 seconds")
|
||||
|
||||
Reference in New Issue
Block a user