Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / server / src / populate / dungeons.rs

//! Port of `WorldGenDungeons`.

use common::JavaRandom;
use common::item::{Item, ItemStack};
use common::world::{Block, CHEST_SIZE, TileEntity};

use crate::level::Level;

const HEIGHT: i32 = 3;

/// A cobblestone room with a spawner and up to two chests, placed where its walls
/// have between one and five openings.
pub fn generate(level: &mut Level, [x, y, z]: [i32; 3], rand: &mut JavaRandom) {
    let rx = rand.next_int(2) + 2;
    let rz = rand.next_int(2) + 2;
    let (min_x, max_x, min_z, max_z) = (x - rx - 1, x + rx + 1, z - rz - 1, z + rz + 1);
    let mut openings = 0;
    for cx in min_x..=max_x {
        for cy in y - 1..=y + HEIGHT + 1 {
            for cz in min_z..=max_z {
                let solid = level.material(cx, cy, cz).is_solid();
                if (cy == y - 1 || cy == y + HEIGHT + 1) && !solid {
                    return;
                }
                if (cx == min_x || cx == max_x || cz == min_z || cz == max_z)
                    && cy == y
                    && level.is_air(cx, cy, cz)
                    && level.is_air(cx, cy + 1, cz)
                {
                    openings += 1;
                }
            }
        }
    }
    if !(1..=5).contains(&openings) {
        return;
    }

    for cx in min_x..=max_x {
        for cy in (y - 1..=y + HEIGHT).rev() {
            for cz in min_z..=max_z {
                let inside =
                    cx != min_x && cx != max_x && cz != min_z && cz != max_z && cy != y - 1;
                if inside || (cy >= 0 && !level.material(cx, cy - 1, cz).is_solid()) {
                    level.set_block_notify(cx, cy, cz, Block::AIR);
                } else if level.material(cx, cy, cz).is_solid() {
                    let wall = if cy == y - 1 && rand.next_int(4) != 0 {
                        Block::MOSSY_COBBLESTONE
                    } else {
                        Block::COBBLESTONE
                    };
                    level.set_block_notify(cx, cy, cz, wall);
                }
            }
        }
    }

    'chests: for _ in 0..2 {
        for _ in 0..3 {
            let cx = x + rand.next_int(rx * 2 + 1) - rx;
            let cz = z + rand.next_int(rz * 2 + 1) - rz;
            if !level.is_air(cx, y, cz) {
                continue;
            }
            let walls = [(-1, 0), (1, 0), (0, -1), (0, 1)]
                .into_iter()
                .filter(|&(dx, dz)| level.material(cx + dx, y, cz + dz).is_solid())
                .count();
            if walls == 1 {
                level.set_block_notify(cx, y, cz, Block::CHEST);
                for _ in 0..8 {
                    if let Some(stack) = loot(rand) {
                        let slot = rand.next_int(CHEST_SIZE as i32) as usize;
                        if let Some(TileEntity::Chest(items)) = level.tile_entity_mut(cx, y, cz) {
                            items[slot] = Some(stack);
                        }
                    }
                }
                continue 'chests;
            }
        }
    }

    level.set_block_notify(x, y, z, Block::SPAWNER);
    let mob = match rand.next_int(4) {
        0 => "Skeleton",
        3 => "Spider",
        _ => "Zombie",
    };
    if let Some(TileEntity::MobSpawner { entity_id, .. }) = level.tile_entity_mut(x, y, z) {
        *entity_id = mob.into();
    }
}

/// `pickCheckLootItem`.
fn loot(rand: &mut JavaRandom) -> Option<ItemStack> {
    let stack = |item, rand: &mut JavaRandom| ItemStack::new(item, rand.next_int(4) + 1, 0);
    Some(match rand.next_int(11) {
        0 => ItemStack::one(Item::SADDLE),
        1 => stack(Item::IRON_INGOT, rand),
        2 => ItemStack::one(Item::BREAD),
        3 => stack(Item::WHEAT, rand),
        4 => stack(Item::GUNPOWDER, rand),
        5 => stack(Item::STRING, rand),
        6 => ItemStack::one(Item::BUCKET),
        7 if rand.next_int(100) == 0 => ItemStack::one(Item::GOLDEN_APPLE),
        8 if rand.next_int(2) == 0 => stack(Item::REDSTONE, rand),
        9 if rand.next_int(10) == 0 => {
            ItemStack::one(Item(Item::RECORD_13.0 + rand.next_int(2) as i16))
        }
        10 => ItemStack::new(Item::DYE, 1, 3),
        _ => return None,
    })
}