use crate::bevy_ryot::drawing::{CommandState, Deletion, DrawingInfo, TileComponent};
use crate::bevy_ryot::map::MapTiles;
use bevy::prelude::*;
#[cfg(feature = "lmdb")]
use crate::bevy_ryot::lmdb::LmdbEnv;
#[cfg(feature = "lmdb")]
use crate::bevy_ryot::GameObjectId;
#[cfg(feature = "lmdb")]
use crate::lmdb::{GetKey, Item, ItemRepository, ItemsFromHeedLmdb, Tile};
#[cfg(feature = "lmdb")]
use crate::position::TilePosition;
#[cfg(feature = "lmdb")]
use bevy::log::error;
#[cfg(feature = "lmdb")]
use std::collections::HashMap;
#[derive(Eq, PartialEq, Component, Default, Copy, Clone)]
pub struct UpdateComponent {
pub new: DrawingInfo,
pub old: DrawingInfo,
pub state: CommandState,
}
impl UpdateComponent {
pub fn new(new: DrawingInfo, old: DrawingInfo) -> Self {
Self {
new,
old,
state: CommandState::default(),
}
}
}
pub fn update(world: &mut World, new: DrawingInfo, old: DrawingInfo, state: CommandState) {
let id = get_or_create_entity_for_info(world, &new);
world
.resource_mut::<MapTiles<Entity>>()
.entry(new.0)
.or_default()
.push_for_layer(new.1, id);
world
.entity_mut(id)
.insert(UpdateComponent { new, old, state });
}
pub fn get_or_create_entity_for_info(world: &mut World, info: &DrawingInfo) -> Entity {
let (pos, layer, ..) = info;
let entity = world
.resource_mut::<MapTiles<Entity>>()
.entry(*pos)
.or_default()
.peek_for_layer(*layer);
match entity {
Some(entity) => entity,
None => world.spawn_empty().id(),
}
}
pub fn apply_update(
mut commands: Commands,
mut q_inserted: Query<
(Entity, &mut UpdateComponent),
Or<(Changed<UpdateComponent>, Added<UpdateComponent>)>,
>,
) {
for (entity, mut update) in q_inserted.iter_mut() {
if update.state.applied {
continue;
}
let (pos, layer, visibility, appearance) = update.new;
let Some(appearance) = appearance else {
commands
.entity(entity)
.insert(Deletion::default())
.remove::<UpdateComponent>();
continue;
};
commands
.entity(entity)
.insert((pos, layer, appearance, visibility, TileComponent))
.remove::<Deletion>();
update.state.applied = true;
}
}
pub fn persist_update(
#[cfg(feature = "lmdb")] lmdb_env: Res<LmdbEnv>,
mut q_inserted: Query<
&mut UpdateComponent,
Or<(Changed<UpdateComponent>, Added<UpdateComponent>)>,
>,
) {
#[cfg(feature = "lmdb")]
{
let Some(lmdb_env) = &lmdb_env.0 else {
return;
};
let mut keys = vec![];
let mut to_draw = vec![];
for update in q_inserted.iter_mut() {
let (tile_pos, layer, _, appearance) = update.new;
if update.state.persisted {
continue;
}
let Some(appearance) = appearance else {
continue;
};
keys.push(tile_pos.get_binary_key());
to_draw.push((tile_pos, layer, appearance));
}
let item_repository = ItemsFromHeedLmdb::new(lmdb_env.clone());
let mut new_tiles: HashMap<TilePosition, Tile> = HashMap::new();
let tiles = item_repository.get_for_keys(keys);
if let Err(e) = tiles {
error!("Failed to get tiles: {}", e);
return;
};
for tile in tiles.unwrap() {
new_tiles.insert(tile.position, tile);
}
for (tile_pos, layer, (object_id, _frame_group)) in &to_draw {
let tile = new_tiles
.entry(*tile_pos)
.or_insert(Tile::from_pos(*tile_pos));
let id = match object_id {
GameObjectId::Object(id) => *id as u16,
_ => continue,
};
tile.set_item(
Item {
id,
attributes: vec![],
},
*layer,
);
}
if let Err(e) = item_repository.save_from_tiles(new_tiles.into_values().collect()) {
error!("Failed to save tile: {}", e);
}
}
for mut update in q_inserted.iter_mut() {
if update.state.persisted {
continue;
}
update.state.persisted = true;
}
}