steel_core/entity/entities/objects/explosives/
end_crystal.rs1use std::sync::Arc;
4use std::sync::Weak;
5
6use glam::DVec3;
7use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView;
8use simdnbt::owned::{NbtCompound, NbtTag};
9use steel_macros::entity_behavior;
10use steel_registry::entity_type::EntityTypeRef;
11use steel_registry::vanilla_entity_data::EndCrystalEntityData;
12use steel_utils::nbt::NbtValueInput as _;
13use steel_utils::{BlockPos, locks::SyncMutex};
14use steel_utils::{DowncastType, DowncastTypeKey};
15
16use crate::entity::{Entity, EntityBase, EntityBaseLoad, EntityMovementEmission, EntitySyncedData};
17use crate::world::World;
18
19#[entity_behavior(class = "EndCrystal")]
25pub struct EndCrystalEntity {
26 base: EntityBase,
27 entity_type: EntityTypeRef,
28 entity_data: SyncMutex<EndCrystalEntityData>,
29}
30
31unsafe impl DowncastType for EndCrystalEntity {
33 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:entity/end_crystal");
34}
35
36impl EndCrystalEntity {
37 #[must_use]
39 pub fn new(entity_type: EntityTypeRef, id: i32, position: DVec3, world: Weak<World>) -> Self {
40 Self {
41 base: EntityBase::new(id, position, entity_type.dimensions, world),
42 entity_type,
43 entity_data: SyncMutex::new(EndCrystalEntityData::new()),
44 }
45 }
46
47 #[must_use]
49 pub fn from_saved(entity_type: EntityTypeRef, load: EntityBaseLoad) -> Self {
50 Self {
51 base: EntityBase::from_load(load, entity_type.dimensions),
52 entity_type,
53 entity_data: SyncMutex::new(EndCrystalEntityData::new()),
54 }
55 }
56
57 pub fn set_beam_target(&self, target: Option<BlockPos>) {
59 self.entity_data.lock().beam_target.set(target);
60 }
61
62 #[must_use]
64 pub fn beam_target(&self) -> Option<BlockPos> {
65 *self.entity_data.lock().beam_target.get()
66 }
67
68 pub fn set_show_bottom(&self, show_bottom: bool) {
70 self.entity_data.lock().show_bottom.set(show_bottom);
71 }
72
73 #[must_use]
75 pub fn shows_bottom(&self) -> bool {
76 *self.entity_data.lock().show_bottom.get()
77 }
78
79 const fn nbt_bool(value: bool) -> i8 {
80 if value { 1 } else { 0 }
81 }
82}
83
84impl Entity for EndCrystalEntity {
85 fn base(&self) -> &EntityBase {
86 &self.base
87 }
88
89 fn entity_type(&self) -> EntityTypeRef {
90 self.entity_type
91 }
92
93 fn movement_emission(&self) -> EntityMovementEmission {
94 EntityMovementEmission::None
95 }
96
97 fn tick(self: Arc<Self>) {
98 }
100
101 fn is_pickable(&self) -> bool {
102 true
103 }
104
105 fn blocks_building(&self) -> bool {
106 true
107 }
108
109 fn synced_data(&self) -> Option<&dyn EntitySyncedData> {
110 Some(&self.entity_data)
111 }
112
113 fn save_additional(&self, nbt: &mut NbtCompound) {
114 if let Some(target) = self.beam_target() {
115 nbt.insert(
116 "beam_target",
117 NbtTag::IntArray(vec![target.x(), target.y(), target.z()]),
118 );
119 }
120
121 nbt.insert("ShowBottom", Self::nbt_bool(self.shows_bottom()));
122 }
123
124 fn load_additional(&self, nbt: BorrowedNbtCompoundView<'_, '_>) {
125 if let Some(target) = nbt.int_array("beam_target")
126 && target.len() == 3
127 {
128 self.set_beam_target(Some(BlockPos::new(target[0], target[1], target[2])));
129 }
130
131 if let Some(show_bottom) = nbt.byte_value("ShowBottom") {
132 self.set_show_bottom(show_bottom != 0);
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 use steel_registry::vanilla_entities;
142
143 #[test]
144 fn end_crystal_does_not_duplicate_shared_invulnerable_state() {
145 let crystal = EndCrystalEntity::new(
146 &vanilla_entities::END_CRYSTAL,
147 1,
148 DVec3::new(1.5, 2.5, 3.5),
149 Weak::new(),
150 );
151 crystal.set_invulnerable(true);
152
153 let mut nbt = NbtCompound::new();
154 crystal.save_additional(&mut nbt);
155
156 assert_eq!(nbt.byte("Invulnerable"), None);
157 }
158
159 #[test]
160 fn end_crystal_is_pickable_like_vanilla() {
161 let crystal = EndCrystalEntity::new(
162 &vanilla_entities::END_CRYSTAL,
163 1,
164 DVec3::new(1.5, 2.5, 3.5),
165 Weak::new(),
166 );
167
168 assert!(crystal.is_pickable());
169 }
170
171 #[test]
172 fn end_crystal_blocks_building_like_vanilla() {
173 let crystal =
174 EndCrystalEntity::new(&vanilla_entities::END_CRYSTAL, 1, DVec3::ZERO, Weak::new());
175
176 assert!(crystal.blocks_building());
177 }
178}