Mapping the Mine pt 2

Let's start fleshing out the top level of the mine, and add a bit more of the mapping system.

Adding Empty Rooms to the Mine Head

We ended the previous tutorial with a mysterious "rooms" array in the mine_top.rs system. The intent was hopefully clear - we're going to use a room generation algorithm on this level. It's very similar to the one in Hands-on Rust, but biased to create rooms outside of the existing mine-head. Open src/map/layerbuilder/mine_top.rs and let's get started.

Carving Tunnels

We'll start with a couple of functions that are straight out of Hands-on Rust - functions to build vertical and horizontal tunnels:


#![allow(unused)]
fn main() {
fn apply_horizontal_tunnel(map: &mut Layer, x1:i32, x2:i32, y:i32) {
    use std::cmp::{min, max};
    for x in min(x1,x2) ..= max(x1,x2) {
        let idx = map.point2d_to_index(Point::new(x, y));
        if map.tiles[idx as usize].tile_type == TileType::Wall {
            map.tiles[idx as usize] = Tile::floor();
        }
    }
}

fn apply_vertical_tunnel(map: &mut Layer, y1:i32, y2:i32, x:i32) {
    use std::cmp::{min, max};
    for y in min(y1,y2) ..= max(y1,y2) {
        let idx = map.point2d_to_index(Point::new(x, y));
        if map.tiles[idx as usize].tile_type == TileType::Wall {
            map.tiles[idx as usize] = Tile::floor();
        }
    }
}
}

These do what it says on the label - they carve either a vertical or a horizontal tunnel between two points. There are no diagonals because it's often tricky to navigate diagonals with a 4-way movement scheme.

Building Rooms

Next up is the try_room function. This function creates a random location for a potential room. If the room is possible (it only contains wall tiles, so it doesn't overlap any existing rooms) then it adds it to the provided rooms list:


#![allow(unused)]
fn main() {
fn try_room(rooms: &mut Vec<Rect>, map: &Layer) {
    let mut rng_lock = crate::RNG.lock();
    let rng = rng_lock.as_mut().unwrap();

    let w = rng.range(4,10);
    let h = rng.range(4,10);
    let x = rng.range(1, WIDTH - w);
    let y = rng.range(1, HEIGHT - h);

    let room_rect = Rect::with_size(x, y, w, h);
    let mut ok = true;
    room_rect.for_each(|pt| {
        let idx = map.point2d_to_index(pt);
        if map.tiles[idx].tile_type != TileType::Wall {
            ok = false;
        }
    });
    if ok {
        rooms.push(room_rect);
    }
}
}

With the infrastructure in place, we can extend the mine_top function to actually build rooms. Add this to the function, starting with the rooms declaration:


#![allow(unused)]
fn main() {
    ...
    // Start building rooms and corridors
    // Using the Hands-On Rust rooms/corridors builder slightly modified to go towards the middle
    let mut rooms = vec![Rect::with_size((WIDTH/2)-10, (HEIGHT/2)-10, 20, 20)];
    while rooms.len() < 14 {
        try_room(&mut rooms, &layer);
    }

    let mut rng_lock = crate::RNG.lock();
    let rng = rng_lock.as_mut().unwrap();
    rooms
        .iter()
        .skip(1)
        .for_each(|r| {
            r.for_each(|pt| {
                let idx = layer.point2d_to_index(pt);
                layer.tiles[idx] = Tile::floor();
            });
            let room_center = r.center();
            if rng.range(0,2) == 1 {
                apply_horizontal_tunnel(&mut layer, room_center.x, center_pt.x, room_center.y);
                apply_vertical_tunnel(&mut layer, room_center.y, center_pt.y, center_pt.x);
            } else {
                apply_vertical_tunnel(&mut layer, room_center.y, center_pt.y, room_center.x);
                apply_horizontal_tunnel(&mut layer, room_center.x, center_pt.x, center_pt.y);
            }
        }
    );

    layer
}
}

Run the program now, and take a look at a mine-head level:

Wrap-Up

You can find the source code for mining_map2 here.

The mine-head is looking pretty decent, now. Let's move on to even more mapping!