use bevy::{
ecs::system::{ParallelCommands, Query},
math::IVec2,
prelude::{Component, Entity},
reflect::Reflect,
render::{color::Color, render_resource::ShaderType},
};
use super::{buffers::Tiles, map::TilemapStorage};
#[derive(Debug, Clone, Copy, Reflect)]
#[cfg_attr(feature = "serializing", derive(serde::Serialize, serde::Deserialize))]
pub struct TileLayer {
#[cfg(feature = "atlas")]
pub texture_index: i32,
pub atlas_index: i32,
#[reflect(ignore)]
pub flip: TileFlip,
}
impl Default for TileLayer {
fn default() -> Self {
Self {
#[cfg(feature = "atlas")]
texture_index: -1,
atlas_index: -1,
flip: Default::default(),
}
}
}
#[cfg(not(feature = "atlas"))]
impl TileLayer {
#[inline]
pub fn no_flip(atlas_index: i32) -> Self {
Self {
atlas_index,
flip: TileFlip::NONE,
}
}
#[inline]
pub fn flip_h(atlas_index: i32) -> Self {
Self {
atlas_index,
flip: TileFlip::HORIZONTAL,
}
}
#[inline]
pub fn flip_v(atlas_index: i32) -> Self {
Self {
atlas_index,
flip: TileFlip::VERTICAL,
}
}
#[inline]
pub fn flip_both(atlas_index: i32) -> Self {
Self {
atlas_index,
flip: TileFlip::BOTH,
}
}
}
#[cfg(feature = "atlas")]
impl TileLayer {
#[inline]
pub fn no_flip(texture_index: i32, atlas_index: i32) -> Self {
Self {
texture_index,
atlas_index,
flip: TileFlip::NONE,
}
}
#[inline]
pub fn flip_h(texture_index: i32, atlas_index: i32) -> Self {
Self {
texture_index,
atlas_index,
flip: TileFlip::HORIZONTAL,
}
}
#[inline]
pub fn flip_v(texture_index: i32, atlas_index: i32) -> Self {
Self {
texture_index,
atlas_index,
flip: TileFlip::VERTICAL,
}
}
#[inline]
pub fn flip_both(texture_index: i32, atlas_index: i32) -> Self {
Self {
texture_index,
atlas_index,
flip: TileFlip::BOTH,
}
}
}
#[derive(Debug, Clone, Copy, Reflect)]
pub enum TileLayerPosition {
Top,
Bottom,
Index(usize),
}
#[derive(Clone, Reflect)]
pub struct LayerUpdater {
pub position: TileLayerPosition,
pub layer: TileLayer,
}
#[derive(Default, Component, Clone, Reflect)]
pub struct TileUpdater {
pub layer: Option<LayerUpdater>,
pub tint: Option<Color>,
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serializing", derive(serde::Serialize, serde::Deserialize))]
pub struct TileFlip: u32 {
const NONE = 0b00;
const HORIZONTAL = 0b10;
const VERTICAL = 0b01;
const BOTH = 0b11;
}
}
impl Default for TileFlip {
fn default() -> Self {
Self::NONE
}
}
#[derive(Debug, Clone, Reflect)]
#[cfg_attr(feature = "serializing", derive(serde::Serialize, serde::Deserialize))]
pub struct TileBuilder {
pub(crate) texture: TileTexture,
pub(crate) tint: Color,
}
impl Tiles for TileBuilder {}
impl TileBuilder {
pub fn new() -> Self {
Self {
texture: TileTexture::Static(Vec::new()),
tint: Color::WHITE,
}
}
pub fn with_tint(mut self, tint: Color) -> Self {
self.tint = tint;
self
}
pub fn with_layer(mut self, index: usize, layer: TileLayer) -> Self {
if let TileTexture::Static(ref mut tex) = self.texture {
if tex.len() <= index {
tex.resize(index + 1, TileLayer::default());
}
tex[index] = layer;
}
self
}
pub fn with_animation(mut self, animation: TileAnimation) -> Self {
self.texture = TileTexture::Animated(animation);
self
}
pub(crate) fn build_component(
&self,
index: IVec2,
storage: &TilemapStorage,
tilemap: Entity,
) -> Tile {
let indices = storage.storage.transform_index(index);
Tile {
tilemap_id: tilemap,
chunk_index: indices.0,
in_chunk_index: indices.1,
index,
texture: self.texture.clone(),
tint: self.tint,
}
}
}
#[derive(ShaderType, Debug, Clone, Copy, Reflect)]
#[cfg_attr(feature = "serializing", derive(serde::Serialize, serde::Deserialize))]
pub struct TileAnimation {
pub(crate) start: u32,
pub(crate) length: u32,
pub(crate) fps: u32,
}
#[derive(Debug, Clone, Reflect)]
pub struct RawTileAnimation {
#[cfg(not(feature = "atlas"))]
pub sequence: Vec<u32>,
#[cfg(feature = "atlas")]
pub sequence: Vec<(u32, u32)>,
pub fps: u32,
}
#[derive(Debug, Clone, Reflect)]
#[cfg_attr(feature = "serializing", derive(serde::Serialize, serde::Deserialize))]
pub enum TileTexture {
Static(Vec<TileLayer>),
Animated(TileAnimation),
}
#[derive(Component, Clone, Debug, Reflect)]
pub struct Tile {
pub tilemap_id: Entity,
pub chunk_index: IVec2,
pub in_chunk_index: usize,
pub index: IVec2,
pub texture: TileTexture,
pub tint: Color,
}
impl Tiles for Tile {}
impl Into<TileBuilder> for Tile {
fn into(self) -> TileBuilder {
TileBuilder {
texture: self.texture,
tint: self.tint,
}
}
}
pub fn tile_updater(
commands: ParallelCommands,
mut tiles_query: Query<(Entity, &mut Tile, &TileUpdater)>,
) {
tiles_query
.par_iter_mut()
.for_each(|(entity, mut tile, updater)| {
if let Some(layer) = &updater.layer {
if let TileTexture::Static(ref mut tex) = tile.texture {
match layer.position {
TileLayerPosition::Top => {
tex.push(layer.layer);
}
TileLayerPosition::Bottom => {
tex.insert(0, layer.layer);
}
TileLayerPosition::Index(i) => {
if i >= tex.len() {
tex.resize(i + 1, TileLayer::default());
}
tex[i] = layer.layer;
}
}
}
}
if let Some(color) = updater.tint {
tile.tint = color;
}
commands.command_scope(|mut c| {
c.entity(entity).remove::<TileUpdater>();
});
});
}