alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH 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
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;
}
}
}