diff --git a/src/blobbi/dev/BlobbiEmotionPanel.tsx b/src/blobbi/dev/BlobbiEmotionPanel.tsx index a79fc017..32f07d53 100644 --- a/src/blobbi/dev/BlobbiEmotionPanel.tsx +++ b/src/blobbi/dev/BlobbiEmotionPanel.tsx @@ -37,7 +37,8 @@ const EMOTIONS: Array<{ value: BlobbiEmotion; label: string; emoji: string }> = { value: 'curious', label: 'Curious', emoji: '🤔' }, { value: 'sleepy', label: 'Sleepy', emoji: '😴' }, { value: 'dizzy', label: 'Dizzy', emoji: '😵' }, - { value: 'excited', label: 'Excited', emoji: '🤩' }, + { value: 'excited', label: 'Excited A', emoji: '🤩' }, + { value: 'excitedB', label: 'Excited B', emoji: '⭐' }, { value: 'mischievous', label: 'Mischievous', emoji: '😏' }, { value: 'adoring', label: 'Adoring', emoji: '🥺' }, ]; diff --git a/src/blobbi/ui/lib/emotions.ts b/src/blobbi/ui/lib/emotions.ts index a48bdf10..8e64d6cb 100644 --- a/src/blobbi/ui/lib/emotions.ts +++ b/src/blobbi/ui/lib/emotions.ts @@ -19,7 +19,7 @@ /** * Available emotion states for Blobbies */ -export type BlobbiEmotion = 'neutral' | 'sad' | 'happy' | 'angry' | 'surprised' | 'sleepy' | 'curious' | 'dizzy' | 'excited' | 'mischievous' | 'adoring'; +export type BlobbiEmotion = 'neutral' | 'sad' | 'happy' | 'angry' | 'surprised' | 'sleepy' | 'curious' | 'dizzy' | 'excited' | 'excitedB' | 'mischievous' | 'adoring'; /** * Blobbi variant for variant-specific adjustments @@ -275,6 +275,21 @@ export const EMOTION_CONFIGS: Record = { curveScale: 1.4, // 40% deeper curve for extra happy look }, }, + excitedB: { + // Variation B: star eyes + round mouth (like curious) + starEyes: { + enabled: true, + points: 5, // 5-pointed star + color: '#fbbf24', // Golden yellow (amber-400) + scale: 0.9, // Same as excited + }, + // Round "O" mouth like curious (no smile) + roundMouth: { + rx: 3.5, // Slightly larger than curious + ry: 4, + filled: true, + }, + }, mischievous: { // Use the same eyebrow config as angry (V-shape), but with animation eyebrows: { @@ -1390,39 +1405,56 @@ function generateStarElement(eye: EyePosition, config: StarEyesConfig): string { } /** - * Generate sparkle elements around the eyes for the excited effect. - * Small animated sparkles that twinkle around the star eyes. + * Generate a single sparkle element at specified position. */ -function generateSparkles(eyes: EyePosition[], config: StarEyesConfig): string { +function createSparkleElement(x: number, y: number, size: number, color: string, delay: number, duration: number): string { + return ` + + + + `; +} + +/** + * Generate sparkle elements around the entire Blobbi for the excited effect. + * Subtle, soft sparkles distributed around the body perimeter. + */ +function generateSparkles(config: StarEyesConfig): string { const color = config.color || '#fbbf24'; - return eyes.map(eye => { - const sparkleRadius = eye.radius * 0.15; - const orbitRadius = eye.radius * 2.2; // Distance from eye center - - // Create 4 sparkles around each eye at different angles - const sparkles = [0, 45, 135, 180].map((angleDeg, i) => { - const angle = (angleDeg * Math.PI) / 180; - const x = eye.cx + orbitRadius * Math.cos(angle); - const y = eye.cy + orbitRadius * Math.sin(angle); - const delay = i * 0.3; // Stagger the animations - - // Small 4-pointed star sparkle - const sparkleSize = sparkleRadius * (0.8 + Math.random() * 0.4); - - return ` - - - - - `; - }); - - return sparkles.join('\n '); - }).join('\n '); + // Sparkle positions around the Blobbi body + // Assuming viewBox is roughly 100x100 for baby, 200x200 for adult + // These positions create a halo effect around the character + const sparklePositions = [ + // Top area + { x: 30, y: 8, size: 2.5, delay: 0 }, + { x: 50, y: 5, size: 3, delay: 0.8 }, + { x: 70, y: 8, size: 2, delay: 1.6 }, + // Upper sides + { x: 15, y: 25, size: 2.5, delay: 0.4 }, + { x: 85, y: 25, size: 2.5, delay: 1.2 }, + // Middle sides + { x: 10, y: 50, size: 2, delay: 0.6 }, + { x: 90, y: 50, size: 2.5, delay: 1.4 }, + // Lower sides + { x: 15, y: 75, size: 2, delay: 1.0 }, + { x: 85, y: 75, size: 2, delay: 0.2 }, + // Bottom corners + { x: 25, y: 90, size: 2.5, delay: 1.8 }, + { x: 75, y: 90, size: 2, delay: 0.5 }, + ]; + + const sparkles = sparklePositions.map(({ x, y, size, delay }) => { + // Vary the duration slightly for more organic feel + const duration = 2 + (delay * 0.3); + return createSparkleElement(x, y, size, color, delay, duration); + }); + + return sparkles.join('\n '); } /** @@ -1488,10 +1520,10 @@ function applyStarEyes(svgText: string, eyes: EyePosition[], config: StarEyesCon } } - // Add sparkles as a separate overlay (they don't need to track) - const sparkles = generateSparkles(eyes, config); + // Add sparkles around the entire Blobbi as a separate overlay + const sparkles = generateSparkles(config); const sparkleOverlay = ` - + ${sparkles} `;