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 / entity / painting.rs

use common::entity::Entity;
use common::entity::painting::{ART, Painting};
use common::item::{Item, ItemStack};
use common::math::floor_f64;
use common::world::shape::BlockView;

use super::Kind;
use crate::level::Level;

impl Level {
    pub fn place_painting(&mut self, anchor: [i32; 3], side: u8) -> bool {
        let direction = match side {
            2 => 0,
            4 => 1,
            3 => 2,
            5 => 3,
            _ => return false,
        };
        let mut painting = Painting {
            art: 0,
            anchor,
            direction,
        };
        let mut stored = self.entities.create(Kind::Painting { painting, check: 0 });
        let choices: Vec<u8> = (0..ART.len() as u8)
            .filter(|&art| self.valid_painting(Painting { art, ..painting }))
            .collect();
        if choices.is_empty() {
            return false;
        }
        let entity = &mut stored.entity;
        painting.art = choices[entity.rand.next_int(choices.len() as i32) as usize];
        entity.set_location_and_angles(painting.position(), f32::from(direction) * 90.0, 0.0);
        entity.bb = painting.bounds();
        stored.kind = Kind::Painting { painting, check: 0 };
        self.spawn(stored);
        true
    }

    /// The painting item of a removed painting, collectable at once.
    pub(crate) fn drop_painting(&mut self, entity: &Entity) {
        self.dropped_items.push(crate::level::DroppedItem {
            x: entity.pos.x,
            y: entity.pos.y,
            z: entity.pos.z,
            stack: ItemStack::one(Item::PAINTING),
            motion: None,
            impulse: glam::DVec3::ZERO,
            pickup_delay: 0,
        });
    }

    pub(super) fn valid_painting(&self, painting: Painting) -> bool {
        let bounds = painting.bounds();
        let overlaps = |is: fn(&Kind) -> bool| {
            self.entities
                .iter()
                .any(|e| !e.entity.dead && is(&e.kind) && e.entity.bb.intersects(&bounds))
        };
        // `getCollidingBoundingBoxes`: blocks and boats
        if !self.block_boxes(&bounds).is_empty() || overlaps(|k| matches!(k, Kind::Boat(_))) {
            return false;
        }
        let [width, height] = painting.art().size.map(|v| i32::from(v / 16));
        let pos = painting.position();
        let x = floor_f64(pos.x - f64::from(width) / 2.0);
        let y = floor_f64(pos.y - f64::from(height) / 2.0);
        let z = floor_f64(pos.z - f64::from(width) / 2.0);
        let [ax, _, az] = painting.anchor;
        for col in 0..width {
            for row in 0..height {
                let block = if painting.direction & 1 == 0 {
                    self.block(x + col, y + row, az)
                } else {
                    self.block(ax, y + row, z + col)
                };
                if !block.material().is_solid() {
                    return false;
                }
            }
        }
        !overlaps(|k| matches!(k, Kind::Painting { .. }))
    }

    pub(super) fn tick_painting(
        &mut self,
        entity: &mut Entity,
        painting: Painting,
        check: &mut u32,
    ) {
        if *check == 100 {
            *check = 0;
            if !self.valid_painting(painting) {
                entity.dead = true;
                self.drop_painting(entity);
            }
        } else {
            *check += 1;
        }
    }
}