use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
use crate::{
loader::SpriteFusionMapLoader,
types::{Collider, SpriteFusionLayerMarker, SpriteFusionMap, SpriteFusionMapMarker, TileAttributes},
};
pub struct SpriteFusionPlugin;
impl Plugin for SpriteFusionPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<SpriteFusionMap>()
.init_asset_loader::<SpriteFusionMapLoader>()
.add_plugins(TilemapPlugin)
.add_systems(Update, spawn_spritefusion_maps);
}
}
#[derive(Component, Default, Clone, Debug, Deref, DerefMut)]
pub struct SpriteFusionMapHandle(pub Handle<SpriteFusionMap>);
#[derive(Component, Default, Clone, Debug, Deref, DerefMut)]
pub struct SpriteFusionTilesetHandle(pub Handle<Image>);
#[derive(Bundle, Default)]
pub struct SpriteFusionBundle {
pub map: SpriteFusionMapHandle,
pub tileset: SpriteFusionTilesetHandle,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub visibility: Visibility,
pub inherited_visibility: InheritedVisibility,
pub view_visibility: ViewVisibility,
pub pending: PendingSpriteFusionMap,
}
#[derive(Component, Default)]
pub struct PendingSpriteFusionMap;
fn spawn_spritefusion_maps(
mut commands: Commands,
pending_maps: Query<(Entity, &SpriteFusionMapHandle, &SpriteFusionTilesetHandle, &Transform), With<PendingSpriteFusionMap>>,
map_assets: Res<Assets<SpriteFusionMap>>,
image_assets: Res<Assets<Image>>,
) {
for (entity, map_handle, tileset_handle, transform) in pending_maps.iter() {
let Some(map) = map_assets.get(&**map_handle) else {
continue;
};
let Some(_tileset_image) = image_assets.get(&**tileset_handle) else {
continue;
};
commands.entity(entity).remove::<PendingSpriteFusionMap>();
commands.entity(entity).insert(SpriteFusionMapMarker {
map: map.clone(),
});
let tile_size = map.tile_size;
for (layer_index, layer) in map.layers.iter().enumerate() {
let map_size = TilemapSize {
x: map.map_width,
y: map.map_height,
};
let tilemap_entity = commands.spawn_empty().id();
let mut tile_storage = TileStorage::empty(map_size);
for tile in &layer.tiles {
let tile_id = tile.tile_id();
let tile_pos = TilePos {
x: tile.x as u32,
y: (map.map_height - 1) - tile.y as u32, };
let texture_index = TileTextureIndex(tile_id);
let mut tile_entity_commands = commands.spawn(TileBundle {
position: tile_pos,
tilemap_id: TilemapId(tilemap_entity),
texture_index,
..default()
});
if layer.collider {
tile_entity_commands.insert(Collider);
}
if let Some(attrs) = &tile.attributes {
if !attrs.is_empty() {
tile_entity_commands.insert(TileAttributes(attrs.clone()));
}
}
let tile_entity = tile_entity_commands.id();
tile_storage.set(&tile_pos, tile_entity);
}
let tile_size_vec = TilemapTileSize {
x: tile_size as f32,
y: tile_size as f32,
};
let grid_size = tile_size_vec.into();
let map_type = TilemapType::Square;
let texture = TilemapTexture::Single(tileset_handle.0.clone());
let layer_z = -((layer_index as f32) * 0.1);
let layer_transform = Transform::from_translation(Vec3::new(
transform.translation.x,
transform.translation.y,
transform.translation.z + layer_z,
));
commands.entity(tilemap_entity).insert((
TilemapBundle {
grid_size,
map_type,
size: map_size,
storage: tile_storage,
texture,
tile_size: tile_size_vec,
transform: layer_transform,
..default()
},
SpriteFusionLayerMarker {
name: layer.name.clone(),
index: layer_index,
collider: layer.collider,
},
));
commands.entity(entity).add_child(tilemap_entity);
}
let tiles_with_attrs = map.layers.iter()
.flat_map(|l| l.tiles.iter())
.filter(|t| t.attributes.as_ref().map(|a| !a.is_empty()).unwrap_or(false))
.count();
info!(
"Spawned SpriteFusion map with {} layers ({} tiles total, {} with attributes)",
map.layers.len(),
map.layers.iter().map(|l| l.tiles.len()).sum::<usize>(),
tiles_with_attrs
);
}
}