321 lines
8.9 KiB
Rust
321 lines
8.9 KiB
Rust
use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle, StartEdge};
|
|
|
|
/// A single frame's state
|
|
#[derive(Debug, Clone)]
|
|
pub struct AnimationState {
|
|
/// The index of the texture we're fading from
|
|
pub texture_a: u32,
|
|
|
|
/// The index of the texture we're fading to
|
|
pub texture_b: u32,
|
|
|
|
/// Between 0.0 and 1.0, denoting how far we are between
|
|
/// texture_a and texture_b
|
|
/// 0.0 means fully show texture_a;
|
|
/// 1.0 means fully show texture_b.
|
|
pub fade: f32,
|
|
}
|
|
|
|
impl AnimationState {
|
|
/// Convenience method.
|
|
/// Get texture index as an array
|
|
pub fn texture_index(&self) -> [u32; 2] {
|
|
[self.texture_a, self.texture_b]
|
|
}
|
|
}
|
|
|
|
/// What direction are we playing our animation in?
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum AnimDirection {
|
|
/// Top to bottom, with increasing frame indices
|
|
/// (normal)
|
|
Up,
|
|
|
|
/// Bottom to top, with decreasing frame indices
|
|
/// (reverse)
|
|
Down,
|
|
|
|
/// Stopped on one frame
|
|
Stop,
|
|
}
|
|
|
|
/// Manages a single sprite's animation state.
|
|
#[derive(Debug, Clone)]
|
|
pub struct SpriteAutomaton {
|
|
/// The sprite we're animating
|
|
sprite: SpriteHandle,
|
|
|
|
/// Which animation section we're on
|
|
/// This MUST be a section from this Automaton's sprite
|
|
current_section: AnimSectionHandle,
|
|
|
|
/// Which frame we're on
|
|
current_frame: usize,
|
|
|
|
/// Where we are between frames.
|
|
current_edge_progress: f32,
|
|
|
|
/// The length of the current edge we're on, in seconds
|
|
current_edge_duration: f32,
|
|
|
|
/// In what direction are we playing the current section?
|
|
current_direction: AnimDirection,
|
|
|
|
/// The texture we're fading from
|
|
/// (if we're moving downwards)
|
|
last_texture: u32,
|
|
|
|
/// The texture we're fading to
|
|
/// (if we're moving downwards)
|
|
next_texture: u32,
|
|
|
|
/// If this is some, take this edge next
|
|
next_edge_override: Option<SectionEdge>,
|
|
}
|
|
|
|
impl SpriteAutomaton {
|
|
/// Create a new AnimAutomaton
|
|
pub fn new(ct: &Content, sprite_handle: SpriteHandle) -> Self {
|
|
let sprite = ct.get_sprite(sprite_handle);
|
|
|
|
let (current_section, texture, current_direction) = match sprite.start_at {
|
|
StartEdge::Top { section } => (
|
|
section,
|
|
*sprite.get_section(section).frames.first().unwrap(),
|
|
AnimDirection::Down,
|
|
),
|
|
StartEdge::Bot { section } => (
|
|
section,
|
|
*sprite.get_section(section).frames.last().unwrap(),
|
|
AnimDirection::Up,
|
|
),
|
|
};
|
|
|
|
let sec = sprite.get_section(current_section);
|
|
|
|
Self {
|
|
sprite: sprite.handle,
|
|
current_frame: 0,
|
|
|
|
current_edge_progress: match current_direction {
|
|
AnimDirection::Down => 0.0,
|
|
AnimDirection::Up => sec.frame_duration,
|
|
AnimDirection::Stop => unreachable!("how'd you get here?"),
|
|
},
|
|
|
|
current_edge_duration: sec.frame_duration,
|
|
next_edge_override: None,
|
|
|
|
current_direction,
|
|
current_section,
|
|
last_texture: texture,
|
|
next_texture: texture,
|
|
}
|
|
}
|
|
|
|
/// Override the next section transition
|
|
pub fn next_edge(&mut self, s: SectionEdge) {
|
|
self.next_edge_override = Some(s);
|
|
}
|
|
|
|
/// Force a transition to the given section right now
|
|
pub fn jump_to(&mut self, ct: &Content, start: SectionEdge) {
|
|
self.take_edge(ct, start);
|
|
}
|
|
|
|
fn take_edge(&mut self, ct: &Content, e: SectionEdge) {
|
|
let sprite = ct.get_sprite(self.sprite);
|
|
let current_section = sprite.get_section(self.current_section);
|
|
|
|
let last = match self.current_direction {
|
|
AnimDirection::Stop => self.next_texture,
|
|
AnimDirection::Down => self.next_texture,
|
|
AnimDirection::Up => self.last_texture,
|
|
};
|
|
|
|
match e {
|
|
SectionEdge::Stop => {
|
|
match self.current_direction {
|
|
AnimDirection::Stop => {}
|
|
AnimDirection::Up => {
|
|
self.current_frame = 0;
|
|
}
|
|
AnimDirection::Down => {
|
|
self.current_frame = current_section.frames.len() - 1;
|
|
}
|
|
}
|
|
|
|
self.current_edge_duration = 1.0;
|
|
self.current_direction = AnimDirection::Stop;
|
|
}
|
|
SectionEdge::Top { section, duration } => {
|
|
self.current_section = section;
|
|
self.current_edge_duration = duration;
|
|
self.current_frame = 0;
|
|
self.current_direction = AnimDirection::Down;
|
|
}
|
|
SectionEdge::Bot { section, duration } => {
|
|
let s = sprite.get_section(section);
|
|
self.current_section = section;
|
|
self.current_frame = s.frames.len() - 1;
|
|
self.current_edge_duration = duration;
|
|
self.current_direction = AnimDirection::Up;
|
|
}
|
|
SectionEdge::Repeat { duration } => {
|
|
match self.current_direction {
|
|
AnimDirection::Stop => {}
|
|
AnimDirection::Up => {
|
|
self.current_frame = current_section.frames.len() - 1;
|
|
}
|
|
AnimDirection::Down => {
|
|
self.current_frame = 0;
|
|
}
|
|
}
|
|
self.current_edge_duration = duration;
|
|
}
|
|
SectionEdge::Reverse { duration } => {
|
|
match self.current_direction {
|
|
AnimDirection::Stop => {}
|
|
AnimDirection::Up => {
|
|
// Jump to SECOND frame, since we've already shown the
|
|
// first during the fade transition
|
|
self.current_frame = {
|
|
if current_section.frames.len() == 1 {
|
|
0
|
|
} else {
|
|
1
|
|
}
|
|
};
|
|
self.current_direction = AnimDirection::Down;
|
|
}
|
|
AnimDirection::Down => {
|
|
self.current_frame = {
|
|
if current_section.frames.len() == 1 {
|
|
0
|
|
} else {
|
|
current_section.frames.len() - 2
|
|
}
|
|
};
|
|
self.current_direction = AnimDirection::Up;
|
|
}
|
|
}
|
|
self.current_edge_duration = duration;
|
|
}
|
|
}
|
|
|
|
match self.current_direction {
|
|
AnimDirection::Stop => {
|
|
let current_section = sprite.get_section(self.current_section);
|
|
self.next_texture = current_section.frames[self.current_frame];
|
|
self.last_texture = current_section.frames[self.current_frame];
|
|
}
|
|
AnimDirection::Down => {
|
|
let current_section = sprite.get_section(self.current_section);
|
|
self.last_texture = last;
|
|
self.next_texture = current_section.frames[self.current_frame];
|
|
self.current_edge_progress = 0.0;
|
|
}
|
|
AnimDirection::Up => {
|
|
let current_section = sprite.get_section(self.current_section);
|
|
self.next_texture = last;
|
|
self.last_texture = current_section.frames[self.current_frame];
|
|
self.current_edge_progress = self.current_edge_duration;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Step this animation by `t` seconds
|
|
pub fn step(&mut self, ct: &Content, t: f32) {
|
|
let sprite = ct.get_sprite(self.sprite);
|
|
let current_section = sprite.get_section(self.current_section);
|
|
|
|
// Current_fade and current_frame keep track of where we are in the current section.
|
|
// current_frame indexes this section frames. When it exceeds the number of frames
|
|
// or falls below zero (when moving in reverse), we switch to the next section.
|
|
//
|
|
// current_fade keeps track of our state between frames. It is zero once a frame starts,
|
|
// and we switch to the next frame when it hits 1.0. If we are stepping foward, it increases,
|
|
// and if we are stepping backwards, it decreases.
|
|
|
|
// Note that frame_duration may be zero!
|
|
// This is only possible in the hidden texture, since
|
|
// user-provided sections are always checked to be positive.
|
|
assert!(current_section.frame_duration >= 0.0);
|
|
|
|
match self.current_direction {
|
|
AnimDirection::Stop => {
|
|
// Edge case: we're stopped and got a request to transition.
|
|
// we should transition right away.
|
|
|
|
if let Some(e) = self.next_edge_override {
|
|
self.take_edge(ct, e);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
AnimDirection::Down => {
|
|
self.current_edge_progress += t;
|
|
|
|
// We're stepping foward and finished this frame
|
|
if self.current_edge_progress > self.current_edge_duration {
|
|
if self.current_frame < current_section.frames.len() - 1 {
|
|
self.current_frame += 1;
|
|
self.last_texture = self.next_texture;
|
|
self.next_texture = current_section.frames[self.current_frame];
|
|
self.current_edge_progress = 0.0;
|
|
self.current_edge_duration = current_section.frame_duration;
|
|
} else {
|
|
let e = {
|
|
if self.next_edge_override.is_some() {
|
|
self.next_edge_override.take().unwrap()
|
|
} else {
|
|
current_section.edge_bot.clone()
|
|
}
|
|
};
|
|
self.take_edge(ct, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
AnimDirection::Up => {
|
|
self.current_edge_progress -= t;
|
|
|
|
// We're stepping backward and finished this frame
|
|
if self.current_edge_progress < 0.0 {
|
|
if self.current_frame > 0 {
|
|
self.current_frame -= 1;
|
|
self.next_texture = self.last_texture;
|
|
self.last_texture = current_section.frames[self.current_frame];
|
|
self.current_edge_progress = current_section.frame_duration;
|
|
self.current_edge_duration = current_section.frame_duration;
|
|
} else {
|
|
let e = {
|
|
if self.next_edge_override.is_some() {
|
|
self.next_edge_override.take().unwrap()
|
|
} else {
|
|
current_section.edge_top.clone()
|
|
}
|
|
};
|
|
self.take_edge(ct, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the current frame of this animation
|
|
pub fn get_texture_idx(&self) -> AnimationState {
|
|
return AnimationState {
|
|
texture_a: self.last_texture,
|
|
texture_b: self.next_texture,
|
|
fade: self.current_edge_progress / self.current_edge_duration,
|
|
};
|
|
}
|
|
|
|
/// Get the sprite this automaton is using
|
|
pub fn get_sprite(&self) -> SpriteHandle {
|
|
self.sprite
|
|
}
|
|
}
|