Cleaned up starfield and added sprite start edge

master
Mark 2024-01-20 10:47:18 -08:00
parent 7d5b244492
commit 56160a8abe
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
12 changed files with 159 additions and 98 deletions

View File

@ -28,8 +28,8 @@ starfield.max_size = 1.8
# Z-axis (parallax) range for starfield stars # Z-axis (parallax) range for starfield stars
starfield.min_dist = 75.0 starfield.min_dist = 75.0
starfield.max_dist = 200.0 starfield.max_dist = 200.0
# Name of starfield sprite # Path to starfield sprite texture
starfield.sprite = "starfield" starfield.texture = "starfield.png"
# Zoom level bounds. # Zoom level bounds.

View File

@ -1,17 +1,41 @@
# TODO: # TODO:
# random start frame # random start frame
# repeat once: stay on last frame
# blending mode: alpha / half-alpha / additive # blending mode: alpha / half-alpha / additive
[sprite."starfield"]
file = "starfield.png"
[sprite."star::star"] [sprite."star::star"]
file = "star/B-09.png" file = "star/B-09.png"
[sprite."flare::ion"] [sprite."flare::ion"]
file = "flare/1.png" start_at = "rise:top"
random_start_frame = false
section.idle.timing.duration = 5
#section.idle.repeat = "reverse"
section.idle.frames = ["flare/1.png", "flare/4.png", "flare/5.png"]
section.idle.top = "reverse"
section.idle.bot = "reverse"
# stop: stop on last frame (special)
# restart: go to opposite end (same as self:tail)
# repeat: reverse and play again
# TODO: implement random
# spec: "idle:bot", "idle:top", or "idle:random"
section.rise.timing.duration = 0.15
section.rise.top = "stop"
section.rise.bot = "run:top"
section.rise.frames = [
"flare/6.png",
"flare/5.png",
"flare/4.png",
"flare/3.png",
"flare/2.png",
]
section.run.timing.duration = 0.01
section.run.top = "stop"
section.run.bot = "stop"
section.run.frames = ["flare/1.png"]
[sprite."planet::earth"] [sprite."planet::earth"]
file = "planet/earth.png" file = "planet/earth.png"
@ -70,6 +94,19 @@ file = "ui/landscape/test.png"
[sprite."ui::landscapemask"] [sprite."ui::landscapemask"]
file = "ui/landscape-mask.png" file = "ui/landscape-mask.png"
[sprite."ui::planet::button"]
start_at = "off:top"
random_start_frame = false
section.off.top = "stop"
section.off.bot = "stop"
section.off.timing.fps = 60
section.off.frames = ["ui/planet-button-off.png"]
section.on.top = "stop"
section.on.bot = "stop"
section.on.timing.fps = 60
section.on.frames = ["ui/planet-button-on.png"]
[sprite."particle::blaster"] [sprite."particle::blaster"]
timing.duration = 0.15 timing.duration = 0.15

View File

@ -1,4 +1,4 @@
use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle}; use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle, SpriteStart};
/// A single frame's state /// A single frame's state
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -72,33 +72,35 @@ impl AnimAutomaton {
/// Create a new AnimAutomaton /// Create a new AnimAutomaton
pub fn new(ct: &Content, sprite_handle: SpriteHandle) -> Self { pub fn new(ct: &Content, sprite_handle: SpriteHandle) -> Self {
let sprite = ct.get_sprite(sprite_handle); let sprite = ct.get_sprite(sprite_handle);
let (current_section, texture, current_direction) = match sprite.start_at {
SpriteStart::Top { section } => (
section,
*sprite.get_section(section).frames.first().unwrap(),
AnimDirection::Down,
),
SpriteStart::Bot { section } => (
section,
*sprite.get_section(section).frames.last().unwrap(),
AnimDirection::Up,
),
};
Self { Self {
current_direction: AnimDirection::Down,
sprite: sprite.handle, sprite: sprite.handle,
current_frame: 0, current_frame: 0,
current_fade: 0.0, current_fade: 0.0,
current_section: sprite.default_section,
last_texture: *sprite current_direction,
.get_section(sprite.default_section) current_section,
.frames last_texture: texture,
.first() next_texture: texture,
.unwrap(),
next_texture: *sprite
.get_section(sprite.default_section)
.frames
.first()
.unwrap(),
} }
} }
/// Reset this animation /// Reset this animation
pub fn reset(&mut self, ct: &Content) { pub fn reset(&mut self, ct: &Content) {
let sprite = ct.get_sprite(self.sprite); *self = Self::new(ct, self.sprite);
self.current_fade = 0.0;
self.current_frame = 0;
self.current_section = sprite.default_section
} }
/// Reverse this animation's direction /// Reverse this animation's direction

View File

@ -141,8 +141,6 @@ pub struct Content {
/// Map strings to texture names. /// Map strings to texture names.
/// This is only necessary because we need to hard-code a few texture names for UI elements. /// This is only necessary because we need to hard-code a few texture names for UI elements.
sprite_index: HashMap<String, SpriteHandle>, sprite_index: HashMap<String, SpriteHandle>,
/// The texture to use for starfield stars
starfield_handle: Option<SpriteHandle>,
/// Keeps track of which images are in which texture /// Keeps track of which images are in which texture
sprite_atlas: SpriteAtlas, sprite_atlas: SpriteAtlas,
@ -206,7 +204,7 @@ impl Content {
let mut content = Self { let mut content = Self {
config: { config: {
if let Some(c) = root.config { if let Some(c) = root.config {
c.build(&asset_root) c.build(&asset_root, &atlas)
.with_context(|| "while parsing config table")? .with_context(|| "while parsing config table")?
} else { } else {
bail!("failed loading content: no config table specified") bail!("failed loading content: no config table specified")
@ -222,7 +220,6 @@ impl Content {
factions: Vec::new(), factions: Vec::new(),
effects: Vec::new(), effects: Vec::new(),
sprite_index: HashMap::new(), sprite_index: HashMap::new(),
starfield_handle: None,
}; };
// TODO: enforce sprite and image limits // TODO: enforce sprite and image limits
@ -280,16 +277,6 @@ impl Content {
(0..self.systems.len()).map(|x| SystemHandle { index: x }) (0..self.systems.len()).map(|x| SystemHandle { index: x })
} }
/// Get the handle for the starfield sprite
pub fn get_starfield_texture(&self) -> u32 {
let h = match self.starfield_handle {
Some(h) => h,
None => unreachable!("Starfield sprite hasn't been loaded yet!"),
};
let sprite = self.get_sprite(h);
sprite.get_section(sprite.default_section).frames[0]
}
/// Get a handle from a sprite name /// Get a handle from a sprite name
pub fn get_sprite_handle(&self, name: &str) -> SpriteHandle { pub fn get_sprite_handle(&self, name: &str) -> SpriteHandle {
return match self.sprite_index.get(name) { return match self.sprite_index.get(name) {

View File

@ -2,6 +2,7 @@ use std::path::PathBuf;
pub(crate) mod syntax { pub(crate) mod syntax {
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use galactica_packer::SpriteAtlas;
use serde::Deserialize; use serde::Deserialize;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -19,7 +20,7 @@ pub(crate) mod syntax {
impl Config { impl Config {
// TODO: clean up build trait // TODO: clean up build trait
pub fn build(self, asset_root: &Path) -> Result<super::Config> { pub fn build(self, asset_root: &Path, atlas: &SpriteAtlas) -> Result<super::Config> {
for i in &self.fonts.files { for i in &self.fonts.files {
if !asset_root.join(i).exists() { if !asset_root.join(i).exists() {
bail!("font file `{}` doesn't exist", i.display()); bail!("font file `{}` doesn't exist", i.display());
@ -35,6 +36,16 @@ pub(crate) mod syntax {
// An insufficient limit will result in some tiles not being drawn // An insufficient limit will result in some tiles not being drawn
let starfield_instance_limit = 12 * starfield_count as u64; let starfield_instance_limit = 12 * starfield_count as u64;
let starfield_texture = match atlas.path_map.get(&self.starfield.texture) {
Some(s) => *s,
None => {
bail!(
"starfield texture `{}` doesn't exist",
self.starfield.texture.display()
)
}
};
return Ok(super::Config { return Ok(super::Config {
sprite_root: asset_root.join(self.sprite_root), sprite_root: asset_root.join(self.sprite_root),
font_files: self font_files: self
@ -53,7 +64,7 @@ pub(crate) mod syntax {
starfield_min_dist: self.starfield.min_dist, starfield_min_dist: self.starfield.min_dist,
starfield_max_size: self.starfield.max_size, starfield_max_size: self.starfield.max_size,
starfield_min_size: self.starfield.min_size, starfield_min_size: self.starfield.min_size,
starfield_sprite: self.starfield.sprite, starfield_texture,
starfield_count, starfield_count,
starfield_density, starfield_density,
starfield_size, starfield_size,
@ -80,7 +91,7 @@ pub(crate) mod syntax {
pub max_size: f32, pub max_size: f32,
pub min_dist: f32, pub min_dist: f32,
pub max_dist: f32, pub max_dist: f32,
pub sprite: String, pub texture: PathBuf,
} }
} }
@ -117,8 +128,8 @@ pub struct Config {
/// Maximum z-distance of starfield star, in game units /// Maximum z-distance of starfield star, in game units
pub starfield_max_dist: f32, pub starfield_max_dist: f32,
/// Name of starfield sprite /// Index of starfield texture
pub starfield_sprite: String, pub starfield_texture: u32,
/// Size of a square starfield tile, in game units. /// Size of a square starfield tile, in game units.
/// A tile of size STARFIELD_Z_MAX * screen-size-in-game-units /// A tile of size STARFIELD_Z_MAX * screen-size-in-game-units

View File

@ -8,7 +8,7 @@ pub(crate) mod syntax {
use galactica_util::to_radians; use galactica_util::to_radians;
use serde::Deserialize; use serde::Deserialize;
use crate::{Content, ContentBuildContext, EffectHandle}; use crate::{Content, ContentBuildContext, EffectHandle, SpriteStart};
// Raw serde syntax structs. // Raw serde syntax structs.
// These are never seen by code outside this crate. // These are never seen by code outside this crate.
@ -56,8 +56,12 @@ pub(crate) mod syntax {
TextOrFloat::Float(f) => f, TextOrFloat::Float(f) => f,
TextOrFloat::Text(s) => { TextOrFloat::Text(s) => {
if s == "inherit" { if s == "inherit" {
// Match lifetime of first section of sprite
let sprite = content.get_sprite(sprite); let sprite = content.get_sprite(sprite);
let sec = sprite.get_section(sprite.default_section); let sec = match sprite.start_at {
SpriteStart::Top { section } => sprite.get_section(section),
SpriteStart::Bot { section } => sprite.get_section(section),
};
sec.frame_duration * sec.frames.len() as f32 sec.frame_duration * sec.frames.len() as f32
} else { } else {
bail!("bad effect lifetime, must be float or \"inherit\"",) bail!("bad effect lifetime, must be float or \"inherit\"",)

View File

@ -51,7 +51,7 @@ pub(crate) mod syntax {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct CompleteSprite { pub struct CompleteSprite {
pub section: HashMap<String, SpriteSection>, pub section: HashMap<String, SpriteSection>,
pub default_section: String, pub start_at: SectionEdge,
} }
/// A single animation section /// A single animation section
@ -109,12 +109,12 @@ pub(crate) mod syntax {
} }
let edge_top = match &self.top { let edge_top = match &self.top {
Some(x) => x.resolve(all_sections)?, Some(x) => x.resolve_as_edge(all_sections)?,
None => super::SectionEdge::Stop, None => super::SectionEdge::Stop,
}; };
let edge_bot = match &self.bot { let edge_bot = match &self.bot {
Some(x) => x.resolve(all_sections)?, Some(x) => x.resolve_as_edge(all_sections)?,
None => super::SectionEdge::Stop, None => super::SectionEdge::Stop,
}; };
@ -138,7 +138,23 @@ pub(crate) mod syntax {
} }
impl SectionEdge { impl SectionEdge {
pub fn resolve( pub fn resolve_as_start(
&self,
all_sections: &HashMap<String, AnimSectionHandle>,
) -> Result<super::SpriteStart> {
let e = self
.resolve_as_edge(all_sections)
.with_context(|| format!("while resolving start edge"))?;
match e {
super::SectionEdge::Bot { section } => Ok(super::SpriteStart::Bot { section }),
super::SectionEdge::Top { section } => Ok(super::SpriteStart::Top { section }),
_ => {
bail!("bad section start specification `{}`", self.val);
}
}
}
pub fn resolve_as_edge(
&self, &self,
all_sections: &HashMap<String, AnimSectionHandle>, all_sections: &HashMap<String, AnimSectionHandle>,
) -> Result<super::SectionEdge> { ) -> Result<super::SectionEdge> {
@ -184,7 +200,7 @@ pub(crate) mod syntax {
// TODO: should be pub crate // TODO: should be pub crate
/// A handle for an animation section inside a sprite /// A handle for an animation section inside a sprite
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct AnimSectionHandle(usize); pub struct AnimSectionHandle(pub(crate) usize);
/// An edge between two animation sections /// An edge between two animation sections
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -211,6 +227,22 @@ pub enum SectionEdge {
Restart, Restart,
} }
/// Where to start an animation
#[derive(Debug, Clone)]
pub enum SpriteStart {
/// Play the given section from the bottm
Bot {
/// The section to play
section: AnimSectionHandle,
},
/// Play the given section from the top
Top {
/// The section to play
section: AnimSectionHandle,
},
}
/// Represents a sprite that may be used in the game. /// Represents a sprite that may be used in the game.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Sprite { pub struct Sprite {
@ -220,8 +252,8 @@ pub struct Sprite {
/// This sprite's handle /// This sprite's handle
pub handle: SpriteHandle, pub handle: SpriteHandle,
/// This sprite's default section /// Where this sprite starts playing
pub default_section: AnimSectionHandle, pub start_at: SpriteStart,
/// This sprite's animation sections /// This sprite's animation sections
sections: Vec<SpriteSection>, sections: Vec<SpriteSection>,
@ -236,6 +268,14 @@ impl Sprite {
&self.sections[section.0] &self.sections[section.0]
} }
/// Get this sprite's first frame
pub fn get_first_frame(&self) -> u32 {
match self.start_at {
SpriteStart::Bot { section } => *self.get_section(section).frames.last().unwrap(),
SpriteStart::Top { section } => *self.get_section(section).frames.first().unwrap(),
}
}
/// Iterate this sprite's sections /// Iterate this sprite's sections
pub fn iter_sections(&self) -> impl Iterator<Item = &SpriteSection> { pub fn iter_sections(&self) -> impl Iterator<Item = &SpriteSection> {
self.sections.iter() self.sections.iter()
@ -293,20 +333,13 @@ impl crate::Build for Sprite {
aspect, aspect,
}; };
if sprite_name == content.config.starfield_sprite {
if content.starfield_handle.is_none() {
content.starfield_handle = Some(h)
} else {
// This can't happen, since this is a hashmap.
unreachable!("Found two starfield sprites! Something is very wrong.")
}
}
content.sprite_index.insert(sprite_name.clone(), h); content.sprite_index.insert(sprite_name.clone(), h);
content.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
default_section: AnimSectionHandle(0), start_at: SpriteStart::Top {
section: AnimSectionHandle(0),
},
sections: vec![SpriteSection { sections: vec![SpriteSection {
frames: vec![img.idx], frames: vec![img.idx],
// We implement unanimated sprites with a very fast framerate // We implement unanimated sprites with a very fast framerate
@ -333,11 +366,6 @@ impl crate::Build for Sprite {
aspect, aspect,
}; };
// TODO: remove?
if sprite_name == content.config.starfield_sprite {
unreachable!("Starfield texture may not be animated")
}
let mut sections = Vec::new(); let mut sections = Vec::new();
sections.push(section); sections.push(section);
@ -345,7 +373,9 @@ impl crate::Build for Sprite {
content.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
sections, sections,
default_section: AnimSectionHandle(0), start_at: SpriteStart::Bot {
section: AnimSectionHandle(0),
},
handle: h, handle: h,
aspect, aspect,
}); });
@ -358,13 +388,10 @@ impl crate::Build for Sprite {
idx += 1; idx += 1;
} }
if !section_names.contains_key(&s.default_section) { let start_at = s
bail!( .start_at
"could not load sprite `{}`, default section `{}` doesn't exist", .resolve_as_start(&section_names)
sprite_name, .with_context(|| format!("while loading sprite `{}`", sprite_name))?;
s.default_section
);
}
let mut sections = Vec::with_capacity(idx); let mut sections = Vec::with_capacity(idx);
let mut dim = None; let mut dim = None;
@ -405,7 +432,7 @@ impl crate::Build for Sprite {
content.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
sections, sections,
default_section: *section_names.get(&s.default_section).unwrap(), start_at,
handle: h, handle: h,
aspect, aspect,
}); });
@ -413,13 +440,6 @@ impl crate::Build for Sprite {
} }
} }
if content.starfield_handle.is_none() {
bail!(
"Could not find a starfield texture (name: `{}`)",
content.config.starfield_sprite
)
}
return Ok(()); return Ok(());
} }
} }

View File

@ -240,7 +240,7 @@ impl GPUState {
); );
let sprite = state.ct.get_sprite(o.sprite); let sprite = state.ct.get_sprite(o.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
// Push this object's instance // Push this object's instance
self.state.push_object_buffer(ObjectInstance { self.state.push_object_buffer(ObjectInstance {

View File

@ -49,7 +49,7 @@ impl<'a> super::GPUState {
// Write all new particles to GPU buffer // Write all new particles to GPU buffer
for i in input.particles.iter() { for i in input.particles.iter() {
let sprite = input.ct.get_sprite(i.sprite); let sprite = input.ct.get_sprite(i.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
self.state.push_particle_buffer(ParticleInstance { self.state.push_particle_buffer(ParticleInstance {
position: [i.pos.x, i.pos.y], position: [i.pos.x, i.pos.y],
@ -306,7 +306,7 @@ impl<'a> super::GPUState {
], ],
window_scale: [self.state.window.scale_factor() as f32, 0.0], window_scale: [self.state.window.scale_factor() as f32, 0.0],
window_aspect: [self.state.window_aspect, 0.0], window_aspect: [self.state.window_aspect, 0.0],
starfield_sprite: [input.ct.get_starfield_texture(), 0], starfield_sprite: [input.ct.get_config().starfield_texture, 0],
starfield_tile_size: [input.ct.get_config().starfield_size, 0.0], starfield_tile_size: [input.ct.get_config().starfield_size, 0.0],
starfield_size_limits: [ starfield_size_limits: [
input.ct.get_config().starfield_min_size, input.ct.get_config().starfield_min_size,

View File

@ -61,7 +61,7 @@ impl Radar {
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow"); let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
let sprite = input.ct.get_sprite(input.ct.get_sprite_handle("ui::radar")); let sprite = input.ct.get_sprite(input.ct.get_sprite_handle("ui::radar"));
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
// Push this object's instance // Push this object's instance
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
@ -92,7 +92,7 @@ impl Radar {
} }
let sprite = input.ct.get_sprite(planet_sprite); let sprite = input.ct.get_sprite(planet_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
// Push this object's instance // Push this object's instance
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
@ -153,7 +153,7 @@ impl Radar {
+ (d * (radar_size / 2.0)); + (d * (radar_size / 2.0));
let sprite = input.ct.get_sprite(ship_sprite); let sprite = input.ct.get_sprite(ship_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
@ -181,7 +181,7 @@ impl Radar {
let size = 7.0f32.min((0.8 - m) * 70.0); let size = 7.0f32.min((0.8 - m) * 70.0);
let sprite = input.ct.get_sprite(sprite); let sprite = input.ct.get_sprite(sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwNw.to_int(), anchor: PositionAnchor::NwNw.to_int(),
@ -253,7 +253,7 @@ impl Radar {
+ Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0); + Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0);
let sprite = input.ct.get_sprite(arrow_sprite); let sprite = input.ct.get_sprite(arrow_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),

View File

@ -48,7 +48,7 @@ impl Status {
let sprite = input let sprite = input
.ct .ct
.get_sprite(input.ct.get_sprite_handle("ui::status")); .get_sprite(input.ct.get_sprite_handle("ui::status"));
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NeNe.to_int(), anchor: PositionAnchor::NeNe.to_int(),

View File

@ -45,7 +45,7 @@ impl UiSprite {
} }
let sprite = input.ct.get_sprite(self.sprite); let sprite = input.ct.get_sprite(self.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::CC.to_int(), anchor: PositionAnchor::CC.to_int(),
@ -59,7 +59,7 @@ impl UiSprite {
.mask .mask
.map(|x| { .map(|x| {
let sprite = input.ct.get_sprite(x); let sprite = input.ct.get_sprite(x);
let texture_b = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_b = sprite.get_first_frame(); // ANIMATE
[1, texture_b] [1, texture_b]
}) })
.unwrap_or([0, 0]), .unwrap_or([0, 0]),
@ -101,7 +101,7 @@ impl UiElement for UiSprite {
} }
let sprite = input.ct.get_sprite(self.sprite); let sprite = input.ct.get_sprite(self.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_a = sprite.get_first_frame(); // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::CNw.to_int(), anchor: PositionAnchor::CNw.to_int(),
@ -115,7 +115,7 @@ impl UiElement for UiSprite {
.mask .mask
.map(|x| { .map(|x| {
let sprite = input.ct.get_sprite(x); let sprite = input.ct.get_sprite(x);
let texture_b = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE let texture_b = sprite.get_first_frame(); // ANIMATE
[1, texture_b] [1, texture_b]
}) })
.unwrap_or([0, 0]), .unwrap_or([0, 0]),