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 / common / src / entity / mob.rs

macro_rules! mob_kinds {
    ($($(#[$doc:meta])* $kind:ident = $id:literal,)*) => {
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        #[repr(i8)]
        pub enum MobKind {
            $($(#[$doc])* $kind = $id,)*
        }

        impl MobKind {
            const ALL: &[Self] = &[$(Self::$kind),*];

            /// The entity name of `EntityList`.
            pub fn name(self) -> &'static str {
                match self {
                    $(Self::$kind => stringify!($kind),)*
                }
            }
        }
    };
}

mob_kinds! {
    /// `EntityMob` itself: a plain biped in the player skin.
    Monster = 49,
    Creeper = 50,
    Skeleton = 51,
    Spider = 52,
    Giant = 53,
    Zombie = 54,
    Slime = 55,
    Ghast = 56,
    PigZombie = 57,
    Pig = 90,
    Sheep = 91,
    Cow = 92,
    Chicken = 93,
    Squid = 94,
    Wolf = 95,
}

impl MobKind {
    pub fn from_id(id: i8) -> Option<Self> {
        Self::ALL.iter().copied().find(|&kind| kind as i8 == id)
    }

    pub fn from_name(name: &str) -> Option<Self> {
        Self::ALL.iter().copied().find(|kind| kind.name() == name)
    }

    pub fn size(self) -> [f32; 2] {
        match self {
            Self::Pig => [0.9, 0.9],
            Self::Cow | Self::Sheep => [0.9, 1.3],
            Self::Chicken => [0.3, 0.4],
            Self::Spider => [1.4, 0.9],
            Self::Squid => [0.95, 0.95],
            Self::Wolf => [0.8, 0.8],
            Self::Slime => [0.6, 0.6],
            Self::Ghast => [4.0, 4.0],
            Self::Giant => [3.6, 10.8],
            _ => [0.6, 1.8],
        }
    }

    /// `getMountedYOffset`: where a rider sits above the position.
    pub fn mounted_y_offset(self) -> f64 {
        let height = f64::from(self.size()[1]) * 0.75;
        if self == Self::Spider {
            height - 0.5
        } else {
            height
        }
    }

    /// `instanceof` between the original classes: pigmen are zombies.
    pub fn is_a(self, class: Self) -> bool {
        self == class
            || class == Self::Zombie && self == Self::PigZombie
            || class == Self::Monster && self.is_monster()
    }

    pub fn hostile(self) -> bool {
        (self as i8) < 90
    }

    /// Whether the original class extends `EntityMob`.
    pub fn is_monster(self) -> bool {
        self.hostile() && !matches!(self, Self::Slime | Self::Ghast)
    }

    pub fn health(self) -> i32 {
        match self {
            Self::Slime => 1,
            Self::Ghast => 10,
            Self::Giant => 200,
            _ if self.hostile() => 20,
            Self::Wolf => 8,
            Self::Chicken => 4,
            _ => 10,
        }
    }

    pub fn speed(self) -> f32 {
        match self {
            Self::Zombie | Self::PigZombie | Self::Giant => 0.5,
            Self::Spider => 0.8,
            Self::Wolf => 1.1,
            _ => 0.7,
        }
    }

    pub fn volume(self) -> f32 {
        match self {
            Self::Cow | Self::Squid | Self::Wolf => 0.4,
            Self::Slime => 0.6,
            Self::Ghast => 10.0,
            _ => 1.0,
        }
    }

    pub fn sound(self, hurt: bool, dead: bool) -> Option<&'static str> {
        Some(match self {
            Self::Pig if dead => "mob.pigdeath",
            Self::Pig => "mob.pig",
            Self::Cow if hurt || dead => "mob.cowhurt",
            Self::Cow => "mob.cow",
            Self::Sheep => "mob.sheep",
            Self::Chicken if hurt || dead => "mob.chickenhurt",
            Self::Chicken => "mob.chicken",
            Self::Zombie if dead => "mob.zombiedeath",
            Self::Zombie if hurt => "mob.zombiehurt",
            Self::Zombie => "mob.zombie",
            Self::Skeleton if hurt || dead => "mob.skeletonhurt",
            Self::Skeleton => "mob.skeleton",
            Self::Spider if dead => "mob.spiderdeath",
            Self::Spider => "mob.spider",
            Self::Creeper if dead => "mob.creeperdeath",
            Self::Creeper if hurt => "mob.creeper",
            Self::Creeper | Self::Squid => return None,
            Self::Slime if hurt || dead => "mob.slime",
            Self::Slime => return None,
            Self::Ghast if dead => "mob.ghast.death",
            Self::Ghast if hurt => "mob.ghast.scream",
            Self::Ghast => "mob.ghast.moan",
            Self::Wolf if dead => "mob.wolf.death",
            Self::Wolf if hurt => "mob.wolf.hurt",
            Self::Wolf => "mob.wolf.bark",
            Self::PigZombie if dead => "mob.zombiepig.zpigdeath",
            Self::PigZombie if hurt => "mob.zombiepig.zpighurt",
            Self::PigZombie => "mob.zombiepig.zpig",
            // `EntityLiving` defaults
            Self::Monster | Self::Giant if hurt || dead => "random.hurt",
            Self::Monster | Self::Giant => return None,
        })
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SpecialPose {
    pub stretch: f32,
    pub charge: f32,
    pub interest: f32,
    pub shake: f32,
    pub wet: bool,
    pub squid_pitch: f32,
    pub squid_roll: f32,
    pub tentacle: f32,
    pub flap: Flap,
}

/// `EntityChicken.flapProgress`, `flapSpeed` and `destPos`: the wing beat.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Flap {
    pub progress: f32,
    pub spread: f32,
    speed: f32,
}

impl Flap {
    pub fn advance(&mut self, on_ground: bool) {
        self.spread = (self.spread + if on_ground { -0.3 } else { 1.2 }).clamp(0.0, 1.0);
        if !on_ground && self.speed < 1.0 {
            self.speed = 1.0;
        }
        self.speed *= 0.9;
        self.progress += self.speed * 2.0;
    }
}

#[derive(Clone, Debug, Default)]
pub struct SquidMotion {
    pub pose: SpecialPose,
    cycle: f32,
    cycle_speed: f32,
    speed: f32,
    roll_velocity: f32,
    direction: [f32; 3],
}

impl SquidMotion {
    pub fn initialize(&mut self, random: &mut crate::JavaRandom) {
        self.cycle_speed = 1.0 / (random.next_float() + 1.0) * 0.2;
    }

    pub fn choose_motion(&mut self, random: &mut crate::JavaRandom, in_water: bool) {
        if random.next_int(50) == 0 || !in_water || self.direction == [0.0; 3] {
            let angle = random.next_float() * std::f32::consts::TAU;
            self.direction = [
                crate::math::cos(angle) * 0.2,
                -0.1 + random.next_float() * 0.2,
                crate::math::sin(angle) * 0.2,
            ];
        }
    }

    pub fn advance(
        &mut self,
        random: &mut crate::JavaRandom,
        motion: &mut glam::DVec3,
        body: &mut f32,
        in_water: bool,
        remote: bool,
    ) {
        use std::f32::consts::PI;
        self.cycle += self.cycle_speed;
        if self.cycle > PI * 2.0 {
            self.cycle -= PI * 2.0;
            if random.next_int(10) == 0 {
                self.initialize(random);
            }
        }
        if in_water {
            if self.cycle < PI {
                let ratio = self.cycle / PI;
                self.pose.tentacle = crate::math::sin(ratio * ratio * PI) * PI * 0.25;
                if ratio > 0.75 {
                    self.speed = 1.0;
                    self.roll_velocity = 1.0;
                } else {
                    self.roll_velocity *= 0.8;
                }
            } else {
                self.pose.tentacle = 0.0;
                self.speed *= 0.9;
                self.roll_velocity *= 0.99;
            }
            if !remote {
                *motion =
                    glam::DVec3::from_array(self.direction.map(|v| f64::from(v * self.speed)));
            }
            let horizontal = (motion.x * motion.x + motion.z * motion.z).sqrt() as f32;
            *body += (-(motion.x.atan2(motion.z) as f32) * 180.0 / PI - *body) * 0.1;
            self.pose.squid_roll += PI * self.roll_velocity * 1.5;
            self.pose.squid_pitch += (-(f64::from(horizontal).atan2(motion.y) as f32) * 180.0 / PI
                - self.pose.squid_pitch)
                * 0.1;
        } else {
            self.pose.tentacle = crate::math::sin(self.cycle).abs() * PI * 0.25;
            if !remote {
                *motion = glam::DVec3::new(0.0, (motion.y - 0.08) * f64::from(0.98_f32), 0.0);
            }
            self.pose.squid_pitch = (f64::from(self.pose.squid_pitch)
                + f64::from(-90.0 - self.pose.squid_pitch) * 0.02)
                as f32;
        }
    }
}