Scoundrel

A roguelike engine with devious intent!

crates.io docs

Features

Simple example

Main loop

```rust pub fn create_hero() -> Entity { // our entities are described in terms of their components World::spawn(( Positioned::at(10, 10), // these are some of the common components Visible(Glyph::HERO), // that all rendered things will require Sighted(16), // the next ones are needed only for things with sight FieldOfView::empty(), PlayerTag)) // this marks that this is a player entity }

pub fn main() { let mut engine = EngineInstance::default();

create_hero();
make_dungeon();

while !should_quit() {
    poll_inputs(&mut engine);

    if should_redraw() {
        clear_screen();
        engine.render();
    }
}

} ```

Dungeon Generation

```rust

pub fn make_dungeon() -> Entity { let mut level = MapLevel::default();

// stamp large rooms
stamp_rooms(RoomStampDistribution{ 
    width: 8..13, height: 8..13, max_count: 2, min_distance: 2 
}, &mut level);

// stamp smaller rooms
stamp_rooms(RoomStampDistribution{ 
    width: 5..8, height: 5..8, max_count: 20, min_distance: 1 
}, &mut level);

// stamp filler rooms
stamp_rooms(RoomStampDistribution{ 
    width: 3..5, height: 3..5, max_count: 300, min_distance: 1 
}, &mut level);

// trunk connects all the rooms with no cycles
// shrub contains all the remaining non-intersecting edges between rooms
let (trunk, shrub) = construct_mesh(level.centers.as_slice());

// add all the corridors for the trunk, making sure to connect all the rooms
stamp_corridors(CorridorStampDistribution {
    glyph: Glyph::FLOOR,
    probability: Percentage::new(100),
    filter_lengths: None,
    corridor_style: &corridors::straight_edge,
    ignore_already_connected: true,
}, &trunk, &mut level);

// add 50% of the rest of the corridors, to make the dungeon more fun
stamp_corridors(CorridorStampDistribution {
    glyph: Glyph::FLOOR,
    probability: Percentage::new(50),
    filter_lengths: Some(1..8),
    corridor_style: &corridors::straight_edge,
    ignore_already_connected: true,
}, &shrub, &mut level);

// fetch the player entity
let (player_entity, _) = <(PlayerTag,)>::get().unwrap();

// select a walkable point in the level
let at = {
    let points = level.walkable.points();
    points[rand::usize(0..points.len())]
};

// position the player entity at that point
player_entity.update((Positioned(at),));

World::spawn((CurrentLevelTag, level))

} ```

Gameplay Systems

```rust pub fn updatefov(playerentity: Entity, level_entity: Entity, mut level: MapLevel, from: &Point, range: u16) {

assert!(player_entity.has_component::<(FieldOfView,)>());
assert!(level_entity.has_component::<(MapLevel,)>());

let fov = level.fov(from, range);
level_entity.update((level,));
player_entity.update((fov,));

}

pub fn checkplayermovement(dir: &Point, flow: &mut Flow) { if !dir.iszero() { // grab these components from the first entity that has them all let (playerentity, (Positioned(at), Sighted(range), _)) = <(Positioned, Sighted, PlayerTag)>::get().unwrap();

    let (level_entity, (level, _)) = 
        <(MapLevel, CurrentLevelTag)>::get().unwrap();

    let next_pos = *dir + at;

    // the player can walk to this location
    if level.walkable.get(&next_pos) {
        player_entity.update((Positioned(next_pos),));

        // update the players field of view
        update_fov(player_entity, level_entity, level, &at, range);

        // we only redraw the screen once the player makes an action
        force_redraw();
    }
}

} ```