205 lines
5.9 KiB
Python
205 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Perspective Patrol Demo
|
|
=======================
|
|
|
|
Demonstrates the FOV/perspective system with an animated patrolling entity.
|
|
|
|
Features:
|
|
- 20x20 grid with 10x10 opaque obstacle in center
|
|
- Entity patrols around the obstacle in a square pattern
|
|
- ColorLayer shows fog of war (visible/discovered/unknown)
|
|
- Press 'R' to reset vision (shows unknown vs discovered difference)
|
|
- Press 'Space' to pause/resume patrol
|
|
"""
|
|
|
|
import mcrfpy
|
|
|
|
# Patrol waypoints (clockwise around the center obstacle)
|
|
WAYPOINTS = [
|
|
(3, 3), # Top-left
|
|
(16, 3), # Top-right
|
|
(16, 16), # Bottom-right
|
|
(3, 16), # Bottom-left
|
|
]
|
|
|
|
# State
|
|
current_waypoint = 0
|
|
patrol_paused = False
|
|
move_timer_ms = 150 # Time between moves
|
|
|
|
# Global references
|
|
g_grid = None
|
|
g_patrol = None
|
|
g_fov_layer = None
|
|
|
|
def setup_scene():
|
|
"""Create the demo scene"""
|
|
global g_grid, g_patrol, g_fov_layer
|
|
|
|
mcrfpy.createScene("patrol_demo")
|
|
mcrfpy.setScene("patrol_demo")
|
|
|
|
ui = mcrfpy.sceneUI("patrol_demo")
|
|
|
|
# Title
|
|
title = mcrfpy.Caption(text="Perspective Patrol Demo", pos=(10, 10))
|
|
title.fill_color = mcrfpy.Color(255, 255, 255)
|
|
ui.append(title)
|
|
|
|
# Instructions
|
|
instructions = mcrfpy.Caption(text="[R] Reset vision [Space] Pause/Resume [Q] Quit", pos=(10, 35))
|
|
instructions.fill_color = mcrfpy.Color(180, 180, 180)
|
|
ui.append(instructions)
|
|
|
|
# Create grid (20x20, each cell 24px) - centered in 1024x768 window
|
|
grid_size_px = 480
|
|
grid = mcrfpy.Grid(
|
|
pos=((1024 - grid_size_px) // 2, (768 - grid_size_px) // 2),
|
|
size=(grid_size_px, grid_size_px),
|
|
grid_size=(20, 20),
|
|
texture=None
|
|
)
|
|
grid.center = (10*16, 10*16)
|
|
grid.fill_color = mcrfpy.Color(40, 40, 50) # Dark floor background
|
|
ui.append(grid)
|
|
|
|
# Set FOV settings
|
|
grid.fov = mcrfpy.FOV.SHADOW
|
|
grid.fov_radius = 8
|
|
|
|
# Initialize all cells as walkable/transparent (floor)
|
|
for y in range(20):
|
|
for x in range(20):
|
|
point = grid.at(x, y)
|
|
point.walkable = True
|
|
point.transparent = True
|
|
|
|
# Create 10x10 obstacle box in center (cells 5-14 in both dimensions)
|
|
for y in range(5, 15):
|
|
for x in range(5, 15):
|
|
point = grid.at(x, y)
|
|
point.walkable = False
|
|
point.transparent = False
|
|
|
|
# Create a color layer for the walls (so we can see them)
|
|
wall_layer = grid.add_layer('color', z_index=-2)
|
|
wall_layer.fill((40, 40, 50, 255)) # Match floor color
|
|
|
|
# Draw walls on the wall layer
|
|
for y in range(5, 15):
|
|
for x in range(5, 15):
|
|
wall_layer.set(x, y, mcrfpy.Color(100, 70, 50, 255)) # Brown walls
|
|
|
|
# Create FOV layer (above walls, below entities)
|
|
fov_layer = grid.add_layer('color', z_index=-1)
|
|
fov_layer.fill((0, 0, 0, 255)) # Start completely black (unknown)
|
|
|
|
# Create patrolling entity
|
|
patrol = mcrfpy.Entity(WAYPOINTS[0])
|
|
patrol.sprite_index = 64 # '@' character typically
|
|
grid.entities.append(patrol)
|
|
|
|
# Bind FOV layer to entity
|
|
fov_layer.apply_perspective(
|
|
entity=patrol,
|
|
visible=(0, 0, 0, 0), # Fully transparent when visible
|
|
discovered=(20, 20, 40, 180), # Dark blue-gray when discovered
|
|
unknown=(0, 0, 0, 255) # Black when never seen
|
|
)
|
|
|
|
# Initial visibility update
|
|
patrol.update_visibility()
|
|
|
|
# Store references for timer callbacks
|
|
g_grid = grid
|
|
g_patrol = patrol
|
|
g_fov_layer = fov_layer
|
|
|
|
# Status caption (below centered grid)
|
|
status = mcrfpy.Caption(text="Status: Patrolling", pos=(10, 720))
|
|
status.fill_color = mcrfpy.Color(100, 255, 100)
|
|
status.name = "status"
|
|
ui.append(status)
|
|
|
|
# Set up keyboard handler
|
|
mcrfpy.keypressScene(on_keypress)
|
|
|
|
# Start patrol timer
|
|
mcrfpy.setTimer("patrol", patrol_step, move_timer_ms)
|
|
|
|
def patrol_step(runtime):
|
|
"""Move entity one step toward current waypoint"""
|
|
global current_waypoint, patrol_paused
|
|
|
|
if patrol_paused:
|
|
return
|
|
|
|
# Get current position and target
|
|
px, py = int(g_patrol.x), int(g_patrol.y)
|
|
tx, ty = WAYPOINTS[current_waypoint]
|
|
|
|
# Calculate direction
|
|
dx = 0 if tx == px else (1 if tx > px else -1)
|
|
dy = 0 if ty == py else (1 if ty > py else -1)
|
|
|
|
# Move one step (prefer horizontal, then vertical)
|
|
if dx != 0:
|
|
g_patrol.x = px + dx
|
|
elif dy != 0:
|
|
g_patrol.y = py + dy
|
|
|
|
# Update visibility after move
|
|
g_patrol.update_visibility()
|
|
|
|
# Check if reached waypoint
|
|
if int(g_patrol.x) == tx and int(g_patrol.y) == ty:
|
|
current_waypoint = (current_waypoint + 1) % len(WAYPOINTS)
|
|
update_status(f"Reached waypoint, heading to {WAYPOINTS[current_waypoint]}")
|
|
|
|
def on_keypress(key, state):
|
|
"""Handle keyboard input"""
|
|
global patrol_paused
|
|
|
|
if state != "start":
|
|
return
|
|
|
|
if key == "R":
|
|
reset_vision()
|
|
elif key == "Space":
|
|
patrol_paused = not patrol_paused
|
|
if patrol_paused:
|
|
update_status("Status: PAUSED")
|
|
else:
|
|
update_status("Status: Patrolling")
|
|
elif key == "Q":
|
|
mcrfpy.setScene(None)
|
|
|
|
def reset_vision():
|
|
"""Reset entity's discovered state to demonstrate unknown vs discovered"""
|
|
global g_patrol, g_fov_layer
|
|
|
|
# Clear entity's gridstate (forget everything)
|
|
for state in g_patrol.gridstate:
|
|
state.visible = False
|
|
state.discovered = False
|
|
|
|
# Re-fill the layer with unknown color
|
|
g_fov_layer.fill((0, 0, 0, 255))
|
|
|
|
# Update visibility from current position (will mark current FOV as visible)
|
|
g_patrol.update_visibility()
|
|
|
|
update_status("Vision RESET - watch discovered vs unknown!")
|
|
|
|
def update_status(text):
|
|
"""Update status caption"""
|
|
ui = mcrfpy.sceneUI("patrol_demo")
|
|
for element in ui:
|
|
if hasattr(element, 'name') and element.name == "status":
|
|
element.text = text
|
|
break
|
|
|
|
# Run the demo
|
|
setup_scene()
|