1use std::time::Duration;
2
3use bevy::prelude::*;
4use serde::Deserialize;
5
6use crate::SpriteFrameUpdate;
7#[cfg(feature = "bevy-inspector-egui")]
8use bevy_inspector_egui::prelude::*;
9
10#[derive(Debug, Deserialize, Clone)]
14#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
15pub enum LayerSpeed {
16 Horizontal(f32),
17 Vertical(f32),
18 Bidirectional(f32, f32),
19}
20
21#[derive(Debug, Deserialize, Clone)]
22#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
23pub enum RepeatStrategy {
24 Same,
25 MirrorHorizontally,
26 MirrorVertically,
27 MirrorBoth,
28}
29
30impl RepeatStrategy {
31 pub fn transform(&self, sprite_bundle: &mut SpriteBundle, pos: (i32, i32)) {
32 match self {
33 Self::Same => (),
34 Self::MirrorHorizontally => {
35 let (x, _) = pos;
36 sprite_bundle.sprite.flip_x ^= x % 2 != 0;
37 }
38 Self::MirrorVertically => {
39 let (_, y) = pos;
40 sprite_bundle.sprite.flip_y ^= y % 2 != 0;
41 }
42 Self::MirrorBoth => {
43 let (x, y) = pos;
44 sprite_bundle.sprite.flip_x ^= x % 2 != 0;
45 sprite_bundle.sprite.flip_y ^= y % 2 != 0;
46 }
47 }
48 }
49}
50
51#[derive(Debug, Deserialize, Clone)]
52#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
53#[cfg_attr(feature = "bevy-inspector-egui", reflect(InspectorOptions))]
54pub enum LayerRepeat {
55 Horizontal(RepeatStrategy),
56 Vertical(RepeatStrategy),
57 Bidirectional(RepeatStrategy),
58}
59
60impl LayerRepeat {
61 pub fn both(strategy: RepeatStrategy) -> Self {
62 Self::Bidirectional(strategy)
63 }
64
65 pub fn horizontally(strategy: RepeatStrategy) -> Self {
66 Self::Horizontal(strategy)
67 }
68
69 pub fn vertically(strategy: RepeatStrategy) -> Self {
70 Self::Vertical(strategy)
71 }
72
73 pub fn has_vertical(&self) -> bool {
74 match self {
75 Self::Horizontal(_) => false,
76 _ => true,
77 }
78 }
79
80 pub fn has_horizontal(&self) -> bool {
81 match self {
82 Self::Vertical(_) => false,
83 _ => true,
84 }
85 }
86
87 pub fn get_strategy(&self) -> RepeatStrategy {
88 match self {
89 Self::Horizontal(strategy) => strategy.clone(),
90 Self::Bidirectional(strategy) => strategy.clone(),
91 Self::Vertical(strategy) => strategy.clone(),
92 }
93 }
94}
95
96#[derive(Debug, Deserialize, Resource, Clone)]
97#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
98pub enum Animation {
99 FPS(f32),
100 FrameDuration(Duration),
101 TotalDuration(Duration),
102}
103
104impl Animation {
105 pub fn to_sprite_update(&self, layer_data: &LayerData) -> SpriteFrameUpdate {
106 let total = layer_data.cols * layer_data.rows;
107 let duration = match self {
108 Self::FPS(fps) => Duration::from_secs_f32(1. / fps),
109 Self::FrameDuration(duration) => duration.clone(),
110 Self::TotalDuration(duration) => duration.div_f32(total as f32),
111 };
112 SpriteFrameUpdate {
113 total,
114 index: layer_data.index,
115 timer: Timer::new(duration, TimerMode::Repeating),
116 }
117 }
118}
119
120#[derive(Debug, Deserialize, Resource, Clone)]
122#[serde(default)]
123#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
124pub struct LayerData {
125 pub speed: LayerSpeed,
128
129 pub repeat: LayerRepeat,
130
131 pub path: String,
133 pub tile_size: UVec2,
135 pub cols: usize,
137 pub rows: usize,
139 pub scale: Vec2,
141 pub z: f32,
143 pub position: Vec2,
145
146 pub color: Color,
147
148 pub index: usize,
149
150 pub flip: (bool, bool),
151
152 pub animation: Option<Animation>,
153}
154
155impl LayerData {
156 pub fn create_texture_atlas_layout(&self) -> TextureAtlasLayout {
157 TextureAtlasLayout::from_grid(self.tile_size, self.cols as u32, self.rows as u32, None, None)
158 }
159
160 pub fn create_sprite(&self) -> Sprite {
161 Sprite {
162 color: self.color,
163 flip_x: self.flip.0,
164 flip_y: self.flip.1,
165 ..Default::default()
166 }
167 }
168
169 pub fn crate_layer_texture(&self) -> LayerTextureComponent {
170 LayerTextureComponent {
171 width: self.tile_size.x as f32,
172 height: self.tile_size.y as f32,
173 }
174 }
175
176 pub fn create_animation_bundle(&self) -> Option<impl Bundle> {
177 self.animation.as_ref().map(|animation| animation.to_sprite_update(self))
178 }
179}
180
181impl Default for LayerData {
182 fn default() -> Self {
183 Self {
184 speed: LayerSpeed::Horizontal(1.0),
185 repeat: LayerRepeat::Bidirectional(RepeatStrategy::Same),
186 path: "".to_string(),
187 tile_size: UVec2::ZERO,
188 cols: 1,
189 rows: 1,
190 scale: Vec2::ONE,
191 z: 0.0,
192 position: Vec2::ZERO,
193 color: Color::WHITE,
194 index: 0,
195 flip: (false, false),
196 animation: None,
197 }
198 }
199}
200
201#[derive(Component)]
203#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
204pub struct LayerComponent {
205 pub speed: Vec2,
207 pub repeat: LayerRepeat,
209 pub texture_count: Vec2,
211
212 pub camera: Entity,
213}
214
215#[derive(Component)]
217#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
218pub struct LayerTextureComponent {
219 pub width: f32,
221
222 pub height: f32,
224}