extends GutTest # ============================================================================= # Test: Gauntlet Cleanser Power-Up (v2) [Gauntlet #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 GridMapMock = preload("res://tests/helpers/gridmap_mock.gd") const MainMock = preload("res://tests/helpers/main_mock.gd") var manager var main_mock: Node var gridmap_mock: Node func before_each(): main_mock = MainMock.new() add_child(main_mock) gridmap_mock = GridMapMock.new() gridmap_mock.name = "EnhancedGridMap" main_mock.add_child(gridmap_mock) manager = GauntletManager.new() main_mock.add_child(manager) manager.initialize(main_mock, gridmap_mock) manager.current_phase = 0 func after_each(): if main_mock: main_mock.queue_free() func _without_peer(fn: Callable) -> void: var saved = multiplayer.multiplayer_peer multiplayer.multiplayer_peer = null fn.call() multiplayer.multiplayer_peer = saved # ============================================================================= # Grant cadence: every 2 missions, inventory max 1 # ============================================================================= func test_no_cleanser_after_one_mission(): manager._on_goal_count_updated(7, 1) assert_eq(manager.player_cleansers.get(7, 0), 0, "No cleanser after 1 mission") func test_cleanser_granted_after_two_missions(): manager._on_goal_count_updated(7, 1) manager._on_goal_count_updated(7, 2) assert_eq(manager.player_cleansers.get(7, 0), 1, "Cleanser granted after 2 missions") func test_cleanser_inventory_capped_at_one(): # Four missions would be two grants, but inventory caps at 1 (not consumed). for i in range(4): manager._on_goal_count_updated(7, i + 1) assert_eq(manager.player_cleansers.get(7, 0), 1, "Inventory never exceeds 1") # ============================================================================= # Activation / immunity lifecycle # ============================================================================= func test_use_cleanser_cell_decrements_until_exhausted(): manager.cleanser_active[3] = true manager.cleanser_cells_left[3] = manager.CLEANSER_MAX_CELLS # First 4 uses keep it active... for i in range(manager.CLEANSER_MAX_CELLS - 1): assert_true(manager.use_cleanser_cell(3), "Still active on use %d" % (i + 1)) # ...the 5th use exhausts it. assert_false(manager.use_cleanser_cell(3), "Exhausted after 5th cell") assert_false(manager.is_cleanser_active(3), "Deactivated after 5th cell") func test_use_cleanser_cell_when_inactive_returns_false(): assert_false(manager.use_cleanser_cell(99), "Inactive cleanser use returns false") func test_deactivate_clears_state(): manager.cleanser_active[5] = true manager.cleanser_cells_left[5] = 3 manager.deactivate_cleanser(5) assert_false(manager.is_cleanser_active(5), "Deactivated") assert_false(manager.cleanser_cells_left.has(5), "Cells-left cleared") # ============================================================================= # Sticky clearing # ============================================================================= func test_clear_sticky_cell_removes_and_protects(): manager.sticky_cells[Vector2i(4, 4)] = true _without_peer(func(): manager.clear_sticky_cell(Vector2i(4, 4)) ) assert_false(manager.is_sticky_cell(Vector2i(4, 4)), "Sticky removed") assert_true(manager.is_cleansed_cell(Vector2i(4, 4)), "Cleared cell gets regrowth protection") # Layer-2 overlay cleared (mock records -1 = erased). assert_eq(gridmap_mock.get_cell_item(Vector3i(4, 2, 4)), -1, "Layer-2 sticky overlay cleared") # ============================================================================= # Safe-stop early termination (#072 acceptance: ends when stopping on safe cell) # ============================================================================= func test_stop_on_safe_cell_ends_cleanser(): manager.cleanser_active[8] = true manager.cleanser_cells_left[8] = 3 manager.notify_movement_stopped(8, Vector2i(5, 5)) # safe cell assert_false(manager.is_cleanser_active(8), "Cleanser ends on safe-cell stop") func test_stop_on_sticky_cell_keeps_cleanser(): manager.sticky_cells[Vector2i(6, 6)] = true manager.cleanser_active[8] = true manager.cleanser_cells_left[8] = 3 manager.notify_movement_stopped(8, Vector2i(6, 6)) # still on sticky assert_true(manager.is_cleanser_active(8), "Cleanser persists while still on sticky") func test_notify_stop_noop_without_cleanser(): # Should not crash or change anything when the player has no cleanser. manager.notify_movement_stopped(123, Vector2i(5, 5)) assert_false(manager.is_cleanser_active(123), "No cleanser → no-op")