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 / mobs / ghast.rs

use common::entity::{Entity, EntityWorld};
use common::math::floor_f64;
use glam::DVec3;

use super::{Mob, special::Special, visible};
use crate::level::Level;

impl Level {
    pub(super) fn ghast_ai(&mut self, entity: &mut Entity, mob: &mut Mob) {
        self.despawn_mob(entity);
        let Special::Ghast {
            waypoint,
            course_delay,
            aggro_delay,
            attack,
        } = &mut mob.special
        else {
            return;
        };
        let delta = *waypoint - entity.pos;
        let distance = f64::from(delta.length() as f32);
        if !(1.0..=60.0).contains(&distance) {
            *waypoint = entity.pos
                + DVec3::from_array(std::array::from_fn(|_| {
                    f64::from((entity.rand.next_float() * 2.0 - 1.0) * 16.0)
                }));
        }
        let course = *course_delay;
        *course_delay -= 1;
        if course <= 0 {
            *course_delay += entity.rand.next_int(5) + 2;
            let step = (*waypoint - entity.pos) / distance.max(1.0);
            // `isCourseTraversable` collides with boats as well as blocks
            let traversable = (1..distance.ceil() as i32).all(|i| {
                common::entity::EntityWorld::colliding_boxes(
                    self,
                    entity,
                    &entity.bb.offset(step * f64::from(i)),
                )
                .is_empty()
            });
            if traversable && distance > 0.0 {
                entity.motion += delta / distance * 0.1;
            } else {
                *waypoint = entity.pos;
            }
        }
        if mob
            .target
            .is_some_and(|id| self.entities.get(id).is_none_or(|e| e.entity.dead))
        {
            mob.target = None;
        }
        let aggro = *aggro_delay;
        if mob.target.is_some() {
            *aggro_delay -= 1;
        }
        if mob.target.is_none() || aggro <= 0 {
            mob.target = self.nearest_player(entity.pos, 100.0).map(|e| e.id);
            if mob.target.is_some() {
                *aggro_delay = 20;
            }
        }
        let target = mob
            .target
            .and_then(|id| self.entities.get(id))
            .map(|e| e.entity.clone())
            .filter(|e| e.pos.distance_squared(entity.pos) < 4096.0);
        if let Some(target) = target {
            let delta = DVec3::new(
                target.pos.x - entity.pos.x,
                target.bb.min.y + f64::from(target.height / 2.0)
                    - (entity.pos.y + f64::from(entity.height / 2.0)),
                target.pos.z - entity.pos.z,
            );
            entity.yaw = -(delta.x.atan2(delta.z) as f32) * 180.0 / std::f32::consts::PI;
            if visible(self, entity, &target) {
                if *attack == 10 {
                    let pitch = entity.sound_pitch();
                    self.play_sound(entity, "mob.ghast.charge", 10.0, pitch);
                }
                *attack += 1;
                if *attack == 20 {
                    let pitch = entity.sound_pitch();
                    self.play_sound(entity, "mob.ghast.fireball", 10.0, pitch);
                    self.shoot_fireball(entity, delta);
                    *attack = -40;
                }
            } else if *attack > 0 {
                *attack -= 1;
            }
        } else {
            entity.yaw =
                -(entity.motion.x.atan2(entity.motion.z) as f32) * 180.0 / std::f32::consts::PI;
            if *attack > 0 {
                *attack -= 1;
            }
        }
        entity.living.as_mut().unwrap().render_yaw_offset = entity.yaw;
    }

    pub(super) fn ghast_move(&mut self, entity: &mut Entity) {
        let friction = if entity.in_water {
            f64::from(0.8_f32)
        } else if entity.handle_lava_movement(self) {
            0.5
        } else if entity.on_ground {
            f64::from(
                self.block(
                    floor_f64(entity.pos.x),
                    floor_f64(entity.bb.min.y) - 1,
                    floor_f64(entity.pos.z),
                )
                .slipperiness()
                    * 0.91,
            )
        } else {
            f64::from(0.91_f32)
        };
        entity.move_entity(self, entity.motion);
        entity.motion *= friction;
        entity.animate_walk();
    }
}