Skip to main content

steel_registry/
timeline.rs

1use rustc_hash::FxHashMap;
2use simdnbt::ToNbtTag;
3use simdnbt::owned::NbtTag;
4use steel_utils::Identifier;
5
6#[derive(Debug, Clone)]
7pub enum KeyframeValue {
8    Float(f32),
9    Int(i32),
10    Bool(bool),
11    String(&'static str),
12}
13
14#[derive(Debug, Clone)]
15pub enum Ease {
16    Named(&'static str),
17    CubicBezier([f32; 4]),
18}
19
20#[derive(Debug)]
21pub struct Keyframe {
22    pub ticks: i64,
23    pub value: KeyframeValue,
24}
25
26#[derive(Debug)]
27pub struct Track {
28    pub name: &'static str,
29    pub ease: Option<Ease>,
30    pub modifier: Option<&'static str>,
31    pub keyframes: &'static [Keyframe],
32}
33
34/// show_in_commands: None  = serialize marker as plain NbtTag::Int(ticks as i32)
35///                  Some(b)= serialize as compound {ticks: int, show_in_commands: byte}
36#[derive(Debug)]
37pub struct TimeMarker {
38    pub name: &'static str,
39    pub ticks: i64,
40    pub show_in_commands: Option<bool>,
41}
42
43/// Represents a timeline definition from a data pack JSON file.
44#[derive(Debug)]
45pub struct Timeline {
46    pub key: Identifier,
47    pub clock: Option<Identifier>,
48    pub period_ticks: Option<i64>,
49    pub tracks: &'static [Track],
50    pub time_markers: &'static [TimeMarker],
51}
52
53impl ToNbtTag for &Timeline {
54    fn to_nbt_tag(self) -> NbtTag {
55        use simdnbt::owned::{NbtCompound, NbtList, NbtTag};
56        let mut compound = NbtCompound::new();
57
58        if let Some(clock) = &self.clock {
59            compound.insert("clock", clock.to_string().as_str());
60        }
61        if let Some(pt) = self.period_ticks {
62            compound.insert("period_ticks", pt); // i64 → Long
63        }
64
65        let mut tracks_compound = NbtCompound::new();
66        for track in self.tracks {
67            let mut tc = NbtCompound::new();
68            if let Some(ease) = &track.ease {
69                match ease {
70                    Ease::Named(s) => tc.insert("ease", *s),
71                    Ease::CubicBezier(coeffs) => {
72                        let mut ec = NbtCompound::new();
73                        ec.insert(
74                            "cubic_bezier",
75                            NbtTag::List(NbtList::Float(coeffs.to_vec())),
76                        );
77                        tc.insert("ease", NbtTag::Compound(ec));
78                    }
79                }
80            }
81            if let Some(modifier) = track.modifier {
82                tc.insert("modifier", modifier);
83            }
84            let kf_compounds: Vec<NbtCompound> = track
85                .keyframes
86                .iter()
87                .map(|kf| {
88                    let mut kc = NbtCompound::new();
89                    kc.insert("ticks", kf.ticks); // i64 → Long
90                    match &kf.value {
91                        KeyframeValue::Float(f) => kc.insert("value", *f),
92                        KeyframeValue::Int(i) => kc.insert("value", *i),
93                        KeyframeValue::Bool(b) => kc.insert("value", if *b { 1i8 } else { 0i8 }),
94                        KeyframeValue::String(s) => kc.insert("value", *s),
95                    }
96                    kc
97                })
98                .collect();
99            tc.insert("keyframes", NbtTag::List(NbtList::Compound(kf_compounds)));
100            tracks_compound.insert(track.name, NbtTag::Compound(tc));
101        }
102        compound.insert("tracks", NbtTag::Compound(tracks_compound));
103
104        if !self.time_markers.is_empty() {
105            let mut mc = NbtCompound::new();
106            for marker in self.time_markers {
107                match marker.show_in_commands {
108                    None => mc.insert(marker.name, marker.ticks as i32),
109                    Some(show) => {
110                        let mut mcc = NbtCompound::new();
111                        mcc.insert("ticks", marker.ticks as i32);
112                        mcc.insert("show_in_commands", if show { 1i8 } else { 0i8 });
113                        mc.insert(marker.name, NbtTag::Compound(mcc));
114                    }
115                }
116            }
117            compound.insert("time_markers", NbtTag::Compound(mc));
118        }
119
120        NbtTag::Compound(compound)
121    }
122}
123
124pub type TimelineRef = &'static Timeline;
125
126pub struct TimelineRegistry {
127    timelines_by_id: Vec<TimelineRef>,
128    timelines_by_key: FxHashMap<Identifier, usize>,
129    tags: FxHashMap<Identifier, Vec<Identifier>>,
130    allows_registering: bool,
131}
132
133impl TimelineRegistry {
134    #[must_use]
135    pub fn new() -> Self {
136        Self {
137            timelines_by_id: Vec::new(),
138            timelines_by_key: FxHashMap::default(),
139            tags: FxHashMap::default(),
140            allows_registering: true,
141        }
142    }
143}
144
145crate::impl_standard_methods!(
146    TimelineRegistry,
147    TimelineRef,
148    timelines_by_id,
149    timelines_by_key,
150    allows_registering
151);
152
153crate::impl_registry!(
154    TimelineRegistry,
155    Timeline,
156    timelines_by_id,
157    timelines_by_key,
158    timelines
159);
160crate::impl_tagged_registry!(TimelineRegistry, timelines_by_key, "timeline");