steel_core/behavior/block/
context.rs1use crate::block_entity::BlockEntity;
2
3use super::{
4 Arc, Axis, BlockLocalAabb, BlockPos, BlockStateId, DVec3, DamageSource, Entity, EntityTypeRef,
5 ItemStack, SharedBlockEntity, SmallVec, SoundEventRef, VoxelShape, World, vanilla_damage_types,
6 vanilla_entities,
7};
8use crate::entity::entities::FallingBlockEntity;
9
10pub struct PickupResult {
11 pub filled_bucket: ItemStack,
12 pub sound: Option<SoundEventRef>,
13}
14
15pub enum BlockEntityCreation {
21 Created(SharedBlockEntity),
23 NoEntity,
25 Unimplemented,
27}
28
29impl BlockEntityCreation {
30 #[must_use]
32 pub fn from_registered_factory(entity: Option<SharedBlockEntity>) -> Self {
33 entity.map_or(Self::Unimplemented, Self::Created)
34 }
35
36 #[must_use]
38 pub fn into_created(self) -> Option<SharedBlockEntity> {
39 match self {
40 Self::Created(entity) => Some(entity),
41 Self::NoEntity | Self::Unimplemented => None,
42 }
43 }
44}
45
46pub trait RailBehavior: Send + Sync {
52 fn is_straight(&self) -> bool;
54}
55
56pub trait Fallable: Send + Sync {
61 fn on_land(
63 &self,
64 _world: &Arc<World>,
65 _pos: BlockPos,
66 _state: BlockStateId,
67 _replaced_state: BlockStateId,
68 _entity: &FallingBlockEntity,
69 ) {
70 }
71
72 fn on_broken_after_fall(
74 &self,
75 _world: &Arc<World>,
76 _pos: BlockPos,
77 _entity: &FallingBlockEntity,
78 ) {
79 }
80
81 fn get_fall_damage_source(&self, entity: &FallingBlockEntity) -> DamageSource {
83 DamageSource::environment(&vanilla_damage_types::FALLING_BLOCK)
84 .with_direct_entity(entity.id())
85 .with_causing_entity(entity.id())
86 }
87
88 fn is_concrete_powder(&self) -> bool {
90 false
91 }
92}
93
94pub type BlockCollisionBoxes = SmallVec<[BlockLocalAabb; 4]>;
100
101pub struct BlockLootContext<'a> {
107 world: &'a Arc<World>,
108 pos: BlockPos,
109 entity: Option<&'a dyn Entity>,
110 block_entity: Option<&'a dyn BlockEntity>,
111 tool: Option<&'a ItemStack>,
112 luck: f32,
113}
114
115impl<'a> BlockLootContext<'a> {
116 #[must_use]
118 pub const fn new(world: &'a Arc<World>, pos: BlockPos) -> Self {
119 Self {
120 world,
121 pos,
122 entity: None,
123 block_entity: None,
124 tool: None,
125 luck: 0.0,
126 }
127 }
128
129 #[must_use]
131 pub const fn with_entity(mut self, entity: Option<&'a dyn Entity>) -> Self {
132 self.entity = entity;
133 self
134 }
135
136 #[must_use]
138 pub const fn with_block_entity(mut self, block_entity: Option<&'a dyn BlockEntity>) -> Self {
139 self.block_entity = block_entity;
140 self
141 }
142
143 #[must_use]
145 pub const fn with_tool(mut self, tool: &'a ItemStack) -> Self {
146 self.tool = Some(tool);
147 self
148 }
149
150 #[must_use]
152 pub const fn with_luck(mut self, luck: f32) -> Self {
153 self.luck = luck;
154 self
155 }
156
157 #[must_use]
159 pub const fn world(&self) -> &'a Arc<World> {
160 self.world
161 }
162
163 #[must_use]
165 pub const fn pos(&self) -> BlockPos {
166 self.pos
167 }
168
169 #[must_use]
171 pub fn get_drops(&self, state: BlockStateId) -> Vec<ItemStack> {
172 World::block_drops(state, self)
173 }
174
175 pub(crate) const fn entity(&self) -> Option<&'a dyn Entity> {
176 self.entity
177 }
178
179 pub(crate) const fn block_entity(&self) -> Option<&'a dyn BlockEntity> {
180 self.block_entity
181 }
182
183 pub(crate) const fn tool(&self) -> Option<&'a ItemStack> {
184 self.tool
185 }
186
187 pub(crate) const fn luck(&self) -> f32 {
188 self.luck
189 }
190}
191
192const COLLISION_CONTEXT_ABOVE_EPSILON: f64 = 1.0e-5;
193
194#[derive(Debug, Clone, Copy, PartialEq)]
196pub struct BlockCollisionContext {
197 entity_bottom: Option<f64>,
198 fall_distance: f64,
199 can_walk_on_powder_snow: bool,
200 is_falling_block: bool,
201 descending: bool,
202 placement: bool,
203}
204
205impl BlockCollisionContext {
206 #[must_use]
208 pub const fn empty() -> Self {
209 Self {
210 entity_bottom: None,
211 fall_distance: 0.0,
212 can_walk_on_powder_snow: false,
213 is_falling_block: false,
214 descending: false,
215 placement: false,
216 }
217 }
218
219 #[must_use]
221 pub const fn entity(entity_bottom: f64, descending: bool) -> Self {
222 Self {
223 entity_bottom: Some(entity_bottom),
224 fall_distance: 0.0,
225 can_walk_on_powder_snow: false,
226 is_falling_block: false,
227 descending,
228 placement: false,
229 }
230 }
231
232 #[must_use]
237 pub const fn with_position(entity_bottom: f64, descending: bool) -> Self {
238 Self {
239 entity_bottom: Some(entity_bottom),
240 fall_distance: 0.0,
241 can_walk_on_powder_snow: false,
242 is_falling_block: false,
243 descending,
244 placement: true,
245 }
246 }
247
248 #[must_use]
252 pub const fn placement_without_entity() -> Self {
253 Self::with_position(f64::MIN, false)
254 }
255
256 #[must_use]
258 pub const fn position_context(y: f64) -> Self {
259 Self {
260 entity_bottom: Some(y),
261 fall_distance: 0.0,
262 can_walk_on_powder_snow: false,
263 is_falling_block: false,
264 descending: false,
265 placement: false,
266 }
267 }
268
269 #[must_use]
271 pub const fn with_fall_distance(mut self, fall_distance: f64) -> Self {
272 self.fall_distance = fall_distance;
273 self
274 }
275
276 #[must_use]
278 pub const fn with_can_walk_on_powder_snow(mut self, can_walk_on_powder_snow: bool) -> Self {
279 self.can_walk_on_powder_snow = can_walk_on_powder_snow;
280 self
281 }
282
283 #[must_use]
285 pub const fn with_falling_block(mut self, is_falling_block: bool) -> Self {
286 self.is_falling_block = is_falling_block;
287 self
288 }
289
290 #[must_use]
292 pub const fn fall_distance(self) -> f64 {
293 self.fall_distance
294 }
295
296 #[must_use]
298 pub const fn can_walk_on_powder_snow(self) -> bool {
299 self.can_walk_on_powder_snow
300 }
301
302 #[must_use]
304 pub const fn is_falling_block(self) -> bool {
305 self.is_falling_block
306 }
307
308 #[must_use]
310 pub const fn is_descending(self) -> bool {
311 self.descending
312 }
313
314 #[must_use]
316 pub const fn is_placement(self) -> bool {
317 self.placement
318 }
319
320 #[must_use]
322 pub fn is_above(self, shape: VoxelShape, pos: BlockPos, default_value: bool) -> bool {
323 let Some(entity_bottom) = self.entity_bottom else {
324 return default_value;
325 };
326
327 entity_bottom > f64::from(pos.y()) + shape.max(Axis::Y) - COLLISION_CONTEXT_ABOVE_EPSILON
328 }
329}
330
331#[derive(Debug, Clone, Copy, PartialEq)]
333pub struct EntityLandingContext {
334 pub velocity: DVec3,
336 pub is_living_entity: bool,
338 pub suppresses_bounce: bool,
340}
341
342#[derive(Debug, Clone, Copy, PartialEq)]
344pub struct EntityFallOnFacts {
345 pub entity_type: EntityTypeRef,
347 pub is_living_entity: bool,
349 pub bounding_box_width: f64,
351 pub bounding_box_height: f64,
353 pub fall_sounds: (SoundEventRef, SoundEventRef),
355}
356
357impl EntityFallOnFacts {
358 #[must_use]
360 pub const fn new(
361 entity_type: EntityTypeRef,
362 is_living_entity: bool,
363 bounding_box_width: f64,
364 bounding_box_height: f64,
365 fall_sounds: (SoundEventRef, SoundEventRef),
366 ) -> Self {
367 Self {
368 entity_type,
369 is_living_entity,
370 bounding_box_width,
371 bounding_box_height,
372 fall_sounds,
373 }
374 }
375
376 #[must_use]
378 pub fn from_entity(entity: &dyn Entity) -> Self {
379 let bounding_box = entity.bounding_box();
380 Self::new(
381 entity.entity_type(),
382 entity.is_living_entity(),
383 bounding_box.width(),
384 bounding_box.height(),
385 entity.fall_sounds(),
386 )
387 }
388
389 #[must_use]
391 pub fn is_player(self) -> bool {
392 self.entity_type == &vanilla_entities::PLAYER
393 }
394
395 #[must_use]
398 pub fn bounding_box_width_squared_height(self) -> f64 {
399 self.bounding_box_width * self.bounding_box_width * self.bounding_box_height
400 }
401}
402
403#[derive(Clone, Copy)]
405pub struct EntityFallOnContext<'a> {
406 pub fall_distance: f64,
408 pub suppresses_bounce: bool,
410 pub entity: EntityFallOnFacts,
412 pub source_entity: Option<&'a dyn Entity>,
414}
415
416impl<'a> EntityFallOnContext<'a> {
417 #[must_use]
419 pub const fn new(
420 fall_distance: f64,
421 suppresses_bounce: bool,
422 entity: EntityFallOnFacts,
423 source_entity: Option<&'a dyn Entity>,
424 ) -> Self {
425 Self {
426 fall_distance,
427 suppresses_bounce,
428 entity,
429 source_entity,
430 }
431 }
432
433 #[must_use]
435 pub fn from_entity(fall_distance: f64, entity: &'a dyn Entity) -> Self {
436 Self::new(
437 fall_distance,
438 entity.is_suppressing_bounce(),
439 EntityFallOnFacts::from_entity(entity),
440 Some(entity),
441 )
442 }
443
444 #[must_use]
446 pub const fn with_fall_distance(mut self, fall_distance: f64) -> Self {
447 self.fall_distance = fall_distance;
448 self
449 }
450
451 #[must_use]
453 pub const fn source_entity(self) -> Option<&'a dyn Entity> {
454 self.source_entity
455 }
456}
457
458#[derive(Debug, Clone)]
460pub struct EntityFallDamage {
461 pub fall_distance: f64,
463 pub damage_modifier: f32,
465 pub source: DamageSource,
467}
468
469impl EntityFallDamage {
470 #[must_use]
472 pub const fn new(fall_distance: f64, damage_modifier: f32, source: DamageSource) -> Self {
473 Self {
474 fall_distance,
475 damage_modifier,
476 source,
477 }
478 }
479}
480
481impl EntityLandingContext {
482 #[must_use]
484 pub const fn new(velocity: DVec3, is_living_entity: bool, suppresses_bounce: bool) -> Self {
485 Self {
486 velocity,
487 is_living_entity,
488 suppresses_bounce,
489 }
490 }
491
492 #[must_use]
494 pub const fn default_velocity_after_fall_on(self) -> DVec3 {
495 DVec3::new(self.velocity.x, 0.0, self.velocity.z)
496 }
497}