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, Debug)]
322#[derive_const(PartialEq)]
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, Debug)]
358#[derive_const(PartialEq)]
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, Debug)]
376#[derive_const(PartialEq)]
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, Debug)]
396#[derive_const(PartialEq)]
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)]
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, Debug)]
432#[derive_const(PartialEq)]
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, Debug)]
448#[derive_const(PartialEq)]
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, Debug)]
464#[derive_const(PartialEq)]
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, Debug)]
527#[derive_const(PartialEq)]
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, Debug)]
543#[derive_const(PartialEq)]
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, Debug)]
561#[derive_const(PartialEq)]
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, Debug)]
577#[derive_const(PartialEq)]
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)]
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    /// Vanilla `NoteBlockInstrument.isTunable`.
626    #[must_use]
627    pub const fn is_tunable(&self) -> bool {
628        !matches!(
629            self,
630            Self::Zombie
631                | Self::Skeleton
632                | Self::Creeper
633                | Self::Dragon
634                | Self::WitherSkeleton
635                | Self::Piglin
636                | Self::CustomHead
637        )
638    }
639
640    /// Vanilla `NoteBlockInstrument.hasCustomSound`.
641    #[must_use]
642    pub const fn has_custom_sound(&self) -> bool {
643        matches!(self, Self::CustomHead)
644    }
645
646    /// Vanilla `NoteBlockInstrument.worksAboveNoteBlock`.
647    #[must_use]
648    pub const fn works_above_note_block(&self) -> bool {
649        !self.is_tunable()
650    }
651}
652
653impl PropertyEnum for NoteBlockInstrument {
654    fn as_str(&self) -> &str {
655        match self {
656            NoteBlockInstrument::Harp => "harp",
657            NoteBlockInstrument::Basedrum => "basedrum",
658            NoteBlockInstrument::Snare => "snare",
659            NoteBlockInstrument::Hat => "hat",
660            NoteBlockInstrument::Bass => "bass",
661            NoteBlockInstrument::Flute => "flute",
662            NoteBlockInstrument::Bell => "bell",
663            NoteBlockInstrument::Guitar => "guitar",
664            NoteBlockInstrument::Chime => "chime",
665            NoteBlockInstrument::Xylophone => "xylophone",
666            NoteBlockInstrument::IronXylophone => "iron_xylophone",
667            NoteBlockInstrument::CowBell => "cow_bell",
668            NoteBlockInstrument::Didgeridoo => "didgeridoo",
669            NoteBlockInstrument::Bit => "bit",
670            NoteBlockInstrument::Banjo => "banjo",
671            NoteBlockInstrument::Pling => "pling",
672            NoteBlockInstrument::Trumpet => "trumpet",
673            NoteBlockInstrument::TrumpetExposed => "trumpet_exposed",
674            NoteBlockInstrument::TrumpetWeathered => "trumpet_weathered",
675            NoteBlockInstrument::TrumpetOxidized => "trumpet_oxidized",
676            NoteBlockInstrument::Zombie => "zombie",
677            NoteBlockInstrument::Skeleton => "skeleton",
678            NoteBlockInstrument::Creeper => "creeper",
679            NoteBlockInstrument::Dragon => "dragon",
680            NoteBlockInstrument::WitherSkeleton => "wither_skeleton",
681            NoteBlockInstrument::Piglin => "piglin",
682            NoteBlockInstrument::CustomHead => "custom_head",
683        }
684    }
685}
686
687#[derive(Clone, Debug)]
688#[derive_const(PartialEq)]
689pub enum PistonType {
690    Normal,
691    Sticky,
692}
693
694impl PropertyEnum for PistonType {
695    fn as_str(&self) -> &str {
696        match self {
697            PistonType::Normal => "normal",
698            PistonType::Sticky => "sticky",
699        }
700    }
701}
702
703#[derive(Clone, Debug)]
704#[derive_const(PartialEq)]
705pub enum SlabType {
706    Bottom,
707    Top,
708    Double,
709}
710
711impl PropertyEnum for SlabType {
712    fn as_str(&self) -> &str {
713        match self {
714            SlabType::Bottom => "bottom",
715            SlabType::Top => "top",
716            SlabType::Double => "double",
717        }
718    }
719}
720
721#[derive(Clone, Debug)]
722#[derive_const(PartialEq)]
723pub enum StairsShape {
724    Straight,
725    InnerLeft,
726    InnerRight,
727    OuterLeft,
728    OuterRight,
729}
730
731impl PropertyEnum for StairsShape {
732    fn as_str(&self) -> &str {
733        match self {
734            StairsShape::Straight => "straight",
735            StairsShape::InnerLeft => "inner_left",
736            StairsShape::InnerRight => "inner_right",
737            StairsShape::OuterLeft => "outer_left",
738            StairsShape::OuterRight => "outer_right",
739        }
740    }
741}
742
743#[derive(Clone, Debug)]
744#[derive_const(PartialEq)]
745pub enum StructureMode {
746    Save,
747    Load,
748    Corner,
749    Data,
750}
751
752impl PropertyEnum for StructureMode {
753    fn as_str(&self) -> &str {
754        match self {
755            StructureMode::Save => "save",
756            StructureMode::Load => "load",
757            StructureMode::Corner => "corner",
758            StructureMode::Data => "data",
759        }
760    }
761}
762
763#[derive(Clone, Debug)]
764#[derive_const(PartialEq)]
765pub enum BambooLeaves {
766    None,
767    Small,
768    Large,
769}
770
771impl PropertyEnum for BambooLeaves {
772    fn as_str(&self) -> &str {
773        match self {
774            BambooLeaves::None => "none",
775            BambooLeaves::Small => "small",
776            BambooLeaves::Large => "large",
777        }
778    }
779}
780
781#[derive(Clone, Debug)]
782#[derive_const(PartialEq)]
783pub enum Tilt {
784    None,
785    Unstable,
786    Partial,
787    Full,
788}
789
790impl PropertyEnum for Tilt {
791    fn as_str(&self) -> &str {
792        match self {
793            Tilt::None => "none",
794            Tilt::Unstable => "unstable",
795            Tilt::Partial => "partial",
796            Tilt::Full => "full",
797        }
798    }
799}
800
801#[derive(Clone, Debug)]
802#[derive_const(PartialEq)]
803pub enum DripstoneThickness {
804    TipMerge,
805    Tip,
806    Frustum,
807    Middle,
808    Base,
809}
810
811impl PropertyEnum for DripstoneThickness {
812    fn as_str(&self) -> &str {
813        match self {
814            DripstoneThickness::TipMerge => "tip_merge",
815            DripstoneThickness::Tip => "tip",
816            DripstoneThickness::Frustum => "frustum",
817            DripstoneThickness::Middle => "middle",
818            DripstoneThickness::Base => "base",
819        }
820    }
821}
822
823#[derive(Clone, Debug)]
824#[derive_const(PartialEq)]
825pub enum SpeleothemThickness {
826    TipMerge,
827    Tip,
828    Frustum,
829    Middle,
830    Base,
831}
832
833impl PropertyEnum for SpeleothemThickness {
834    fn as_str(&self) -> &str {
835        match self {
836            SpeleothemThickness::TipMerge => "tip_merge",
837            SpeleothemThickness::Tip => "tip",
838            SpeleothemThickness::Frustum => "frustum",
839            SpeleothemThickness::Middle => "middle",
840            SpeleothemThickness::Base => "base",
841        }
842    }
843}
844
845#[derive(Clone, Debug)]
846#[derive_const(PartialEq)]
847pub enum SculkSensorPhase {
848    Inactive,
849    Active,
850    Cooldown,
851}
852
853impl PropertyEnum for SculkSensorPhase {
854    fn as_str(&self) -> &str {
855        match self {
856            SculkSensorPhase::Inactive => "inactive",
857            SculkSensorPhase::Active => "active",
858            SculkSensorPhase::Cooldown => "cooldown",
859        }
860    }
861}
862
863#[derive(Clone, Debug)]
864#[derive_const(PartialEq)]
865pub enum TrialSpawnerState {
866    Inactive,
867    WaitingForPlayers,
868    Active,
869    WaitingForRewardEjection,
870    EjectingReward,
871    Cooldown,
872}
873
874impl PropertyEnum for TrialSpawnerState {
875    fn as_str(&self) -> &str {
876        match self {
877            TrialSpawnerState::Inactive => "inactive",
878            TrialSpawnerState::WaitingForPlayers => "waiting_for_players",
879            TrialSpawnerState::Active => "active",
880            TrialSpawnerState::WaitingForRewardEjection => "waiting_for_reward_ejection",
881            TrialSpawnerState::EjectingReward => "ejecting_reward",
882            TrialSpawnerState::Cooldown => "cooldown",
883        }
884    }
885}
886
887#[derive(Clone, Debug)]
888#[derive_const(PartialEq)]
889pub enum VaultState {
890    Inactive,
891    Active,
892    Unlocking,
893    Ejecting,
894}
895
896impl PropertyEnum for VaultState {
897    fn as_str(&self) -> &str {
898        match self {
899            VaultState::Inactive => "inactive",
900            VaultState::Active => "active",
901            VaultState::Unlocking => "unlocking",
902            VaultState::Ejecting => "ejecting",
903        }
904    }
905}
906
907#[derive(Clone, Debug)]
908#[derive_const(PartialEq)]
909pub enum CreakingHeartState {
910    Uprooted,
911    Dormant,
912    Awake,
913}
914
915impl PropertyEnum for CreakingHeartState {
916    fn as_str(&self) -> &str {
917        match self {
918            CreakingHeartState::Uprooted => "uprooted",
919            CreakingHeartState::Dormant => "dormant",
920            CreakingHeartState::Awake => "awake",
921        }
922    }
923}
924
925#[derive(Clone, Debug)]
926#[derive_const(PartialEq)]
927pub enum TestBlockMode {
928    Start,
929    Log,
930    Fail,
931    Accept,
932}
933
934impl PropertyEnum for TestBlockMode {
935    fn as_str(&self) -> &str {
936        match self {
937            TestBlockMode::Start => "start",
938            TestBlockMode::Log => "log",
939            TestBlockMode::Fail => "fail",
940            TestBlockMode::Accept => "accept",
941        }
942    }
943}
944
945#[derive(Clone, Debug)]
946#[derive_const(PartialEq)]
947pub enum Pose {
948    Standing,
949    Sitting,
950    Running,
951    Star,
952}
953
954impl PropertyEnum for Pose {
955    fn as_str(&self) -> &str {
956        match self {
957            Pose::Standing => "standing",
958            Pose::Sitting => "sitting",
959            Pose::Running => "running",
960            Pose::Star => "star",
961        }
962    }
963}
964
965#[derive(Clone, Debug)]
966#[derive_const(PartialEq)]
967pub enum PotentSulfurState {
968    Dry,
969    Wet,
970    Dormant,
971    Erupting,
972    Continuous,
973}
974
975impl PropertyEnum for PotentSulfurState {
976    fn as_str(&self) -> &str {
977        match self {
978            PotentSulfurState::Dry => "dry",
979            PotentSulfurState::Wet => "wet",
980            PotentSulfurState::Dormant => "dormant",
981            PotentSulfurState::Erupting => "erupting",
982            PotentSulfurState::Continuous => "continuous",
983        }
984    }
985}
986
987impl PropertyEnum for Axis {
988    fn as_str(&self) -> &str {
989        self.as_str()
990    }
991}
992
993pub struct BlockStateProperties;
994
995//TODO: These got quickly implemented so the ordering might be off. Fix in the future.
996impl BlockStateProperties {
997    pub const ATTACHED: BoolProperty = BoolProperty::new("attached");
998    pub const BERRIES: BoolProperty = BoolProperty::new("berries");
999    pub const BLOOM: BoolProperty = BoolProperty::new("bloom");
1000    pub const BOTTOM: BoolProperty = BoolProperty::new("bottom");
1001    pub const CAN_SUMMON: BoolProperty = BoolProperty::new("can_summon");
1002    pub const CONDITIONAL: BoolProperty = BoolProperty::new("conditional");
1003    pub const DISARMED: BoolProperty = BoolProperty::new("disarmed");
1004    pub const DRAG: BoolProperty = BoolProperty::new("drag");
1005    pub const ENABLED: BoolProperty = BoolProperty::new("enabled");
1006    pub const EXTENDED: BoolProperty = BoolProperty::new("extended");
1007    pub const EYE: BoolProperty = BoolProperty::new("eye");
1008    pub const FALLING: BoolProperty = BoolProperty::new("falling");
1009    pub const HANGING: BoolProperty = BoolProperty::new("hanging");
1010    pub const HAS_BOTTLE_0: BoolProperty = BoolProperty::new("has_bottle_0");
1011    pub const HAS_BOTTLE_1: BoolProperty = BoolProperty::new("has_bottle_1");
1012    pub const HAS_BOTTLE_2: BoolProperty = BoolProperty::new("has_bottle_2");
1013    pub const HAS_RECORD: BoolProperty = BoolProperty::new("has_record");
1014    pub const HAS_BOOK: BoolProperty = BoolProperty::new("has_book");
1015    pub const INVERTED: BoolProperty = BoolProperty::new("inverted");
1016    pub const IN_WALL: BoolProperty = BoolProperty::new("in_wall");
1017    pub const LIT: BoolProperty = BoolProperty::new("lit");
1018    pub const LOCKED: BoolProperty = BoolProperty::new("locked");
1019    pub const NATURAL: BoolProperty = BoolProperty::new("natural");
1020    pub const OCCUPIED: BoolProperty = BoolProperty::new("occupied");
1021    pub const OPEN: BoolProperty = BoolProperty::new("open");
1022    pub const PERSISTENT: BoolProperty = BoolProperty::new("persistent");
1023    pub const POWERED: BoolProperty = BoolProperty::new("powered");
1024    pub const SHORT: BoolProperty = BoolProperty::new("short");
1025    pub const SHRIEKING: BoolProperty = BoolProperty::new("shrieking");
1026    pub const SIGNAL_FIRE: BoolProperty = BoolProperty::new("signal_fire");
1027    pub const SNOWY: BoolProperty = BoolProperty::new("snowy");
1028    pub const TIP: BoolProperty = BoolProperty::new("tip");
1029    pub const TRIGGERED: BoolProperty = BoolProperty::new("triggered");
1030    pub const UNSTABLE: BoolProperty = BoolProperty::new("unstable");
1031    pub const WATERLOGGED: BoolProperty = BoolProperty::new("waterlogged");
1032    pub const HORIZONTAL_AXIS: EnumProperty<Axis> = EnumProperty::new("axis", &[Axis::X, Axis::Z]);
1033    pub const AXIS: EnumProperty<Axis> = EnumProperty::new("axis", &[Axis::X, Axis::Y, Axis::Z]);
1034    pub const UP: BoolProperty = BoolProperty::new("up");
1035    pub const DOWN: BoolProperty = BoolProperty::new("down");
1036    pub const NORTH: BoolProperty = BoolProperty::new("north");
1037    pub const EAST: BoolProperty = BoolProperty::new("east");
1038    pub const SOUTH: BoolProperty = BoolProperty::new("south");
1039    pub const WEST: BoolProperty = BoolProperty::new("west");
1040    pub const FACING: EnumProperty<Direction> = EnumProperty::with_comparison_order(
1041        "facing",
1042        &[
1043            Direction::North,
1044            Direction::East,
1045            Direction::South,
1046            Direction::West,
1047            Direction::Up,
1048            Direction::Down,
1049        ],
1050        &[
1051            Direction::Down,
1052            Direction::Up,
1053            Direction::North,
1054            Direction::South,
1055            Direction::West,
1056            Direction::East,
1057        ],
1058    );
1059    pub const FACING_HOPPER: EnumProperty<Direction> = EnumProperty::new(
1060        "facing",
1061        &[
1062            Direction::Down,
1063            Direction::North,
1064            Direction::South,
1065            Direction::West,
1066            Direction::East,
1067        ],
1068    );
1069    pub const HORIZONTAL_FACING: EnumProperty<Direction> = EnumProperty::new(
1070        "facing",
1071        &[
1072            Direction::North,
1073            Direction::South,
1074            Direction::West,
1075            Direction::East,
1076        ],
1077    );
1078    pub const FLOWER_AMOUNT: IntProperty = IntProperty::new("flower_amount", 1, 4);
1079    pub const SEGMENT_AMOUNT: IntProperty = IntProperty::new("segment_amount", 1, 4);
1080
1081    // Additional enum types needed for properties
1082    pub const ORIENTATION: EnumProperty<FrontAndTop> = EnumProperty::new(
1083        "orientation",
1084        &[
1085            FrontAndTop::DownEast,
1086            FrontAndTop::DownNorth,
1087            FrontAndTop::DownSouth,
1088            FrontAndTop::DownWest,
1089            FrontAndTop::UpEast,
1090            FrontAndTop::UpNorth,
1091            FrontAndTop::UpSouth,
1092            FrontAndTop::UpWest,
1093            FrontAndTop::WestUp,
1094            FrontAndTop::EastUp,
1095            FrontAndTop::NorthUp,
1096            FrontAndTop::SouthUp,
1097        ],
1098    );
1099    pub const ATTACH_FACE: EnumProperty<AttachFace> = EnumProperty::new(
1100        "face",
1101        &[AttachFace::Floor, AttachFace::Wall, AttachFace::Ceiling],
1102    );
1103    pub const BELL_ATTACHMENT: EnumProperty<BellAttachType> = EnumProperty::new(
1104        "attachment",
1105        &[
1106            BellAttachType::Floor,
1107            BellAttachType::Ceiling,
1108            BellAttachType::SingleWall,
1109            BellAttachType::DoubleWall,
1110        ],
1111    );
1112    pub const EAST_WALL: EnumProperty<WallSide> =
1113        EnumProperty::new("east", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1114    pub const NORTH_WALL: EnumProperty<WallSide> =
1115        EnumProperty::new("north", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1116    pub const SOUTH_WALL: EnumProperty<WallSide> =
1117        EnumProperty::new("south", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1118    pub const WEST_WALL: EnumProperty<WallSide> =
1119        EnumProperty::new("west", &[WallSide::None, WallSide::Low, WallSide::Tall]);
1120    pub const EAST_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1121        "east",
1122        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1123    );
1124    pub const NORTH_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1125        "north",
1126        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1127    );
1128    pub const SOUTH_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1129        "south",
1130        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1131    );
1132    pub const WEST_REDSTONE: EnumProperty<RedstoneSide> = EnumProperty::new(
1133        "west",
1134        &[RedstoneSide::Up, RedstoneSide::Side, RedstoneSide::None],
1135    );
1136    pub const DOUBLE_BLOCK_HALF: EnumProperty<DoubleBlockHalf> =
1137        EnumProperty::new("half", &[DoubleBlockHalf::Upper, DoubleBlockHalf::Lower]);
1138    pub const HALF: EnumProperty<Half> = EnumProperty::new("half", &[Half::Top, Half::Bottom]);
1139    pub const SIDE_CHAIN_PART: EnumProperty<SideChainPart> = EnumProperty::new(
1140        "side_chain",
1141        &[
1142            SideChainPart::Unconnected,
1143            SideChainPart::Right,
1144            SideChainPart::Center,
1145            SideChainPart::Left,
1146        ],
1147    );
1148    pub const RAIL_SHAPE: EnumProperty<RailShape> = EnumProperty::new(
1149        "shape",
1150        &[
1151            RailShape::NorthSouth,
1152            RailShape::EastWest,
1153            RailShape::AscendingEast,
1154            RailShape::AscendingWest,
1155            RailShape::AscendingNorth,
1156            RailShape::AscendingSouth,
1157            RailShape::SouthEast,
1158            RailShape::SouthWest,
1159            RailShape::NorthWest,
1160            RailShape::NorthEast,
1161        ],
1162    );
1163    pub const RAIL_SHAPE_STRAIGHT: EnumProperty<RailShape> = EnumProperty::new(
1164        "shape",
1165        &[
1166            RailShape::NorthSouth,
1167            RailShape::EastWest,
1168            RailShape::AscendingEast,
1169            RailShape::AscendingWest,
1170            RailShape::AscendingNorth,
1171            RailShape::AscendingSouth,
1172        ],
1173    );
1174
1175    // Age properties
1176    pub const AGE_1: IntProperty = IntProperty::new("age", 0, 1);
1177    pub const AGE_2: IntProperty = IntProperty::new("age", 0, 2);
1178    pub const AGE_3: IntProperty = IntProperty::new("age", 0, 3);
1179    pub const AGE_4: IntProperty = IntProperty::new("age", 0, 4);
1180    pub const AGE_5: IntProperty = IntProperty::new("age", 0, 5);
1181    pub const AGE_7: IntProperty = IntProperty::new("age", 0, 7);
1182    pub const AGE_15: IntProperty = IntProperty::new("age", 0, 15);
1183    pub const AGE_25: IntProperty = IntProperty::new("age", 0, 25);
1184
1185    // Other integer properties
1186    pub const BITES: IntProperty = IntProperty::new("bites", 0, 6);
1187    pub const CANDLES: IntProperty = IntProperty::new("candles", 1, 4);
1188    pub const DELAY: IntProperty = IntProperty::new("delay", 1, 4);
1189    pub const DISTANCE: IntProperty = IntProperty::new("distance", 1, 7);
1190    pub const EGGS: IntProperty = IntProperty::new("eggs", 1, 4);
1191    pub const HATCH: IntProperty = IntProperty::new("hatch", 0, 2);
1192    pub const LAYERS: IntProperty = IntProperty::new("layers", 1, 8);
1193    pub const LEVEL_CAULDRON: IntProperty = IntProperty::new("level", 1, 3);
1194    pub const LEVEL_COMPOSTER: IntProperty = IntProperty::new("level", 0, 8);
1195    pub const LEVEL_FLOWING: IntProperty = IntProperty::new("level", 1, 8);
1196    pub const LEVEL_HONEY: IntProperty = IntProperty::new("honey_level", 0, 5);
1197    pub const LEVEL: IntProperty = IntProperty::new("level", 0, 15);
1198    pub const MOISTURE: IntProperty = IntProperty::new("moisture", 0, 7);
1199    pub const NOTE: IntProperty = IntProperty::new("note", 0, 24);
1200    pub const PICKLES: IntProperty = IntProperty::new("pickles", 1, 4);
1201    pub const POWER: IntProperty = IntProperty::new("power", 0, 15);
1202    pub const STAGE: IntProperty = IntProperty::new("stage", 0, 1);
1203    pub const STABILITY_DISTANCE: IntProperty = IntProperty::new("distance", 0, 7);
1204    pub const RESPAWN_ANCHOR_CHARGES: IntProperty = IntProperty::new("charges", 0, 4);
1205    pub const DRIED_GHAST_HYDRATION_LEVELS: IntProperty = IntProperty::new("hydration", 0, 3);
1206    pub const ROTATION_16: IntProperty = IntProperty::new("rotation", 0, 15);
1207    pub const DUSTED: IntProperty = IntProperty::new("dusted", 0, 3);
1208
1209    // Enum properties
1210    pub const BED_PART: EnumProperty<BedPart> =
1211        EnumProperty::new("part", &[BedPart::Head, BedPart::Foot]);
1212    pub const CHEST_TYPE: EnumProperty<ChestType> = EnumProperty::new(
1213        "type",
1214        &[ChestType::Single, ChestType::Left, ChestType::Right],
1215    );
1216    pub const MODE_COMPARATOR: EnumProperty<ComparatorMode> =
1217        EnumProperty::new("mode", &[ComparatorMode::Compare, ComparatorMode::Subtract]);
1218    pub const DOOR_HINGE: EnumProperty<DoorHingeSide> =
1219        EnumProperty::new("hinge", &[DoorHingeSide::Left, DoorHingeSide::Right]);
1220    pub const NOTEBLOCK_INSTRUMENT: EnumProperty<NoteBlockInstrument> = EnumProperty::new(
1221        "instrument",
1222        &[
1223            NoteBlockInstrument::Harp,
1224            NoteBlockInstrument::Basedrum,
1225            NoteBlockInstrument::Snare,
1226            NoteBlockInstrument::Hat,
1227            NoteBlockInstrument::Bass,
1228            NoteBlockInstrument::Flute,
1229            NoteBlockInstrument::Bell,
1230            NoteBlockInstrument::Guitar,
1231            NoteBlockInstrument::Chime,
1232            NoteBlockInstrument::Xylophone,
1233            NoteBlockInstrument::IronXylophone,
1234            NoteBlockInstrument::CowBell,
1235            NoteBlockInstrument::Didgeridoo,
1236            NoteBlockInstrument::Bit,
1237            NoteBlockInstrument::Banjo,
1238            NoteBlockInstrument::Pling,
1239            NoteBlockInstrument::Trumpet,
1240            NoteBlockInstrument::TrumpetExposed,
1241            NoteBlockInstrument::TrumpetOxidized,
1242            NoteBlockInstrument::TrumpetWeathered,
1243            NoteBlockInstrument::Zombie,
1244            NoteBlockInstrument::Skeleton,
1245            NoteBlockInstrument::Creeper,
1246            NoteBlockInstrument::Dragon,
1247            NoteBlockInstrument::WitherSkeleton,
1248            NoteBlockInstrument::Piglin,
1249            NoteBlockInstrument::CustomHead,
1250        ],
1251    );
1252    pub const PISTON_TYPE: EnumProperty<PistonType> =
1253        EnumProperty::new("type", &[PistonType::Normal, PistonType::Sticky]);
1254    pub const SLAB_TYPE: EnumProperty<SlabType> =
1255        EnumProperty::new("type", &[SlabType::Top, SlabType::Bottom, SlabType::Double]);
1256    pub const STAIRS_SHAPE: EnumProperty<StairsShape> = EnumProperty::new(
1257        "shape",
1258        &[
1259            StairsShape::Straight,
1260            StairsShape::InnerLeft,
1261            StairsShape::InnerRight,
1262            StairsShape::OuterLeft,
1263            StairsShape::OuterRight,
1264        ],
1265    );
1266    pub const STRUCTUREBLOCK_MODE: EnumProperty<StructureMode> = EnumProperty::new(
1267        "mode",
1268        &[
1269            StructureMode::Save,
1270            StructureMode::Load,
1271            StructureMode::Corner,
1272            StructureMode::Data,
1273        ],
1274    );
1275    pub const BAMBOO_LEAVES: EnumProperty<BambooLeaves> = EnumProperty::new(
1276        "leaves",
1277        &[BambooLeaves::None, BambooLeaves::Small, BambooLeaves::Large],
1278    );
1279    pub const TILT: EnumProperty<Tilt> = EnumProperty::new(
1280        "tilt",
1281        &[Tilt::None, Tilt::Unstable, Tilt::Partial, Tilt::Full],
1282    );
1283    pub const VERTICAL_DIRECTION: EnumProperty<Direction> = EnumProperty::with_comparison_order(
1284        "vertical_direction",
1285        &[Direction::Up, Direction::Down],
1286        &[Direction::Down, Direction::Up],
1287    );
1288    pub const DRIPSTONE_THICKNESS: EnumProperty<DripstoneThickness> = EnumProperty::new(
1289        "thickness",
1290        &[
1291            DripstoneThickness::TipMerge,
1292            DripstoneThickness::Tip,
1293            DripstoneThickness::Frustum,
1294            DripstoneThickness::Middle,
1295            DripstoneThickness::Base,
1296        ],
1297    );
1298    pub const SPELEOTHEM_THICKNESS: EnumProperty<SpeleothemThickness> = EnumProperty::new(
1299        "thickness",
1300        &[
1301            SpeleothemThickness::TipMerge,
1302            SpeleothemThickness::Tip,
1303            SpeleothemThickness::Frustum,
1304            SpeleothemThickness::Middle,
1305            SpeleothemThickness::Base,
1306        ],
1307    );
1308    pub const SCULK_SENSOR_PHASE: EnumProperty<SculkSensorPhase> = EnumProperty::new(
1309        "sculk_sensor_phase",
1310        &[
1311            SculkSensorPhase::Inactive,
1312            SculkSensorPhase::Active,
1313            SculkSensorPhase::Cooldown,
1314        ],
1315    );
1316    pub const TRIAL_SPAWNER_STATE: EnumProperty<TrialSpawnerState> = EnumProperty::new(
1317        "trial_spawner_state",
1318        &[
1319            TrialSpawnerState::Inactive,
1320            TrialSpawnerState::WaitingForPlayers,
1321            TrialSpawnerState::Active,
1322            TrialSpawnerState::WaitingForRewardEjection,
1323            TrialSpawnerState::EjectingReward,
1324            TrialSpawnerState::Cooldown,
1325        ],
1326    );
1327    pub const VAULT_STATE: EnumProperty<VaultState> = EnumProperty::new(
1328        "vault_state",
1329        &[
1330            VaultState::Inactive,
1331            VaultState::Active,
1332            VaultState::Unlocking,
1333            VaultState::Ejecting,
1334        ],
1335    );
1336    pub const CREAKING_HEART_STATE: EnumProperty<CreakingHeartState> = EnumProperty::new(
1337        "creaking_heart_state",
1338        &[
1339            CreakingHeartState::Uprooted,
1340            CreakingHeartState::Dormant,
1341            CreakingHeartState::Awake,
1342        ],
1343    );
1344    pub const TEST_BLOCK_MODE: EnumProperty<TestBlockMode> = EnumProperty::new(
1345        "mode",
1346        &[
1347            TestBlockMode::Start,
1348            TestBlockMode::Log,
1349            TestBlockMode::Fail,
1350            TestBlockMode::Accept,
1351        ],
1352    );
1353    pub const COPPER_GOLEM_POSE: EnumProperty<Pose> = EnumProperty::new(
1354        "copper_golem_pose",
1355        &[Pose::Standing, Pose::Sitting, Pose::Running, Pose::Star],
1356    );
1357    pub const POTENT_SULFUR_STATE: EnumProperty<PotentSulfurState> = EnumProperty::new(
1358        "potent_sulfur_state",
1359        &[
1360            PotentSulfurState::Dry,
1361            PotentSulfurState::Wet,
1362            PotentSulfurState::Dormant,
1363            PotentSulfurState::Erupting,
1364            PotentSulfurState::Continuous,
1365        ],
1366    );
1367
1368    // Additional boolean properties
1369    pub const SLOT_0_OCCUPIED: BoolProperty = BoolProperty::new("slot_0_occupied");
1370    pub const SLOT_1_OCCUPIED: BoolProperty = BoolProperty::new("slot_1_occupied");
1371    pub const SLOT_2_OCCUPIED: BoolProperty = BoolProperty::new("slot_2_occupied");
1372    pub const SLOT_3_OCCUPIED: BoolProperty = BoolProperty::new("slot_3_occupied");
1373    pub const SLOT_4_OCCUPIED: BoolProperty = BoolProperty::new("slot_4_occupied");
1374    pub const SLOT_5_OCCUPIED: BoolProperty = BoolProperty::new("slot_5_occupied");
1375    pub const CRACKED: BoolProperty = BoolProperty::new("cracked");
1376    pub const CRAFTING: BoolProperty = BoolProperty::new("crafting");
1377    pub const OMINOUS: BoolProperty = BoolProperty::new("ominous");
1378    pub const MAP: BoolProperty = BoolProperty::new("map");
1379}