use std::time::{Duration, Instant};
use crate::scene::types::PacingTag;
use super::theme;
#[derive(Debug)]
pub struct TextReveal {
pub total_lines: usize,
pub lines_complete: usize,
pub char_index: usize,
pub current_line_len: usize,
pub rate: Duration,
pub last_tick: Instant,
pub all_complete: bool,
}
impl TextReveal {
pub fn new(line_lengths: &[usize], pacing: PacingTag) -> Self {
let rate_ms = theme::pacing_reveal_ms(pacing);
let total = line_lengths.len();
let first_len = line_lengths.first().copied().unwrap_or(0);
let all_complete = total == 0 || rate_ms == 0;
Self {
total_lines: total,
lines_complete: if all_complete { total } else { 0 },
char_index: if all_complete { first_len } else { 0 },
current_line_len: first_len,
rate: Duration::from_millis(rate_ms),
last_tick: Instant::now(),
all_complete,
}
}
pub fn tick(&mut self, line_lengths: &[usize]) {
if self.all_complete {
return;
}
if self.rate.is_zero() {
self.lines_complete = self.total_lines;
self.all_complete = true;
return;
}
let now = Instant::now();
let elapsed = now.duration_since(self.last_tick);
if elapsed >= self.rate {
let chars_to_advance = (elapsed.as_millis() / self.rate.as_millis().max(1)) as usize;
for _ in 0..chars_to_advance.max(1) {
if self.lines_complete >= self.total_lines {
self.all_complete = true;
break;
}
if self.char_index < self.current_line_len {
self.char_index += 1;
} else {
self.lines_complete += 1;
if self.lines_complete >= self.total_lines {
self.all_complete = true;
break;
}
self.char_index = 0;
self.current_line_len = line_lengths
.get(self.lines_complete)
.copied()
.unwrap_or(0);
}
}
self.last_tick = now;
}
}
pub fn skip_line(&mut self, line_lengths: &[usize]) -> bool {
if self.all_complete {
return false;
}
if self.char_index < self.current_line_len {
self.char_index = self.current_line_len;
true
} else {
self.lines_complete += 1;
if self.lines_complete >= self.total_lines {
self.all_complete = true;
} else {
self.char_index = 0;
self.current_line_len = line_lengths
.get(self.lines_complete)
.copied()
.unwrap_or(0);
}
true
}
}
pub fn complete_all(&mut self) {
self.lines_complete = self.total_lines;
self.all_complete = true;
}
pub fn visible_chars(&self, line_idx: usize) -> usize {
if line_idx < self.lines_complete {
usize::MAX } else if line_idx == self.lines_complete {
self.char_index
} else {
0 }
}
pub fn line_visible(&self, line_idx: usize) -> bool {
line_idx < self.lines_complete
|| (line_idx == self.lines_complete && self.all_complete)
}
}