375 lines
11 KiB
Python
375 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Path & Vision Sizzle Reel (Fixed)
|
|
=================================
|
|
|
|
Fixed version with proper animation chaining to prevent glitches.
|
|
"""
|
|
|
|
import mcrfpy
|
|
import sys
|
|
|
|
class PathAnimator:
|
|
"""Handles step-by-step animation with proper completion tracking"""
|
|
|
|
def __init__(self, entity, name="animator"):
|
|
self.entity = entity
|
|
self.name = name
|
|
self.path = []
|
|
self.current_index = 0
|
|
self.step_duration = 0.4
|
|
self.animating = False
|
|
self.on_step = None
|
|
self.on_complete = None
|
|
|
|
def set_path(self, path):
|
|
"""Set the path to animate along"""
|
|
self.path = path
|
|
self.current_index = 0
|
|
|
|
def start(self):
|
|
"""Start animating"""
|
|
if not self.path:
|
|
return
|
|
|
|
self.animating = True
|
|
self.current_index = 0
|
|
self._move_to_next()
|
|
|
|
def stop(self):
|
|
"""Stop animating"""
|
|
self.animating = False
|
|
mcrfpy.delTimer(f"{self.name}_check")
|
|
|
|
def _move_to_next(self):
|
|
"""Move to next position in path"""
|
|
if not self.animating or self.current_index >= len(self.path):
|
|
self.animating = False
|
|
if self.on_complete:
|
|
self.on_complete()
|
|
return
|
|
|
|
# Get next position
|
|
x, y = self.path[self.current_index]
|
|
|
|
# Create animations
|
|
anim_x = mcrfpy.Animation("x", float(x), self.step_duration, "easeInOut")
|
|
anim_y = mcrfpy.Animation("y", float(y), self.step_duration, "easeInOut")
|
|
|
|
anim_x.start(self.entity)
|
|
anim_y.start(self.entity)
|
|
|
|
# Update visibility
|
|
self.entity.update_visibility()
|
|
|
|
# Callback for each step
|
|
if self.on_step:
|
|
self.on_step(self.current_index, x, y)
|
|
|
|
# Schedule next move
|
|
delay = int(self.step_duration * 1000) + 50 # Add small buffer
|
|
mcrfpy.setTimer(f"{self.name}_next", self._handle_next, delay)
|
|
|
|
def _handle_next(self, dt):
|
|
"""Timer callback to move to next position"""
|
|
self.current_index += 1
|
|
mcrfpy.delTimer(f"{self.name}_next")
|
|
self._move_to_next()
|
|
|
|
# Global state
|
|
grid = None
|
|
player = None
|
|
enemy = None
|
|
player_animator = None
|
|
enemy_animator = None
|
|
demo_phase = 0
|
|
|
|
def create_scene():
|
|
"""Create the demo environment"""
|
|
global grid, player, enemy
|
|
|
|
mcrfpy.createScene("fixed_demo")
|
|
|
|
# Create grid
|
|
grid = mcrfpy.Grid(grid_x=30, grid_y=20)
|
|
grid.fill_color = mcrfpy.Color(20, 20, 30)
|
|
|
|
# Simple dungeon layout
|
|
map_layout = [
|
|
"##############################",
|
|
"#......#########.....#########",
|
|
"#......#########.....#########",
|
|
"#......#.........#...#########",
|
|
"#......#.........#...#########",
|
|
"####.###.........#.###########",
|
|
"####.............#.###########",
|
|
"####.............#.###########",
|
|
"####.###.........#.###########",
|
|
"#......#.........#...#########",
|
|
"#......#.........#...#########",
|
|
"#......#########.#...........#",
|
|
"#......#########.#...........#",
|
|
"#......#########.#...........#",
|
|
"#......#########.#############",
|
|
"####.###########.............#",
|
|
"####.........................#",
|
|
"####.###########.............#",
|
|
"#......#########.............#",
|
|
"##############################",
|
|
]
|
|
|
|
# Build 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 = mcrfpy.Color(40, 30, 30)
|
|
else:
|
|
cell.walkable = True
|
|
cell.transparent = True
|
|
cell.color = mcrfpy.Color(80, 80, 100)
|
|
|
|
# Create entities
|
|
player = mcrfpy.Entity(3, 3, grid=grid)
|
|
player.sprite_index = 64 # @
|
|
|
|
enemy = mcrfpy.Entity(26, 16, grid=grid)
|
|
enemy.sprite_index = 69 # E
|
|
|
|
# Initial visibility
|
|
player.update_visibility()
|
|
enemy.update_visibility()
|
|
|
|
# Set initial perspective
|
|
grid.perspective = 0
|
|
|
|
def setup_ui():
|
|
"""Create UI elements"""
|
|
ui = mcrfpy.sceneUI("fixed_demo")
|
|
ui.append(grid)
|
|
|
|
grid.position = (50, 80)
|
|
grid.size = (700, 500)
|
|
|
|
title = mcrfpy.Caption("Path & Vision Demo (Fixed)", 300, 20)
|
|
title.fill_color = mcrfpy.Color(255, 255, 255)
|
|
ui.append(title)
|
|
|
|
global status_text, perspective_text
|
|
status_text = mcrfpy.Caption("Initializing...", 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 = mcrfpy.Caption("Space: Start/Pause | R: Restart | Q: Quit", 250, 600)
|
|
controls.fill_color = mcrfpy.Color(150, 150, 150)
|
|
ui.append(controls)
|
|
|
|
def update_camera_smooth(target, duration=0.3):
|
|
"""Smoothly move camera to entity"""
|
|
center_x = target.x * 23 # Approximate pixel size
|
|
center_y = target.y * 23
|
|
|
|
cam_anim = mcrfpy.Animation("center", (center_x, center_y), duration, "easeOut")
|
|
cam_anim.start(grid)
|
|
|
|
def start_demo():
|
|
"""Start the demo sequence"""
|
|
global demo_phase, player_animator, enemy_animator
|
|
|
|
demo_phase = 1
|
|
status_text.text = "Phase 1: Player movement with camera follow"
|
|
|
|
# Player path
|
|
player_path = [
|
|
(3, 3), (3, 6), (4, 6), (7, 6), (7, 8),
|
|
(10, 8), (13, 8), (16, 8), (16, 10),
|
|
(16, 13), (16, 16), (20, 16), (24, 16)
|
|
]
|
|
|
|
# Setup player animator
|
|
player_animator = PathAnimator(player, "player")
|
|
player_animator.set_path(player_path)
|
|
player_animator.step_duration = 0.5
|
|
|
|
def on_player_step(index, x, y):
|
|
"""Called for each player step"""
|
|
status_text.text = f"Player step {index+1}/{len(player_path)}"
|
|
if grid.perspective == 0:
|
|
update_camera_smooth(player, 0.4)
|
|
|
|
def on_player_complete():
|
|
"""Called when player path is complete"""
|
|
start_phase_2()
|
|
|
|
player_animator.on_step = on_player_step
|
|
player_animator.on_complete = on_player_complete
|
|
player_animator.start()
|
|
|
|
def start_phase_2():
|
|
"""Start enemy movement phase"""
|
|
global demo_phase
|
|
|
|
demo_phase = 2
|
|
status_text.text = "Phase 2: Enemy movement (may enter player's view)"
|
|
|
|
# Enemy path
|
|
enemy_path = [
|
|
(26, 16), (22, 16), (18, 16), (16, 16),
|
|
(16, 13), (16, 10), (16, 8), (13, 8),
|
|
(10, 8), (7, 8), (7, 6), (4, 6)
|
|
]
|
|
|
|
# Setup enemy animator
|
|
enemy_animator.set_path(enemy_path)
|
|
enemy_animator.step_duration = 0.4
|
|
|
|
def on_enemy_step(index, x, y):
|
|
"""Check if enemy is visible to player"""
|
|
if grid.perspective == 0:
|
|
# Check if enemy is in player's view
|
|
enemy_idx = int(y) * grid.grid_x + int(x)
|
|
if enemy_idx < len(player.gridstate) and player.gridstate[enemy_idx].visible:
|
|
status_text.text = "Enemy spotted in player's view!"
|
|
|
|
def on_enemy_complete():
|
|
"""Start perspective transition"""
|
|
start_phase_3()
|
|
|
|
enemy_animator.on_step = on_enemy_step
|
|
enemy_animator.on_complete = on_enemy_complete
|
|
enemy_animator.start()
|
|
|
|
def start_phase_3():
|
|
"""Dramatic perspective shift"""
|
|
global demo_phase
|
|
|
|
demo_phase = 3
|
|
status_text.text = "Phase 3: Perspective shift..."
|
|
|
|
# Stop any ongoing animations
|
|
player_animator.stop()
|
|
enemy_animator.stop()
|
|
|
|
# Zoom out
|
|
zoom_out = mcrfpy.Animation("zoom", 0.6, 2.0, "easeInExpo")
|
|
zoom_out.start(grid)
|
|
|
|
# Schedule perspective switch
|
|
mcrfpy.setTimer("switch_persp", switch_perspective, 2100)
|
|
|
|
def switch_perspective(dt):
|
|
"""Switch to enemy perspective"""
|
|
grid.perspective = 1
|
|
perspective_text.text = "Perspective: Enemy"
|
|
perspective_text.fill_color = mcrfpy.Color(255, 100, 100)
|
|
|
|
# Update camera
|
|
update_camera_smooth(enemy, 0.5)
|
|
|
|
# Zoom back in
|
|
zoom_in = mcrfpy.Animation("zoom", 1.0, 2.0, "easeOutExpo")
|
|
zoom_in.start(grid)
|
|
|
|
status_text.text = "Now following enemy perspective"
|
|
|
|
# Clean up timer
|
|
mcrfpy.delTimer("switch_persp")
|
|
|
|
# Continue enemy movement after transition
|
|
mcrfpy.setTimer("continue_enemy", continue_enemy_movement, 2500)
|
|
|
|
def continue_enemy_movement(dt):
|
|
"""Continue enemy movement after perspective shift"""
|
|
mcrfpy.delTimer("continue_enemy")
|
|
|
|
# Continue path
|
|
enemy_path_2 = [
|
|
(4, 6), (3, 6), (3, 3), (3, 2), (3, 1)
|
|
]
|
|
|
|
enemy_animator.set_path(enemy_path_2)
|
|
|
|
def on_step(index, x, y):
|
|
update_camera_smooth(enemy, 0.4)
|
|
status_text.text = f"Following enemy: step {index+1}"
|
|
|
|
def on_complete():
|
|
status_text.text = "Demo complete! Press R to restart"
|
|
|
|
enemy_animator.on_step = on_step
|
|
enemy_animator.on_complete = on_complete
|
|
enemy_animator.start()
|
|
|
|
# Control state
|
|
running = False
|
|
|
|
def handle_keys(key, state):
|
|
"""Handle keyboard input"""
|
|
global running
|
|
|
|
if state != "start":
|
|
return
|
|
|
|
key = key.lower()
|
|
|
|
if key == "q":
|
|
sys.exit(0)
|
|
elif key == "space":
|
|
if not running:
|
|
running = True
|
|
start_demo()
|
|
else:
|
|
running = False
|
|
player_animator.stop()
|
|
enemy_animator.stop()
|
|
status_text.text = "Paused"
|
|
elif key == "r":
|
|
# Reset everything
|
|
player.x, player.y = 3, 3
|
|
enemy.x, enemy.y = 26, 16
|
|
grid.perspective = 0
|
|
perspective_text.text = "Perspective: Player"
|
|
perspective_text.fill_color = mcrfpy.Color(100, 255, 100)
|
|
grid.zoom = 1.0
|
|
update_camera_smooth(player, 0.5)
|
|
|
|
if running:
|
|
player_animator.stop()
|
|
enemy_animator.stop()
|
|
running = False
|
|
|
|
status_text.text = "Reset - Press SPACE to start"
|
|
|
|
# Initialize
|
|
create_scene()
|
|
setup_ui()
|
|
|
|
# Setup animators
|
|
player_animator = PathAnimator(player, "player")
|
|
enemy_animator = PathAnimator(enemy, "enemy")
|
|
|
|
# Set scene
|
|
mcrfpy.setScene("fixed_demo")
|
|
mcrfpy.keypressScene(handle_keys)
|
|
|
|
# Initial camera
|
|
grid.zoom = 1.0
|
|
update_camera_smooth(player, 0.5)
|
|
|
|
print("Path & Vision Demo (Fixed)")
|
|
print("==========================")
|
|
print("This version properly chains animations to prevent glitches.")
|
|
print()
|
|
print("The demo will:")
|
|
print("1. Move player with camera following")
|
|
print("2. Move enemy (may enter player's view)")
|
|
print("3. Dramatic perspective shift to enemy")
|
|
print("4. Continue following enemy")
|
|
print()
|
|
print("Press SPACE to start, Q to quit") |