forked from StrafesNET/roblox-bot-player
158 lines
5.0 KiB
Rust
158 lines
5.0 KiB
Rust
use glam::Vec3Swizzles;
|
|
use strafesnet_common::timer::{Scaled,Timer,TimerState};
|
|
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
|
use strafesnet_roblox_bot_file::v0::{EventType,Head,Timed};
|
|
|
|
use crate::bot::CompleteBot;
|
|
use crate::state::PlaybackState;
|
|
|
|
fn vector3_to_glam(v:&strafesnet_roblox_bot_file::v0::Vector3)->glam::Vec3{
|
|
glam::vec3(v.x,v.y,v.z)
|
|
}
|
|
|
|
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
|
pub enum TimeInner{}
|
|
pub type Time=strafesnet_common::integer::Time<TimeInner>;
|
|
|
|
/// A playback context. Advance time and then generate a camera position to pass to the renderer.
|
|
pub struct PlaybackHead{
|
|
head:Head,
|
|
timer:Timer<Scaled<SessionTimeInner,TimeInner>>,
|
|
state:PlaybackState,
|
|
}
|
|
impl PlaybackHead{
|
|
pub fn new(bot:&CompleteBot,time:SessionTime)->Self{
|
|
let timer=Timer::unpaused(time,Time::ZERO);
|
|
let head=Head::after_time(bot.timelines(),bot.time(Time::ZERO).into());
|
|
let mut state=PlaybackState::new();
|
|
state.process_head(bot.timelines(),&head);
|
|
Self{
|
|
head,
|
|
timer,
|
|
state,
|
|
}
|
|
}
|
|
pub const fn state(&self)->&PlaybackState{
|
|
&self.state
|
|
}
|
|
pub fn time(&self,time:SessionTime)->Time{
|
|
self.timer.time(time)
|
|
}
|
|
pub fn timer(&self)->&Timer<Scaled<SessionTimeInner,TimeInner>>{
|
|
&self.timer
|
|
}
|
|
pub fn set_paused(&mut self,time:SessionTime,paused:bool){
|
|
_=self.timer.set_paused(time,paused);
|
|
}
|
|
pub fn set_time(&mut self,bot:&CompleteBot,time:SessionTime,new_time:Time){
|
|
let new_time=new_time.rem_euclid(bot.duration().coerce());
|
|
self.timer.set_time(time,new_time);
|
|
// reset head
|
|
self.head=Head::after_time(bot.timelines(),bot.time(new_time).into());
|
|
|
|
self.state=PlaybackState::new();
|
|
self.state.process_head(bot.timelines(),&self.head);
|
|
}
|
|
pub fn get_scale(&self)->strafesnet_common::integer::Ratio64{
|
|
self.timer.get_scale()
|
|
}
|
|
pub fn set_scale(&mut self,time:SessionTime,new_scale:strafesnet_common::integer::Ratio64){
|
|
self.timer.set_scale(time,new_scale);
|
|
}
|
|
pub fn next_event(&self,bot:&CompleteBot)->Option<Timed<EventType>>{
|
|
self.head.next_event(bot.timelines())
|
|
}
|
|
pub fn process_event(&mut self,bot:&CompleteBot,event_type:EventType){
|
|
self.state.process_event(bot.timelines(),event_type,self.head.get_event_index(event_type));
|
|
self.head.push(event_type);
|
|
}
|
|
pub fn advance_time(&mut self,bot:&CompleteBot,time:SessionTime){
|
|
let mut simulation_time=bot.time(self.time(time));
|
|
let mut time_float=simulation_time.into();
|
|
loop{
|
|
match self.next_event(bot){
|
|
Some(next_event)=>{
|
|
if next_event.time<time_float{
|
|
self.process_event(bot,next_event.event);
|
|
}else{
|
|
break;
|
|
}
|
|
},
|
|
None=>{
|
|
//reset playback
|
|
self.head=Head::after_time(bot.timelines(),bot.time(Time::ZERO).into());
|
|
self.state=PlaybackState::new();
|
|
self.state.process_head(bot.timelines(),&self.head);
|
|
|
|
// hack to wind back timer offset without precise session timestamp
|
|
let (mut state,paused)=self.timer.clone().into_state();
|
|
let offset=state.get_offset()-bot.duration().coerce();
|
|
state.set_offset(offset);
|
|
self.timer=Timer::from_state(state,paused);
|
|
|
|
// update loop variables
|
|
simulation_time-=bot.duration();
|
|
time_float=simulation_time.into();
|
|
},
|
|
}
|
|
}
|
|
}
|
|
fn interpolate_output<'a>(&self,bot:&'a CompleteBot,time:SessionTime)->InterpolateOutput<'a>{
|
|
let time=bot.time(self.time(time));
|
|
let event0=&bot.timelines().output_events[self.head.get_event_index(EventType::Output)-1];
|
|
let event1=&bot.timelines().output_events[self.head.get_event_index(EventType::Output)];
|
|
let t0=event0.time;
|
|
let t1=event1.time;
|
|
let time_float:f64=time.into();
|
|
let t=((time_float-t0)/(t1-t0)) as f32;
|
|
InterpolateOutput{
|
|
event0:&event0.event,
|
|
event1:&event1.event,
|
|
t:t,
|
|
}
|
|
}
|
|
pub fn get_position_angles(&self,bot:&CompleteBot,time:SessionTime)->(glam::Vec3,glam::Vec2){
|
|
let interp=self.interpolate_output(bot,time);
|
|
|
|
let p=interp.position();
|
|
let a=interp.angles();
|
|
|
|
(p-bot.world_offset()+CompleteBot::CAMERA_OFFSET,a.yx())
|
|
}
|
|
pub fn get_position(&self,bot:&CompleteBot,time:SessionTime)->glam::Vec3{
|
|
let interp=self.interpolate_output(bot,time);
|
|
interp.position()
|
|
}
|
|
pub fn get_velocity(&self,bot:&CompleteBot,time:SessionTime)->glam::Vec3{
|
|
let interp=self.interpolate_output(bot,time);
|
|
interp.velocity()
|
|
}
|
|
pub fn get_angles(&self,bot:&CompleteBot,time:SessionTime)->glam::Vec3{
|
|
let interp=self.interpolate_output(bot,time);
|
|
interp.angles()
|
|
}
|
|
}
|
|
|
|
struct InterpolateOutput<'a>{
|
|
event0:&'a strafesnet_roblox_bot_file::v0::OutputEvent,
|
|
event1:&'a strafesnet_roblox_bot_file::v0::OutputEvent,
|
|
t:f32,
|
|
}
|
|
impl InterpolateOutput<'_>{
|
|
fn position(&self)->glam::Vec3{
|
|
let p0=vector3_to_glam(&self.event0.position);
|
|
let p1=vector3_to_glam(&self.event1.position);
|
|
p0.lerp(p1,self.t)
|
|
}
|
|
fn velocity(&self)->glam::Vec3{
|
|
let v0=vector3_to_glam(&self.event0.velocity);
|
|
let v1=vector3_to_glam(&self.event1.velocity);
|
|
v0.lerp(v1,self.t)
|
|
}
|
|
fn angles(&self)->glam::Vec3{
|
|
let a0=vector3_to_glam(&self.event0.angles);
|
|
let a1=vector3_to_glam(&self.event1.angles);
|
|
a0.lerp(a1,self.t)
|
|
}
|
|
}
|