Skip to main content

steel_core/behavior/blocks/container/
copper_chest_block.rs

1//! Copper chest behavior
2// TODO: Create the chest block entity with inventory loot table lock and custom name support
3// TODO: Combine connected halves into one container/ menu and check blocks and sitting cats above them
4// TODO: Open the chest menu award OPEN_CHEST and anger nearby piglins on interaction
5// TODO: Track viewers recheck them on scheduled ticks and synchronize opening through block events
6// TODO: Use the extracted open or close sounds for each copper chest variant
7// TODO: Expose container fullness to comparators and update neighbors after removal
8// TODO: Reject pathfinding through copper chests for every path computation type
9// TODO: Support facing rotation and mirroring for structure transforms
10// TODO: Provide copperblock conversion with connected half and oxidation normalization
11
12use steel_macros::block_behavior;
13use steel_registry::blocks::BlockRef;
14use steel_registry::blocks::block_state_ext::BlockStateExt as _;
15use steel_registry::blocks::properties::{BlockStateProperties, ChestType};
16use steel_registry::vanilla_block_tags::BlockTag;
17use steel_utils::{BlockPos, BlockStateId, Direction};
18
19use crate::behavior::BlockPlaceContext;
20use crate::behavior::block::{BlockBehavior, schedule_water_tick_if_waterlogged};
21use crate::world::ScheduledTickAccess;
22
23/// Copper chest behaviour
24#[block_behavior]
25pub struct CopperChestBlock {
26    block: BlockRef,
27}
28
29impl CopperChestBlock {
30    /// creates a new chest behaviour
31    #[must_use]
32    pub const fn new(block: BlockRef) -> Self {
33        Self { block }
34    }
35}
36
37impl BlockBehavior for CopperChestBlock {
38    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
39        // TODO: Resolve facing waterlogging and doublechest placement (including secondary use)
40        // TODO: Normalize connected halves to the least oxidized state and reconcile waxing
41        Some(self.block.default_state())
42    }
43
44    fn update_shape(
45        &self,
46        state: BlockStateId,
47        world: &dyn ScheduledTickAccess,
48        pos: BlockPos,
49        direction: Direction,
50        _neighbor_pos: BlockPos,
51        neighbor_state: BlockStateId,
52    ) -> BlockStateId {
53        schedule_water_tick_if_waterlogged(state, world, pos);
54        update_shape(state, direction, neighbor_state)
55    }
56}
57
58/// Copper chest behaviour
59#[block_behavior]
60pub struct WeatheringCopperChestBlock {
61    block: BlockRef,
62}
63
64impl WeatheringCopperChestBlock {
65    /// creates a new chest behaviour
66    #[must_use]
67    pub const fn new(block: BlockRef) -> Self {
68        Self { block }
69    }
70}
71
72impl BlockBehavior for WeatheringCopperChestBlock {
73    // TODO: Implement weathering random ticks skipping right halves and chests with active viewers
74    // TODO: Expose the weathering age to neighboring copper blocks through the shared capability
75    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
76        // TODO: Resolve facing waterlogging and doublechest placement (including secondary use)
77        // TODO: Normalize connected halves to the least oxidized state and reconcile waxing
78        Some(self.block.default_state())
79    }
80
81    fn update_shape(
82        &self,
83        state: BlockStateId,
84        world: &dyn ScheduledTickAccess,
85        pos: BlockPos,
86        direction: Direction,
87        _neighbor_pos: BlockPos,
88        neighbor_state: BlockStateId,
89    ) -> BlockStateId {
90        schedule_water_tick_if_waterlogged(state, world, pos);
91        update_shape(state, direction, neighbor_state)
92    }
93}
94
95fn update_shape(
96    mut state: BlockStateId,
97    direction: Direction,
98    neighbor_state: BlockStateId,
99) -> BlockStateId {
100    let neighbor_type = neighbor_state
101        .get_block()
102        .has_tag(&BlockTag::COPPER_CHESTS)
103        .then(|| neighbor_state.try_get_value(&BlockStateProperties::CHEST_TYPE))
104        .flatten();
105    if let Some(neighbor_type) = &neighbor_type
106        && direction.is_horizontal()
107    {
108        if state.get_value(&BlockStateProperties::CHEST_TYPE) == ChestType::Single
109            && *neighbor_type != ChestType::Single
110            && state.get_value(&BlockStateProperties::FACING)
111                == neighbor_state.get_value(&BlockStateProperties::FACING)
112            && connected_direction(neighbor_state) == direction.opposite()
113        {
114            let opposite_type = match neighbor_type {
115                ChestType::Left => ChestType::Right,
116                ChestType::Right => ChestType::Left,
117                ChestType::Single => ChestType::Single,
118            };
119            state = state.set_value(&BlockStateProperties::CHEST_TYPE, opposite_type);
120        }
121    } else if connected_direction(state) == direction {
122        state = state.set_value(&BlockStateProperties::CHEST_TYPE, ChestType::Single);
123    }
124
125    if neighbor_type.is_some()
126        && state.get_value(&BlockStateProperties::CHEST_TYPE) != ChestType::Single
127        && connected_direction(state) == direction
128    {
129        return neighbor_state.with_properties_of(state);
130    }
131    state
132}
133
134fn connected_direction(state: BlockStateId) -> Direction {
135    let facing = state.get_value(&BlockStateProperties::FACING);
136    if state.get_value(&BlockStateProperties::CHEST_TYPE) == ChestType::Left {
137        facing.rotate_y_clockwise()
138    } else {
139        facing.rotate_y_counter_clockwise()
140    }
141}