use bevy::prelude::*;
use tiled_parse::{relations::{get_tile_id, get_tileset_for_gid}, types::{Gid, TiledLayer}};
use try_match::match_ok;
use crate::types::*;
use tiled_parse::types::*;
pub fn get_entities_with_tiled_map(
a: &TiledMapAsset,
q: &Query<(Entity, &SceneRoot)>,
) -> Vec<Entity> {
q.iter()
.filter_map(|(e, SceneRoot(h))| if a.scene == *h { Some(e) } else { None })
.collect()
}
pub fn is_collider(o: &Object) -> bool {
matches!(o.properties.get("collider"), Some(TiledPropertyType::Bool(true)))
}
pub fn get_layer_from_id(a: &TiledMapAsset, t_id: TiledId) -> Option<&TiledLayer> {
let id = match_ok!(t_id, TiledId::Layer(x))?;
a.map.get_layer_from_id(id)
}
pub fn get_tile_sprite(a: &TiledMapAsset, t_gid: Gid) -> Option<Sprite> {
let tile_sets = &a.map.tile_sets;
let tile_tileset = get_tileset_for_gid(&tile_sets, t_gid)?;
let tileset_index = tile_sets
.iter()
.position(|ts| ts.first_gid == tile_tileset.first_gid)?;
let local_tile_id = get_tile_id(&tile_tileset, t_gid);
Some(Sprite {
image: a.tilemap_textures.get(tileset_index)?.clone(),
texture_atlas: Some(TextureAtlas {
layout: a.tilemap_atlases.get(tileset_index)?.clone(),
index: local_tile_id as usize,
}),
anchor: bevy::sprite::Anchor::TopLeft,
..Default::default()
})
}
pub fn get_animation(a: &TiledMapAsset, t_gid: Gid, start_time_secs: f32) -> Option<(TiledAnimation, Sprite)> {
let tile_sets = &a.map.tile_sets;
let tile_tileset = get_tileset_for_gid(&tile_sets, t_gid)?;
let tileset_index = tile_sets
.iter()
.position(|ts| ts.first_gid == tile_tileset.first_gid)?;
let local_tile_id = get_tile_id(&tile_tileset, t_gid);
let animation = tile_tileset.tile_stuff.get(&local_tile_id)?.animation.as_ref()?;
Some((
TiledAnimation {
animation: animation.into_iter().map(|&af| af.into()).collect(),
start_time: start_time_secs,
player: TiledAnimationPlayer::Once
},
Sprite {
image: a.tilemap_textures.get(tileset_index).unwrap().clone(),
texture_atlas: Some(TextureAtlas {
layout: a.tilemap_atlases.get(tileset_index).unwrap().clone(),
index: animation[0].tile_id as usize,
}),
anchor: bevy::sprite::Anchor::TopLeft,
..Default::default()
}
))
}
impl TiledId {
pub fn as_tile_gid(&self) -> Option<Gid> {
match_ok!(self, TiledId::Tile(x)).map(|&id| Gid(id))
}
}
impl TiledAnimation {
pub fn get_current_tile(&self, now_secs: f32) -> Option<tiled_parse::types::ID> {
match self.player {
TiledAnimationPlayer::Once => self.animation.iter().enumerate().scan(self.start_time, |acc_t, (i, &AnimationFrameReflect {tile_id, duration})| {
if *acc_t <= now_secs {
*acc_t += duration / 1000.;
if i == (self.animation.len() - 1) && now_secs > *acc_t {
Some(None)
} else {
Some(Some(tile_id))
}
} else {
None
}
}).last().flatten(),
TiledAnimationPlayer::Cycled => {
let remainder = now_secs % self.get_net_duration();
self.animation.iter().scan(self.start_time, |acc_t, &AnimationFrameReflect {tile_id, duration}| {
if *acc_t <= (self.start_time + remainder) {
*acc_t += duration / 1000.;
Some(tile_id)
} else {
None
}
}).last()
}
}
}
pub fn get_net_duration(&self) -> f32 {
self.animation.iter().map(|af| af.duration / 1000.).sum()
}
pub fn get_end_time(&self, now_secs: f32) -> f32 {
match self.player {
TiledAnimationPlayer::Once => self.start_time + self.get_net_duration(),
TiledAnimationPlayer::Cycled =>
self.start_time + ((now_secs - self.start_time) / self.get_net_duration()).ceil() * self.get_net_duration()
}
}
}