freecs 2.0.1

A high-performance, archetype-based Entity Component System (ECS) written in Rust.
Documentation
use freecs::{Entity, ecs};

ecs! {
    World {
        position: Position => POSITION,
        velocity: Velocity => VELOCITY,
        health: Health => HEALTH,
    }
    Tags {
        player => PLAYER,
        enemy => ENEMY,
    }
    Events {
        collision: CollisionEvent,
    }
    Resources {
        delta_time: f32,
        frame_count: u32,
        #[cfg(feature = "audio")]
        audio_volume: f32,
    }
}

pub fn main() {
    let mut world = World::default();
    world.resources.delta_time = 0.016;
    world.resources.frame_count = 0;

    println!("=== Entity Creation ===");

    let player = EntityBuilder::new()
        .with_position(Position { x: 0.0, y: 0.0 })
        .with_velocity(Velocity { x: 5.0, y: 0.0 })
        .with_health(Health { value: 100.0 })
        .spawn(&mut world, 1)[0];
    world.add_player(player);
    println!("Created player entity: {:?}", player);

    let entities = world.spawn_batch(POSITION | VELOCITY | HEALTH, 3, |table, idx| {
        table.position[idx] = Position {
            x: idx as f32 * 10.0,
            y: 0.0,
        };
        table.velocity[idx] = Velocity { x: -2.0, y: 1.0 };
        table.health[idx] = Health { value: 50.0 };
    });
    for entity in &entities {
        world.add_enemy(*entity);
    }
    println!("Created {} enemy entities", entities.len());

    println!("\n=== Component Access ===");

    if let Some(pos) = world.get_position(player) {
        println!("Player position: ({}, {})", pos.x, pos.y);
    }

    if let Some(health) = world.get_health_mut(player) {
        health.value += 10.0;
        println!("Player health increased to: {}", health.value);
    }

    world.set_velocity(player, Velocity { x: 10.0, y: 5.0 });
    println!("Player velocity updated");

    println!("\n=== Querying ===");

    let all_entities: Vec<Entity> = world.query_entities(POSITION | VELOCITY).collect();
    println!(
        "Entities with position and velocity: {}",
        all_entities.len()
    );

    for entity in world.query_entities(POSITION) {
        if world.has_enemy(entity) {
            println!("  Enemy at position: {:?}", world.get_position(entity));
        }
    }

    println!("\n=== Systems ===");

    for frame in 0..3 {
        world.resources.frame_count = frame;
        println!("Frame {}", frame);
        systems::run_systems(&mut world);
        world.step();
    }

    println!("\n=== Query Builder API ===");

    for entity in world.query_entities(POSITION | VELOCITY) {
        let pos = world.get_position(entity).unwrap();
        let is_player = world.has_player(entity);
        let tag = if is_player { "PLAYER" } else { "ENEMY" };
        println!(
            "  [{}] Entity {:?} at ({:.1}, {:.1})",
            tag, entity, pos.x, pos.y
        );
    }

    println!("\n=== Command Buffers ===");

    let entities_to_despawn: Vec<Entity> = world
        .query_entities(HEALTH)
        .filter(|&entity| world.get_health(entity).map_or(false, |h| h.value <= 0.0))
        .collect();

    if entities_to_despawn.is_empty() {
        println!("No entities to despawn (all have health > 0)");
    } else {
        println!(
            "Despawning {} entities with health <= 0",
            entities_to_despawn.len()
        );
        for entity in entities_to_despawn {
            world.queue_despawn_entity(entity);
        }
    }
    world.apply_commands();

    println!("\n=== Event Processing ===");

    let event_count = world.len_collision();
    if event_count > 0 {
        println!("Processing {} collision events", event_count);
        for event in world.collect_collision() {
            println!(
                "  Collision between {:?} and {:?}",
                event.entity_a, event.entity_b
            );
        }
    } else {
        println!("No collision events detected");
    }

    println!("\n=== Final State ===");
    println!("Total entities: {}", world.get_all_entities().len());
    println!(
        "Entities with health: {}",
        world.query_entities(HEALTH).count()
    );

    #[cfg(feature = "audio")]
    {
        println!("\n=== Audio Feature (enabled via --features audio) ===");
        world.resources.audio_volume = 0.8;
        println!("Audio volume set to: {}", world.resources.audio_volume);
    }
}

use components::*;
mod components {
    #[derive(Default, Debug, Clone, Copy)]
    pub struct Position {
        pub x: f32,
        pub y: f32,
    }

    #[derive(Default, Debug, Clone, Copy)]
    pub struct Velocity {
        pub x: f32,
        pub y: f32,
    }

    #[derive(Default, Debug, Clone, Copy)]
    pub struct Health {
        pub value: f32,
    }
}

#[derive(Debug, Clone)]
pub struct CollisionEvent {
    pub entity_a: Entity,
    pub entity_b: Entity,
}

mod systems {
    use super::*;

    pub fn run_systems(world: &mut World) {
        physics_system(world);
        collision_system(world);
        damage_system(world);
        health_decay_system(world);
    }

    fn physics_system(world: &mut World) {
        let dt = world.resources.delta_time;

        world.for_each_mut(POSITION | VELOCITY, 0, |_entity, table, idx| {
            table.position[idx].x += table.velocity[idx].x * dt;
            table.position[idx].y += table.velocity[idx].y * dt;
        });
    }

    fn collision_system(world: &mut World) {
        let entities: Vec<(Entity, Position)> = world
            .query_entities(POSITION)
            .filter_map(|e| world.get_position(e).map(|p| (e, *p)))
            .collect();

        for idx_a in 0..entities.len() {
            for idx_b in (idx_a + 1)..entities.len() {
                let (entity_a, pos_a) = entities[idx_a];
                let (entity_b, pos_b) = entities[idx_b];

                let dx = pos_a.x - pos_b.x;
                let dy = pos_a.y - pos_b.y;
                let distance_squared = dx * dx + dy * dy;

                if distance_squared < 4.0 {
                    world.send_collision(CollisionEvent { entity_a, entity_b });
                }
            }
        }
    }

    fn damage_system(world: &mut World) {
        let collision_events = world.collect_collision();

        for event in collision_events {
            if let Some(health) = world.get_health_mut(event.entity_a) {
                health.value -= 5.0;
            }
            if let Some(health) = world.get_health_mut(event.entity_b) {
                health.value -= 5.0;
            }
        }
    }

    fn health_decay_system(world: &mut World) {
        world.for_each_health_mut(|health| {
            health.value *= 0.99;
        });
    }
}