use std::{collections::BTreeMap, ops::Bound};
pub mod helpers;
pub mod keyframe_trait;
use bevy::{
prelude::{Component, Entity, Resource},
reflect::Reflect,
utils::HashMap,
};
use keyframe_trait::{LinearKeyFrame, PulseKeyframe, SteppedKeyframe};
pub enum CurveType {
Stepped,
Pulse,
Linear,
}
pub enum CurveData {
Stepped(BTreeMap<GameTick, Box<dyn SteppedKeyframe>>),
Pulse(BTreeMap<GameTick, Box<dyn PulseKeyframe>>),
Linear(BTreeMap<GameTick, Box<dyn LinearKeyFrame>>),
}
impl CurveData {
pub fn insert(&mut self, tick: GameTick, data: CurveKeyFrame) {
match (self, data) {
(CurveData::Stepped(map), CurveKeyFrame::Stepped(data)) => {
map.insert(tick, data);
}
(CurveData::Pulse(map), CurveKeyFrame::Pulse(data)) => {
map.insert(tick, data);
}
(CurveData::Linear(map), CurveKeyFrame::Linear(data)) => {
map.insert(tick, data);
}
_ => {}
};
}
pub fn remove(&mut self, tick: GameTick) {
match self {
CurveData::Stepped(map) => {
map.remove(&tick);
}
CurveData::Pulse(map) => {
map.remove(&tick);
}
CurveData::Linear(map) => {
map.remove(&tick);
}
};
}
pub fn get_keyframe(&self, tick: GameTick) -> Option<CurveKeyFrame> {
match self {
CurveData::Stepped(map) => Some(CurveKeyFrame::Stepped(map.get(&tick)?.clone())),
CurveData::Pulse(map) => Some(CurveKeyFrame::Pulse(map.get(&tick)?.clone())),
CurveData::Linear(map) => Some(CurveKeyFrame::Linear(map.get(&tick)?.clone())),
}
}
pub fn get_state(&self, tick: GameTick) -> Option<CurveKeyFrame> {
match self {
CurveData::Stepped(map) => {
let data = match map.get(&tick) {
Some(frame) => frame.clone(),
None => match map.range((Bound::Unbounded, Bound::Excluded(&tick))).last() {
Some(data) => data.1.clone(),
None => return None,
},
};
Some(CurveKeyFrame::Stepped(data))
}
CurveData::Pulse(map) => Some(CurveKeyFrame::Pulse(map.get(&tick)?.clone())),
CurveData::Linear(map) => {
let prev_frame = match map.range((Bound::Unbounded, Bound::Excluded(&tick))).last()
{
Some(data) => data.clone(),
None => match map.get(&tick) {
Some(frame) => return Some(CurveKeyFrame::Linear(frame.clone())),
None => return None,
},
};
let next_frame = match map.get_key_value(&tick) {
Some(frame) => frame.clone(),
None => match map.range((Bound::Excluded(&tick), Bound::Unbounded)).next() {
Some(data) => data.clone(),
None => return Some(CurveKeyFrame::Linear(prev_frame.1.clone())),
},
};
let ratio = (tick as f32 - *prev_frame.0 as f32)
/ (*next_frame.0 as f32 - *prev_frame.0 as f32);
Some(CurveKeyFrame::Linear(
prev_frame.1.lerp(&next_frame.1, ratio),
))
}
}
}
pub fn iter_future_curves(
&self,
tick: GameTick,
) -> Box<dyn Iterator<Item = (GameTick, CurveKeyFrame)> + '_> {
match self {
CurveData::Stepped(map) => {
let new_map = map
.range((Bound::Excluded(&tick), Bound::Unbounded))
.map(|(tick, data)| (tick.clone(), CurveKeyFrame::Stepped(data.clone())));
Box::new(new_map)
}
CurveData::Pulse(map) => {
let new_map = map
.range((Bound::Excluded(&tick), Bound::Unbounded))
.map(|(tick, data)| (tick.clone(), CurveKeyFrame::Pulse(data.clone())));
Box::new(new_map)
}
CurveData::Linear(map) => {
let new_map = map
.range((Bound::Excluded(&tick), Bound::Unbounded))
.map(|(tick, data)| (tick.clone(), CurveKeyFrame::Linear(data.clone())));
Box::new(new_map)
}
}
}
pub fn next_curve(&self, tick: GameTick) -> Option<(GameTick, CurveKeyFrame)> {
match self {
CurveData::Stepped(map) => {
let data = match map.range((Bound::Excluded(&tick), Bound::Unbounded)).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Stepped(data.1.clone())))
}
CurveData::Pulse(map) => {
let data = match map.range((Bound::Excluded(&tick), Bound::Unbounded)).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Pulse(data.1.clone())))
}
CurveData::Linear(map) => {
let data = match map.range((Bound::Excluded(&tick), Bound::Unbounded)).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Linear(data.1.clone())))
}
}
}
pub fn prev_curve(&self, tick: GameTick) -> Option<(GameTick, CurveKeyFrame)> {
match self {
CurveData::Stepped(map) => {
let data = match map.range((Bound::Unbounded, Bound::Excluded(&tick))).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Stepped(data.1.clone())))
}
CurveData::Pulse(map) => {
let data = match map.range((Bound::Unbounded, Bound::Excluded(&tick))).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Pulse(data.1.clone())))
}
CurveData::Linear(map) => {
let data = match map.range((Bound::Unbounded, Bound::Excluded(&tick))).next() {
Some(data) => data,
None => return None,
};
Some((data.0.clone(), CurveKeyFrame::Linear(data.1.clone())))
}
}
}
}
#[derive(Clone)]
pub enum CurveKeyFrame {
Stepped(Box<dyn SteppedKeyframe>),
Pulse(Box<dyn PulseKeyframe>),
Linear(Box<dyn LinearKeyFrame>),
}
impl CurveKeyFrame {
pub fn curve_type(&self) -> CurveType {
match self {
CurveKeyFrame::Stepped(_) => CurveType::Stepped,
CurveKeyFrame::Pulse(_) => CurveType::Pulse,
CurveKeyFrame::Linear(_) => CurveType::Linear,
}
}
pub fn as_reflect(&self) -> Box<dyn Reflect> {
match self {
CurveKeyFrame::Stepped(data) => data.clone_value(),
CurveKeyFrame::Pulse(data) => data.clone_value(),
CurveKeyFrame::Linear(data) => data.clone_value(),
}
}
pub fn type_name(&self) -> String {
match self {
CurveKeyFrame::Stepped(data) => data.type_name().to_owned(),
CurveKeyFrame::Pulse(data) => data.type_name().to_owned(),
CurveKeyFrame::Linear(data) => data.type_name().to_owned(),
}
}
pub fn downcast<T: 'static>(&self) -> &T {
match self {
CurveKeyFrame::Stepped(data) => data.self_as_any().downcast_ref::<T>().unwrap(),
CurveKeyFrame::Pulse(data) => data.self_as_any().downcast_ref::<T>().unwrap(),
CurveKeyFrame::Linear(data) => data.self_as_any().downcast_ref::<T>().unwrap(),
}
}
pub fn try_downcast<T: 'static>(&self) -> Option<&T> {
match self {
CurveKeyFrame::Stepped(data) => data.self_as_any().downcast_ref::<T>(),
CurveKeyFrame::Pulse(data) => data.self_as_any().downcast_ref::<T>(),
CurveKeyFrame::Linear(data) => data.self_as_any().downcast_ref::<T>(),
}
}
}
pub struct CurveInfo {
pub component_type_name: String,
pub curve_type: CurveType,
}
#[derive(Component)]
pub struct ObjectId(pub u32);
#[derive(Resource)]
pub struct ObjectTimings {
object_timings: HashMap<GameTick, Vec<ObjectId>>,
}
#[derive(Component)]
pub struct ObjectState {
object_created: GameTick,
object_destroyed: Option<GameTick>,
curves: HashMap<String, (CurveInfo, CurveData)>,
}
impl ObjectState {
pub fn new(tick: GameTick) -> Self {
Self {
object_created: tick,
object_destroyed: None,
curves: HashMap::new(),
}
}
pub fn created_tick(&self) -> GameTick {
self.object_created
}
pub fn destroyed_tick(&self) -> Option<GameTick> {
self.object_destroyed
}
pub fn is_destroyed(&self) -> bool {
self.object_destroyed.is_some()
}
pub fn is_alive(&self) -> bool {
self.object_destroyed.is_none()
}
pub fn destroy(&mut self, tick: GameTick) {
self.object_destroyed = Some(tick);
}
pub fn get_curves(&self, curve_name: &str) -> Option<&(CurveInfo, CurveData)> {
self.curves.get(curve_name)
}
pub fn register_new_curve(
&mut self,
curve_name: &str,
curve_type: CurveType,
component_type_name: String,
) {
if let None = self.curves.get(curve_name) {
let map = match curve_type {
CurveType::Stepped => CurveData::Stepped(BTreeMap::new()),
CurveType::Pulse => CurveData::Pulse(BTreeMap::new()),
CurveType::Linear => CurveData::Linear(BTreeMap::new()),
};
self.curves.insert(
curve_name.to_owned(),
(
CurveInfo {
component_type_name,
curve_type,
},
map,
),
);
}
}
pub fn add_keyframe(&mut self, curve_name: &str, tick: GameTick, keyframe: CurveKeyFrame) {
match self.curves.get_mut(curve_name) {
Some((info, map)) => {
if info.component_type_name != keyframe.type_name() {
panic!(
"Attempted to insert a different component type into curve {:?}",
curve_name
);
}
map.insert(tick, keyframe);
}
None => {
self.register_new_curve(curve_name, keyframe.curve_type(), keyframe.type_name());
self.add_keyframe(curve_name, tick, keyframe);
}
}
}
pub fn add_stepped_keyframe<T: SteppedKeyframe>(
&mut self,
curve_name: &str,
tick: GameTick,
keyframe: T,
) {
self.add_keyframe(curve_name, tick, CurveKeyFrame::Stepped(Box::new(keyframe)));
}
pub fn add_linear_keyframe<T: LinearKeyFrame>(
&mut self,
curve_name: &str,
tick: GameTick,
keyframe: T,
) {
self.add_keyframe(curve_name, tick, CurveKeyFrame::Linear(Box::new(keyframe)));
}
pub fn add_pulse_keyframe<T: PulseKeyframe>(
&mut self,
curve_name: &str,
tick: GameTick,
keyframe: T,
) {
self.add_keyframe(curve_name, tick, CurveKeyFrame::Pulse(Box::new(keyframe)));
}
pub fn remove_keyframe(&mut self, curve_name: &str, tick: GameTick) {
if let Some((_, map)) = self.curves.get_mut(curve_name) {
map.remove(tick);
}
}
pub fn get_keyframes_in_tick(&self, tick: GameTick) -> Vec<(String, CurveKeyFrame)> {
let mut return_vec = vec![];
for (curve_name, (curve_info, curve_map)) in self.curves.iter() {
match curve_info.curve_type {
CurveType::Stepped => {
if let Some(component) = curve_map.get_keyframe(tick.clone()) {
return_vec.push((curve_name.clone(), component.clone()))
}
}
CurveType::Pulse => {
if let Some(component) = curve_map.get_keyframe(tick.clone()) {
return_vec.push((curve_name.clone(), component.clone()))
}
}
CurveType::Linear => {
if let Some(component) = curve_map.get_keyframe(tick.clone()) {
return_vec.push((curve_name.clone(), component.clone()))
}
}
}
}
return_vec
}
pub fn get_object_state_for_tick(&self, tick: GameTick) -> Vec<(String, CurveKeyFrame)> {
let mut return_vec = vec![];
for (curve_name, (_curve_info, curve_map)) in self.curves.iter() {
if let Some(frame) = curve_map.get_state(tick) {
return_vec.push((curve_name.clone(), frame));
}
}
return_vec
}
}
#[derive(Resource)]
pub struct ObjectStateCurveMap {
object_state: HashMap<ObjectId, Entity>,
}
pub type GameTick = u64;