Added hidden texture support in content
parent
267ec5a40a
commit
e61c580247
|
@ -642,6 +642,7 @@ dependencies = [
|
|||
"galactica-packer",
|
||||
"galactica-util",
|
||||
"image",
|
||||
"lazy_static",
|
||||
"nalgebra",
|
||||
"rapier2d",
|
||||
"serde",
|
||||
|
|
|
@ -64,3 +64,4 @@ rand = "0.8.5"
|
|||
walkdir = "2.4.0"
|
||||
toml = "0.8.8"
|
||||
glyphon = "0.4.1"
|
||||
lazy_static = "1.4.0"
|
||||
|
|
|
@ -27,3 +27,4 @@ walkdir = { workspace = true }
|
|||
nalgebra = { workspace = true }
|
||||
image = { workspace = true }
|
||||
rapier2d = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
|
|
|
@ -13,8 +13,8 @@ use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
hash::Hash,
|
||||
io::Read,
|
||||
num::NonZeroU32,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use toml;
|
||||
|
@ -304,8 +304,8 @@ impl Content {
|
|||
}
|
||||
|
||||
/// Get a texture by its index
|
||||
pub fn get_image(&self, idx: u32) -> &SpriteAtlasImage {
|
||||
&self.sprite_atlas.index[idx as usize]
|
||||
pub fn get_image(&self, idx: NonZeroU32) -> &SpriteAtlasImage {
|
||||
&self.sprite_atlas.get_by_idx(idx)
|
||||
}
|
||||
|
||||
/// Get an outfit from a handle
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{num::NonZeroU32, path::PathBuf};
|
||||
|
||||
pub(crate) mod syntax {
|
||||
use anyhow::{bail, Result};
|
||||
|
@ -36,8 +36,8 @@ pub(crate) mod syntax {
|
|||
// An insufficient limit will result in some tiles not being drawn
|
||||
let starfield_instance_limit = 12 * starfield_count as u64;
|
||||
|
||||
let starfield_texture = match atlas.path_map.get(&self.starfield.texture) {
|
||||
Some(s) => *s,
|
||||
let starfield_texture = match atlas.get_idx_by_path(&self.starfield.texture) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
bail!(
|
||||
"starfield texture `{}` doesn't exist",
|
||||
|
@ -129,7 +129,7 @@ pub struct Config {
|
|||
pub starfield_max_dist: f32,
|
||||
|
||||
/// Index of starfield texture
|
||||
pub starfield_texture: u32,
|
||||
pub starfield_texture: NonZeroU32,
|
||||
|
||||
/// Size of a square starfield tile, in game units.
|
||||
/// A tile of size STARFIELD_Z_MAX * screen-size-in-game-units
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::{
|
||||
handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace,
|
||||
StartEdge,
|
||||
SectionEdge,
|
||||
};
|
||||
|
||||
pub(crate) mod syntax {
|
||||
|
@ -148,10 +148,10 @@ pub struct Outfit {
|
|||
pub engine_flare_sprite: Option<SpriteHandle>,
|
||||
|
||||
/// Jump to this edge when engines turn on
|
||||
pub engine_flare_on_start: Option<StartEdge>,
|
||||
pub engine_flare_on_start: Option<SectionEdge>,
|
||||
|
||||
/// Jump to this edge when engines turn off
|
||||
pub engine_flare_on_stop: Option<StartEdge>,
|
||||
pub engine_flare_on_stop: Option<SectionEdge>,
|
||||
|
||||
/// Shield hit points
|
||||
pub shield_strength: f32,
|
||||
|
@ -289,30 +289,75 @@ impl crate::Build for Outfit {
|
|||
};
|
||||
o.engine_thrust = engine.thrust;
|
||||
o.engine_flare_sprite = Some(sprite_handle);
|
||||
let sprite = content.get_sprite(sprite_handle);
|
||||
|
||||
// Flare animation will traverse this edge when the player presses the thrust key
|
||||
// This leads from the idle animation to the transition animation
|
||||
o.engine_flare_on_start = {
|
||||
let x = engine.flare.on_start;
|
||||
if x.is_none() {
|
||||
None
|
||||
} else {
|
||||
let x = x.unwrap();
|
||||
Some(
|
||||
x.resolve_as_start(sprite_handle, build_context)
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name))?,
|
||||
)
|
||||
let mut e = x
|
||||
.resolve_as_edge(sprite_handle, build_context, 0.0)
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name))?;
|
||||
match e {
|
||||
// Inherit duration from transition sequence
|
||||
SectionEdge::Top {
|
||||
section,
|
||||
ref mut duration,
|
||||
}
|
||||
| SectionEdge::Bot {
|
||||
section,
|
||||
ref mut duration,
|
||||
} => {
|
||||
*duration = sprite.get_section(section).frame_duration;
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"bad edge `{}`: must be `top` or `bot`",
|
||||
x.val
|
||||
))
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name));
|
||||
}
|
||||
};
|
||||
Some(e)
|
||||
}
|
||||
};
|
||||
|
||||
// Flare animation will traverse this edge when the player releases the thrust key
|
||||
// This leads from the idle animation to the transition animation
|
||||
o.engine_flare_on_stop = {
|
||||
let x = engine.flare.on_stop;
|
||||
if x.is_none() {
|
||||
None
|
||||
} else {
|
||||
let x = x.unwrap();
|
||||
Some(
|
||||
x.resolve_as_start(sprite_handle, build_context)
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name))?,
|
||||
)
|
||||
let mut e = x
|
||||
.resolve_as_edge(sprite_handle, build_context, 0.0)
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name))?;
|
||||
match e {
|
||||
// Inherit duration from transition sequence
|
||||
SectionEdge::Top {
|
||||
section,
|
||||
ref mut duration,
|
||||
}
|
||||
| SectionEdge::Bot {
|
||||
section,
|
||||
ref mut duration,
|
||||
} => {
|
||||
*duration = sprite.get_section(section).frame_duration;
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"bad edge `{}`: must be `top` or `bot`",
|
||||
x.val
|
||||
))
|
||||
.with_context(|| format!("in outfit `{}`", outfit_name));
|
||||
}
|
||||
};
|
||||
Some(e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{handle::SpriteHandle, Content, ContentBuildContext};
|
||||
|
@ -71,15 +72,15 @@ pub(crate) mod syntax {
|
|||
// Make sure all frames have the same size and add them
|
||||
// to the frame vector
|
||||
let mut dim = None;
|
||||
let mut frames = Vec::new();
|
||||
let mut frames: Vec<u32> = Vec::new();
|
||||
for f in &self.frames {
|
||||
let idx = match content.sprite_atlas.path_map.get(f) {
|
||||
Some(s) => *s,
|
||||
let idx = match content.sprite_atlas.get_idx_by_path(f) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
bail!("error: file `{}` isn't in the sprite atlas", f.display());
|
||||
}
|
||||
};
|
||||
let img = &content.sprite_atlas.index[idx as usize];
|
||||
let img = &content.sprite_atlas.get_by_idx(idx);
|
||||
|
||||
match dim {
|
||||
None => dim = Some(img.true_size),
|
||||
|
@ -90,8 +91,13 @@ pub(crate) mod syntax {
|
|||
}
|
||||
}
|
||||
|
||||
frames.push(img.idx);
|
||||
frames.push(img.idx.into());
|
||||
}
|
||||
|
||||
if frames.len() == 0 {
|
||||
bail!("sprite sections must not be empty",)
|
||||
}
|
||||
|
||||
let dim = dim.unwrap();
|
||||
|
||||
let frame_duration = match self.timing.variant {
|
||||
|
@ -104,12 +110,12 @@ pub(crate) mod syntax {
|
|||
}
|
||||
|
||||
let edge_top = match &self.top {
|
||||
Some(x) => x.resolve_as_edge(this_sprite, build_context)?,
|
||||
Some(x) => x.resolve_as_edge(this_sprite, build_context, frame_duration)?,
|
||||
None => super::SectionEdge::Stop,
|
||||
};
|
||||
|
||||
let edge_bot = match &self.bot {
|
||||
Some(x) => x.resolve_as_edge(this_sprite, build_context)?,
|
||||
Some(x) => x.resolve_as_edge(this_sprite, build_context, frame_duration)?,
|
||||
None => super::SectionEdge::Stop,
|
||||
};
|
||||
|
||||
|
@ -139,11 +145,11 @@ pub(crate) mod syntax {
|
|||
build_context: &ContentBuildContext,
|
||||
) -> Result<super::StartEdge> {
|
||||
let e = self
|
||||
.resolve_as_edge(sprite, build_context)
|
||||
.resolve_as_edge(sprite, build_context, 0.0)
|
||||
.with_context(|| format!("while resolving start edge"))?;
|
||||
match e {
|
||||
super::SectionEdge::Bot { section } => Ok(super::StartEdge::Bot { section }),
|
||||
super::SectionEdge::Top { section } => Ok(super::StartEdge::Top { section }),
|
||||
super::SectionEdge::Bot { section, .. } => Ok(super::StartEdge::Bot { section }),
|
||||
super::SectionEdge::Top { section, .. } => Ok(super::StartEdge::Top { section }),
|
||||
_ => {
|
||||
bail!("bad section start specification `{}`", self.val);
|
||||
}
|
||||
|
@ -154,19 +160,27 @@ pub(crate) mod syntax {
|
|||
&self,
|
||||
sprite: SpriteHandle,
|
||||
build_context: &ContentBuildContext,
|
||||
duration: f32,
|
||||
) -> Result<super::SectionEdge> {
|
||||
let all_sections = build_context.sprite_section_index.get(&sprite).unwrap();
|
||||
|
||||
if self.val == "hidden" {
|
||||
return Ok(super::SectionEdge::Top {
|
||||
section: crate::AnimSectionHandle::Hidden,
|
||||
duration,
|
||||
});
|
||||
}
|
||||
|
||||
if self.val == "stop" {
|
||||
return Ok(super::SectionEdge::Stop);
|
||||
}
|
||||
|
||||
if self.val == "reverse" {
|
||||
return Ok(super::SectionEdge::Reverse);
|
||||
return Ok(super::SectionEdge::Reverse { duration });
|
||||
}
|
||||
|
||||
if self.val == "restart" {
|
||||
return Ok(super::SectionEdge::Restart);
|
||||
if self.val == "repeat" {
|
||||
return Ok(super::SectionEdge::Repeat { duration });
|
||||
}
|
||||
|
||||
let (s, p) = match self.val.split_once(":") {
|
||||
|
@ -185,8 +199,8 @@ pub(crate) mod syntax {
|
|||
};
|
||||
|
||||
match p {
|
||||
"top" => Ok(super::SectionEdge::Top { section }),
|
||||
"bot" => Ok(super::SectionEdge::Bot { section }),
|
||||
"top" => Ok(super::SectionEdge::Top { section, duration }),
|
||||
"bot" => Ok(super::SectionEdge::Bot { section, duration }),
|
||||
_ => {
|
||||
return Err(anyhow!("bad section edge specification `{}`", self.val))
|
||||
.with_context(|| format!("invalid target `{}`", p));
|
||||
|
@ -196,10 +210,24 @@ pub(crate) mod syntax {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: should be pub crate
|
||||
/// A handle for an animation section inside a sprite
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AnimSectionHandle(pub(crate) usize);
|
||||
pub enum AnimSectionHandle {
|
||||
/// The hidden section
|
||||
Hidden,
|
||||
|
||||
/// An index into this sprite's section array
|
||||
Idx(usize),
|
||||
}
|
||||
|
||||
impl AnimSectionHandle {
|
||||
fn get_idx(&self) -> Option<usize> {
|
||||
match self {
|
||||
Self::Hidden => None,
|
||||
Self::Idx(idx) => Some(*idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An edge between two animation sections
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -211,19 +239,31 @@ pub enum SectionEdge {
|
|||
Bot {
|
||||
/// The section to play
|
||||
section: AnimSectionHandle,
|
||||
|
||||
/// The length of this edge, in seconds
|
||||
duration: f32,
|
||||
},
|
||||
|
||||
/// Play the given section from the top
|
||||
Top {
|
||||
/// The section to play
|
||||
section: AnimSectionHandle,
|
||||
|
||||
/// The length of this edge, in seconds
|
||||
duration: f32,
|
||||
},
|
||||
|
||||
/// Replay this section in the opposite direction
|
||||
Reverse,
|
||||
Reverse {
|
||||
/// The length of this edge, in seconds
|
||||
duration: f32,
|
||||
},
|
||||
|
||||
/// Restart this section from the opposite end
|
||||
Restart,
|
||||
Repeat {
|
||||
/// The length of this edge, in seconds
|
||||
duration: f32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Where to start an animation
|
||||
|
@ -245,8 +285,14 @@ pub enum StartEdge {
|
|||
impl Into<SectionEdge> for StartEdge {
|
||||
fn into(self) -> SectionEdge {
|
||||
match self {
|
||||
Self::Bot { section } => SectionEdge::Bot { section },
|
||||
Self::Top { section } => SectionEdge::Top { section },
|
||||
Self::Bot { section } => SectionEdge::Bot {
|
||||
section,
|
||||
duration: 0.0,
|
||||
},
|
||||
Self::Top { section } => SectionEdge::Top {
|
||||
section,
|
||||
duration: 0.0,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,13 +316,25 @@ pub struct Sprite {
|
|||
pub aspect: f32,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref HIDDEN_SECTION: SpriteSection = SpriteSection {
|
||||
frames: vec![0],
|
||||
frame_duration: 0.0,
|
||||
edge_bot: SectionEdge::Stop,
|
||||
edge_top: SectionEdge::Stop,
|
||||
};
|
||||
}
|
||||
|
||||
impl Sprite {
|
||||
/// Get an animation section from a handle
|
||||
pub fn get_section(&self, section: AnimSectionHandle) -> &SpriteSection {
|
||||
&self.sections[section.0]
|
||||
match section {
|
||||
AnimSectionHandle::Hidden => &HIDDEN_SECTION,
|
||||
AnimSectionHandle::Idx(idx) => &self.sections[idx],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this sprite's first frame
|
||||
/// Get the index of the texture of this sprite's first frame
|
||||
pub fn get_first_frame(&self) -> u32 {
|
||||
match self.start_at {
|
||||
StartEdge::Bot { section } => *self.get_section(section).frames.last().unwrap(),
|
||||
|
@ -327,8 +385,8 @@ impl crate::Build for Sprite {
|
|||
for (sprite_name, t) in sprites {
|
||||
match t {
|
||||
syntax::Sprite::Static(t) => {
|
||||
let idx = match content.sprite_atlas.path_map.get(&t.file) {
|
||||
Some(s) => *s,
|
||||
let idx = match content.sprite_atlas.get_idx_by_path(&t.file) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(
|
||||
anyhow!("error while processing sprite `{}`", sprite_name,),
|
||||
|
@ -341,7 +399,7 @@ impl crate::Build for Sprite {
|
|||
});
|
||||
}
|
||||
};
|
||||
let img = &content.sprite_atlas.index[idx as usize];
|
||||
let img = &content.sprite_atlas.get_by_idx(idx);
|
||||
let aspect = img.w / img.h;
|
||||
|
||||
let h = SpriteHandle {
|
||||
|
@ -350,16 +408,16 @@ impl crate::Build for Sprite {
|
|||
|
||||
content.sprite_index.insert(sprite_name.clone(), h);
|
||||
let mut smap = HashMap::new();
|
||||
smap.insert("anim".to_string(), AnimSectionHandle(0));
|
||||
smap.insert("anim".to_string(), AnimSectionHandle::Idx(0));
|
||||
build_context.sprite_section_index.insert(h, smap);
|
||||
|
||||
content.sprites.push(Self {
|
||||
name: sprite_name,
|
||||
start_at: StartEdge::Top {
|
||||
section: AnimSectionHandle(0),
|
||||
section: AnimSectionHandle::Idx(0),
|
||||
},
|
||||
sections: vec![SpriteSection {
|
||||
frames: vec![img.idx],
|
||||
frames: vec![img.idx.into()],
|
||||
// We implement unanimated sprites with a very fast framerate
|
||||
// and STOP endpoints.
|
||||
frame_duration: 0.01,
|
||||
|
@ -373,7 +431,7 @@ impl crate::Build for Sprite {
|
|||
syntax::Sprite::OneSection(s) => {
|
||||
let mut section_names: HashMap<String, _> = HashMap::new();
|
||||
// Name the one section in this sprite "anim"
|
||||
section_names.insert("anim".to_owned(), AnimSectionHandle(0));
|
||||
section_names.insert("anim".to_owned(), AnimSectionHandle::Idx(0));
|
||||
|
||||
let sprite_handle = SpriteHandle {
|
||||
index: content.sprites.len(),
|
||||
|
@ -382,7 +440,7 @@ impl crate::Build for Sprite {
|
|||
.sprite_index
|
||||
.insert(sprite_name.clone(), sprite_handle);
|
||||
let mut smap = HashMap::new();
|
||||
smap.insert("anim".to_string(), AnimSectionHandle(0));
|
||||
smap.insert("anim".to_string(), AnimSectionHandle::Idx(0));
|
||||
build_context
|
||||
.sprite_section_index
|
||||
.insert(sprite_handle, smap);
|
||||
|
@ -398,19 +456,18 @@ impl crate::Build for Sprite {
|
|||
content.sprites.push(Self {
|
||||
name: sprite_name,
|
||||
sections,
|
||||
start_at: StartEdge::Bot {
|
||||
section: AnimSectionHandle(0),
|
||||
start_at: StartEdge::Top {
|
||||
section: AnimSectionHandle::Idx(0),
|
||||
},
|
||||
handle: sprite_handle,
|
||||
aspect,
|
||||
});
|
||||
}
|
||||
syntax::Sprite::Complete(s) => {
|
||||
let mut idx = 0;
|
||||
let mut section_names = HashMap::new();
|
||||
for (name, _) in &s.section {
|
||||
section_names.insert(name.to_owned(), AnimSectionHandle(idx));
|
||||
idx += 1;
|
||||
section_names
|
||||
.insert(name.to_owned(), AnimSectionHandle::Idx(section_names.len()));
|
||||
}
|
||||
|
||||
let sprite_handle = SpriteHandle {
|
||||
|
@ -428,19 +485,19 @@ impl crate::Build for Sprite {
|
|||
.resolve_as_start(sprite_handle, build_context)
|
||||
.with_context(|| format!("while loading sprite `{}`", sprite_name))?;
|
||||
|
||||
let mut sections = Vec::with_capacity(idx);
|
||||
let mut sections = Vec::with_capacity(section_names.len());
|
||||
let mut dim = None;
|
||||
|
||||
// Make sure we add sections in order
|
||||
let mut names = section_names.iter().collect::<Vec<_>>();
|
||||
names.sort_by(|a, b| (a.1).0.cmp(&(b.1).0));
|
||||
names.sort_by(|a, b| (a.1).get_idx().unwrap().cmp(&(b.1).get_idx().unwrap()));
|
||||
|
||||
for (k, _) in names {
|
||||
let v = s.section.get(k).unwrap();
|
||||
let (d, s) = v
|
||||
.add_to(sprite_handle, build_context, content)
|
||||
.with_context(|| format!("while parsing sprite `{}`", sprite_name))
|
||||
.with_context(|| format!("while parsing section `{}`", k))?;
|
||||
.with_context(|| format!("while parsing section `{}`", k))
|
||||
.with_context(|| format!("while parsing sprite `{}`", sprite_name))?;
|
||||
|
||||
// Make sure all dimensions are the same
|
||||
if dim.is_none() {
|
||||
|
|
|
@ -25,7 +25,7 @@ impl AnimationState {
|
|||
}
|
||||
|
||||
/// What direction are we playing our animation in?
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum AnimDirection {
|
||||
/// Top to bottom, with increasing frame indices
|
||||
/// (normal)
|
||||
|
@ -35,7 +35,7 @@ enum AnimDirection {
|
|||
/// (reverse)
|
||||
Down,
|
||||
|
||||
/// Stopped, no animation
|
||||
/// Stopped on one frame
|
||||
Stop,
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,10 @@ pub struct SpriteAutomaton {
|
|||
current_frame: usize,
|
||||
|
||||
/// Where we are between frames.
|
||||
/// Always between zero and one.
|
||||
current_fade: f32,
|
||||
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,
|
||||
|
@ -89,10 +91,19 @@ impl SpriteAutomaton {
|
|||
),
|
||||
};
|
||||
|
||||
let sec = sprite.get_section(current_section);
|
||||
|
||||
Self {
|
||||
sprite: sprite.handle,
|
||||
current_frame: 0,
|
||||
current_fade: 0.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,
|
||||
|
@ -108,49 +119,108 @@ impl SpriteAutomaton {
|
|||
}
|
||||
|
||||
/// Force a transition to the given section right now
|
||||
pub fn jump_to(&mut self, ct: &Content, start: StartEdge) {
|
||||
self.take_edge(ct, start.into());
|
||||
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_direction = self.current_direction;
|
||||
|
||||
match e {
|
||||
SectionEdge::Stop => {
|
||||
self.current_fade = 0.0;
|
||||
self.current_frame = current_section.frames.len() - 1;
|
||||
self.current_direction = AnimDirection::Stop;
|
||||
}
|
||||
SectionEdge::Top { section } => {
|
||||
self.current_section = section;
|
||||
self.current_frame = 0;
|
||||
self.current_direction = AnimDirection::Down;
|
||||
}
|
||||
SectionEdge::Bot { section } => {
|
||||
let s = sprite.get_section(section);
|
||||
self.current_section = section;
|
||||
self.current_frame = s.frames.len() - 1;
|
||||
self.current_direction = AnimDirection::Up;
|
||||
}
|
||||
SectionEdge::Restart => {
|
||||
self.current_frame = 0;
|
||||
}
|
||||
SectionEdge::Reverse => {
|
||||
// Jump to SECOND frame, since we've already shown the
|
||||
// first during the fade transition
|
||||
|
||||
match self.current_direction {
|
||||
AnimDirection::Stop => {}
|
||||
AnimDirection::Up => {
|
||||
self.current_frame = 0;
|
||||
self.current_direction = AnimDirection::Down;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
let last = match last_direction {
|
||||
AnimDirection::Stop => self.last_texture,
|
||||
AnimDirection::Down => self.next_texture,
|
||||
AnimDirection::Up => self.last_texture,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,13 +238,12 @@ impl SpriteAutomaton {
|
|||
// 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.
|
||||
|
||||
// This should always be positive, and this fact is enforced by the content loader.
|
||||
// if we get here, something is very wrong.
|
||||
assert!(current_section.frame_duration > 0.0);
|
||||
// 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::Down => self.current_fade += t / current_section.frame_duration,
|
||||
AnimDirection::Up => self.current_fade -= t / current_section.frame_duration,
|
||||
AnimDirection::Stop => {
|
||||
// Edge case: we're stopped and got a request to transition.
|
||||
// we should transition right away.
|
||||
|
@ -182,18 +251,21 @@ impl SpriteAutomaton {
|
|||
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
|
||||
// (implies we're travelling downwards)
|
||||
if self.current_fade > 1.0 {
|
||||
while self.current_fade > 1.0 {
|
||||
self.current_fade -= 1.0;
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -204,21 +276,20 @@ impl SpriteAutomaton {
|
|||
};
|
||||
self.take_edge(ct, e);
|
||||
}
|
||||
|
||||
let current_section = sprite.get_section(self.current_section);
|
||||
self.last_texture = self.next_texture;
|
||||
self.next_texture = current_section.frames[self.current_frame];
|
||||
}
|
||||
}
|
||||
|
||||
AnimDirection::Up => {
|
||||
self.current_edge_progress -= t;
|
||||
|
||||
// We're stepping backward and finished this frame
|
||||
// (implies we're travelling upwards)
|
||||
if self.current_fade < 0.0 {
|
||||
while self.current_fade < 0.0 {
|
||||
self.current_fade += 1.0;
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -229,10 +300,8 @@ impl SpriteAutomaton {
|
|||
};
|
||||
self.take_edge(ct, e);
|
||||
}
|
||||
|
||||
let current_section = sprite.get_section(self.current_section);
|
||||
self.next_texture = self.last_texture;
|
||||
self.last_texture = current_section.frames[self.current_frame];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +310,7 @@ impl SpriteAutomaton {
|
|||
return AnimationState {
|
||||
texture_a: self.last_texture,
|
||||
texture_b: self.next_texture,
|
||||
fade: self.current_fade,
|
||||
fade: self.current_edge_progress / self.current_edge_duration,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue