diff --git a/src/head.rs b/src/head.rs index a1a2100..06cb33b 100644 --- a/src/head.rs +++ b/src/head.rs @@ -1,4 +1,7 @@ -use std::time::{Duration, Instant}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; use eframe::{ egui::{Context, Image, Ui}, @@ -16,14 +19,14 @@ pub struct Head { /// The threshold at which the character is considered to be half speaking, in dBFS. half_speak_threshold_dbfs: f32, - /// The head base image to use when the character is quiet. - quiet: RetainedImage, + /// Base images to use for the character's head. + head_bases: HashMap<(HeadExpression, SpeakPhase), RetainedImage>, - /// The head base image to use when the character is half speaking. - half_speak: RetainedImage, + /// The default head base to use. + default_head_base: RetainedImage, - /// The head base image to use when the character is fully speaking. - full_speak: RetainedImage, + /// The current expression on the character. + expression: HeadExpression, /// The current speaking phase. speak_phase: SpeakPhase, @@ -50,12 +53,10 @@ impl Head { } } - let head_base = match self.speak_phase { - SpeakPhase::Quiet => &self.quiet, - SpeakPhase::HalfSpeak => &self.half_speak, - SpeakPhase::FullSpeak => &self.full_speak, - }; - + let head_base = self + .head_bases + .get(&(self.expression, self.speak_phase)) + .unwrap_or(&self.default_head_base); Image::new(head_base.texture_id(ctx), rect.size()).paint_at(ui, rect); } } @@ -66,35 +67,82 @@ impl Default for Head { full_speak_threshold_dbfs: -20.0, half_speak_threshold_dbfs: -30.0, - quiet: RetainedImage::from_image_bytes( - "quiet", + head_bases: HashMap::from([ + // happy faces + ( + (HeadExpression::Happy, SpeakPhase::Quiet), + RetainedImage::from_image_bytes( + "head_happy_quiet", + include_bytes!("assets/head_happy_quiet.png"), + ) + .unwrap(), + ), + ( + (HeadExpression::Happy, SpeakPhase::HalfSpeak), + RetainedImage::from_image_bytes( + "head_happy_quiet", + include_bytes!("assets/head_happy_halfspeak.png"), + ) + .unwrap(), + ), + ( + (HeadExpression::Happy, SpeakPhase::FullSpeak), + RetainedImage::from_image_bytes( + "head_happy_quiet", + include_bytes!("assets/head_happy_speak.png"), + ) + .unwrap(), + ), + + // frowny faces + ( + (HeadExpression::Frown, SpeakPhase::Quiet), + RetainedImage::from_image_bytes( + "head_frown_quiet", + include_bytes!("assets/head_frown_quiet.png"), + ) + .unwrap(), + ), + ( + (HeadExpression::Frown, SpeakPhase::HalfSpeak), + RetainedImage::from_image_bytes( + "head_frown_quiet", + include_bytes!("assets/head_frown_halfspeak.png"), + ) + .unwrap(), + ), + ( + (HeadExpression::Frown, SpeakPhase::FullSpeak), + RetainedImage::from_image_bytes( + "head_frown_quiet", + include_bytes!("assets/head_frown_speak.png"), + ) + .unwrap(), + ), + ]), + + // RetainedImage does not implement Clone >:c + default_head_base: RetainedImage::from_image_bytes( + "head_default", include_bytes!("assets/head_happy_quiet.png"), ) .unwrap(), - half_speak: RetainedImage::from_image_bytes( - "half_speak", - include_bytes!("assets/head_happy_halfspeak.png"), - ) - .unwrap(), - full_speak: RetainedImage::from_image_bytes( - "full_speak", - include_bytes!("assets/head_happy_speak.png"), - ) - .unwrap(), + expression: HeadExpression::Happy, speak_phase: SpeakPhase::Quiet, last_phase_change: Instant::now(), } } } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] enum SpeakPhase { Quiet, HalfSpeak, FullSpeak, } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] enum HeadExpression { Happy, Frown,