Files

72 lines
2.3 KiB
Rust

// Crookfinger's voice: he "speaks" in a squeaky made-up imp language - short synthesised
// syllables in the rhythm of the text (the text itself is shown as usual).
use crate::audio;
use crate::game::VOICE_SYLLABLES;
pub struct Voice {
queue: Vec<(f64, usize)>, // time, sound index
busy_until: f64,
}
// which messages Crookfinger says out loud
pub fn speaks(key: &str) -> bool {
key.starts_with("hint.")
|| key.starts_with("msg.wave_warn")
|| matches!(
key,
"msg.payday_soon" | "msg.researched" | "msg.mission_start" | "msg.mission_won" | "msg.rune_chest" | "msg.trap_made" | "msg.revenant_rises" | "msg.altar_gift" | "msg.altar_blessing" | "msg.brawl" | "msg.alarm" | "msg.creature_freed" | "msg.spring_taken"
)
}
impl Voice {
pub fn new() -> Voice {
Voice { queue: Vec::new(), busy_until: 0.0 }
}
// babble a text: one syllable per vowel group, pauses at spaces and punctuation
pub fn say(&mut self, text: &str, now: f64, max_syllables: usize) {
if now < self.busy_until {
return;
}
let mut t = now + 0.05;
let mut n = 0;
let mut h: u32 = 2166136261;
let mut in_vowel = false;
for ch in text.chars() {
h = (h ^ ch as u32).wrapping_mul(16777619);
let vowel = "aeiouäöüyAEIOUÄÖÜY".contains(ch);
if vowel && !in_vowel {
let v = VOICE_SYLLABLES[(h >> 7) as usize % VOICE_SYLLABLES.len()];
self.queue.push((t, v.index()));
t += 0.085 + ((h >> 13) % 4) as f64 * 0.012;
n += 1;
if n >= max_syllables {
break;
}
}
in_vowel = vowel;
match ch {
' ' => t += 0.035,
',' | ';' | '-' => t += 0.14,
'.' | '!' | '?' => t += 0.28,
_ => {}
}
}
self.busy_until = t + 4.0;
}
pub fn update(&mut self, now: f64, volume: f32) {
let due: Vec<usize> = self.queue.iter().filter(|q| q.0 <= now).map(|q| q.1).collect();
self.queue.retain(|q| q.0 > now);
for i in due {
audio::play_sfx(i, 0.75 * volume.max(0.0), 0.0);
}
}
pub fn stop(&mut self) {
self.queue.clear();
}
}