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 / vehicles / boat.rs

use common::entity::vehicle::VehiclePose;
use common::entity::{Entity, EntityWorld};
use common::item::{Item, ItemStack};
use common::math::floor_f64;
use common::world::Block;
use glam::DVec3;

use crate::entity::Kind;
use crate::level::Level;

#[derive(Clone, Debug)]
pub struct Boat {
    pub pose: VehiclePose,
}
impl Default for Boat {
    fn default() -> Self {
        Self {
            pose: VehiclePose {
                direction: 1,
                ..Default::default()
            },
        }
    }
}

impl Level {
    pub fn spawn_boat(&mut self, at: DVec3) {
        let mut stored = self.entities.create(Kind::Boat(Boat::default()));
        let entity = &mut stored.entity;
        entity.set_position(at + DVec3::Y * f64::from(entity.y_offset));
        self.spawn(stored);
    }

    pub(crate) fn break_boat(&mut self, entity: &mut Entity) {
        if entity.dead {
            return;
        }
        entity.dead = true;
        for _ in 0..3 {
            self.mob_drop(entity, ItemStack::one(Item::from_block(Block::PLANKS)));
        }
        for _ in 0..2 {
            self.mob_drop(entity, ItemStack::one(Item::STICK));
        }
        self.eject_rider(entity);
    }

    pub(crate) fn hurt_boat(&mut self, entity: &mut Entity, boat: &mut Boat, amount: i32) -> bool {
        boat.pose.direction = -boat.pose.direction;
        boat.pose.hit = 10;
        boat.pose.damage += amount * 10;
        if boat.pose.damage > 40 {
            self.break_boat(entity);
        }
        true
    }

    pub(crate) fn tick_boat(&mut self, entity: &mut Entity, boat: &mut Boat) {
        entity.base_update(self);
        let damage = std::mem::take(&mut entity.damage_received);
        if damage > 0 {
            self.hurt_boat(entity, boat, damage);
        }
        boat.pose.hit = (boat.pose.hit - 1).max(0);
        boat.pose.damage = (boat.pose.damage - 1).max(0);
        let water = self.submerged(entity, 0.125);
        if water < 1.0 {
            entity.motion.y += f64::from(0.04_f32) * (water * 2.0 - 1.0);
        } else {
            if entity.motion.y < 0.0 {
                entity.motion.y /= 2.0;
            }
            entity.motion.y += f64::from(0.007_f32);
        }
        if let Some(rider) = entity.rider.and_then(|id| self.entities.get(id)) {
            entity.motion.x += rider.entity.motion.x * 0.2;
            entity.motion.z += rider.entity.motion.z * 0.2;
        }
        entity.motion.x = entity.motion.x.clamp(-0.4, 0.4);
        entity.motion.z = entity.motion.z.clamp(-0.4, 0.4);
        if entity.on_ground {
            entity.motion *= 0.5;
        }
        entity.move_entity(self, entity.motion);
        // Fire and cactus touched while moving hurt it as well
        let damage = std::mem::take(&mut entity.damage_received);
        if damage > 0 {
            self.hurt_boat(entity, boat, damage);
        }
        let speed = (entity.motion.x * entity.motion.x + entity.motion.z * entity.motion.z).sqrt();
        if speed > 0.15 {
            let yaw = f64::from(entity.yaw) * std::f64::consts::PI / 180.0;
            let (sin, cos) = yaw.sin_cos();
            for _ in 0..(1.0 + speed * 60.0).ceil() as i32 {
                let along = f64::from(entity.rand.next_float() * 2.0 - 1.0);
                let side = f64::from(entity.rand.next_int(2) * 2 - 1) * 0.7;
                let (x, z) = if entity.rand.next_bool() {
                    (
                        entity.pos.x - cos * along * 0.8 + sin * side,
                        entity.pos.z - sin * along * 0.8 - cos * side,
                    )
                } else {
                    (
                        entity.pos.x + cos + sin * along * 0.7,
                        entity.pos.z + sin - cos * along * 0.7,
                    )
                };
                self.spawn_particle(
                    "splash",
                    DVec3::new(x, entity.pos.y - 0.125, z),
                    entity.motion,
                );
            }
        }
        if entity.collided_horizontally && speed > 0.15 {
            self.break_boat(entity);
        } else {
            entity.motion *= DVec3::new(
                f64::from(0.99_f32),
                f64::from(0.95_f32),
                f64::from(0.99_f32),
            );
        }
        entity.pitch = 0.0;
        let delta = entity.prev_pos - entity.pos;
        if delta.x * delta.x + delta.z * delta.z > 0.001 {
            let yaw = (delta.z.atan2(delta.x) * 180.0 / std::f64::consts::PI) as f32;
            let turn = ((yaw - entity.yaw + 180.0).rem_euclid(360.0) - 180.0).clamp(-20.0, 20.0);
            entity.yaw += turn;
        }
        let area = entity
            .bb
            .inflate(DVec3::new(f64::from(0.2_f32), 0.0, f64::from(0.2_f32)));
        let ids: Vec<i32> = self
            .entities
            .near(&area)
            .filter(|other| {
                matches!(other.kind, Kind::Boat(_))
                    && !other.entity.dead
                    && other.entity.bb.intersects(&area)
                    && Some(other.entity.id) != entity.rider
            })
            .map(|other| other.entity.id)
            .collect();
        for id in ids {
            if let Some(other) = self.entities.get_mut(id) {
                let delta = other.entity.pos - entity.pos;
                let max = delta.x.abs().max(delta.z.abs());
                if max >= 0.01 {
                    let distance = max.sqrt();
                    let push = delta.with_y(0.0) / distance * (1.0 / distance).min(1.0) * 0.05;
                    entity.motion -= push;
                    other.entity.motion += push;
                }
            }
        }
        for corner in 0..4 {
            let x = floor_f64(entity.pos.x + (f64::from(corner % 2) - 0.5) * 0.8);
            let y = floor_f64(entity.pos.y);
            let z = floor_f64(entity.pos.z + (f64::from(corner / 2) - 0.5) * 0.8);
            if self.block(x, y, z) == Block::SNOW_LAYER {
                self.set_block_notify(x, y, z, Block::AIR);
            }
        }
    }
}