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 / player.rs

//! The local player's tick: `EntityPlayerSP.onLivingUpdate` with `EntityPlayer.onLivingUpdate`.

use glam::DVec3;

use super::{Entity, EntityWorld, Sleep, flags};
use crate::world::shape::BlockView;
use crate::world::{Block, bed};

/// The keys of `MovementInputFromOptions`.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MovementKeys {
    pub forward: bool,
    pub back: bool,
    pub left: bool,
    pub right: bool,
    pub jump: bool,
    pub sneak: bool,
}

/// Player state beyond `EntityLiving`.
#[derive(Clone, Debug, Default)]
pub struct PlayerState {
    pub camera_yaw: f32,
    pub prev_camera_yaw: f32,
    swing_ticks: i32,
    pub swinging: bool,
}

impl PlayerState {
    /// `swingItem`.
    pub fn swing(&mut self) {
        self.swing_ticks = -1;
        self.swinging = true;
    }

    pub fn tick_swing(&mut self) -> f32 {
        if self.swinging {
            self.swing_ticks += 1;
            if self.swing_ticks >= 8 {
                self.swing_ticks = 0;
                self.swinging = false;
            }
        } else {
            self.swing_ticks = 0;
        }
        self.swing_ticks as f32 / 8.0
    }
}

/// `Entity.onUpdate` of the local player: the living update, then movement from `keys`.
pub fn tick(
    e: &mut Entity,
    state: &mut PlayerState,
    keys: MovementKeys,
    world: &mut impl EntityWorld,
) {
    e.sleep.tick();
    e.living_entity_update(world);
    let axis = |positive: bool, negative: bool| f32::from(i8::from(positive) - i8::from(negative));
    let mut strafe = axis(keys.left, keys.right);
    let mut forward = axis(keys.forward, keys.back);
    if keys.sneak {
        strafe = (f64::from(strafe) * 0.3) as f32;
        forward = (f64::from(forward) * 0.3) as f32;
    }
    let blocked = e.sleep.sleeping;
    e.set_flag(flags::SNEAKING, keys.sneak && !blocked);
    if keys.sneak && e.y_size < 0.2 {
        e.y_size = 0.2;
    }
    let half = f64::from(e.width) * 0.35;
    let y = e.bb.min.y + 0.5;
    let (x, z) = (e.pos.x, e.pos.z);
    for (dx, dz) in [(-half, half), (-half, -half), (half, -half), (half, half)] {
        e.push_out_of_blocks(world, x + dx, y, z + dz);
    }

    state.prev_camera_yaw = state.camera_yaw;
    let swing = state.tick_swing();
    if let Some(living) = &mut e.living {
        living.swing_progress = swing;
        if living.health > 0 && !blocked {
            living.move_strafing = strafe;
            living.move_forward = forward;
            living.jumping = keys.jump;
        }
    }
    e.living_movement(world);

    let mut bob = (e.motion.x * e.motion.x + e.motion.z * e.motion.z).sqrt() as f32;
    let mut tilt = ((-e.motion.y * f64::from(0.2_f32)).atan() as f32) * 15.0;
    let dead = e.living.as_ref().is_some_and(|l| l.health <= 0);
    bob = bob.min(0.1);
    if !e.on_ground || dead {
        bob = 0.0;
    }
    if e.on_ground || dead {
        tilt = 0.0;
    }
    state.camera_yaw += (bob - state.camera_yaw) * 0.4;
    if let Some(living) = &mut e.living {
        living.camera_pitch += (tilt - living.camera_pitch) * 0.8;
    }
    e.update_body_rotation();
}

/// `EntityPlayer.sleepInBedAt` once its checks passed: lies down in the bed at `bed`.
pub fn lie_down(entity: &mut Entity, world: &impl EntityWorld, bed: [i32; 3]) {
    let [x, y, z] = bed;
    entity.set_size(0.2, 0.2);
    entity.y_offset = 0.2;
    let (dx, dz) = if world.chunks_exist(bed, bed) {
        match bed::direction(world.metadata(x, y, z)) {
            0 => (0.5, 0.9),
            1 => (0.1, 0.5),
            2 => (0.5, 0.1),
            _ => (0.9, 0.5),
        }
    } else {
        (0.5, 0.5)
    };
    entity.set_position(DVec3::new(
        f64::from(x as f32 + dx),
        f64::from(y as f32 + 15.0 / 16.0),
        f64::from(z as f32 + dz),
    ));
    entity.sleep = Sleep {
        sleeping: true,
        bed: Some(bed),
        ..Sleep::default()
    };
    entity.motion = DVec3::ZERO;
}

/// `EntityPlayer.wakeUpPlayer` without its world side effects: stands up beside the bed,
/// at `y_offset` above the feet.
pub fn get_up(entity: &mut Entity, world: &impl BlockView, y_offset: f32, reset_timer: bool) {
    entity.set_size(0.6, 1.8);
    entity.y_offset = y_offset;
    if let Some(bed @ [x, y, z]) = entity.sleep.bed
        && world.block(x, y, z) == Block::BED
    {
        let [sx, sy, sz] = bed::standing_spot(world, bed, 0).unwrap_or([x, y + 1, z]);
        entity.set_position(DVec3::new(
            f64::from(sx as f32 + 0.5),
            f64::from(sy as f32 + y_offset + 0.1),
            f64::from(sz as f32 + 0.5),
        ));
    }
    entity.sleep.sleeping = false;
    entity.sleep.timer = if reset_timer { 0 } else { 100 };
}