How can I search for another entity inside a Specs system?

246 views Asked by At

I've been playing around with Rust by following along with Roguelike Tutorial, and have started to branch out a bit in hopes of creating some kind of nature simulation.

In my simple POC, I'm trying to have multiple "Creatures" wandering the map looking for entities with the ProvidesHealth component (so like plants or bushes or something that get eaten).

In the Roguelike tutorial, monsters can easily locate the player at all times because the player is shared throughout the world as a resource, but in my case, I can't figure out the best way to simulate this behavior in my Specs system.

The Creature entities have a Viewshed component to act as their vision. I originally thought I'd be able to iterate thru the Viewshed's visible_tiles and check if an entity with the ProvidesHealth entity was there, but I wasn't able to get that work.

Any thoughts on this would be greatly appreciated! I'm not sure if my approach is totally off, or I'm missing something simple.

Thanks!

// [dependencies]
// bracket-lib = { git = "https://github.com/thebracket/bracket-lib.git", rev = "927d229" }
// specs = "0.16.1"
// specs-derive = "0.4.1"

use bracket_lib::prelude::*;
use specs::prelude::*;
use specs_derive::*;
use std::{thread, time};

#[derive(Component)]
struct Position {
    x: i32,
    y: i32,
}

#[derive(Component)]
struct Renderable {
    glyph: FontCharType,
    fg: RGB,
    bg: RGB,
}

#[derive(Component)]
struct Creature {}

#[derive(Component)]
struct ProvidesHealth {
    pub hp_gain: i32
}

#[derive(Component)]
pub struct Viewshed {
    pub visible_tiles: Vec<Point>,
    pub range: i32,
    pub dirty: bool
}

struct State {
    ecs: World
}

impl State {
    fn run_systems(&mut self) {
        let mut vis = VisSystem {};
        vis.run_now(&self.ecs);

        let mut ai = CreatureAI {};
        ai.run_now(&self.ecs);

        self.ecs.maintain();
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        ctx.cls();

        self.run_systems();

        // map defined in separate file, but isn't really
        // important for this question
        // draw_map(&self.ecs, ctx);

        let positions = self.ecs.read_storage::<Position>();
        let renderables = self.ecs.read_storage::<Renderable>();

        for (pos, ren) in (&positions, &renderables).join() {
            ctx.set(pos.x, pos.y, ren.fg, ren.bg, ren.glyph);
        }

        let sleep = time::Duration::from_millis(200);
        thread::sleep(sleep);
    }
}

fn main() -> BError {
    let mut context = BTermBuilder::simple80x50()
        .build()?;

    let mut gs = State {
        ecs: World::new(),
    };

    gs.ecs.register::<Position>();
    gs.ecs.register::<Renderable>();
    gs.ecs.register::<Creature>();
    gs.ecs.register::<ProvidesHealth>();
    gs.ecs.register::<Viewshed>();

    // add one Creature
    gs.ecs
        .create_entity()
        .with(Position {x: 10, y: 20})
        .with(Renderable {
            glyph: to_cp437('@'),
            fg: RGB::named(WHITE),
            bg: RGB::named(BLACK)
        })
        .with(Creature {})
        .with(Viewshed { visible_tiles : Vec::new(), range: 6, dirty: true })
        .with(HealthStats { max_hp: 100, hp: 100 })
        .build();

    // add one "food" item
    gs.ecs
        .create_entity()
        .with(Position {x: 35, y: 35})
        .with(Renderable {
            glyph: to_cp437('*'),
            fg: RGB::named(WHITE),
            bg: RGB::named(BLACK),
        })
        .with(ProvidesHealth { hp_gain: 10 })
        .build();

    // map defined in separate file, but isn't really
    // important for this question
    let mut map = Map::new_map();
    gs.ecs.insert(map);

    main_loop(context, gs)
}

struct VisSystem {}
impl<'a> System<'a> for VisSystem {
    type SystemData = (
        WriteExpect<'a, Map>,
        Entities<'a>,
        WriteStorage<'a, Viewshed>,
        ReadStorage<'a, Position>,
    );

    fn run(&mut self, data: Self::SystemData) {
        let (map, entities, mut viewshed, pos) = data;

        for (_ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
            if viewshed.dirty {
                viewshed.visible_tiles = field_of_view(
                    Point::new(pos.x, pos.y),
                    viewshed.range,
                    &*map,
                )
            }
        }
    }
}

struct CreatureAI {}
impl<'a> System<'a> for CreatureAI {
    #[allow(clippy::type_complexity)]
    type SystemData = (
        // ...
    );

    fn run(&mut self, data: Self::SystemData) {
        // ... not sure what to do here\
        //
        // by doing a join on (viewshed, position),
        // i'd be able to iterate thru viewshed.visible_tiles,
        // but i can't figure out how I could check if a given
        // entity located at the Point has the "ProvidesHealth"
        // component or not
    }
}

0

There are 0 answers