1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! ECS-based game engine
//!
//! This module contains the [specs][specs] implementation, which defines the
//! interactions that occur when levels are played. Unsurprisingly, this
//! [ECS][ecs]-based engine has Entities, Components, and Systems.
//!
//! ### Entities
//!
//! There are one or more entities created, depending on the level. There
//! is always a warrior. There can be one or more sludge enemies.
//!
//! ### Components
//!
//! See `components` module.
//!
//! ### Systems
//!
//! See `systems` module.
//!
//! [specs]: https://github.com/slide-rs/specs
//! [ecs]: https://en.wikipedia.org/wiki/Entity_component_system

use std::{thread, time};

use specs::{prelude::*, World};

use crate::{floor::Floor, unit::UnitType, Player};

pub mod components;
pub mod systems;

use components::UnitComponent;
use systems::{PlayerSystem, SludgeSystem, UiSystem};

/// The entry point for the engine, called by [`Game`](crate::game::Game)
pub fn start(floor: Floor, player: impl Player + Send + Sync + 'static) -> Result<(), String> {
    let mut world = World::new();

    let player_system = PlayerSystem::new(player);
    let ui_system = UiSystem::new(floor.clone());
    let mut dispatcher = DispatcherBuilder::new()
        .with(player_system, "player", &[])
        .with(SludgeSystem, "sludge", &["player"])
        .with(ui_system, "ui", &["player", "sludge"])
        .build();

    dispatcher.setup(&mut world);

    UnitComponent::create(&mut world, *floor.warrior());

    for sludge in floor.sludges() {
        UnitComponent::create(&mut world, *sludge);
    }

    floor.draw();

    let mut step = 0;
    loop {
        step += 1;

        if step > 100 {
            return Err("You seem to have gotten lost...".to_owned());
        }

        {
            let units = world.read_storage::<UnitComponent>();
            for entity in world.entities().join() {
                match units.get(entity) {
                    Some(warrior_comp) if warrior_comp.unit.unit_type == UnitType::Warrior => {
                        let (current, _) = warrior_comp.unit.hp;
                        if current == 0 {
                            return Err("You died!".to_owned());
                        }
                        if warrior_comp.unit.position == floor.stairs {
                            return Ok(());
                        }
                    }
                    _ => {}
                }
            }
        }

        dispatcher.dispatch(&world);
        world.maintain();

        thread::sleep(time::Duration::from_millis(500));
    }
}