McRogueFace/tests/unit/test_tcod_fov_entities.py
John McCardle 018e73590f feat: Implement FOV enum and layer draw_fov for #114 and #113
Phase 1 - FOV Enum System:
- Create PyFOV.h/cpp with mcrfpy.FOV IntEnum (BASIC, DIAMOND, SHADOW, etc.)
- Add mcrfpy.default_fov module property initialized to FOV.BASIC
- Add grid.fov and grid.fov_radius properties for per-grid defaults
- Remove deprecated module-level FOV_* constants (breaking change)

Phase 2 - Layer Operations:
- Implement ColorLayer.fill_rect(pos, size, color) for rectangle fills
- Implement TileLayer.fill_rect(pos, size, index) for tile rectangle fills
- Implement ColorLayer.draw_fov(source, radius, fov, visible, discovered, unknown)
  to paint FOV-based visibility on color layers using parent grid's TCOD map

The FOV enum uses Python's IntEnum for type safety while maintaining
backward compatibility with integer values. Tests updated to use new API.

Addresses #114 (FOV enum), #113 (layer operations)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:18:10 -05:00

148 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
Test TCOD Field of View with Two Entities
==========================================
Demonstrates:
1. Grid with obstacles (walls)
2. Two entities at different positions
3. Entity-specific FOV calculation
4. Color layer for FOV visualization (new API)
"""
import mcrfpy
import sys
def run_tests():
"""Run FOV entity tests"""
print("=== TCOD FOV Entity Tests ===\n")
# Test 1: FOV enum availability
print("Test 1: FOV Enum")
try:
print(f" FOV.BASIC = {mcrfpy.FOV.BASIC}")
print(f" FOV.SHADOW = {mcrfpy.FOV.SHADOW}")
print("✓ FOV enum available\n")
except Exception as e:
print(f"✗ FOV enum not available: {e}")
return False
# Test 2: Create grid with walls
print("Test 2: Grid Creation with Walls")
grid = mcrfpy.Grid(pos=(0, 0), size=(640, 400), grid_size=(40, 25))
# Set up walls
for y in range(25):
for x in range(40):
point = grid.at(x, y)
# Border walls
if x == 0 or x == 39 or y == 0 or y == 24:
point.walkable = False
point.transparent = False
# Central wall
elif x == 20 and y != 12: # Wall with door at y=12
point.walkable = False
point.transparent = False
else:
point.walkable = True
point.transparent = True
print("✓ Grid with walls created\n")
# Test 3: Create entities
print("Test 3: Entity Creation")
player = mcrfpy.Entity((5, 12))
enemy = mcrfpy.Entity((35, 12))
grid.entities.append(player)
grid.entities.append(enemy)
print(f" Player at ({player.x}, {player.y})")
print(f" Enemy at ({enemy.x}, {enemy.y})")
print("✓ Entities created\n")
# Test 4: FOV calculation for player
print("Test 4: Player FOV Calculation")
grid.compute_fov(int(player.x), int(player.y), radius=15, algorithm=mcrfpy.FOV.SHADOW)
# Player should see themselves
assert grid.is_in_fov(int(player.x), int(player.y)), "Player should see themselves"
print(" Player can see their own position")
# Player should see nearby cells
assert grid.is_in_fov(6, 12), "Player should see adjacent cells"
print(" Player can see adjacent cells")
# Player should NOT see behind the wall (outside door line)
assert not grid.is_in_fov(21, 5), "Player should not see behind wall"
print(" Player cannot see behind wall at (21, 5)")
# Player should NOT see enemy (behind wall even with door)
assert not grid.is_in_fov(int(enemy.x), int(enemy.y)), "Player should not see enemy"
print(" Player cannot see enemy")
print("✓ Player FOV working correctly\n")
# Test 5: FOV calculation for enemy
print("Test 5: Enemy FOV Calculation")
grid.compute_fov(int(enemy.x), int(enemy.y), radius=15, algorithm=mcrfpy.FOV.SHADOW)
# Enemy should see themselves
assert grid.is_in_fov(int(enemy.x), int(enemy.y)), "Enemy should see themselves"
print(" Enemy can see their own position")
# Enemy should NOT see player (behind wall)
assert not grid.is_in_fov(int(player.x), int(player.y)), "Enemy should not see player"
print(" Enemy cannot see player")
print("✓ Enemy FOV working correctly\n")
# Test 6: FOV with color layer
print("Test 6: FOV Color Layer Visualization")
fov_layer = grid.add_layer('color', z_index=-1)
fov_layer.fill((0, 0, 0, 255)) # Start with black (unknown)
# Draw player FOV
fov_layer.draw_fov(
source=(int(player.x), int(player.y)),
radius=10,
fov=mcrfpy.FOV.SHADOW,
visible=(255, 255, 200, 64),
discovered=(100, 100, 100, 128),
unknown=(0, 0, 0, 255)
)
# Check visible cell
visible_cell = fov_layer.at(int(player.x), int(player.y))
assert visible_cell.r == 255, "Player position should be visible"
print(" Player position has visible color")
# Check hidden cell (behind wall)
hidden_cell = fov_layer.at(int(enemy.x), int(enemy.y))
assert hidden_cell.r == 0, "Enemy position should be unknown"
print(" Enemy position has unknown color")
print("✓ FOV color layer working correctly\n")
# Test 7: Line of sight via libtcod
print("Test 7: Line Drawing")
line = mcrfpy.libtcod.line(int(player.x), int(player.y), int(enemy.x), int(enemy.y))
print(f" Line from player to enemy: {len(line)} cells")
assert len(line) > 0, "Line should have cells"
print("✓ Line drawing working\n")
print("=== All FOV Entity Tests Passed! ===")
return True
# Main execution
if __name__ == "__main__":
try:
if run_tests():
print("\nPASS")
sys.exit(0)
else:
print("\nFAIL")
sys.exit(1)
except Exception as e:
print(f"\nFAIL: {e}")
import traceback
traceback.print_exc()
sys.exit(1)