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

use common::entity::{Entity, EntityWorld};
use common::item::{Item, ItemStack};
use common::math::floor_f64;
use common::world::shape::BlockView;
use glam::DVec3;

use super::projectiles::{heading, smooth_angles};
use super::{Kind, Stuck, velocity_packet};
use crate::level::{DroppedItem, Level};

#[derive(Clone, Debug)]
pub struct Hook {
    pub owner: i32,
    pub caught: Option<i32>,
    pub catchable: i32,
    pub stuck: Stuck,
}

impl Level {
    pub fn fish(&mut self, owner: &Entity) -> i32 {
        let existing = self
            .entities
            .iter()
            .find(|e| matches!(&e.kind, Kind::Hook(h) if h.owner == owner.id))
            .map(|e| e.entity.id);
        if let Some(id) = existing {
            let Some(mut stored) = self.entities.remove(id) else {
                return 0;
            };
            let Kind::Hook(hook) = &stored.kind else {
                return 0;
            };
            let delta = owner.pos - stored.entity.pos;
            let lift = f64::from((f64::from(delta.length() as f32)).sqrt() as f32) * 0.08;
            let motion = delta * 0.1 + DVec3::Y * lift;
            let mut wear = 0;
            if let Some(caught) = hook.caught.and_then(|id| self.entities.get_mut(id)) {
                caught.entity.motion += motion;
                self.events.push(velocity_packet(&caught.entity));
                wear = 3;
            } else if hook.catchable > 0 {
                self.add_stat(owner.id, 2025, 1);
                self.dropped_items.push(DroppedItem {
                    x: stored.entity.pos.x,
                    y: stored.entity.pos.y,
                    z: stored.entity.pos.z,
                    stack: ItemStack::one(Item::RAW_FISH),
                    motion: Some(motion),
                    impulse: glam::DVec3::ZERO,
                    pickup_delay: 0,
                });
                wear = 1;
            }
            if hook.stuck.grounded {
                wear = 2;
            }
            stored.entity.dead = true;
            wear
        } else {
            let mut stored = self.entities.create(Kind::Hook(Hook {
                owner: owner.id,
                caught: None,
                catchable: 0,
                stuck: Stuck::default(),
            }));
            let entity = &mut stored.entity;
            let yaw = owner.yaw.to_radians();
            let at = owner.pos
                + DVec3::new(
                    -f64::from(common::math::cos(yaw) * 0.16),
                    1.62 - f64::from(owner.y_offset) - f64::from(0.1_f32),
                    -f64::from(common::math::sin(yaw) * 0.16),
                );
            entity.set_location_and_angles(at, owner.yaw, owner.pitch);
            heading(entity, owner.look() * f64::from(0.4_f32), 1.5, 1.0);
            let pitch = 0.4 / (self.rand.next_float() * 0.4 + 0.8);
            self.play_sound(owner, "random.bow", 0.5, pitch);
            self.entities.insert(stored);
            0
        }
    }

    pub(super) fn tick_hook(&mut self, entity: &mut Entity, hook: &mut Hook) {
        entity.base_update(self);
        let fishing = self.entities.get(hook.owner).is_some_and(|e| {
            !e.entity.dead
                && e.entity.living.as_ref().is_some_and(|l| l.health > 0)
                && matches!(e.kind, Kind::Player { held: Some(stack), .. } if stack.item == Item::FISHING_ROD)
                && entity.pos.distance_squared(e.entity.pos) <= 1024.0
        });
        if !fishing {
            entity.dead = true;
            return;
        }
        if let Some(caught) = hook
            .caught
            .and_then(|id| self.entities.get(id))
            .filter(|e| !e.entity.dead)
        {
            entity.set_position(
                caught
                    .entity
                    .pos
                    .with_y(caught.entity.bb.min.y + f64::from(caught.entity.height) * 0.8),
            );
            return;
        }
        hook.caught = None;
        let stuck = &mut hook.stuck;
        if self.stays_stuck(entity, stuck) {
            return;
        }
        let (hit, target) = self.projectile_hit(entity, false, Some(hook.owner), stuck.air_ticks);
        if let Some(target) = target {
            let owner = self.entity_copy(Some(hook.owner));
            if self.attack_entity(target, owner.as_ref(), 0) {
                hook.caught = Some(target);
            }
        } else if let Some(hit) = hit {
            stuck.grounded = true;
            stuck.tile = [hit.x, hit.y, hit.z];
            stuck.block = BlockView::block(self, hit.x, hit.y, hit.z);
            return;
        }
        entity.move_entity(self, entity.motion);
        smooth_angles(entity);
        let mut drag = if entity.on_ground || entity.collided_horizontally {
            0.5_f32
        } else {
            0.92_f32
        };
        let water = self.submerged(entity, 0.0);
        if water > 0.0 {
            if hook.catchable > 0 {
                hook.catchable -= 1;
            } else {
                let [x, y, z] = entity.pos.to_array().map(floor_f64);
                if entity.rand.next_int(if self.rained_on(x, y + 1, z) {
                    300
                } else {
                    500
                }) == 0
                {
                    hook.catchable = entity.rand.next_int(30) + 10;
                    entity.motion.y -= f64::from(0.2_f32);
                    let pitch = 1.0 + (entity.rand.next_float() - entity.rand.next_float()) * 0.4;
                    self.play_sound(entity, "random.splash", 0.25, pitch);
                    for bubble in [true, false] {
                        for _ in 0..6 {
                            let x = (entity.rand.next_float() * 2.0 - 1.0) * entity.width;
                            let z = (entity.rand.next_float() * 2.0 - 1.0) * entity.width;
                            let position = entity
                                .pos
                                .with_y(f64::from(floor_f64(entity.bb.min.y) as f32 + 1.0))
                                + DVec3::new(f64::from(x), 0.0, f64::from(z));
                            let motion = if bubble {
                                entity.motion - DVec3::Y * f64::from(entity.rand.next_float() * 0.2)
                            } else {
                                entity.motion
                            };
                            self.spawn_particle(
                                if bubble { "bubble" } else { "splash" },
                                position,
                                motion,
                            );
                        }
                    }
                }
            }
        }
        if hook.catchable > 0 {
            entity.motion.y -= f64::from(
                entity.rand.next_float() * entity.rand.next_float() * entity.rand.next_float(),
            ) * 0.2;
        }
        entity.motion.y += f64::from(0.04_f32) * (water * 2.0 - 1.0);
        if water > 0.0 {
            drag = (f64::from(drag) * 0.9) as f32;
            entity.motion.y *= 0.8;
        }
        entity.motion *= f64::from(drag);
    }
}