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 / blocks / fire.rs

use common::world::Block;
use common::world::shape::BlockView;

use crate::level::Level;

fn encouragement(level: &Level, [x, y, z]: [i32; 3]) -> i32 {
    common::interaction::OFFSETS
        .iter()
        .map(|[dx, dy, dz]| {
            BlockView::block(level, x + dx, y + dy, z + dz)
                .fire_rates()
                .0
        })
        .max()
        .unwrap_or(0)
}

pub fn supported(level: &Level, x: i32, y: i32, z: i32) -> bool {
    BlockView::block(level, x, y - 1, z).is_normal_cube() || encouragement(level, [x, y, z]) > 0
}

pub fn added(level: &mut Level, x: i32, y: i32, z: i32) {
    if level.block(x, y - 1, z) == Block::OBSIDIAN && super::portal::try_create(level, x, y, z) {
        return;
    }
    if supported(level, x, y, z) {
        level.schedule_tick(x, y, z, Block::FIRE, 40);
    } else {
        level.set_block_notify(x, y, z, Block::AIR);
    }
}

/// Rain reaching the block or one beside it.
fn rain_near(level: &Level, x: i32, y: i32, z: i32) -> bool {
    level.rained_on(x, y, z)
        || super::SIDES
            .iter()
            .any(|[dx, dz]| level.rained_on(x + dx, y, z + dz))
}

/// `BlockFire.updateTick`: ages, burns out, spreads, and goes out in the rain.
pub fn tick(level: &mut Level, x: i32, y: i32, z: i32) {
    let eternal = BlockView::block(level, x, y - 1, z) == Block::NETHERRACK;
    if !supported(level, x, y, z) {
        level.set_block_notify(x, y, z, Block::AIR);
    }
    if !eternal && level.weather.is_raining() && rain_near(level, x, y, z) {
        level.set_block_notify(x, y, z, Block::AIR);
        return;
    }
    let age = level.metadata(x, y, z);
    if age < 15 {
        let next = age + (level.rand.next_int(3) / 2) as u8;
        level.set_metadata(x, y, z, next);
    }
    level.schedule_tick(x, y, z, Block::FIRE, 40);
    if !eternal && encouragement(level, [x, y, z]) == 0 {
        if !BlockView::block(level, x, y - 1, z).is_normal_cube() || age > 3 {
            level.set_block_notify(x, y, z, Block::AIR);
        }
        return;
    }
    if !eternal
        && BlockView::block(level, x, y - 1, z).fire_rates().0 == 0
        && age == 15
        && level.rand.next_int(4) == 0
    {
        level.set_block_notify(x, y, z, Block::AIR);
        return;
    }
    for [dx, dy, dz] in [
        [1, 0, 0],
        [-1, 0, 0],
        [0, -1, 0],
        [0, 1, 0],
        [0, 0, -1],
        [0, 0, 1],
    ] {
        let [x, y, z] = [x + dx, y + dy, z + dz];
        let block = level.block(x, y, z);
        if level.rand.next_int(if dy == 0 { 300 } else { 250 }) < block.fire_rates().1 {
            if level.rand.next_int(i32::from(age) + 10) < 5 && !level.rained_on(x, y, z) {
                let next = (age + (level.rand.next_int(5) / 4) as u8).min(15);
                level.set_block_with_metadata_notify(x, y, z, Block::FIRE, next);
            } else {
                level.set_block_notify(x, y, z, Block::AIR);
            }
            if block == Block::TNT {
                level.prime_tnt(glam::IVec3::new(x, y, z).as_dvec3() + 0.5, false);
            }
        }
    }
    for tx in x - 1..=x + 1 {
        for tz in z - 1..=z + 1 {
            for ty in y - 1..=y + 4 {
                if [tx, ty, tz] == [x, y, z] || !level.is_air(tx, ty, tz) {
                    continue;
                }
                let encouragement = encouragement(level, [tx, ty, tz]);
                if encouragement <= 0 {
                    continue;
                }
                let chance = (encouragement + 40) / (i32::from(age) + 30);
                let distance = 100 + (ty - y - 1).max(0) * 100;
                // The original checks the western neighbor in the fire's own row
                if chance > 0
                    && level.rand.next_int(distance) <= chance
                    && (!level.weather.is_raining() || !level.rained_on(tx, ty, tz))
                    && !level.rained_on(tx - 1, ty, z)
                    && !level.rained_on(tx + 1, ty, tz)
                    && !level.rained_on(tx, ty, tz - 1)
                    && !level.rained_on(tx, ty, tz + 1)
                {
                    let next = (age + (level.rand.next_int(5) / 4) as u8).min(15);
                    level.set_block_with_metadata_notify(tx, ty, tz, Block::FIRE, next);
                }
            }
        }
    }
}