Skip to main content

steel_core/behavior/blocks/building/
slime_block.rs

1use std::sync::Arc;
2
3use glam::DVec3;
4use steel_macros::block_behavior;
5use steel_registry::{blocks::BlockRef, vanilla_damage_types};
6use steel_utils::{BlockPos, BlockStateId};
7
8use crate::{
9    behavior::{BlockBehavior, BlockPlaceContext, EntityFallDamage, EntityFallOnContext},
10    entity::{Entity, damage::DamageSource},
11    world::World,
12};
13
14/// Behavior for slime blocks.
15#[block_behavior]
16pub struct SlimeBlock {
17    block: BlockRef,
18}
19
20impl SlimeBlock {
21    /// Creates a slime block behavior.
22    #[must_use]
23    pub const fn new(block: BlockRef) -> Self {
24        Self { block }
25    }
26
27    #[must_use]
28    fn velocity_after_step_on(velocity: DVec3, is_stepping_carefully: bool) -> DVec3 {
29        let abs_delta_y = velocity.y.abs();
30        if abs_delta_y >= 0.1 || is_stepping_carefully {
31            return velocity;
32        }
33
34        let scale = 0.4 + abs_delta_y * 0.2;
35        DVec3::new(velocity.x * scale, velocity.y, velocity.z * scale)
36    }
37}
38
39impl BlockBehavior for SlimeBlock {
40    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
41        Some(self.block.default_state())
42    }
43
44    fn fall_on(
45        &self,
46        _state: BlockStateId,
47        _world: &Arc<World>,
48        _pos: BlockPos,
49        context: EntityFallOnContext<'_>,
50    ) -> Option<EntityFallDamage> {
51        if context.suppresses_bounce {
52            None
53        } else {
54            Some(EntityFallDamage::new(
55                context.fall_distance,
56                0.0,
57                DamageSource::environment(&vanilla_damage_types::FALL),
58            ))
59        }
60    }
61
62    fn step_on(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos, entity: &dyn Entity) {
63        entity.set_velocity(Self::velocity_after_step_on(
64            entity.velocity(),
65            entity.is_stepping_carefully(),
66        ));
67
68        self.default_step_on(state, world, pos, entity);
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn step_on_damps_horizontal_velocity_for_non_careful_entities() {
78        let velocity = SlimeBlock::velocity_after_step_on(DVec3::new(1.0, 0.05, -2.0), false);
79
80        assert!((velocity - DVec3::new(0.41, 0.05, -0.82)).length() < 1.0e-12);
81    }
82
83    #[test]
84    fn step_on_keeps_velocity_for_careful_entities() {
85        let velocity = DVec3::new(1.0, 0.05, -2.0);
86
87        assert_eq!(SlimeBlock::velocity_after_step_on(velocity, true), velocity);
88    }
89
90    #[test]
91    fn step_on_keeps_horizontal_velocity_when_vertical_speed_is_large() {
92        let velocity = DVec3::new(1.0, -0.1, -2.0);
93
94        assert_eq!(
95            SlimeBlock::velocity_after_step_on(velocity, false),
96            velocity
97        );
98    }
99}