/** * @fileoverview Sleep state handler implementation * @module states */ import { StateHandler } from './StateHandler.js' import { States, Emotions } from '../constants.js' /** * Handler for the Sleep state * @class * @extends StateHandler */ export class SleepStateHandler extends StateHandler { /** * Create a sleep state handler * @param {OwenAnimationContext} context - The animation context */ constructor (context) { super(States.SLEEPING, context) /** * Sleep animation clip * @type {AnimationClip|null} */ this.sleepClip = null /** * Whether the character is in deep sleep * @type {boolean} */ this.isDeepSleep = false } /** * Enter the sleep state * @param {string|null} [fromState=null] - The previous state * @param {string} [_emotion=Emotions.NEUTRAL] - The emotion to enter with (unused) * @returns {Promise} */ async enter (fromState = null, _emotion = Emotions.NEUTRAL) { console.log(`Entering SLEEPING state from ${fromState}`) // Play sleep transition if available const sleepTransition = this.context.getClip('wait_2sleep_T') if (sleepTransition) { await sleepTransition.play() await this.waitForClipEnd(sleepTransition) } // Start sleep loop this.sleepClip = this.context.getClip('sleep_idle_L') if (this.sleepClip) { await this.sleepClip.play() this.currentClip = this.sleepClip } this.isDeepSleep = true } /** * Exit the sleep state * @param {string|null} [toState=null] - The next state * @param {string} [emotion=Emotions.NEUTRAL] - The emotion to exit with * @returns {Promise} */ async exit (toState = null, _emotion = Emotions.NEUTRAL) { console.log(`Exiting SLEEPING state to ${toState}`) this.isDeepSleep = false if (this.currentClip) { await this.stopCurrentClip() } // Play wake up animation const wakeUpClip = this.context.getClip('sleep_wakeup_T') if (wakeUpClip) { await wakeUpClip.play() await this.waitForClipEnd(wakeUpClip) } // Play transition to next state if available const transitionName = `sleep_2${toState}_T` const transition = this.context.getClip(transitionName) if (transition) { await transition.play() await this.waitForClipEnd(transition) } } /** * Update the sleep state * @param {number} _deltaTime - Time elapsed since last update (ms, unused) * @returns {void} */ update (_deltaTime) { // Sleep state doesn't need regular updates // Character remains asleep until external stimulus } /** * Handle a user message in sleep state (wake up) * @param {string} _message - The user message (unused, just triggers wake up) * @returns {Promise} */ async handleMessage (_message) { // Any message should wake up the character if (this.isDeepSleep) { console.log('Waking up due to user message') // This will trigger a state transition to REACTING await this.context.transitionTo(States.REACTING) } } /** * Get available transitions from sleep state * @returns {string[]} Array of available state transitions */ getAvailableTransitions () { return [States.WAITING, States.REACTING] } /** * Check if in deep sleep * @returns {boolean} True if in deep sleep, false otherwise */ isInDeepSleep () { return this.isDeepSleep } /** * Force wake up from sleep * @returns {Promise} */ async wakeUp () { if (this.isDeepSleep) { await this.context.transitionTo(States.WAITING) } } }