nightshade-api 0.40.0

Procedural high level API for the nightshade game engine
Documentation
//! Grouping without bookkeeping. Tags live in the engine's entity registry,
//! survive scene round trips, and clean up on despawn, so a game never has to
//! hand maintain a `Vec<Entity>` of its coins.

use nightshade::prelude::*;

/// Tags the entity with a label. An entity can carry any number of tags.
pub fn tag(world: &mut World, entity: Entity, label: &str) {
    let tags = world.resources.entities.tags.entry(entity).or_default();
    if !tags.iter().any(|existing| existing == label) {
        tags.push(label.to_string());
    }
}

/// Removes a label from the entity.
pub fn untag(world: &mut World, entity: Entity, label: &str) {
    if let Some(tags) = world.resources.entities.tags.get_mut(&entity) {
        tags.retain(|existing| existing != label);
    }
}

/// True when the entity carries the label.
pub fn has_tag(world: &World, entity: Entity, label: &str) -> bool {
    world
        .resources
        .entities
        .tags
        .get(&entity)
        .is_some_and(|tags| tags.iter().any(|existing| existing == label))
}

/// Every entity carrying the label, in no particular order.
pub fn tagged(world: &World, label: &str) -> Vec<Entity> {
    world
        .resources
        .entities
        .tags
        .iter()
        .filter(|(_, tags)| tags.iter().any(|existing| existing == label))
        .map(|(&entity, _)| entity)
        .collect()
}

/// Runs `action` for every entity carrying the label, with full world access,
/// so despawning, recoloring, or moving members from inside the closure is
/// fine.
///
/// ```ignore
/// for_each_tagged(world, "coin", |world, coin| {
///     let step = delta_time(world);
///     rotate(world, coin, Vec3::y(), step * 2.0);
/// });
/// ```
pub fn for_each_tagged(world: &mut World, label: &str, mut action: impl FnMut(&mut World, Entity)) {
    let entities = tagged(world, label);
    for entity in entities {
        action(world, entity);
    }
}

/// Visits every entity that has a transform, handing the closure its world
/// space position straight from the table, the same loop an ECS query
/// compiles to.
pub fn for_each_with_position(world: &World, mut action: impl FnMut(Entity, Vec3)) {
    world
        .core
        .query()
        .with(LOCAL_TRANSFORM | GLOBAL_TRANSFORM)
        .iter(|entity, table, index| {
            action(entity, table.global_transform[index].translation());
        });
}