alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH 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
root crates server src populate trees.rs
//! Ports of `WorldGenTrees`, `WorldGenForest`, `WorldGenTaiga1` and `WorldGenTaiga2`.
use common::JavaRandom;
use common::biome::Biome;
use common::world::Block;
use super::big_tree;
use crate::level::Level;
#[derive(Clone, Copy, Debug)]
pub enum Kind {
Oak,
Birch,
BigOak,
/// `WorldGenBigTree` as saplings grow it, unscaled.
SaplingBigOak,
/// `WorldGenTaiga1`.
Pine,
/// `WorldGenTaiga2`.
Spruce,
}
impl Kind {
/// `BiomeGenBase.getRandomWorldGenForTrees`.
pub fn pick(biome: Biome, rand: &mut JavaRandom) -> Self {
match biome {
Biome::Forest if rand.next_int(5) == 0 => Self::Birch,
Biome::Forest | Biome::Rainforest if rand.next_int(3) == 0 => Self::BigOak,
Biome::Forest | Biome::Rainforest => Self::Oak,
Biome::Taiga if rand.next_int(3) == 0 => Self::Pine,
Biome::Taiga => Self::Spruce,
_ if rand.next_int(10) == 0 => Self::BigOak,
_ => Self::Oak,
}
}
pub fn generate(self, level: &mut Level, pos: [i32; 3], rand: &mut JavaRandom) -> bool {
match self {
Self::Oak => small(level, pos, rand, 4, None),
Self::Birch => small(level, pos, rand, 5, Some(2)),
Self::BigOak => big_tree::generate(level, pos, rand, 5),
Self::SaplingBigOak => big_tree::generate(level, pos, rand, 4),
Self::Pine => pine(level, pos, rand),
Self::Spruce => spruce(level, pos, rand),
}
}
}
/// Whether the tree fits: in height, with each layer's square of `radius(y)` around the column
/// only air or leaves. If so, turns the soil below into dirt, or fails if it is not grass or dirt.
fn plant(level: &mut Level, [x, y, z]: [i32; 3], height: i32, radius: impl Fn(i32) -> i32) -> bool {
if y < 1 || y + height + 1 > 128 {
return false;
}
for cy in y..=y + 1 + height {
if cy >= 128 {
return false;
}
let radius = radius(cy);
for cx in x - radius..=x + radius {
for cz in z - radius..=z + radius {
if !matches!(level.block(cx, cy, cz), Block::AIR | Block::LEAVES) {
return false;
}
}
}
}
let soil = level.block(x, y - 1, z);
if !matches!(soil, Block::GRASS | Block::DIRT) || y >= 128 - height - 1 {
return false;
}
level.set_block(x, y - 1, z, Block::DIRT);
true
}
fn trunk(level: &mut Level, [x, y, z]: [i32; 3], height: i32, metadata: Option<u8>) {
for dy in 0..height {
if matches!(level.block(x, y + dy, z), Block::AIR | Block::LEAVES) {
level.change_block(x, y + dy, z, Block::LOG, metadata);
}
}
}
/// `WorldGenTrees` (oak) and `WorldGenForest` (birch).
fn small(
level: &mut Level,
[x, y, z]: [i32; 3],
rand: &mut JavaRandom,
base_height: i32,
metadata: Option<u8>,
) -> bool {
let height = rand.next_int(3) + base_height;
let radius = |cy| match cy - y {
dy if dy >= height - 1 => 2,
0 => 0,
_ => 1,
};
if !plant(level, [x, y, z], height, radius) {
return false;
}
for cy in y - 3 + height..=y + height {
let offset = cy - (y + height);
let radius = 1 - offset / 2;
for cx in x - radius..=x + radius {
for cz in z - radius..=z + radius {
let corner = (cx - x).abs() == radius && (cz - z).abs() == radius;
if (!corner || (rand.next_int(2) != 0 && offset != 0))
&& !level.block(cx, cy, cz).is_opaque_cube()
{
level.change_block(cx, cy, cz, Block::LEAVES, metadata);
}
}
}
}
trunk(level, [x, y, z], height, metadata);
true
}
/// `WorldGenTaiga1`: a tall trunk with a single narrow cone at the top.
fn pine(level: &mut Level, [x, y, z]: [i32; 3], rand: &mut JavaRandom) -> bool {
let height = rand.next_int(5) + 7;
let bare = height - rand.next_int(2) - 3;
let crown = height - bare;
let max_radius = 1 + rand.next_int(crown + 1);
let radius = |cy| if cy - y < bare { 0 } else { max_radius };
if !plant(level, [x, y, z], height, radius) {
return false;
}
let mut radius = 0;
for cy in (y + bare..=y + height).rev() {
leaf_square(level, [x, cy, z], radius);
if radius >= 1 && cy == y + bare + 1 {
radius -= 1;
} else if radius < max_radius {
radius += 1;
}
}
trunk(level, [x, y, z], height - 1, Some(1));
true
}
/// `WorldGenTaiga2`: stacked rings of alternating width.
fn spruce(level: &mut Level, [x, y, z]: [i32; 3], rand: &mut JavaRandom) -> bool {
let height = rand.next_int(4) + 6;
let bare = 1 + rand.next_int(2);
let crown = height - bare;
let max_radius = 2 + rand.next_int(2);
let radius = |cy| if cy - y < bare { 0 } else { max_radius };
if !plant(level, [x, y, z], height, radius) {
return false;
}
let mut radius = rand.next_int(2);
let mut limit = 1;
let mut restart = 0;
for i in 0..=crown {
leaf_square(level, [x, y + height - i, z], radius);
if radius >= limit {
radius = restart;
restart = 1;
limit = (limit + 1).min(max_radius);
} else {
radius += 1;
}
}
let cut = rand.next_int(3);
trunk(level, [x, y, z], height - cut, Some(1));
true
}
/// A square of spruce leaves without its corners, or a single block for radius 0.
fn leaf_square(level: &mut Level, [x, y, z]: [i32; 3], radius: i32) {
for cx in x - radius..=x + radius {
for cz in z - radius..=z + radius {
let corner = (cx - x).abs() == radius && (cz - z).abs() == radius;
if (!corner || radius <= 0) && !level.block(cx, y, cz).is_opaque_cube() {
level.set_block_with_metadata(cx, y, cz, Block::LEAVES, 1);
}
}
}
}