/** * Avatar Animation System * Defines all animation sequences for the avatar system across 4 rendering modes. */ (function() { 'use strict'; // ============================================================================= // RENDERING MODES // ============================================================================= var MODES = { HEAD_ONLY: { width: 64, height: 64, showBody: false, showShoulders: false }, HEAD_AND_SHOULDERS: { width: 64, height: 96, showBody: false, showShoulders: true }, UPPER_BODY: { width: 64, height: 128, showBody: 'upper', showShoulders: true }, FULL_BODY: { width: 64, height: 160, showBody: 'full', showShoulders: true }, }; // ============================================================================= // ACCESSORY ANIMATION RULES // ============================================================================= var ACCESSORY_ANIMATION = { headwear: { followHead: true, bounceMultiplier: 1.2, rotationDamping: 0.8, }, eyewear: { followHead: true, bounceMultiplier: 1.0, rotationDamping: 1.0, }, effects: { independent: true, particleSpeed: 1.0, }, }; // ============================================================================= // DEFAULT FRAME TRANSFORM // ============================================================================= function defaultFrame() { return { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }; } // ============================================================================= // HEAD_ONLY ANIMATIONS (5 animations) // ============================================================================= var HEAD_ONLY_ANIMATIONS = [ // 1. bob - Gentle up/down bobbing { id: 'bob', name: 'Bob', modes: ['HEAD_ONLY'], loop: true, frameCount: 3, frameDuration: 150, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -2, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: -2, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: -1, rotation: 0 }, }, ], }, // 2. flap - For Flappy Bird style (ears/hair wiggle) { id: 'flap', name: 'Flap', modes: ['HEAD_ONLY'], loop: true, frameCount: 2, frameDuration: 80, frames: [ { head: { x: 0, y: 0, rotation: -3, scaleX: 1.05, scaleY: 0.95 }, eyes: { scaleY: 1 }, accessories: { bounce: 2, rotation: -5 }, }, { head: { x: 0, y: -3, rotation: 3, scaleX: 0.95, scaleY: 1.05 }, eyes: { scaleY: 1 }, accessories: { bounce: -3, rotation: 5 }, }, ], }, // 3. blink - Eye blink { id: 'blink', name: 'Blink', modes: ['HEAD_ONLY'], loop: false, frameCount: 3, frameDuration: 60, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.1 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 4. celebrate - Happy bounce { id: 'celebrate', name: 'Celebrate', modes: ['HEAD_ONLY'], loop: false, frameCount: 4, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -6, rotation: 5, scaleX: 1, scaleY: 1.1 }, eyes: { scaleY: 1 }, accessories: { bounce: -8, rotation: 5 }, }, { head: { x: 0, y: -3, rotation: -3, scaleX: 1, scaleY: 1.05 }, eyes: { scaleY: 1 }, accessories: { bounce: -4, rotation: -3 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 5. hurt - Damage reaction { id: 'hurt', name: 'Hurt', modes: ['HEAD_ONLY'], loop: false, frameCount: 2, frameDuration: 80, frames: [ { head: { x: -3, y: 0, rotation: -5, scaleX: 1.1, scaleY: 0.9 }, eyes: { scaleY: 0.5 }, accessories: { bounce: 0, rotation: -8 }, }, { head: { x: 3, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, accessories: { bounce: 0, rotation: 5 }, }, ], }, ]; // ============================================================================= // HEAD_AND_SHOULDERS ANIMATIONS (6 animations) // ============================================================================= var HEAD_AND_SHOULDERS_ANIMATIONS = [ // 1. idle - Subtle breathing { id: 'idle', name: 'Idle', modes: ['HEAD_AND_SHOULDERS'], loop: true, frameCount: 3, frameDuration: 400, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 1, rotation: 0 }, accessories: { bounce: -1, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 2. turn-left - Head turns left { id: 'turn-left', name: 'Turn Left', modes: ['HEAD_AND_SHOULDERS'], loop: false, frameCount: 2, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: -2, y: 0, rotation: -15, scaleX: 0.95, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: -3 }, accessories: { bounce: 0, rotation: -12 }, }, ], }, // 3. turn-right - Head turns right { id: 'turn-right', name: 'Turn Right', modes: ['HEAD_AND_SHOULDERS'], loop: false, frameCount: 2, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 2, y: 0, rotation: 15, scaleX: 0.95, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 3 }, accessories: { bounce: 0, rotation: 12 }, }, ], }, // 4. look-up - Head tilts up { id: 'look-up', name: 'Look Up', modes: ['HEAD_AND_SHOULDERS'], loop: false, frameCount: 2, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -3, rotation: -10, scaleX: 1, scaleY: 0.95 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: 1, rotation: 0 }, accessories: { bounce: -4, rotation: -8 }, }, ], }, // 5. celebrate - Bounce and smile { id: 'celebrate', name: 'Celebrate', modes: ['HEAD_AND_SHOULDERS'], loop: false, frameCount: 3, frameDuration: 120, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 1 }, body: { x: 0, y: 2, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -8, rotation: 5, scaleX: 1.05, scaleY: 1.1 }, eyes: { scaleY: 0.8 }, body: { x: 0, y: -2, rotation: 3 }, accessories: { bounce: -10, rotation: 5 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 6. hurt - Recoil { id: 'hurt', name: 'Hurt', modes: ['HEAD_AND_SHOULDERS'], loop: false, frameCount: 2, frameDuration: 100, frames: [ { head: { x: -4, y: 2, rotation: -10, scaleX: 1.1, scaleY: 0.9 }, eyes: { scaleY: 0.3 }, body: { x: -2, y: 2, rotation: -5 }, accessories: { bounce: 3, rotation: -15 }, }, { head: { x: 2, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 1, y: 0, rotation: 2 }, accessories: { bounce: 0, rotation: 5 }, }, ], }, ]; // ============================================================================= // UPPER_BODY ANIMATIONS (8 animations) // ============================================================================= var UPPER_BODY_ANIMATIONS = [ // 1. idle - Breathing with arm sway { id: 'idle', name: 'Idle', modes: ['UPPER_BODY'], loop: true, frameCount: 3, frameDuration: 400, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 1, rotation: 0 }, leftArm: { rotation: 2, x: 0, y: 0 }, rightArm: { rotation: -2, x: 0, y: 0 }, accessories: { bounce: -1, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 2. wave - Wave one arm { id: 'wave', name: 'Wave', modes: ['UPPER_BODY'], loop: false, frameCount: 4, frameDuration: 120, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: -45, x: 0, y: -5 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.9 }, body: { x: 0, y: 0, rotation: 2 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: -120, x: 5, y: -10 }, accessories: { bounce: 0, rotation: 3 }, }, { head: { x: 0, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.85 }, body: { x: 0, y: 0, rotation: 3 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: -90, x: 8, y: -12 }, accessories: { bounce: 0, rotation: 5 }, }, { head: { x: 0, y: 0, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.9 }, body: { x: 0, y: 0, rotation: 2 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: -120, x: 5, y: -10 }, accessories: { bounce: 0, rotation: 3 }, }, ], }, // 3. point - Point forward { id: 'point', name: 'Point', modes: ['UPPER_BODY'], loop: false, frameCount: 3, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 5 }, leftArm: { rotation: 10, x: 0, y: 0 }, rightArm: { rotation: -90, x: 10, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 1, y: 0, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 1, y: 0, rotation: 8 }, leftArm: { rotation: 15, x: 0, y: 0 }, rightArm: { rotation: -85, x: 15, y: 0 }, accessories: { bounce: 0, rotation: 3 }, }, ], }, // 4. steer-left - Lean left for racing { id: 'steer-left', name: 'Steer Left', modes: ['UPPER_BODY'], loop: false, frameCount: 2, frameDuration: 80, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: -30, x: -5, y: 0 }, rightArm: { rotation: -30, x: -5, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: -3, y: 0, rotation: -12, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: -2, y: 0, rotation: -10 }, leftArm: { rotation: -45, x: -8, y: -2 }, rightArm: { rotation: -15, x: -3, y: 2 }, accessories: { bounce: 0, rotation: -10 }, }, ], }, // 5. steer-right - Lean right for racing { id: 'steer-right', name: 'Steer Right', modes: ['UPPER_BODY'], loop: false, frameCount: 2, frameDuration: 80, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 30, x: 5, y: 0 }, rightArm: { rotation: 30, x: 5, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 3, y: 0, rotation: 12, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 2, y: 0, rotation: 10 }, leftArm: { rotation: 15, x: 3, y: 2 }, rightArm: { rotation: 45, x: 8, y: -2 }, accessories: { bounce: 0, rotation: 10 }, }, ], }, // 6. shoot - Arm extends forward { id: 'shoot', name: 'Shoot', modes: ['UPPER_BODY'], loop: false, frameCount: 3, frameDuration: 60, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: -45, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: -1, y: 0, rotation: -2, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: -1, y: 0, rotation: -3 }, leftArm: { rotation: 5, x: -2, y: 0 }, rightArm: { rotation: -90, x: 15, y: 0 }, accessories: { bounce: 0, rotation: -2 }, }, { head: { x: -2, y: 1, rotation: -3, scaleX: 1.02, scaleY: 0.98 }, eyes: { scaleY: 1 }, body: { x: -2, y: 1, rotation: -5 }, leftArm: { rotation: 10, x: -3, y: 1 }, rightArm: { rotation: -85, x: 12, y: 2 }, accessories: { bounce: 1, rotation: -3 }, }, ], }, // 7. celebrate - Arms up celebration { id: 'celebrate', name: 'Celebrate', modes: ['UPPER_BODY'], loop: false, frameCount: 4, frameDuration: 120, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 1 }, body: { x: 0, y: 2, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -8, rotation: 0, scaleX: 1, scaleY: 1.1 }, eyes: { scaleY: 0.8 }, body: { x: 0, y: -4, rotation: 0 }, leftArm: { rotation: -120, x: -5, y: -10 }, rightArm: { rotation: 120, x: 5, y: -10 }, accessories: { bounce: -10, rotation: 0 }, }, { head: { x: 0, y: -6, rotation: 5, scaleX: 1, scaleY: 1.05 }, eyes: { scaleY: 0.85 }, body: { x: 0, y: -2, rotation: 3 }, leftArm: { rotation: -135, x: -8, y: -12 }, rightArm: { rotation: 135, x: 8, y: -12 }, accessories: { bounce: -8, rotation: 5 }, }, { head: { x: 0, y: -6, rotation: -5, scaleX: 1, scaleY: 1.05 }, eyes: { scaleY: 0.85 }, body: { x: 0, y: -2, rotation: -3 }, leftArm: { rotation: -130, x: -6, y: -11 }, rightArm: { rotation: 130, x: 6, y: -11 }, accessories: { bounce: -8, rotation: -5 }, }, ], }, // 8. hurt - Recoil backwards { id: 'hurt', name: 'Hurt', modes: ['UPPER_BODY'], loop: false, frameCount: 2, frameDuration: 100, frames: [ { head: { x: -5, y: 3, rotation: -15, scaleX: 1.1, scaleY: 0.9 }, eyes: { scaleY: 0.2 }, body: { x: -3, y: 3, rotation: -10 }, leftArm: { rotation: 30, x: -5, y: 5 }, rightArm: { rotation: 45, x: -3, y: 5 }, accessories: { bounce: 5, rotation: -20 }, }, { head: { x: 2, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.8 }, body: { x: 1, y: 0, rotation: 3 }, leftArm: { rotation: 10, x: 0, y: 0 }, rightArm: { rotation: -10, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 5 }, }, ], }, ]; // ============================================================================= // FULL_BODY ANIMATIONS (12 animations) // ============================================================================= var FULL_BODY_ANIMATIONS = [ // 1. idle - Standing breathing { id: 'idle', name: 'Idle', modes: ['FULL_BODY'], loop: true, frameCount: 3, frameDuration: 400, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 1, rotation: 0 }, leftArm: { rotation: 2, x: 0, y: 0 }, rightArm: { rotation: -2, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: -1, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 2. walk - Walk cycle with leg/arm swing { id: 'walk', name: 'Walk Cycle', modes: ['FULL_BODY'], loop: true, frameCount: 4, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 15, x: 0, y: 0 }, rightArm: { rotation: -15, x: 0, y: 0 }, leftLeg: { rotation: -20, x: 0, y: 0 }, rightLeg: { rotation: 20, x: 0, y: -2 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: -2 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: -1, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: -15, x: 0, y: 0 }, rightArm: { rotation: 15, x: 0, y: 0 }, leftLeg: { rotation: 20, x: 0, y: -2 }, rightLeg: { rotation: -20, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: -1, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: -2 }, accessories: { bounce: -1, rotation: 0 }, }, ], }, // 3. run - Faster with more movement { id: 'run', name: 'Run Cycle', modes: ['FULL_BODY'], loop: true, frameCount: 6, frameDuration: 60, frames: [ { head: { x: 0, y: -2, rotation: 2, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 8 }, leftArm: { rotation: 45, x: 3, y: -2 }, rightArm: { rotation: -60, x: -5, y: 0 }, leftLeg: { rotation: -40, x: -3, y: -5 }, rightLeg: { rotation: 35, x: 5, y: 0 }, accessories: { bounce: -3, rotation: 2 }, }, { head: { x: 0, y: -4, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -3, rotation: 10 }, leftArm: { rotation: 30, x: 2, y: -3 }, rightArm: { rotation: -45, x: -3, y: -1 }, leftLeg: { rotation: -20, x: -1, y: -8 }, rightLeg: { rotation: 15, x: 2, y: -3 }, accessories: { bounce: -5, rotation: 3 }, }, { head: { x: 0, y: -2, rotation: 2, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 8 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 10, x: 1, y: -4 }, rightLeg: { rotation: -15, x: -2, y: -1 }, accessories: { bounce: -3, rotation: 2 }, }, { head: { x: 0, y: -2, rotation: -2, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 8 }, leftArm: { rotation: -60, x: -5, y: 0 }, rightArm: { rotation: 45, x: 3, y: -2 }, leftLeg: { rotation: 35, x: 5, y: 0 }, rightLeg: { rotation: -40, x: -3, y: -5 }, accessories: { bounce: -3, rotation: -2 }, }, { head: { x: 0, y: -4, rotation: -3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -3, rotation: 10 }, leftArm: { rotation: -45, x: -3, y: -1 }, rightArm: { rotation: 30, x: 2, y: -3 }, leftLeg: { rotation: 15, x: 2, y: -3 }, rightLeg: { rotation: -20, x: -1, y: -8 }, accessories: { bounce: -5, rotation: -3 }, }, { head: { x: 0, y: -2, rotation: -2, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 8 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: -15, x: -2, y: -1 }, rightLeg: { rotation: 10, x: 1, y: -4 }, accessories: { bounce: -3, rotation: -2 }, }, ], }, // 4. jump - Crouch, leap, stretch { id: 'jump', name: 'Jump', modes: ['FULL_BODY'], loop: false, frameCount: 3, frameDuration: 100, frames: [ { head: { x: 0, y: 4, rotation: 0, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 1 }, body: { x: 0, y: 6, rotation: 0 }, leftArm: { rotation: 20, x: -2, y: 3 }, rightArm: { rotation: -20, x: 2, y: 3 }, leftLeg: { rotation: 30, x: -3, y: 5 }, rightLeg: { rotation: -30, x: 3, y: 5 }, accessories: { bounce: 5, rotation: 0 }, }, { head: { x: 0, y: -15, rotation: 0, scaleX: 1, scaleY: 1.1 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: -12, rotation: 0 }, leftArm: { rotation: -120, x: -5, y: -10 }, rightArm: { rotation: 120, x: 5, y: -10 }, leftLeg: { rotation: 15, x: -1, y: -3 }, rightLeg: { rotation: -15, x: 1, y: -3 }, accessories: { bounce: -18, rotation: 0 }, }, { head: { x: 0, y: -20, rotation: 0, scaleX: 1, scaleY: 1.15 }, eyes: { scaleY: 1 }, body: { x: 0, y: -18, rotation: 0 }, leftArm: { rotation: -90, x: -8, y: -12 }, rightArm: { rotation: 90, x: 8, y: -12 }, leftLeg: { rotation: 5, x: 0, y: -5 }, rightLeg: { rotation: -5, x: 0, y: -5 }, accessories: { bounce: -22, rotation: 0 }, }, ], }, // 5. fall - Arms up, legs down { id: 'fall', name: 'Fall', modes: ['FULL_BODY'], loop: true, frameCount: 2, frameDuration: 150, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.2 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: -150, x: -5, y: -5 }, rightArm: { rotation: 150, x: 5, y: -5 }, leftLeg: { rotation: 10, x: -2, y: 5 }, rightLeg: { rotation: -10, x: 2, y: 5 }, accessories: { bounce: -5, rotation: 0 }, }, { head: { x: 0, y: -2, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.2 }, body: { x: 0, y: -1, rotation: 2 }, leftArm: { rotation: -140, x: -6, y: -6 }, rightArm: { rotation: 160, x: 4, y: -4 }, leftLeg: { rotation: 15, x: -3, y: 6 }, rightLeg: { rotation: -5, x: 1, y: 4 }, accessories: { bounce: -7, rotation: 3 }, }, ], }, // 6. crouch - Duck down { id: 'crouch', name: 'Crouch', modes: ['FULL_BODY'], loop: false, frameCount: 2, frameDuration: 80, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: 0, x: 0, y: 0 }, rightArm: { rotation: 0, x: 0, y: 0 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, { head: { x: 0, y: 15, rotation: -5, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 0.8 }, body: { x: 0, y: 12, rotation: -8 }, leftArm: { rotation: 30, x: -3, y: 8 }, rightArm: { rotation: -30, x: 3, y: 8 }, leftLeg: { rotation: 60, x: -5, y: 10 }, rightLeg: { rotation: -60, x: 5, y: 10 }, accessories: { bounce: 12, rotation: -5 }, }, ], }, // 7. celebrate - Jump and arm wave { id: 'celebrate', name: 'Celebrate', modes: ['FULL_BODY'], loop: false, frameCount: 6, frameDuration: 100, frames: [ { head: { x: 0, y: 3, rotation: 0, scaleX: 1, scaleY: 0.9 }, eyes: { scaleY: 1 }, body: { x: 0, y: 4, rotation: 0 }, leftArm: { rotation: 10, x: 0, y: 2 }, rightArm: { rotation: -10, x: 0, y: 2 }, leftLeg: { rotation: 15, x: -1, y: 3 }, rightLeg: { rotation: -15, x: 1, y: 3 }, accessories: { bounce: 4, rotation: 0 }, }, { head: { x: 0, y: -12, rotation: 0, scaleX: 1.05, scaleY: 1.1 }, eyes: { scaleY: 0.8 }, body: { x: 0, y: -10, rotation: 0 }, leftArm: { rotation: -135, x: -8, y: -12 }, rightArm: { rotation: 135, x: 8, y: -12 }, leftLeg: { rotation: 10, x: -2, y: -5 }, rightLeg: { rotation: -10, x: 2, y: -5 }, accessories: { bounce: -15, rotation: 0 }, }, { head: { x: 0, y: -10, rotation: 8, scaleX: 1.03, scaleY: 1.08 }, eyes: { scaleY: 0.85 }, body: { x: 0, y: -8, rotation: 5 }, leftArm: { rotation: -120, x: -10, y: -10 }, rightArm: { rotation: 150, x: 6, y: -14 }, leftLeg: { rotation: 5, x: -1, y: -4 }, rightLeg: { rotation: -5, x: 1, y: -4 }, accessories: { bounce: -12, rotation: 8 }, }, { head: { x: 0, y: -10, rotation: -8, scaleX: 1.03, scaleY: 1.08 }, eyes: { scaleY: 0.85 }, body: { x: 0, y: -8, rotation: -5 }, leftArm: { rotation: -150, x: -6, y: -14 }, rightArm: { rotation: 120, x: 10, y: -10 }, leftLeg: { rotation: 5, x: -1, y: -4 }, rightLeg: { rotation: -5, x: 1, y: -4 }, accessories: { bounce: -12, rotation: -8 }, }, { head: { x: 0, y: -8, rotation: 5, scaleX: 1.02, scaleY: 1.05 }, eyes: { scaleY: 0.9 }, body: { x: 0, y: -6, rotation: 3 }, leftArm: { rotation: -130, x: -8, y: -11 }, rightArm: { rotation: 140, x: 7, y: -12 }, leftLeg: { rotation: 3, x: 0, y: -3 }, rightLeg: { rotation: -3, x: 0, y: -3 }, accessories: { bounce: -10, rotation: 5 }, }, { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 0 }, leftArm: { rotation: -60, x: -3, y: -5 }, rightArm: { rotation: 60, x: 3, y: -5 }, leftLeg: { rotation: 0, x: 0, y: 0 }, rightLeg: { rotation: 0, x: 0, y: 0 }, accessories: { bounce: 0, rotation: 0 }, }, ], }, // 8. attack - Punch or swing { id: 'attack', name: 'Attack', modes: ['FULL_BODY'], loop: false, frameCount: 4, frameDuration: 60, frames: [ { head: { x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: -5 }, leftArm: { rotation: 15, x: -2, y: 0 }, rightArm: { rotation: -90, x: -5, y: -5 }, leftLeg: { rotation: 5, x: 0, y: 0 }, rightLeg: { rotation: -10, x: 0, y: 0 }, accessories: { bounce: 0, rotation: -3 }, }, { head: { x: 2, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 1, y: 0, rotation: 10 }, leftArm: { rotation: 20, x: -3, y: 0 }, rightArm: { rotation: -45, x: 15, y: 0 }, leftLeg: { rotation: -10, x: -2, y: 0 }, rightLeg: { rotation: 15, x: 3, y: -2 }, accessories: { bounce: 0, rotation: 5 }, }, { head: { x: 3, y: -1, rotation: 8, scaleX: 1.02, scaleY: 0.98 }, eyes: { scaleY: 1 }, body: { x: 2, y: 0, rotation: 15 }, leftArm: { rotation: 25, x: -4, y: 0 }, rightArm: { rotation: -30, x: 20, y: 2 }, leftLeg: { rotation: -15, x: -3, y: 0 }, rightLeg: { rotation: 20, x: 5, y: -3 }, accessories: { bounce: -1, rotation: 8 }, }, { head: { x: 1, y: 0, rotation: 3, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 5 }, leftArm: { rotation: 10, x: -1, y: 0 }, rightArm: { rotation: -60, x: 8, y: -2 }, leftLeg: { rotation: -5, x: -1, y: 0 }, rightLeg: { rotation: 8, x: 2, y: -1 }, accessories: { bounce: 0, rotation: 3 }, }, ], }, // 9. hurt - Stagger back { id: 'hurt', name: 'Hurt', modes: ['FULL_BODY'], loop: false, frameCount: 3, frameDuration: 80, frames: [ { head: { x: -5, y: 3, rotation: -15, scaleX: 1.1, scaleY: 0.9 }, eyes: { scaleY: 0.2 }, body: { x: -4, y: 3, rotation: -12 }, leftArm: { rotation: 40, x: -5, y: 5 }, rightArm: { rotation: 50, x: -3, y: 6 }, leftLeg: { rotation: 20, x: -3, y: 2 }, rightLeg: { rotation: -15, x: 2, y: 0 }, accessories: { bounce: 5, rotation: -20 }, }, { head: { x: -8, y: 5, rotation: -20, scaleX: 1.05, scaleY: 0.95 }, eyes: { scaleY: 0.1 }, body: { x: -6, y: 5, rotation: -18 }, leftArm: { rotation: 60, x: -8, y: 8 }, rightArm: { rotation: 70, x: -5, y: 10 }, leftLeg: { rotation: 30, x: -5, y: 4 }, rightLeg: { rotation: -25, x: 3, y: 2 }, accessories: { bounce: 8, rotation: -25 }, }, { head: { x: -2, y: 1, rotation: -5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 0.7 }, body: { x: -1, y: 1, rotation: -3 }, leftArm: { rotation: 15, x: -2, y: 2 }, rightArm: { rotation: 20, x: -1, y: 2 }, leftLeg: { rotation: 8, x: -1, y: 1 }, rightLeg: { rotation: -5, x: 1, y: 0 }, accessories: { bounce: 2, rotation: -5 }, }, ], }, // 10. climb - Climbing motion { id: 'climb', name: 'Climb', modes: ['FULL_BODY'], loop: true, frameCount: 4, frameDuration: 120, frames: [ { head: { x: 0, y: 0, rotation: -5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: -3 }, leftArm: { rotation: -150, x: -3, y: -15 }, rightArm: { rotation: 60, x: 5, y: 5 }, leftLeg: { rotation: 40, x: -2, y: 8 }, rightLeg: { rotation: -30, x: 2, y: -5 }, accessories: { bounce: 0, rotation: -5 }, }, { head: { x: 0, y: -2, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -2, rotation: 0 }, leftArm: { rotation: -120, x: -4, y: -10 }, rightArm: { rotation: 30, x: 4, y: 0 }, leftLeg: { rotation: 20, x: -1, y: 3 }, rightLeg: { rotation: -10, x: 1, y: -2 }, accessories: { bounce: -2, rotation: 0 }, }, { head: { x: 0, y: 0, rotation: 5, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 3 }, leftArm: { rotation: 60, x: -5, y: 5 }, rightArm: { rotation: -150, x: 3, y: -15 }, leftLeg: { rotation: -30, x: -2, y: -5 }, rightLeg: { rotation: 40, x: 2, y: 8 }, accessories: { bounce: 0, rotation: 5 }, }, { head: { x: 0, y: -2, rotation: 0, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -2, rotation: 0 }, leftArm: { rotation: 30, x: -4, y: 0 }, rightArm: { rotation: -120, x: 4, y: -10 }, leftLeg: { rotation: -10, x: -1, y: -2 }, rightLeg: { rotation: 20, x: 1, y: 3 }, accessories: { bounce: -2, rotation: 0 }, }, ], }, // 11. swim - Swimming strokes { id: 'swim', name: 'Swim', modes: ['FULL_BODY'], loop: true, frameCount: 4, frameDuration: 150, frames: [ { head: { x: 0, y: 0, rotation: 10, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 15 }, leftArm: { rotation: -60, x: 10, y: -5 }, rightArm: { rotation: 30, x: -5, y: 5 }, leftLeg: { rotation: 20, x: 0, y: 5 }, rightLeg: { rotation: -20, x: 0, y: -5 }, accessories: { bounce: 0, rotation: 10 }, }, { head: { x: 0, y: -2, rotation: 15, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 20 }, leftArm: { rotation: -30, x: 5, y: -8 }, rightArm: { rotation: -60, x: 10, y: -5 }, leftLeg: { rotation: -10, x: 0, y: -3 }, rightLeg: { rotation: 10, x: 0, y: 3 }, accessories: { bounce: -2, rotation: 15 }, }, { head: { x: 0, y: 0, rotation: 10, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: 0, rotation: 15 }, leftArm: { rotation: 30, x: -5, y: 5 }, rightArm: { rotation: -60, x: 10, y: -5 }, leftLeg: { rotation: -20, x: 0, y: -5 }, rightLeg: { rotation: 20, x: 0, y: 5 }, accessories: { bounce: 0, rotation: 10 }, }, { head: { x: 0, y: -2, rotation: 15, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1 }, body: { x: 0, y: -1, rotation: 20 }, leftArm: { rotation: -60, x: 10, y: -5 }, rightArm: { rotation: -30, x: 5, y: -8 }, leftLeg: { rotation: 10, x: 0, y: 3 }, rightLeg: { rotation: -10, x: 0, y: -3 }, accessories: { bounce: -2, rotation: 15 }, }, ], }, // 12. fly - Superhero flying pose { id: 'fly', name: 'Fly', modes: ['FULL_BODY'], loop: true, frameCount: 4, frameDuration: 100, frames: [ { head: { x: 0, y: 0, rotation: 25, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: 0, rotation: 30 }, leftArm: { rotation: -170, x: 10, y: -15 }, rightArm: { rotation: -160, x: 12, y: -12 }, leftLeg: { rotation: 20, x: 0, y: 10 }, rightLeg: { rotation: 25, x: 2, y: 12 }, accessories: { bounce: -5, rotation: 25 }, }, { head: { x: 0, y: -2, rotation: 28, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: -1, rotation: 32 }, leftArm: { rotation: -175, x: 12, y: -17 }, rightArm: { rotation: -165, x: 14, y: -14 }, leftLeg: { rotation: 15, x: -1, y: 8 }, rightLeg: { rotation: 20, x: 1, y: 10 }, accessories: { bounce: -7, rotation: 28 }, }, { head: { x: 0, y: 0, rotation: 25, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: 0, rotation: 30 }, leftArm: { rotation: -165, x: 8, y: -13 }, rightArm: { rotation: -155, x: 10, y: -10 }, leftLeg: { rotation: 25, x: 1, y: 12 }, rightLeg: { rotation: 30, x: 3, y: 14 }, accessories: { bounce: -5, rotation: 25 }, }, { head: { x: 0, y: -1, rotation: 27, scaleX: 1, scaleY: 1 }, eyes: { scaleY: 1.1 }, body: { x: 0, y: -1, rotation: 31 }, leftArm: { rotation: -172, x: 11, y: -16 }, rightArm: { rotation: -162, x: 13, y: -13 }, leftLeg: { rotation: 18, x: 0, y: 9 }, rightLeg: { rotation: 22, x: 2, y: 11 }, accessories: { bounce: -6, rotation: 27 }, }, ], }, ]; // ============================================================================= // ORGANIZE ANIMATIONS BY MODE // ============================================================================= var ANIMATIONS = { HEAD_ONLY: HEAD_ONLY_ANIMATIONS, HEAD_AND_SHOULDERS: HEAD_AND_SHOULDERS_ANIMATIONS, UPPER_BODY: UPPER_BODY_ANIMATIONS, FULL_BODY: FULL_BODY_ANIMATIONS, }; // Create a flat map for quick lookup by animation ID var ANIMATION_MAP = {}; Object.keys(ANIMATIONS).forEach(function(modeName) { ANIMATION_MAP[modeName] = {}; ANIMATIONS[modeName].forEach(function(anim) { ANIMATION_MAP[modeName][anim.id] = anim; }); }); // ============================================================================= // HELPER FUNCTIONS // ============================================================================= /** * Get mode configuration by name */ function getMode(modeName) { return MODES[modeName] || null; } /** * Get a specific animation for a mode */ function getAnimation(modeName, animationId) { if (!ANIMATION_MAP[modeName]) return null; return ANIMATION_MAP[modeName][animationId] || null; } /** * Get all animations for a mode */ function getAnimationsForMode(modeName) { return ANIMATIONS[modeName] || []; } /** * Get a specific frame from an animation */ function getFrame(animation, frameIndex) { if (!animation || !animation.frames) return defaultFrame(); var index = frameIndex % animation.frames.length; return animation.frames[index]; } /** * Interpolate between two values */ function lerp(a, b, t) { return a + (b - a) * t; } /** * Deep interpolate between two frame objects */ function interpolateObjects(frameA, frameB, t) { var result = {}; Object.keys(frameA).forEach(function(key) { if (typeof frameA[key] === 'object' && frameA[key] !== null) { result[key] = {}; Object.keys(frameA[key]).forEach(function(subKey) { var valA = frameA[key][subKey]; var valB = frameB[key] && frameB[key][subKey] !== undefined ? frameB[key][subKey] : valA; result[key][subKey] = lerp(valA, valB, t); }); } else { var valA = frameA[key]; var valB = frameB[key] !== undefined ? frameB[key] : valA; result[key] = lerp(valA, valB, t); } }); // Include any keys in frameB that aren't in frameA Object.keys(frameB).forEach(function(key) { if (!(key in frameA)) { if (typeof frameB[key] === 'object' && frameB[key] !== null) { result[key] = {}; Object.keys(frameB[key]).forEach(function(subKey) { result[key][subKey] = frameB[key][subKey]; }); } else { result[key] = frameB[key]; } } }); return result; } /** * Interpolate frame data based on animation progress (0-1) * Returns smoothly interpolated transform values between frames */ function interpolateFrame(animation, progress) { if (!animation || !animation.frames || animation.frames.length === 0) { return defaultFrame(); } var frameCount = animation.frames.length; var exactFrame = progress * frameCount; var frameIndex = Math.floor(exactFrame); var frameFraction = exactFrame - frameIndex; // Handle looping if (animation.loop) { frameIndex = frameIndex % frameCount; } else { frameIndex = Math.min(frameIndex, frameCount - 1); } var nextFrameIndex; if (animation.loop) { nextFrameIndex = (frameIndex + 1) % frameCount; } else { nextFrameIndex = Math.min(frameIndex + 1, frameCount - 1); } var frameA = animation.frames[frameIndex]; var frameB = animation.frames[nextFrameIndex]; // If we're at the last frame of a non-looping animation, just return it if (!animation.loop && frameIndex === frameCount - 1) { return frameA; } return interpolateObjects(frameA, frameB, frameFraction); } /** * Get the duration of each frame in milliseconds */ function getFrameDuration(animation) { return animation && animation.frameDuration ? animation.frameDuration : 100; } /** * Get the total duration of an animation in milliseconds */ function getTotalDuration(animation) { if (!animation || !animation.frames) return 0; return animation.frames.length * getFrameDuration(animation); } /** * Check if an animation loops */ function isLooping(animation) { return animation && animation.loop === true; } // ============================================================================= // PUBLIC API // ============================================================================= window.AvatarAnimations = { MODES: MODES, ANIMATIONS: ANIMATIONS, ACCESSORY_ANIMATION: ACCESSORY_ANIMATION, getMode: getMode, getAnimation: getAnimation, getAnimationsForMode: getAnimationsForMode, getFrame: getFrame, interpolateFrame: interpolateFrame, getFrameDuration: getFrameDuration, getTotalDuration: getTotalDuration, isLooping: isLooping, defaultFrame: defaultFrame, }; })();