392 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			392 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
#!/usr/bin/env python3
 | 
						|
"""
 | 
						|
Path & Vision Sizzle Reel
 | 
						|
=========================
 | 
						|
 | 
						|
A choreographed demo showing:
 | 
						|
- Smooth entity movement along paths
 | 
						|
- Camera following with grid center animation
 | 
						|
- Field of view updates as entities move
 | 
						|
- Dramatic perspective transitions with zoom effects
 | 
						|
"""
 | 
						|
 | 
						|
import mcrfpy
 | 
						|
import sys
 | 
						|
 | 
						|
# Colors
 | 
						|
WALL_COLOR = mcrfpy.Color(40, 30, 30)
 | 
						|
FLOOR_COLOR = mcrfpy.Color(80, 80, 100)
 | 
						|
PATH_COLOR = mcrfpy.Color(120, 120, 180)
 | 
						|
DARK_FLOOR = mcrfpy.Color(40, 40, 50)
 | 
						|
 | 
						|
# Global state
 | 
						|
grid = None
 | 
						|
player = None
 | 
						|
enemy = None
 | 
						|
sequence_step = 0
 | 
						|
player_path = []
 | 
						|
enemy_path = []
 | 
						|
player_path_index = 0
 | 
						|
enemy_path_index = 0
 | 
						|
 | 
						|
def create_scene():
 | 
						|
    """Create the demo environment"""
 | 
						|
    global grid, player, enemy
 | 
						|
    
 | 
						|
    mcrfpy.createScene("path_vision_demo")
 | 
						|
    
 | 
						|
    # Create larger grid for more dramatic movement
 | 
						|
    grid = mcrfpy.Grid(grid_x=40, grid_y=25)
 | 
						|
    grid.fill_color = mcrfpy.Color(20, 20, 30)
 | 
						|
    
 | 
						|
    # Map layout - interconnected rooms with corridors
 | 
						|
    map_layout = [
 | 
						|
        "########################################",  # 0
 | 
						|
        "#......##########......################",  # 1
 | 
						|
        "#......##########......################",  # 2
 | 
						|
        "#......##########......################",  # 3
 | 
						|
        "#......#.........#.....################",  # 4
 | 
						|
        "#......#.........#.....################",  # 5
 | 
						|
        "####.###.........####.#################",  # 6
 | 
						|
        "####.....................##############",  # 7
 | 
						|
        "####.....................##############",  # 8
 | 
						|
        "####.###.........####.#################",  # 9
 | 
						|
        "#......#.........#.....################",  # 10
 | 
						|
        "#......#.........#.....################",  # 11
 | 
						|
        "#......#.........#.....################",  # 12
 | 
						|
        "#......###.....###.....################",  # 13
 | 
						|
        "#......###.....###.....################",  # 14
 | 
						|
        "#......###.....###.....#########......#",  # 15
 | 
						|
        "#......###.....###.....#########......#",  # 16
 | 
						|
        "#......###.....###.....#########......#",  # 17
 | 
						|
        "#####.############.#############......#",  # 18
 | 
						|
        "#####...........................#.....#",  # 19
 | 
						|
        "#####...........................#.....#",  # 20
 | 
						|
        "#####.############.#############......#",  # 21
 | 
						|
        "#......###########.##########.........#",  # 22
 | 
						|
        "#......###########.##########.........#",  # 23
 | 
						|
        "########################################",  # 24
 | 
						|
    ]
 | 
						|
    
 | 
						|
    # Build the map
 | 
						|
    for y, row in enumerate(map_layout):
 | 
						|
        for x, char in enumerate(row):
 | 
						|
            cell = grid.at(x, y)
 | 
						|
            if char == '#':
 | 
						|
                cell.walkable = False
 | 
						|
                cell.transparent = False
 | 
						|
                cell.color = WALL_COLOR
 | 
						|
            else:
 | 
						|
                cell.walkable = True
 | 
						|
                cell.transparent = True
 | 
						|
                cell.color = FLOOR_COLOR
 | 
						|
    
 | 
						|
    # Create player in top-left room
 | 
						|
    player = mcrfpy.Entity(3, 3, grid=grid)
 | 
						|
    player.sprite_index = 64  # @
 | 
						|
    
 | 
						|
    # Create enemy in bottom-right area
 | 
						|
    enemy = mcrfpy.Entity(35, 20, grid=grid)
 | 
						|
    enemy.sprite_index = 69  # E
 | 
						|
    
 | 
						|
    # Initial visibility
 | 
						|
    player.update_visibility()
 | 
						|
    enemy.update_visibility()
 | 
						|
    
 | 
						|
    # Set initial perspective to player
 | 
						|
    grid.perspective = 0
 | 
						|
 | 
						|
def setup_paths():
 | 
						|
    """Define the paths for entities"""
 | 
						|
    global player_path, enemy_path
 | 
						|
    
 | 
						|
    # Player path: Top-left room → corridor → middle room
 | 
						|
    player_waypoints = [
 | 
						|
        (3, 3),    # Start
 | 
						|
        (3, 8),    # Move down
 | 
						|
        (7, 8),    # Enter corridor
 | 
						|
        (16, 8),   # Through corridor
 | 
						|
        (16, 12),  # Enter middle room
 | 
						|
        (12, 12),  # Move in room
 | 
						|
        (12, 16),  # Move down
 | 
						|
        (16, 16),  # Move right
 | 
						|
        (16, 19),  # Exit room
 | 
						|
        (25, 19),  # Move right
 | 
						|
        (30, 19),  # Continue
 | 
						|
        (35, 19),  # Near enemy start
 | 
						|
    ]
 | 
						|
    
 | 
						|
    # Enemy path: Bottom-right → around → approach player area
 | 
						|
    enemy_waypoints = [
 | 
						|
        (35, 20),  # Start
 | 
						|
        (30, 20),  # Move left
 | 
						|
        (25, 20),  # Continue
 | 
						|
        (20, 20),  # Continue
 | 
						|
        (16, 20),  # Corridor junction
 | 
						|
        (16, 16),  # Move up (might see player)
 | 
						|
        (16, 12),  # Continue up
 | 
						|
        (16, 8),   # Top corridor
 | 
						|
        (10, 8),   # Move left
 | 
						|
        (7, 8),    # Continue
 | 
						|
        (3, 8),    # Player's area
 | 
						|
        (3, 12),   # Move down
 | 
						|
    ]
 | 
						|
    
 | 
						|
    # Calculate full paths using pathfinding
 | 
						|
    player_path = []
 | 
						|
    for i in range(len(player_waypoints) - 1):
 | 
						|
        x1, y1 = player_waypoints[i]
 | 
						|
        x2, y2 = player_waypoints[i + 1]
 | 
						|
        
 | 
						|
        # Use grid's A* pathfinding
 | 
						|
        segment = grid.compute_astar_path(x1, y1, x2, y2)
 | 
						|
        if segment:
 | 
						|
            # Add segment (avoiding duplicates)
 | 
						|
            if not player_path or segment[0] != player_path[-1]:
 | 
						|
                player_path.extend(segment)
 | 
						|
            else:
 | 
						|
                player_path.extend(segment[1:])
 | 
						|
    
 | 
						|
    enemy_path = []
 | 
						|
    for i in range(len(enemy_waypoints) - 1):
 | 
						|
        x1, y1 = enemy_waypoints[i]
 | 
						|
        x2, y2 = enemy_waypoints[i + 1]
 | 
						|
        
 | 
						|
        segment = grid.compute_astar_path(x1, y1, x2, y2)
 | 
						|
        if segment:
 | 
						|
            if not enemy_path or segment[0] != enemy_path[-1]:
 | 
						|
                enemy_path.extend(segment)
 | 
						|
            else:
 | 
						|
                enemy_path.extend(segment[1:])
 | 
						|
    
 | 
						|
    print(f"Player path: {len(player_path)} steps")
 | 
						|
    print(f"Enemy path: {len(enemy_path)} steps")
 | 
						|
 | 
						|
def setup_ui():
 | 
						|
    """Create UI elements"""
 | 
						|
    ui = mcrfpy.sceneUI("path_vision_demo")
 | 
						|
    ui.append(grid)
 | 
						|
    
 | 
						|
    # Position and size grid
 | 
						|
    grid.position = (50, 80)
 | 
						|
    grid.size = (700, 500)  # Adjust based on zoom
 | 
						|
    
 | 
						|
    # Title
 | 
						|
    title = mcrfpy.Caption("Path & Vision Sizzle Reel", 300, 20)
 | 
						|
    title.fill_color = mcrfpy.Color(255, 255, 255)
 | 
						|
    ui.append(title)
 | 
						|
    
 | 
						|
    # Status
 | 
						|
    global status_text, perspective_text
 | 
						|
    status_text = mcrfpy.Caption("Starting demo...", 50, 50)
 | 
						|
    status_text.fill_color = mcrfpy.Color(200, 200, 200)
 | 
						|
    ui.append(status_text)
 | 
						|
    
 | 
						|
    perspective_text = mcrfpy.Caption("Perspective: Player", 550, 50)
 | 
						|
    perspective_text.fill_color = mcrfpy.Color(100, 255, 100)
 | 
						|
    ui.append(perspective_text)
 | 
						|
    
 | 
						|
    # Controls
 | 
						|
    controls = mcrfpy.Caption("Space: Pause/Resume | R: Restart | Q: Quit", 250, 600)
 | 
						|
    controls.fill_color = mcrfpy.Color(150, 150, 150)
 | 
						|
    ui.append(controls)
 | 
						|
 | 
						|
# Animation control
 | 
						|
paused = False
 | 
						|
move_timer = 0
 | 
						|
zoom_transition = False
 | 
						|
 | 
						|
def move_entity_smooth(entity, target_x, target_y, duration=0.3):
 | 
						|
    """Smoothly animate entity to position"""
 | 
						|
    # Create position animation
 | 
						|
    anim_x = mcrfpy.Animation("x", float(target_x), duration, "easeInOut")
 | 
						|
    anim_y = mcrfpy.Animation("y", float(target_y), duration, "easeInOut")
 | 
						|
    
 | 
						|
    anim_x.start(entity)
 | 
						|
    anim_y.start(entity)
 | 
						|
 | 
						|
def update_camera_smooth(center_x, center_y, duration=0.3):
 | 
						|
    """Smoothly move camera center"""
 | 
						|
    # Convert grid coords to pixel coords (assuming 16x16 tiles)
 | 
						|
    pixel_x = center_x * 16
 | 
						|
    pixel_y = center_y * 16
 | 
						|
    
 | 
						|
    anim = mcrfpy.Animation("center", (pixel_x, pixel_y), duration, "easeOut")
 | 
						|
    anim.start(grid)
 | 
						|
 | 
						|
def start_perspective_transition():
 | 
						|
    """Begin the dramatic perspective shift"""
 | 
						|
    global zoom_transition, sequence_step
 | 
						|
    zoom_transition = True
 | 
						|
    sequence_step = 100  # Special sequence number
 | 
						|
    
 | 
						|
    status_text.text = "Perspective shift: Zooming out..."
 | 
						|
    
 | 
						|
    # Zoom out with elastic easing
 | 
						|
    zoom_out = mcrfpy.Animation("zoom", 0.5, 2.0, "easeInExpo")
 | 
						|
    zoom_out.start(grid)
 | 
						|
    
 | 
						|
    # Schedule the perspective switch
 | 
						|
    mcrfpy.setTimer("switch_perspective", switch_perspective, 2100)
 | 
						|
 | 
						|
def switch_perspective(dt):
 | 
						|
    """Switch perspective at the peak of zoom"""
 | 
						|
    global sequence_step
 | 
						|
    
 | 
						|
    # Switch to enemy perspective
 | 
						|
    grid.perspective = 1
 | 
						|
    perspective_text.text = "Perspective: Enemy"
 | 
						|
    perspective_text.fill_color = mcrfpy.Color(255, 100, 100)
 | 
						|
    
 | 
						|
    status_text.text = "Perspective shift: Following enemy..."
 | 
						|
    
 | 
						|
    # Update camera to enemy position
 | 
						|
    update_camera_smooth(enemy.x, enemy.y, 0.1)
 | 
						|
    
 | 
						|
    # Zoom back in
 | 
						|
    zoom_in = mcrfpy.Animation("zoom", 1.2, 2.0, "easeOutExpo")
 | 
						|
    zoom_in.start(grid)
 | 
						|
    
 | 
						|
    # Resume sequence
 | 
						|
    mcrfpy.setTimer("resume_enemy", resume_enemy_sequence, 2100)
 | 
						|
    
 | 
						|
    # Cancel this timer
 | 
						|
    mcrfpy.delTimer("switch_perspective")
 | 
						|
 | 
						|
def resume_enemy_sequence(dt):
 | 
						|
    """Resume following enemy after perspective shift"""
 | 
						|
    global sequence_step, zoom_transition
 | 
						|
    zoom_transition = False
 | 
						|
    sequence_step = 101  # Continue with enemy movement
 | 
						|
    mcrfpy.delTimer("resume_enemy")
 | 
						|
 | 
						|
def sequence_tick(dt):
 | 
						|
    """Main sequence controller"""
 | 
						|
    global sequence_step, player_path_index, enemy_path_index, move_timer
 | 
						|
    
 | 
						|
    if paused or zoom_transition:
 | 
						|
        return
 | 
						|
    
 | 
						|
    move_timer += dt
 | 
						|
    if move_timer < 400:  # Move every 400ms
 | 
						|
        return
 | 
						|
    move_timer = 0
 | 
						|
    
 | 
						|
    if sequence_step < 50:
 | 
						|
        # Phase 1: Follow player movement
 | 
						|
        if player_path_index < len(player_path):
 | 
						|
            x, y = player_path[player_path_index]
 | 
						|
            move_entity_smooth(player, x, y)
 | 
						|
            player.update_visibility()
 | 
						|
            
 | 
						|
            # Camera follows player
 | 
						|
            if grid.perspective == 0:
 | 
						|
                update_camera_smooth(player.x, player.y)
 | 
						|
            
 | 
						|
            player_path_index += 1
 | 
						|
            status_text.text = f"Player moving... Step {player_path_index}/{len(player_path)}"
 | 
						|
            
 | 
						|
            # Start enemy movement after player has moved a bit
 | 
						|
            if player_path_index == 10:
 | 
						|
                sequence_step = 1  # Enable enemy movement
 | 
						|
        else:
 | 
						|
            # Player reached destination, start perspective transition
 | 
						|
            start_perspective_transition()
 | 
						|
    
 | 
						|
    if sequence_step >= 1 and sequence_step < 50:
 | 
						|
        # Phase 2: Enemy movement (concurrent with player)
 | 
						|
        if enemy_path_index < len(enemy_path):
 | 
						|
            x, y = enemy_path[enemy_path_index]
 | 
						|
            move_entity_smooth(enemy, x, y)
 | 
						|
            enemy.update_visibility()
 | 
						|
            
 | 
						|
            # Check if enemy is visible to player
 | 
						|
            if grid.perspective == 0:
 | 
						|
                enemy_cell_idx = int(enemy.y) * grid.grid_x + int(enemy.x)
 | 
						|
                if enemy_cell_idx < len(player.gridstate) and player.gridstate[enemy_cell_idx].visible:
 | 
						|
                    status_text.text = "Enemy spotted!"
 | 
						|
                
 | 
						|
            enemy_path_index += 1
 | 
						|
    
 | 
						|
    elif sequence_step == 101:
 | 
						|
        # Phase 3: Continue following enemy after perspective shift
 | 
						|
        if enemy_path_index < len(enemy_path):
 | 
						|
            x, y = enemy_path[enemy_path_index]
 | 
						|
            move_entity_smooth(enemy, x, y)
 | 
						|
            enemy.update_visibility()
 | 
						|
            
 | 
						|
            # Camera follows enemy
 | 
						|
            update_camera_smooth(enemy.x, enemy.y)
 | 
						|
            
 | 
						|
            enemy_path_index += 1
 | 
						|
            status_text.text = f"Following enemy... Step {enemy_path_index}/{len(enemy_path)}"
 | 
						|
        else:
 | 
						|
            status_text.text = "Demo complete! Press R to restart"
 | 
						|
            sequence_step = 200  # Done
 | 
						|
 | 
						|
def handle_keys(key, state):
 | 
						|
    """Handle keyboard input"""
 | 
						|
    global paused, sequence_step, player_path_index, enemy_path_index, move_timer
 | 
						|
    key = key.lower()
 | 
						|
    if state != "start":
 | 
						|
        return
 | 
						|
    
 | 
						|
    if key == "q":
 | 
						|
        print("Exiting sizzle reel...")
 | 
						|
        sys.exit(0)
 | 
						|
    elif key == "space":
 | 
						|
        paused = not paused
 | 
						|
        status_text.text = "PAUSED" if paused else "Running..."
 | 
						|
    elif key == "r":
 | 
						|
        # Reset everything
 | 
						|
        player.x, player.y = 3, 3
 | 
						|
        enemy.x, enemy.y = 35, 20
 | 
						|
        player.update_visibility()
 | 
						|
        enemy.update_visibility()
 | 
						|
        grid.perspective = 0
 | 
						|
        perspective_text.text = "Perspective: Player"
 | 
						|
        perspective_text.fill_color = mcrfpy.Color(100, 255, 100)
 | 
						|
        sequence_step = 0
 | 
						|
        player_path_index = 0
 | 
						|
        enemy_path_index = 0
 | 
						|
        move_timer = 0
 | 
						|
        update_camera_smooth(player.x, player.y, 0.5)
 | 
						|
        
 | 
						|
        # Reset zoom
 | 
						|
        zoom_reset = mcrfpy.Animation("zoom", 1.2, 0.5, "easeOut")
 | 
						|
        zoom_reset.start(grid)
 | 
						|
        
 | 
						|
        status_text.text = "Demo restarted!"
 | 
						|
 | 
						|
# Initialize everything
 | 
						|
print("Path & Vision Sizzle Reel")
 | 
						|
print("=========================")
 | 
						|
print("Demonstrating:")
 | 
						|
print("- Smooth entity movement along calculated paths")
 | 
						|
print("- Camera following with animated grid centering")
 | 
						|
print("- Field of view updates as entities move")
 | 
						|
print("- Dramatic perspective transitions with zoom effects")
 | 
						|
print()
 | 
						|
 | 
						|
create_scene()
 | 
						|
setup_paths()
 | 
						|
setup_ui()
 | 
						|
 | 
						|
# Set scene and input
 | 
						|
mcrfpy.setScene("path_vision_demo")
 | 
						|
mcrfpy.keypressScene(handle_keys)
 | 
						|
 | 
						|
# Initial camera setup
 | 
						|
grid.zoom = 1.2
 | 
						|
update_camera_smooth(player.x, player.y, 0.1)
 | 
						|
 | 
						|
# Start the sequence
 | 
						|
mcrfpy.setTimer("sequence", sequence_tick, 50)  # Tick every 50ms
 | 
						|
 | 
						|
print("Demo started!")
 | 
						|
print("- Player (@) will navigate through rooms")
 | 
						|
print("- Enemy (E) will move on a different path")
 | 
						|
print("- Watch for the dramatic perspective shift!")
 | 
						|
print()
 | 
						|
print("Controls: Space=Pause, R=Restart, Q=Quit")
 |