cduss's picture
external b64 image
d4c9987
raw
history blame
10.4 kB
"""Sample Story - Example visual novel story with branching paths."""
import copy
from typing import List
from engine import (
VisualNovelBuilder,
SceneState,
CharacterDefinition,
Choice,
background_asset,
sprite_asset,
audio_asset,
create_sprite_data_url,
)
def build_sample_story() -> List[SceneState]:
"""Build the sample story with branching paths."""
builder = VisualNovelBuilder()
builder.set_characters(
[
CharacterDefinition(
name="Ari",
image_url=sprite_asset('reachy-mini-cartoon.svg'),
),
CharacterDefinition(
name="Bo",
image_url=sprite_asset('ReachyMini_emotions_happy.svg'),
animated=True,
),
]
)
builder.set_background(
background_asset('workshop_bg.png'),
)
builder.set_stage(background_asset("p60-back-cover.png"))
builder.narration("A hush falls over the academy courtyard as the gates creak open.")
builder.set_stage(
background_asset('p3.png'),
)
# Request player name
builder.request_input("What is your name, traveler?", "player_name")
# After input, create a new state without input_request
state = builder._clone_state()
state.input_request = None # Clear the input request
state.note = "Continuing story"
builder._push_state(state)
builder.show_character("Ari", position="left")
builder.play_sound(audio_asset("wake_up.wav"))
builder.dialogue("Ari", "Welcome, {player_name}! I'm Ari, and this is Bo.")
builder.show_character("Bo", position="right")
builder.dialogue("Bo", "Nice to meet you, {player_name}. We're on a quest to find the star fragment.")
builder.dialogue("Ari", "Will you help us on our quest?")
# ACCEPT BRANCH - tag all scenes with path="accept"
accept_index = len(builder._states)
builder.set_path("accept")
builder.dialogue("Bo", "Excellent! We knew we could count on you, {player_name}!")
builder.move_character("Ari", position="center")
builder.narration("You join Ari and Bo on their adventure...")
# Demonstrate camera feature
builder.set_camera(True)
builder.dialogue("Ari", "First, let me see your face, {player_name}. The camera will help us verify your identity.")
builder.narration("The camera activates, showing your live feed...")
# Demonstrate voice feature
builder.set_camera(False)
builder.set_voice(True)
builder.dialogue("Bo", "Now, tell us about yourself using the voice recorder.")
builder.narration("You can now record or upload audio to interact with the companions.")
# Demonstrate motors feature
builder.set_voice(False)
builder.set_motors(True)
builder.dialogue("Ari", "Finally, we need to test the portal controls. Use the motor panel to align the crystals.")
builder.narration("Motor controls are now available. Adjust the servos to proceed.")
# Example: Send motor commands from the story
builder.send_motor_command(1, 90) # Move motor ID 1 to 90 degrees
builder.dialogue("Ari", "Watch as the first crystal aligns itself!")
# Example: Send multiple motor commands at once
builder.send_motor_commands([(1, 180), (2, 90)]) # Move motors 1 and 2
builder.dialogue("Ari", "Now the portal crystals are synchronizing!")
# Example: Play sound effect
builder.play_sound(audio_asset("confused1.wav")) # Uncomment when you add audio files
builder.dialogue("Ari", "Listen! The portal resonates with magical energy!")
# Demonstrate robot control (Reachy Mini)
builder.set_motors(False)
builder.set_robot(True)
builder.dialogue("Bo", "Now let's test the Reachy Mini robot! It should be at localhost:8000.")
builder.narration("The robot control panel appears. Make sure your Reachy Mini server is running.")
# Send robot pose command - head looking up and antennas raised
builder.send_robot_pose(
head_x=0.0,
head_y=0.0,
head_z=0.02, # Raise head 2cm
head_pitch=-0.1, # Look up (negative pitch)
antenna_left=-0.2, # Raise left antenna
antenna_right=0.2, # Raise right antenna
)
builder.dialogue("Ari", "Watch! The robot looks up in wonder!")
# Send another pose - head tilted
builder.send_robot_pose(
head_z=-0.04, # Raise head 2cm
head_roll=0.1, # Tilt head to the side
head_yaw=0.1, # Turn head slightly
antenna_left=-0.3, # Lower left antenna
antenna_right=0.8, # Raise right antenna more
)
builder.dialogue("Bo", "The robot is expressing curiosity!")
# Demonstrate stage layer with separate blur
builder.set_robot(False)
builder.set_stage(background_asset('p3.png')) # Add a stage layer
builder.dialogue("Ari", "Look! The portal is opening...")
builder.narration("A mystical stage appears between you and the background.")
# Demonstrate separate blur controls
builder.set_background_blur(8)
builder.set_stage_blur(3)
builder.dialogue("Ari", "Wait! Do you sense that? Something magical is happening...")
builder.narration("The background and stage blur independently as Ari steps forward.")
# Clear stage and blur
builder.set_background_blur(0)
builder.set_stage_blur(0)
builder.set_stage("") # Remove stage layer
# Demonstrate character animations and sprite changes
builder.set_character_animation("Bo", "shake")
builder.dialogue("Bo", "Whoa! Did you feel that tremor?!")
builder.set_character_animation("Bo", "bounce")
builder.dialogue("Bo", "This is so exciting! We're getting close!")
builder.set_character_animation("Bo", "")
builder.set_character_animation("Ari", "pulse")
builder.dialogue("Ari", "The star fragment... I can feel its power pulsing nearby.")
# Demonstrate character scaling
builder.set_character_animation("Ari", "")
builder.set_character_scale("Ari", 1.5)
builder.dialogue("Ari", "The power... it's making me grow stronger!")
builder.set_character_scale("Bo", 0.7)
builder.dialogue("Bo", "Whoa, you're getting really big! Or am I shrinking?")
# Reset scales
builder.set_character_scale("Ari", 1.0)
builder.set_character_scale("Bo", 1.0)
# Turn off all features
builder.set_motors(False)
builder.dialogue("Ari", "The portal is ready! But wait...")
builder.dialogue("Bo", "The path splits here! We need to split up to cover more ground.")
builder.dialogue("Ari", "You'll need to choose who to follow, {player_name}.")
# SECOND CHOICE - Follow Ari or Bo
# Remember the index before the branches
follow_ari_index = len(builder._states)
# FOLLOW ARI SUB-BRANCH
builder.set_path("accept.follow_ari")
builder.dialogue("Ari", "Wise choice! My path leads through the ancient library.")
builder.hide_character("Bo")
builder.move_character("Ari", position="center")
builder.set_background(background_asset('p3.png'))
builder.narration("Bo waves goodbye as you follow Ari into the misty corridors...")
builder.dialogue("Ari", "The fragment's energy is strongest here. Stay close!")
builder.set_character_animation("Ari", "pulse")
builder.send_motor_command(1, 45) # Different motor position for this path
builder.dialogue("Ari", "The ancient mechanisms are responding!")
builder.set_character_animation("Ari", "")
builder.narration("You discover the star fragment hidden in an ancient tome.")
builder.dialogue("Ari", "We did it, {player_name}! The knowledge was the key all along.")
builder.play_sound(audio_asset("wake_up.wav"))
builder.narration("✨ Ending: The Scholar's Path (Follow Ari)")
# FOLLOW BO SUB-BRANCH
follow_bo_index = len(builder._states)
builder.set_path("accept.follow_bo")
builder.dialogue("Bo", "Adventure time! My route goes through the crystal caves!")
builder.hide_character("Ari")
builder.move_character("Bo", position="center")
builder.set_background(background_asset('workshop_bg.png'))
builder.narration("Ari nods encouragingly as you follow Bo into the glowing caves...")
builder.dialogue("Bo", "Can you feel the energy? It's electric!")
builder.set_character_animation("Bo", "bounce")
builder.send_motor_commands([(1, 135), (2, 135)]) # Different motor positions
builder.dialogue("Bo", "The crystals are resonating! We're so close!")
builder.set_character_animation("Bo", "shake")
builder.narration("A powerful tremor shakes the cavern as the fragment reveals itself!")
builder.dialogue("Bo", "Whoa! Grab it, {player_name}!")
builder.set_character_animation("Bo", "")
builder.play_sound(audio_asset("wake_up.wav"))
builder.narration("✨ Ending: The Adventurer's Path (Follow Bo)")
# Insert the second choice scene before the sub-branches
second_choice_scene = copy.deepcopy(builder._states[follow_ari_index - 1])
second_choice_scene.choices = [
Choice(text="Follow Ari (Library)", next_scene_index=follow_ari_index),
Choice(text="Follow Bo (Caves)", next_scene_index=follow_bo_index),
]
second_choice_scene.note = "Second Choice (2 paths)"
second_choice_scene.input_request = None
second_choice_scene.path = "accept" # This choice is within the accept path
builder._states[follow_ari_index - 1] = second_choice_scene
# DECLINE BRANCH - tag all scenes with path="decline"
decline_index = len(builder._states)
builder.set_path("decline")
builder.dialogue("Ari", "That's... disappointing, {player_name}.")
builder.hide_character("Bo")
builder.dialogue("Ari", "I guess we're on our own, Bo.")
builder.narration("Ari and Bo leave without you... (Decline path)")
# Insert the choice scene before the branches
choice_scene = copy.deepcopy(builder._states[accept_index - 1])
choice_scene.choices = [
Choice(text="Yes, I'll help!", next_scene_index=accept_index),
Choice(text="No, sorry.", next_scene_index=decline_index),
]
choice_scene.note = "Choice (2 options)"
choice_scene.input_request = None # Make sure no input request on choice scene
choice_scene.path = None # Choice scene is on the main path
builder._states[accept_index - 1] = choice_scene
return builder.build()