feat: Thread-safe FOV system with improved API

Major improvements to the Field of View (FOV) system:

1. Added thread safety with mutex protection
   - Added mutable std::mutex fov_mutex to UIGrid class
   - Protected computeFOV() and isInFOV() with lock_guard
   - Minimal overhead for current single-threaded operation
   - Ready for future multi-threading requirements

2. Enhanced compute_fov() API to return visible cells
   - Changed return type from void to List[Tuple[int, int, bool, bool]]
   - Returns (x, y, visible, discovered) for all visible cells
   - Maintains backward compatibility by still updating internal FOV state
   - Allows FOV queries without affecting entity states

3. Fixed Part 4 tutorial visibility rendering
   - Added required entity.update_visibility() calls after compute_fov()
   - Fixed black grid issue in perspective rendering
   - Updated hallway generation to use L-shaped corridors

The architecture now properly separates concerns while maintaining
performance and preparing for future enhancements. Each entity can
have independent FOV calculations without race conditions.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John McCardle 2025-07-22 23:00:34 -04:00
commit 7aef412343
4 changed files with 86 additions and 14 deletions

View file

@ -80,8 +80,17 @@ def carve_room(room):
point.transparent = True
def carve_hallway(x1, y1, x2, y2):
points = mcrfpy.libtcod.line(x1, y1, x2, y2)
#points = mcrfpy.libtcod.line(x1, y1, x2, y2)
points = []
if random.choice([True, False]):
# x1,y1 -> x2,y1 -> x2,y2
points.extend(mcrfpy.libtcod.line(x1, y1, x2, y1))
points.extend(mcrfpy.libtcod.line(x2, y1, x2, y2))
else:
# x1,y1 -> x1,y2 -> x2,y2
points.extend(mcrfpy.libtcod.line(x1, y1, x1, y2))
points.extend(mcrfpy.libtcod.line(x1, y2, x2, y2))
for x, y in points:
if 0 <= x < grid_width and 0 <= y < grid_height:
point = grid.at(x, y)
@ -173,8 +182,10 @@ def update_fov():
"""
if grid.perspective == player:
grid.compute_fov(int(player.x), int(player.y), radius=8, algorithm=0)
player.update_visibility()
elif enemy and grid.perspective == enemy:
grid.compute_fov(int(enemy.x), int(enemy.y), radius=6, algorithm=0)
enemy.update_visibility()
# Perform initial FOV calculation
update_fov()
@ -352,4 +363,4 @@ print("- Unexplored areas are black")
print("- Previously seen areas are dark")
print("- Currently visible areas are lit")
print("Press Tab to switch between player and enemy perspective!")
print("Use WASD or Arrow keys to move!")
print("Use WASD or Arrow keys to move!")