aboutsummaryrefslogtreecommitdiff
path: root/azalea-physics/src/collision/shape_offset.rs
blob: 2832d68095df6cba20dfd5a89b470cef936ffc36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use std::borrow::Cow;

use azalea_block::BlockState;
use azalea_core::{
    math::get_seed,
    position::{BlockPos, Vec3},
};
use azalea_registry::builtin::BlockKind;

use crate::collision::{VoxelShape, blocks::RANDOM_SHAPE_OFFSETS_MAP};

/// Adds the random offset for the shape, given the block state and position.
///
/// For most blocks, this won't have any effect. It's only used for a few blocks
/// like flowers and bamboo.
pub fn apply_shape_offset(
    block: BlockState,
    pos: BlockPos,
    shape: &'static VoxelShape,
) -> Cow<'static, VoxelShape> {
    // don't waste time checking the block if it's already known to be empty. also,
    // it's faster to compare addresses than to call `.is_empty`.
    if std::ptr::eq(shape, &*super::EMPTY_SHAPE) {
        return Cow::Borrowed(shape);
    }

    let offset_kind = RANDOM_SHAPE_OFFSETS_MAP[block.id() as usize];
    if offset_kind == 0 {
        return Cow::Borrowed(shape);
    }

    let kind = block.as_block_kind();

    let mut max_horizontal_offset = 0.25;
    // search `getMaxHorizontalOffset` in the vanilla code
    // TODO: sulfur spike gets added here in 26.2
    if kind == BlockKind::PointedDripstone {
        max_horizontal_offset = 2. / 16.;
    }

    // these ids are required to be the same as the ones in shapes.py
    let delta = match offset_kind {
        // see offsetType in BlockBehaviour.java
        1 => {
            // xz
            xyz_offset_for_pos(pos.with_y(0), max_horizontal_offset, 0.)
        }
        2 => {
            // xyz

            let mut max_vertical_offset = 0.2;
            if kind == BlockKind::SmallDripleaf {
                // search `getMaxVerticalOffset` in the vanilla code
                max_vertical_offset = 0.1;
            }

            xyz_offset_for_pos(pos, max_horizontal_offset, max_vertical_offset)
        }
        _ => unreachable!(),
    };

    Cow::Owned(shape.move_relative(delta))
}

fn xyz_offset_for_pos(pos: BlockPos, max_horizontal_offset: f64, max_vertical_offset: f64) -> Vec3 {
    let seed = get_seed(pos);
    let y = if max_vertical_offset == 0. {
        0.
    } else {
        ((((seed >> 4) & 15) as f32 / 15.) as f64 - 1.) * max_vertical_offset
    };

    let x = (((seed & 15) as f32 / 15.) as f64 - 0.5) * 0.5;
    let x = x.clamp(-max_horizontal_offset, max_horizontal_offset);
    let z = ((((seed >> 8) & 15) as f32 / 15.) as f64 - 0.5) * 0.5;
    let z = z.clamp(-max_horizontal_offset, max_horizontal_offset);

    Vec3 { x, y, z }
}