steel_registry/
timeline.rs1use 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#[derive(Debug)]
37pub struct TimeMarker {
38 pub name: &'static str,
39 pub ticks: i64,
40 pub show_in_commands: Option<bool>,
41}
42
43#[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); }
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); 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");