Skip to main content

steel_registry/blocks/
properties.rs

1#![expect(
2    clippy::unwrap_used,
3    reason = "property value lookup only unwraps values known to be declared by the property"
4)]
5
6use std::cmp::Ordering;
7use std::fmt::Debug;
8
9pub use steel_utils::{Direction, axis::Axis, codec::VarInt, serial::ReadFrom};
10
11pub trait Property: Debug + Sync + Send {
12    type Value
13    where
14        Self: Sized;
15
16    fn get_value(&self, value: &str) -> Option<Self::Value>
17    where
18        Self: Sized;
19
20    fn get_possible_values(&self) -> Box<[Self::Value]>
21    where
22        Self: Sized;
23
24    fn get_internal_index(&self, value: &Self::Value) -> usize
25    where
26        Self: Sized;
27
28    fn value_from_index(&self, index: usize) -> Self::Value
29    where
30        Self: Sized;
31
32    fn value_count(&self) -> usize;
33    fn value_name_from_index(&self, index: usize) -> &str;
34    fn get_possible_value_names(&self) -> Box<[&str]>;
35    fn get_name(&self) -> &'static str;
36
37    /// Parses and compares two serialized values using Vanilla's `Comparable` order.
38    fn compare_value_names(&self, left: &str, right: &str) -> Option<Ordering>;
39}
40
41pub trait PropertyEnum: PartialEq + Clone + Debug + Sync + Send {
42    fn as_str(&self) -> &str;
43}
44
45#[derive(Debug, Clone)]
46pub struct BoolProperty {
47    pub name: &'static str,
48}
49impl BoolProperty {
50    #[must_use]
51    pub const fn new(name: &'static str) -> Self {
52        Self { name }
53    }
54
55    #[must_use]
56    pub const fn value_count(&self) -> usize {
57        2
58    }
59
60    /// Convert a boolean value to its internal index (true=0, false=1 for Java compatibility)
61    #[must_use]
62    pub const fn index_of(&self, value: bool) -> usize {
63        !value as usize
64    }
65}
66
67impl Property for BoolProperty {
68    type Value = bool;
69
70    fn value_count(&self) -> usize {
71        2
72    }
73
74    fn value_name_from_index(&self, index: usize) -> &str {
75        ["true", "false"][index]
76    }
77
78    fn get_possible_value_names(&self) -> Box<[&str]> {
79        ["true", "false"].into()
80    }
81
82    fn get_name(&self) -> &'static str {
83        self.name
84    }
85
86    fn compare_value_names(&self, left: &str, right: &str) -> Option<Ordering> {
87        Some(self.get_value(left)?.cmp(&self.get_value(right)?))
88    }
89
90    fn get_value(&self, value: &str) -> Option<Self::Value> {
91        if value == "true" {
92            Some(true)
93        } else if value == "false" {
94            Some(false)
95        } else {
96            None
97        }
98    }
99
100    fn get_possible_values(&self) -> Box<[Self::Value]> {
101        [true, false].into()
102    }
103
104    fn get_internal_index(&self, value: &Self::Value) -> usize {
105        usize::from(!*value)
106    }
107
108    fn value_from_index(&self, index: usize) -> Self::Value {
109        index == 0
110    }
111}
112
113impl BoolProperty {
114    #[must_use]
115    pub const fn get_internal_index_const(self, value: bool) -> usize {
116        if value { 0 } else { 1 }
117    }
118}
119
120// Instead of million heap allocs we just use 42 bytes of static mem :)
121const NUM_STR: [&str; 26] = [
122    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
123    "17", "18", "19", "20", "21", "22", "23", "24", "25",
124];
125
126#[derive(Debug, Clone)]
127pub struct IntProperty {
128    pub min: u8,
129    pub max: u8,
130    pub name: &'static str,
131}
132
133impl IntProperty {
134    #[must_use]
135    pub const fn new(name: &'static str, min: u8, max: u8) -> Self {
136        Self { min, max, name }
137    }
138
139    #[must_use]
140    pub const fn value_count(&self) -> usize {
141        (self.max - self.min + 1) as usize
142    }
143}
144
145impl Property for IntProperty {
146    type Value = u8;
147
148    fn value_count(&self) -> usize {
149        IntProperty::value_count(self)
150    }
151
152    fn value_name_from_index(&self, index: usize) -> &str {
153        NUM_STR[self.min as usize + index]
154    }
155
156    fn get_possible_value_names(&self) -> Box<[&str]> {
157        (self.min..=self.max).map(|v| NUM_STR[v as usize]).collect()
158    }
159
160    fn get_name(&self) -> &'static str {
161        self.name
162    }
163
164    fn compare_value_names(&self, left: &str, right: &str) -> Option<Ordering> {
165        Some(self.get_value(left)?.cmp(&self.get_value(right)?))
166    }
167
168    fn get_value(&self, value: &str) -> Option<Self::Value> {
169        value
170            .parse()
171            .ok()
172            .filter(|v| v >= &self.min && v <= &self.max)
173    }
174
175    fn get_possible_values(&self) -> Box<[Self::Value]> {
176        (self.min..=self.max).collect()
177    }
178
179    fn get_internal_index(&self, value: &Self::Value) -> usize {
180        if *value <= self.max {
181            (*value - self.min) as usize
182        } else {
183            0
184        }
185    }
186
187    fn value_from_index(&self, index: usize) -> Self::Value {
188        self.min + index as u8
189    }
190}
191
192impl IntProperty {
193    #[must_use]
194    pub const fn get_internal_index_const(self, value: &u8) -> usize {
195        if *value <= self.max {
196            (*value - self.min) as usize
197        } else {
198            0
199        }
200    }
201}
202
203#[derive(Debug, Clone)]
204pub struct EnumProperty<T: PropertyEnum + 'static> {
205    pub name: &'static str,
206    pub possible_values: &'static [T],
207    comparison_values: &'static [T],
208}
209
210impl<T: PropertyEnum + 'static> Property for EnumProperty<T> {
211    type Value = T;
212
213    fn value_count(&self) -> usize {
214        EnumProperty::value_count(self)
215    }
216
217    fn value_name_from_index(&self, index: usize) -> &str {
218        self.possible_values[index].as_str()
219    }
220
221    fn get_possible_value_names(&self) -> Box<[&str]> {
222        self.possible_values
223            .iter()
224            .map(PropertyEnum::as_str)
225            .collect()
226    }
227
228    fn get_name(&self) -> &'static str {
229        self.name
230    }
231
232    fn compare_value_names(&self, left: &str, right: &str) -> Option<Ordering> {
233        let left = self.get_value(left)?;
234        let right = self.get_value(right)?;
235        let left = self
236            .comparison_values
237            .iter()
238            .position(|value| value == &left)?;
239        let right = self
240            .comparison_values
241            .iter()
242            .position(|value| value == &right)?;
243        Some(left.cmp(&right))
244    }
245
246    fn get_value(&self, value: &str) -> Option<Self::Value> {
247        self.possible_values
248            .iter()
249            .find(|v| v.as_str() == value)
250            .cloned()
251    }
252
253    fn get_possible_values(&self) -> Box<[Self::Value]> {
254        self.possible_values.into()
255    }
256
257    fn get_internal_index(&self, value: &Self::Value) -> usize {
258        self.possible_values
259            .iter()
260            .position(|v| v == value)
261            .unwrap()
262    }
263
264    fn value_from_index(&self, index: usize) -> Self::Value {
265        self.possible_values[index].clone()
266    }
267}
268
269impl<T: PropertyEnum> EnumProperty<T> {
270    /// Creates an enum property whose state and natural comparison orders match.
271    pub const fn new(name: &'static str, possible_values: &'static [T]) -> Self {
272        Self {
273            name,
274            possible_values,
275            comparison_values: possible_values,
276        }
277    }
278
279    /// Creates a property whose state order differs from the enum's natural order.
280    ///
281    /// `comparison_values` must contain every possible value in the order used
282    /// by the Vanilla enum's `Comparable` implementation.
283    pub const fn with_comparison_order(
284        name: &'static str,
285        possible_values: &'static [T],
286        comparison_values: &'static [T],
287    ) -> Self {
288        Self {
289            name,
290            possible_values,
291            comparison_values,
292        }
293    }
294
295    #[must_use]
296    pub const fn value_count(&self) -> usize {
297        self.possible_values.len()
298    }
299}
300
301impl<T: const PartialEq + PropertyEnum + 'static> EnumProperty<T> {
302    pub const fn get_internal_index_const(&self, value: &T) -> usize {
303        let mut i = 0;
304        while i < self.possible_values.len() {
305            if &self.possible_values[i] == value {
306                return i;
307            }
308            i += 1;
309        }
310        panic!("value not found in possible_values");
311    }
312}
313
314impl PropertyEnum for Direction {
315    fn as_str(&self) -> &str {
316        Direction::as_str(self)
317    }
318}
319
320// Additional enum types for properties
321#[derive(Clone, Copy, Debug)]
322#[derive_const(PartialEq, Eq)]
323pub enum FrontAndTop {
324    DownEast,
325    DownNorth,
326    DownSouth,
327    DownWest,
328    UpEast,
329    UpNorth,
330    UpSouth,
331    UpWest,
332    WestUp,
333    EastUp,
334    NorthUp,
335    SouthUp,
336}
337
338impl PropertyEnum for FrontAndTop {
339    fn as_str(&self) -> &str {
340        match self {
341            FrontAndTop::DownEast => "down_east",
342            FrontAndTop::DownNorth => "down_north",
343            FrontAndTop::DownSouth => "down_south",
344            FrontAndTop::DownWest => "down_west",
345            FrontAndTop::UpEast => "up_east",
346            FrontAndTop::UpNorth => "up_north",
347            FrontAndTop::UpSouth => "up_south",
348            FrontAndTop::UpWest => "up_west",
349            FrontAndTop::WestUp => "west_up",
350            FrontAndTop::EastUp => "east_up",
351            FrontAndTop::NorthUp => "north_up",
352            FrontAndTop::SouthUp => "south_up",
353        }
354    }
355}
356
357#[derive(Clone, Copy, Debug)]
358#[derive_const(PartialEq, Eq)]
359pub enum AttachFace {
360    Floor,
361    Wall,
362    Ceiling,
363}
364
365impl PropertyEnum for AttachFace {
366    fn as_str(&self) -> &str {
367        match self {
368            AttachFace::Floor => "floor",
369            AttachFace::Wall => "wall",
370            AttachFace::Ceiling => "ceiling",
371        }
372    }
373}
374
375#[derive(Clone, Copy, Debug)]
376#[derive_const(PartialEq, Eq)]
377pub enum BellAttachType {
378    Floor,
379    Ceiling,
380    SingleWall,
381    DoubleWall,
382}
383
384impl PropertyEnum for BellAttachType {
385    fn as_str(&self) -> &str {
386        match self {
387            BellAttachType::Floor => "floor",
388            BellAttachType::Ceiling => "ceiling",
389            BellAttachType::SingleWall => "single_wall",
390            BellAttachType::DoubleWall => "double_wall",
391        }
392    }
393}
394
395#[derive(Clone, Copy, Debug)]
396#[derive_const(PartialEq, Eq)]
397pub enum WallSide {
398    None,
399    Low,
400    Tall,
401}
402
403impl PropertyEnum for WallSide {
404    fn as_str(&self) -> &str {
405        match self {
406            WallSide::None => "none",
407            WallSide::Low => "low",
408            WallSide::Tall => "tall",
409        }
410    }
411}
412
413#[derive(Clone, Copy, Debug)]
414#[derive_const(PartialEq, Eq)]
415pub enum RedstoneSide {
416    Up,
417    Side,
418    None,
419}
420
421impl PropertyEnum for RedstoneSide {
422    fn as_str(&self) -> &str {
423        match self {
424            RedstoneSide::None => "none",
425            RedstoneSide::Side => "side",
426            RedstoneSide::Up => "up",
427        }
428    }
429}
430
431#[derive(Clone, Copy, Debug)]
432#[derive_const(PartialEq, Eq)]
433pub enum DoubleBlockHalf {
434    Upper,
435    Lower,
436}
437
438impl PropertyEnum for DoubleBlockHalf {
439    fn as_str(&self) -> &str {
440        match self {
441            DoubleBlockHalf::Upper => "upper",
442            DoubleBlockHalf::Lower => "lower",
443        }
444    }
445}
446
447#[derive(Clone, Copy, Debug)]
448#[derive_const(PartialEq, Eq)]
449pub enum Half {
450    Top,
451    Bottom,
452}
453
454impl PropertyEnum for Half {
455    fn as_str(&self) -> &str {
456        match self {
457            Half::Top => "top",
458            Half::Bottom => "bottom",
459        }
460    }
461}
462
463#[derive(Clone, Copy, Debug)]
464#[derive_const(PartialEq, Eq)]
465pub enum SideChainPart {
466    Unconnected,
467    Right,
468    Center,
469    Left,
470}
471
472impl PropertyEnum for SideChainPart {
473    fn as_str(&self) -> &str {
474        match self {
475            SideChainPart::Unconnected => "unconnected",
476            SideChainPart::Right => "right",
477            SideChainPart::Center => "center",
478            SideChainPart::Left => "left",
479        }
480    }
481}
482
483#[derive(Clone, Copy, Debug, Eq)]
484#[derive_const(PartialEq)]
485pub enum RailShape {
486    NorthSouth,
487    EastWest,
488    AscendingEast,
489    AscendingWest,
490    AscendingNorth,
491    AscendingSouth,
492    SouthEast,
493    SouthWest,
494    NorthWest,
495    NorthEast,
496}
497
498impl RailShape {
499    /// Returns whether this shape raises one end of a rail by one block.
500    #[must_use]
501    pub const fn is_slope(&self) -> bool {
502        matches!(
503            self,
504            Self::AscendingEast | Self::AscendingWest | Self::AscendingNorth | Self::AscendingSouth
505        )
506    }
507}
508
509impl PropertyEnum for RailShape {
510    fn as_str(&self) -> &str {
511        match self {
512            RailShape::NorthSouth => "north_south",
513            RailShape::EastWest => "east_west",
514            RailShape::AscendingEast => "ascending_east",
515            RailShape::AscendingWest => "ascending_west",
516            RailShape::AscendingNorth => "ascending_north",
517            RailShape::AscendingSouth => "ascending_south",
518            RailShape::SouthEast => "south_east",
519            RailShape::SouthWest => "south_west",
520            RailShape::NorthWest => "north_west",
521            RailShape::NorthEast => "north_east",
522        }
523    }
524}
525
526#[derive(Clone, Copy, Debug)]
527#[derive_const(PartialEq, Eq)]
528pub enum BedPart {
529    Head,
530    Foot,
531}
532
533impl PropertyEnum for BedPart {
534    fn as_str(&self) -> &str {
535        match self {
536            BedPart::Head => "head",
537            BedPart::Foot => "foot",
538        }
539    }
540}
541
542#[derive(Clone, Copy, Debug)]
543#[derive_const(PartialEq, Eq)]
544pub enum ChestType {
545    Single,
546    Left,
547    Right,
548}
549
550impl PropertyEnum for ChestType {
551    fn as_str(&self) -> &str {
552        match self {
553            ChestType::Single => "single",
554            ChestType::Left => "left",
555            ChestType::Right => "right",
556        }
557    }
558}
559
560#[derive(Clone, Copy, Debug)]
561#[derive_const(PartialEq, Eq)]
562pub enum ComparatorMode {
563    Compare,
564    Subtract,
565}
566
567impl PropertyEnum for ComparatorMode {
568    fn as_str(&self) -> &str {
569        match self {
570            ComparatorMode::Compare => "compare",
571            ComparatorMode::Subtract => "subtract",
572        }
573    }
574}
575
576#[derive(Clone, Copy, Debug)]
577#[derive_const(PartialEq, Eq)]
578pub enum DoorHingeSide {
579    Left,
580    Right,
581}
582
583impl PropertyEnum for DoorHingeSide {
584    fn as_str(&self) -> &str {
585        match self {
586            DoorHingeSide::Left => "left",
587            DoorHingeSide::Right => "right",
588        }
589    }
590}
591
592#[derive(Clone, Copy, Debug)]
593#[derive_const(PartialEq, Eq)]
594pub enum NoteBlockInstrument {
595    Harp,
596    Basedrum,
597    Snare,
598    Hat,
599    Bass,
600    Flute,
601    Bell,
602    Guitar,
603    Chime,
604    Xylophone,
605    IronXylophone,
606    CowBell,
607    Didgeridoo,
608    Bit,
609    Banjo,
610    Pling,
611    Trumpet,
612    TrumpetExposed,
613    TrumpetOxidized,
614    TrumpetWeathered,
615    Zombie,
616    Skeleton,
617    Creeper,
618    Dragon,
619    WitherSkeleton,
620    Piglin,
621    CustomHead,
622}
623
624impl NoteBlockInstrument {
625    #[must_use]
626    pub const fn is_tunable(&self) -> bool {
627        !matches!(
628            self,
629            Self::Zombie
630                | Self::Skeleton
631                | Self::Creeper
632                | Self::Dragon
633                | Self::WitherSkeleton
634                | Self::Piglin
635                | Self::CustomHead
636        )
637    }
638
639    #[must_use]
640    pub const fn has_custom_sound(&self) -> bool {
641        matches!(self, Self::CustomHead)
642    }
643
644    #[must_use]
645    pub const fn works_above_note_block(&self) -> bool {
646        !self.is_tunable()
647    }
648}
649
650impl PropertyEnum for NoteBlockInstrument {
651    fn as_str(&self) -> &str {
652        match self {
653            NoteBlockInstrument::Harp => "harp",
654            NoteBlockInstrument::Basedrum => "basedrum",
655            NoteBlockInstrument::Snare => "snare",
656            NoteBlockInstrument::Hat => "hat",
657            NoteBlockInstrument::Bass => "bass",
658            NoteBlockInstrument::Flute => "flute",
659            NoteBlockInstrument::Bell => "bell",
660            NoteBlockInstrument::Guitar => "guitar",
661            NoteBlockInstrument::Chime => "chime",
662            NoteBlockInstrument::Xylophone => "xylophone",
663            NoteBlockInstrument::IronXylophone => "iron_xylophone",
664            NoteBlockInstrument::CowBell => "cow_bell",
665            NoteBlockInstrument::Didgeridoo => "didgeridoo",
666            NoteBlockInstrument::Bit => "bit",
667            NoteBlockInstrument::Banjo => "banjo",
668            NoteBlockInstrument::Pling => "pling",
669            NoteBlockInstrument::Trumpet => "trumpet",
670            NoteBlockInstrument::TrumpetExposed => "trumpet_exposed",
671            NoteBlockInstrument::TrumpetWeathered => "trumpet_weathered",
672            NoteBlockInstrument::TrumpetOxidized => "trumpet_oxidized",
673            NoteBlockInstrument::Zombie => "zombie",
674            NoteBlockInstrument::Skeleton => "skeleton",
675            NoteBlockInstrument::Creeper => "creeper",
676            NoteBlockInstrument::Dragon => "dragon",
677            NoteBlockInstrument::WitherSkeleton => "wither_skeleton",
678            NoteBlockInstrument::Piglin => "piglin",
679            NoteBlockInstrument::CustomHead => "custom_head",
680        }
681    }
682}
683
684#[derive(Clone, Copy, Debug)]
685#[derive_const(PartialEq, Eq)]
686pub enum PistonType {
687    Normal,
688    Sticky,
689}
690
691impl PropertyEnum for PistonType {
692    fn as_str(&self) -> &str {
693        match self {
694            PistonType::Normal => "normal",
695            PistonType::Sticky => "sticky",
696        }
697    }
698}
699
700#[derive(Clone, Copy, Debug)]
701#[derive_const(PartialEq, Eq)]
702pub enum SlabType {
703    Bottom,
704    Top,
705    Double,
706}
707
708impl PropertyEnum for SlabType {
709    fn as_str(&self) -> &str {
710        match self {
711            SlabType::Bottom => "bottom",
712            SlabType::Top => "top",
713            SlabType::Double => "double",
714        }
715    }
716}
717
718#[derive(Clone, Copy, Debug)]
719#[derive_const(PartialEq, Eq)]
720pub enum StairsShape {
721    Straight,
722    InnerLeft,
723    InnerRight,
724    OuterLeft,
725    OuterRight,
726}
727
728impl PropertyEnum for StairsShape {
729    fn as_str(&self) -> &str {
730        match self {
731            StairsShape::Straight => "straight",
732            StairsShape::InnerLeft => "inner_left",
733            StairsShape::InnerRight => "inner_right",
734            StairsShape::OuterLeft => "outer_left",
735            StairsShape::OuterRight => "outer_right",
736        }
737    }
738}
739
740#[derive(Clone, Copy, Debug)]
741#[derive_const(PartialEq, Eq)]
742pub enum StructureMode {
743    Save,
744    Load,
745    Corner,
746    Data,
747}
748
749impl PropertyEnum for StructureMode {
750    fn as_str(&self) -> &str {
751        match self {
752            StructureMode::Save => "save",
753            StructureMode::Load => "load",
754            StructureMode::Corner => "corner",
755            StructureMode::Data => "data",
756        }
757    }
758}
759
760#[derive(Clone, Copy, Debug)]
761#[derive_const(PartialEq, Eq)]
762pub enum BambooLeaves {
763    None,
764    Small,
765    Large,
766}
767
768impl PropertyEnum for BambooLeaves {
769    fn as_str(&self) -> &str {
770        match self {
771            BambooLeaves::None => "none",
772            BambooLeaves::Small => "small",
773            BambooLeaves::Large => "large",
774        }
775    }
776}
777
778#[derive(Clone, Copy, Debug)]
779#[derive_const(PartialEq, Eq)]
780pub enum Tilt {
781    None,
782    Unstable,
783    Partial,
784    Full,
785}
786
787impl PropertyEnum for Tilt {
788    fn as_str(&self) -> &str {
789        match self {
790            Tilt::None => "none",
791            Tilt::Unstable => "unstable",
792            Tilt::Partial => "partial",
793            Tilt::Full => "full",
794        }
795    }
796}
797
798#[derive(Clone, Copy, Debug)]
799#[derive_const(PartialEq, Eq)]
800pub enum DripstoneThickness {
801    TipMerge,
802    Tip,
803    Frustum,
804    Middle,
805    Base,
806}
807
808impl PropertyEnum for DripstoneThickness {
809    fn as_str(&self) -> &str {
810        match self {
811            DripstoneThickness::TipMerge => "tip_merge",
812            DripstoneThickness::Tip => "tip",
813            DripstoneThickness::Frustum => "frustum",
814            DripstoneThickness::Middle => "middle",
815            DripstoneThickness::Base => "base",
816        }
817    }
818}
819
820#[derive(Clone, Copy, Debug)]
821#[derive_const(PartialEq, Eq)]
822pub enum SpeleothemThickness {
823    TipMerge,
824    Tip,
825    Frustum,
826    Middle,
827    Base,
828}
829
830impl PropertyEnum for SpeleothemThickness {
831    fn as_str(&self) -> &str {
832        match self {
833            SpeleothemThickness::TipMerge => "tip_merge",
834            SpeleothemThickness::Tip => "tip",
835            SpeleothemThickness::Frustum => "frustum",
836            SpeleothemThickness::Middle => "middle",
837            SpeleothemThickness::Base => "base",
838        }
839    }
840}
841
842#[derive(Clone, Copy, Debug)]
843#[derive_const(PartialEq, Eq)]
844pub enum SculkSensorPhase {
845    Inactive,
846    Active,
847    Cooldown,
848}
849
850impl PropertyEnum for SculkSensorPhase {
851    fn as_str(&self) -> &str {
852        match self {
853            SculkSensorPhase::Inactive => "inactive",
854            SculkSensorPhase::Active => "active",
855            SculkSensorPhase::Cooldown => "cooldown",
856        }
857    }
858}
859
860#[derive(Clone, Copy, Debug)]
861#[derive_const(PartialEq, Eq)]
862pub enum TrialSpawnerState {
863    Inactive,
864    WaitingForPlayers,
865    Active,
866    WaitingForRewardEjection,
867    EjectingReward,
868    Cooldown,
869}
870
871impl PropertyEnum for TrialSpawnerState {
872    fn as_str(&self) -> &str {
873        match self {
874            TrialSpawnerState::Inactive => "inactive",
875            TrialSpawnerState::WaitingForPlayers => "waiting_for_players",
876            TrialSpawnerState::Active => "active",
877            TrialSpawnerState::WaitingForRewardEjection => "waiting_for_reward_ejection",
878            TrialSpawnerState::EjectingReward => "ejecting_reward",
879            TrialSpawnerState::Cooldown => "cooldown",
880        }
881    }
882}
883
884#[derive(Clone, Copy, Debug)]
885#[derive_const(PartialEq, Eq)]
886pub enum VaultState {
887    Inactive,
888    Active,
889    Unlocking,
890    Ejecting,
891}
892
893impl PropertyEnum for VaultState {
894    fn as_str(&self) -> &str {
895        match self {
896            VaultState::Inactive => "inactive",
897            VaultState::Active => "active",
898            VaultState::Unlocking => "unlocking",
899            VaultState::Ejecting => "ejecting",
900        }
901    }
902}
903
904#[derive(Clone, Copy, Debug)]
905#[derive_const(PartialEq, Eq)]
906pub enum CreakingHeartState {
907    Uprooted,
908    Dormant,
909    Awake,
910}
911
912impl PropertyEnum for CreakingHeartState {
913    fn as_str(&self) -> &str {
914        match self {
915            CreakingHeartState::Uprooted => "uprooted",
916            CreakingHeartState::Dormant => "dormant",
917            CreakingHeartState::Awake => "awake",
918        }
919    }
920}
921
922#[derive(Clone, Copy, Debug)]
923#[derive_const(PartialEq, Eq)]
924pub enum TestBlockMode {
925    Start,
926    Log,
927    Fail,
928    Accept,
929}
930
931impl PropertyEnum for TestBlockMode {
932    fn as_str(&self) -> &str {
933        match self {
934            TestBlockMode::Start => "start",
935            TestBlockMode::Log => "log",
936            TestBlockMode::Fail => "fail",
937            TestBlockMode::Accept => "accept",
938        }
939    }
940}
941
942#[derive(Clone, Copy, Debug)]
943#[derive_const(PartialEq, Eq)]
944pub enum Pose {
945    Standing,
946    Sitting,
947    Running,
948    Star,
949}
950
951impl PropertyEnum for Pose {
952    fn as_str(&self) -> &str {
953        match self {
954            Pose::Standing => "standing",
955            Pose::Sitting => "sitting",
956            Pose::Running => "running",
957            Pose::Star => "star",
958        }
959    }
960}
961
962#[derive(Clone, Copy, Debug)]
963#[derive_const(PartialEq, Eq)]
964pub enum PotentSulfurState {
965    Dry,
966    Wet,
967    Dormant,
968    Erupting,
969    Continuous,
970}
971
972impl PropertyEnum for PotentSulfurState {
973    fn as_str(&self) -> &str {
974        match self {
975            PotentSulfurState::Dry => "dry",
976            PotentSulfurState::Wet => "wet",
977            PotentSulfurState::Dormant => "dormant",
978            PotentSulfurState::Erupting => "erupting",
979            PotentSulfurState::Continuous => "continuous",
980        }
981    }
982}
983
984impl PropertyEnum for Axis {
985    fn as_str(&self) -> &str {
986        self.as_str()
987    }
988}
989
990pub struct BlockStateProperties;
991
992//TODO: These got quickly implemented so the ordering might be off. Fix in the future.
993impl BlockStateProperties {
994    pub const ATTACHED: BoolProperty = BoolProperty::new("attached");
995    pub const BERRIES: BoolProperty = BoolProperty::new("berries");
996    pub const BLOOM: BoolProperty = BoolProperty::new("bloom");
997    pub const BOTTOM: BoolProperty = BoolProperty::new("bottom");
998    pub const CAN_SUMMON: BoolProperty = BoolProperty::new("can_summon");
999    pub const CONDITIONAL: BoolProperty = BoolProperty::new("conditional");
1000    pub const DISARMED: BoolProperty = BoolProperty::new("disarmed");
1001    pub const DRAG: BoolProperty = BoolProperty::new("drag");
1002    pub const ENABLED: BoolProperty = BoolProperty::new("enabled");
1003    pub const EXTENDED: BoolProperty = BoolProperty::new("extended");
1004    pub const EYE: BoolProperty = BoolProperty::new("eye");
1005    pub const FALLING: BoolProperty = BoolProperty::new("falling");
1006    pub const HANGING: BoolProperty = BoolProperty::new("hanging");
1007    pub const HAS_BOTTLE_0: BoolProperty = BoolProperty::new("has_bottle_0");
1008    pub const HAS_BOTTLE_1: BoolProperty = BoolProperty::new("has_bottle_1");
1009    pub const HAS_BOTTLE_2: BoolProperty = BoolProperty::new("has_bottle_2");
1010    pub const HAS_RECORD: BoolProperty = BoolProperty::new("has_record");
1011    pub const HAS_BOOK: BoolProperty = BoolProperty::new("has_book");
1012    pub const INVERTED: BoolProperty = BoolProperty::new("inverted");
1013    pub const IN_WALL: BoolProperty = BoolProperty::new("in_wall");
1014    pub const LIT: BoolProperty = BoolProperty::new("lit");
1015    pub const LOCKED: BoolProperty = BoolProperty::new("locked");
1016    pub const NATURAL: BoolProperty = BoolProperty::new("natural");
1017    pub const OCCUPIED: BoolProperty = BoolProperty::new("occupied");
1018    pub const OPEN: BoolProperty = BoolProperty::new("open");
1019    pub const PERSISTENT: BoolProperty = BoolProperty::new("persistent");
1020    pub const POWERED: BoolProperty = BoolProperty::new("powered");
1021    pub const SHORT: BoolProperty = BoolProperty::new("short");
1022    pub const SHRIEKING: BoolProperty = BoolProperty::new("shrieking");
1023    pub const SIGNAL_FIRE: BoolProperty = BoolProperty::new("signal_fire");
1024    pub const SNOWY: BoolProperty = BoolProperty::new("snowy");
1025    pub const TIP: BoolProperty = BoolProperty::new("tip");
1026    pub const TRIGGERED: BoolProperty = BoolProperty::new("triggered");
1027    pub const UNSTABLE: BoolProperty = BoolProperty::new("unstable");
1028    pub const WATERLOGGED: BoolProperty = BoolProperty::new("waterlogged");
1029    pub const HORIZONTAL_AXIS: EnumProperty<Axis> = EnumProperty::new("axis", &[Axis::X, Axis::Z]);
1030    pub const AXIS: EnumProperty<Axis> = EnumProperty::new("axis", &[Axis::X, Axis::Y, Axis::Z]);
1031    pub const UP: BoolProperty = BoolProperty::new("up");
1032    pub const DOWN: BoolProperty = BoolProperty::new("down");
1033    pub const NORTH: BoolProperty = BoolProperty::new("north");
1034    pub const EAST: BoolProperty = BoolProperty::new("east");
1035    pub const SOUTH: BoolProperty = BoolProperty::new("south");
1036    pub const WEST: BoolProperty = BoolProperty::new("west");
1037    pub const FACING: EnumProperty<Direction> = EnumProperty::with_comparison_order(
1038        "facing",
1039        &[
1040            Direction::North,
1041            Direction::East,
1042            Direction::South,
1043            Direction::West,
1044            Direction::Up,
1045            Direction::Down,
1046        ],
1047        &[
1048            Direction::Down,
1049            Direction::Up,
1050            Direction::North,
1051            Direction::South,
1052            Direction::West,
1053            Direction::East,
1054        ],
1055    );
1056    pub const FACING_HOPPER: EnumProperty<Direction> = EnumProperty::new(
1057        "facing",
1058        &[
1059            Direction::Down,
1060            Direction::North,
1061            Direction::South,
1062            Direction::West,
1063            Direction::East,
1064        ],
1065    );
1066    pub const HORIZONTAL_FACING: EnumProperty<Direction> = EnumProperty::new(
1067        "facing",
1068        &[
1069            Direction::North,
1070            Direction::South,
1071            Direction::West,
1072            Direction::East,
1073        ],
1074    );
1075    pub const FLOWER_AMOUNT: IntProperty = IntProperty::new("flower_amount", 1, 4);
1076    pub const SEGMENT_AMOUNT: IntProperty = IntProperty::new("segment_amount", 1, 4);
1077
1078    // Additional enum types needed for properties
1079    pub const ORIENTATION: EnumProperty<FrontAndTop> = EnumProperty::new(
1080        "orientation",
1081        &[
1082            FrontAndTop::DownEast,
1083            FrontAndTop::DownNorth,
1084            FrontAndTop::DownSouth,
1085            FrontAndTop::DownWest,
1086            FrontAndTop::UpEast,
1087            FrontAndTop::UpNorth,
1088            FrontAndTop::UpSouth,
1089            FrontAndTop::UpWest,
1090            FrontAndTop::WestUp,
1091            FrontAndTop::EastUp,
1092            FrontAndTop::NorthUp,
1093            FrontAndTop::SouthUp,
1094        ],
1095    );
1096    pub const ATTACH_FACE: EnumProperty<AttachFace> = EnumProperty::new(
1097        "face",
1098        &[AttachFace::Floor, AttachFace::Wall, AttachFace::Ceiling],
1099    );
1100    pub const BELL_ATTACHMENT: EnumProperty<BellAttachType> = EnumProperty::new(
1101        "attachment",
1102        &[
1103            BellAttachType::Floor,
1104            BellAttachType::Ceiling,
1105            BellAttachType::SingleWall,
1106            BellAttachType::DoubleWall,
1107        ],
1108    );
1109    pub const EAST_WALL: EnumProperty<WallSide> =
1110        EnumProperty::new("east", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1111    pub const NORTH_WALL: EnumProperty<WallSide> =
1112        EnumProperty::new("north", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1113    pub const SOUTH_WALL: EnumProperty<WallSide> =
1114        EnumProperty::new("south", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1115    pub const WEST_WALL: EnumProperty<WallSide> =
1116        EnumProperty::new("west", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1117    pub const EAST_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1118        "east",
1119        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1120    );
1121    pub const NORTH_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1122        "north",
1123        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1124    );
1125    pub const SOUTH_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1126        "south",
1127        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1128    );
1129    pub const WEST_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1130        "west",
1131        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1132    );
1133    pub const DOUBLE_BLOCK_HALF: EnumProperty<DoubleBlockHalf> =
1134        EnumProperty::new("half", &[DoubleBlockHalf::Upper, DoubleBlockHalf::Lower]);
1135    pub const HALF: EnumProperty<Half> = EnumProperty::new("half", &[Half::Top, Half::Bottom]);
1136    pub const SIDE_CHAIN_PART: EnumProperty<SideChainPart> = EnumProperty::new(
1137        "side_chain",
1138        &[
1139            SideChainPart::Unconnected,
1140            SideChainPart::Right,
1141            SideChainPart::Center,
1142            SideChainPart::Left,
1143        ],
1144    );
1145    pub const RAIL_SHAPE: EnumProperty<RailShape> = EnumProperty::new(
1146        "shape",
1147        &[
1148            RailShape::NorthSouth,
1149            RailShape::EastWest,
1150            RailShape::AscendingEast,
1151            RailShape::AscendingWest,
1152            RailShape::AscendingNorth,
1153            RailShape::AscendingSouth,
1154            RailShape::SouthEast,
1155            RailShape::SouthWest,
1156            RailShape::NorthWest,
1157            RailShape::NorthEast,
1158        ],
1159    );
1160    pub const RAIL_SHAPE_STRAIGHT: EnumProperty<RailShape> = EnumProperty::new(
1161        "shape",
1162        &[
1163            RailShape::NorthSouth,
1164            RailShape::EastWest,
1165            RailShape::AscendingEast,
1166            RailShape::AscendingWest,
1167            RailShape::AscendingNorth,
1168            RailShape::AscendingSouth,
1169        ],
1170    );
1171
1172    // Age properties
1173    pub const AGE_1: IntProperty = IntProperty::new("age", 0, 1);
1174    pub const AGE_2: IntProperty = IntProperty::new("age", 0, 2);
1175    pub const AGE_3: IntProperty = IntProperty::new("age", 0, 3);
1176    pub const AGE_4: IntProperty = IntProperty::new("age", 0, 4);
1177    pub const AGE_5: IntProperty = IntProperty::new("age", 0, 5);
1178    pub const AGE_7: IntProperty = IntProperty::new("age", 0, 7);
1179    pub const AGE_15: IntProperty = IntProperty::new("age", 0, 15);
1180    pub const AGE_25: IntProperty = IntProperty::new("age", 0, 25);
1181
1182    // Other integer properties
1183    pub const BITES: IntProperty = IntProperty::new("bites", 0, 6);
1184    pub const CANDLES: IntProperty = IntProperty::new("candles", 1, 4);
1185    pub const DELAY: IntProperty = IntProperty::new("delay", 1, 4);
1186    pub const DISTANCE: IntProperty = IntProperty::new("distance", 1, 7);
1187    pub const EGGS: IntProperty = IntProperty::new("eggs", 1, 4);
1188    pub const HATCH: IntProperty = IntProperty::new("hatch", 0, 2);
1189    pub const LAYERS: IntProperty = IntProperty::new("layers", 1, 8);
1190    pub const LEVEL_CAULDRON: IntProperty = IntProperty::new("level", 1, 3);
1191    pub const LEVEL_COMPOSTER: IntProperty = IntProperty::new("level", 0, 8);
1192    pub const LEVEL_FLOWING: IntProperty = IntProperty::new("level", 1, 8);
1193    pub const LEVEL_HONEY: IntProperty = IntProperty::new("honey_level", 0, 5);
1194    pub const LEVEL: IntProperty = IntProperty::new("level", 0, 15);
1195    pub const MOISTURE: IntProperty = IntProperty::new("moisture", 0, 7);
1196    pub const NOTE: IntProperty = IntProperty::new("note", 0, 24);
1197    pub const PICKLES: IntProperty = IntProperty::new("pickles", 1, 4);
1198    pub const POWER: IntProperty = IntProperty::new("power", 0, 15);
1199    pub const STAGE: IntProperty = IntProperty::new("stage", 0, 1);
1200    pub const STABILITY_DISTANCE: IntProperty = IntProperty::new("distance", 0, 7);
1201    pub const RESPAWN_ANCHOR_CHARGES: IntProperty = IntProperty::new("charges", 0, 4);
1202    pub const DRIED_GHAST_HYDRATION_LEVELS: IntProperty = IntProperty::new("hydration", 0, 3);
1203    pub const ROTATION_16: IntProperty = IntProperty::new("rotation", 0, 15);
1204    pub const DUSTED: IntProperty = IntProperty::new("dusted", 0, 3);
1205
1206    // Enum properties
1207    pub const BED_PART: EnumProperty<BedPart> =
1208        EnumProperty::new("part", &[BedPart::Head, BedPart::Foot]);
1209    pub const CHEST_TYPE: EnumProperty<ChestType> = EnumProperty::new(
1210        "type",
1211        &[ChestType::Single, ChestType::Left, ChestType::Right],
1212    );
1213    pub const MODE_COMPARATOR: EnumProperty<ComparatorMode> =
1214        EnumProperty::new("mode", &[ComparatorMode::Compare, ComparatorMode::Subtract]);
1215    pub const DOOR_HINGE: EnumProperty<DoorHingeSide> =
1216        EnumProperty::new("hinge", &[DoorHingeSide::Left, DoorHingeSide::Right]);
1217    pub const NOTEBLOCK_INSTRUMENT: EnumProperty<NoteBlockInstrument> = EnumProperty::new(
1218        "instrument",
1219        &[
1220            NoteBlockInstrument::Harp,
1221            NoteBlockInstrument::Basedrum,
1222            NoteBlockInstrument::Snare,
1223            NoteBlockInstrument::Hat,
1224            NoteBlockInstrument::Bass,
1225            NoteBlockInstrument::Flute,
1226            NoteBlockInstrument::Bell,
1227            NoteBlockInstrument::Guitar,
1228            NoteBlockInstrument::Chime,
1229            NoteBlockInstrument::Xylophone,
1230            NoteBlockInstrument::IronXylophone,
1231            NoteBlockInstrument::CowBell,
1232            NoteBlockInstrument::Didgeridoo,
1233            NoteBlockInstrument::Bit,
1234            NoteBlockInstrument::Banjo,
1235            NoteBlockInstrument::Pling,
1236            NoteBlockInstrument::Trumpet,
1237            NoteBlockInstrument::TrumpetExposed,
1238            NoteBlockInstrument::TrumpetOxidized,
1239            NoteBlockInstrument::TrumpetWeathered,
1240            NoteBlockInstrument::Zombie,
1241            NoteBlockInstrument::Skeleton,
1242            NoteBlockInstrument::Creeper,
1243            NoteBlockInstrument::Dragon,
1244            NoteBlockInstrument::WitherSkeleton,
1245            NoteBlockInstrument::Piglin,
1246            NoteBlockInstrument::CustomHead,
1247        ],
1248    );
1249    pub const PISTON_TYPE: EnumProperty<PistonType> =
1250        EnumProperty::new("type", &[PistonType::Normal, PistonType::Sticky]);
1251    pub const SLAB_TYPE: EnumProperty<SlabType> =
1252        EnumProperty::new("type", &[SlabType::Top, SlabType::Bottom, SlabType::Double]);
1253    pub const STAIRS_SHAPE: EnumProperty<StairsShape> = EnumProperty::new(
1254        "shape",
1255        &[
1256            StairsShape::Straight,
1257            StairsShape::InnerLeft,
1258            StairsShape::InnerRight,
1259            StairsShape::OuterLeft,
1260            StairsShape::OuterRight,
1261        ],
1262    );
1263    pub const STRUCTUREBLOCK_MODE: EnumProperty<StructureMode> = EnumProperty::new(
1264        "mode",
1265        &[
1266            StructureMode::Save,
1267            StructureMode::Load,
1268            StructureMode::Corner,
1269            StructureMode::Data,
1270        ],
1271    );
1272    pub const BAMBOO_LEAVES: EnumProperty<BambooLeaves> = EnumProperty::new(
1273        "leaves",
1274        &[BambooLeaves::None, BambooLeaves::Small, BambooLeaves::Large],
1275    );
1276    pub const TILT: EnumProperty<Tilt> = EnumProperty::new(
1277        "tilt",
1278        &[Tilt::None, Tilt::Unstable, Tilt::Partial, Tilt::Full],
1279    );
1280    pub const VERTICAL_DIRECTION: EnumProperty<Direction> = EnumProperty::with_comparison_order(
1281        "vertical_direction",
1282        &[Direction::Up, Direction::Down],
1283        &[Direction::Down, Direction::Up],
1284    );
1285    pub const DRIPSTONE_THICKNESS: EnumProperty<DripstoneThickness> = EnumProperty::new(
1286        "thickness",
1287        &[
1288            DripstoneThickness::TipMerge,
1289            DripstoneThickness::Tip,
1290            DripstoneThickness::Frustum,
1291            DripstoneThickness::Middle,
1292            DripstoneThickness::Base,
1293        ],
1294    );
1295    pub const SPELEOTHEM_THICKNESS: EnumProperty<SpeleothemThickness> = EnumProperty::new(
1296        "thickness",
1297        &[
1298            SpeleothemThickness::TipMerge,
1299            SpeleothemThickness::Tip,
1300            SpeleothemThickness::Frustum,
1301            SpeleothemThickness::Middle,
1302            SpeleothemThickness::Base,
1303        ],
1304    );
1305    pub const SCULK_SENSOR_PHASE: EnumProperty<SculkSensorPhase> = EnumProperty::new(
1306        "sculk_sensor_phase",
1307        &[
1308            SculkSensorPhase::Inactive,
1309            SculkSensorPhase::Active,
1310            SculkSensorPhase::Cooldown,
1311        ],
1312    );
1313    pub const TRIAL_SPAWNER_STATE: EnumProperty<TrialSpawnerState> = EnumProperty::new(
1314        "trial_spawner_state",
1315        &[
1316            TrialSpawnerState::Inactive,
1317            TrialSpawnerState::WaitingForPlayers,
1318            TrialSpawnerState::Active,
1319            TrialSpawnerState::WaitingForRewardEjection,
1320            TrialSpawnerState::EjectingReward,
1321            TrialSpawnerState::Cooldown,
1322        ],
1323    );
1324    pub const VAULT_STATE: EnumProperty<VaultState> = EnumProperty::new(
1325        "vault_state",
1326        &[
1327            VaultState::Inactive,
1328            VaultState::Active,
1329            VaultState::Unlocking,
1330            VaultState::Ejecting,
1331        ],
1332    );
1333    pub const CREAKING_HEART_STATE: EnumProperty<CreakingHeartState> = EnumProperty::new(
1334        "creaking_heart_state",
1335        &[
1336            CreakingHeartState::Uprooted,
1337            CreakingHeartState::Dormant,
1338            CreakingHeartState::Awake,
1339        ],
1340    );
1341    pub const TEST_BLOCK_MODE: EnumProperty<TestBlockMode> = EnumProperty::new(
1342        "mode",
1343        &[
1344            TestBlockMode::Start,
1345            TestBlockMode::Log,
1346            TestBlockMode::Fail,
1347            TestBlockMode::Accept,
1348        ],
1349    );
1350    pub const COPPER_GOLEM_POSE: EnumProperty<Pose> = EnumProperty::new(
1351        "copper_golem_pose",
1352        &[Pose::Standing, Pose::Sitting, Pose::Running, Pose::Star],
1353    );
1354    pub const POTENT_SULFUR_STATE: EnumProperty<PotentSulfurState> = EnumProperty::new(
1355        "potent_sulfur_state",
1356        &[
1357            PotentSulfurState::Dry,
1358            PotentSulfurState::Wet,
1359            PotentSulfurState::Dormant,
1360            PotentSulfurState::Erupting,
1361            PotentSulfurState::Continuous,
1362        ],
1363    );
1364
1365    // Additional boolean properties
1366    pub const SLOT_0_OCCUPIED: BoolProperty = BoolProperty::new("slot_0_occupied");
1367    pub const SLOT_1_OCCUPIED: BoolProperty = BoolProperty::new("slot_1_occupied");
1368    pub const SLOT_2_OCCUPIED: BoolProperty = BoolProperty::new("slot_2_occupied");
1369    pub const SLOT_3_OCCUPIED: BoolProperty = BoolProperty::new("slot_3_occupied");
1370    pub const SLOT_4_OCCUPIED: BoolProperty = BoolProperty::new("slot_4_occupied");
1371    pub const SLOT_5_OCCUPIED: BoolProperty = BoolProperty::new("slot_5_occupied");
1372    pub const CRACKED: BoolProperty = BoolProperty::new("cracked");
1373    pub const CRAFTING: BoolProperty = BoolProperty::new("crafting");
1374    pub const OMINOUS: BoolProperty = BoolProperty::new("ominous");
1375    pub const MAP: BoolProperty = BoolProperty::new("map");
1376}