Crookfinger's voice: he babbles in a squeaky imp language in the rhythm of his hints, important messages and the mission briefings (can be switched off in the sound settings)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
776c9ad9de
commit
ada14951c4
9 files changed
+118
-3
No files matched your search
@@ -0,0 +1,71 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user