use crate::in_game::types::duration;
use crate::replay::types::sealed::KeyFrameValue;
use serde_derive::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::default::Default;
use std::fmt::Debug;
use sysinfo::Pid;
use time::Duration;
mod sealed {
use crate::replay::types::{ColorValue, Vector3f};
pub trait KeyFrameValue {}
impl KeyFrameValue for String {}
impl KeyFrameValue for f64 {}
impl KeyFrameValue for ColorValue {}
impl KeyFrameValue for Vector3f {}
impl KeyFrameValue for bool {}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AVContainer {
#[default]
Webm,
Png,
PngAndDepth,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ColorValue {
pub r: f64,
pub g: f64,
pub b: f64,
pub a: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EasingType {
#[default]
Linear,
Snap,
SmoothStep,
SmootherStep,
QuadraticEaseIn,
QuadraticEaseOut,
QuadraticEaseInOut,
CubicEaseIn,
CubicEaseOut,
CubicEaseInOut,
QuarticEaseIn,
QuarticEaseOut,
QuarticEaseInOut,
QuinticEaseIn,
QuinticEaseOut,
QuinticEaseInOut,
SineEaseIn,
SineEaseOut,
SineEaseInOut,
CircularEaseIn,
CircularEaseOut,
CircularEaseInOut,
ExponentialEaseIn,
ExponentialEaseOut,
ExponentialEaseInOut,
ElasticEaseIn,
ElasticEaseOut,
ElasticEaseInOut,
BackEaseIn,
BackEaseOut,
BackEaseInOut,
BounceEaseIn,
BounceEaseOut,
BounceEaseInOut,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Game {
#[serde(rename = "processID")]
#[serde(with = "pid")]
process_id: Pid,
}
impl Game {
#[must_use]
pub fn process_id(&self) -> Pid {
self.process_id
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum HudCameraMode {
Top,
Fps,
Tps,
Focus,
Path,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct KeyFrameT<T: KeyFrameValue> {
pub blend: EasingType,
#[serde(with = "duration")]
pub time: Duration,
#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
pub value: T,
}
impl<T: KeyFrameValue> KeyFrameT<T> {
#[must_use]
pub fn new(value: T, time: Duration, blend: EasingType) -> Self {
Self { blend, time, value }
}
#[must_use]
pub fn new_default_blending(value: T, time: Duration) -> Self {
Self {
value,
time,
blend: EasingType::Linear,
}
}
}
impl<T: KeyFrameValue + PartialEq> PartialEq<KeyFrameT<T>> for KeyFrameT<T> {
fn eq(&self, other: &KeyFrameT<T>) -> bool {
self.value == other.value
&& ((self.blend == EasingType::Linear && other.blend == EasingType::Linear)
|| (self.blend == other.blend && self.time == other.time))
}
}
impl<T: KeyFrameValue + PartialEq> PartialOrd<Self> for KeyFrameT<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
#[test]
fn test_deserialize() {
use serde::Deserialize;
let json = serde_json::json!({
"blend": "linear",
"value": 10.0,
"time": 1.1001,
});
let keyframe = KeyFrameF64::deserialize(json).unwrap();
println!("{keyframe:?}");
let json = serde_json::to_value(keyframe).unwrap();
println!("{json}");
}
type KeyFrameString = KeyFrameT<String>;
type KeyFrameBool = KeyFrameT<bool>;
type KeyFrameColor = KeyFrameT<ColorValue>;
type KeyFrameF64 = KeyFrameT<f64>;
type KeyFrameVector3 = KeyFrameT<Vector3f>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Playback {
#[serde(with = "duration")]
pub length: Duration,
pub paused: bool,
pub seeking: bool,
pub speed: f64,
#[serde(with = "duration")]
pub time: Duration,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecordingState {
pub codec: AVContainer,
#[serde(with = "duration")]
pub current_time: Duration,
#[serde(with = "duration")]
pub end_time: Duration,
pub enforce_frame_rate: bool,
pub frames_per_second: i32,
pub height: i32,
pub lossless: bool,
pub path: String,
pub recording: bool,
pub replay_speed: f64,
#[serde(with = "duration")]
pub start_time: Duration,
pub width: i32,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Render {
pub floating_text: bool,
pub fog_of_war: bool,
pub health_bar_champions: bool,
pub health_bar_minions: bool,
pub health_bar_pets: bool,
pub health_bar_structures: bool,
pub health_bar_wards: bool,
pub interface_all: bool,
pub height_fog_enabled: bool,
pub interface_announce: bool,
pub interface_chat: bool,
pub interface_frames: bool,
pub interface_kill_callouts: bool,
pub interface_minimap: bool,
pub interface_replay: bool,
pub interface_score: bool,
pub interface_scoreboard: bool,
pub interface_target: bool,
pub interface_timeline: bool,
pub outline_hover: bool,
pub outline_select: bool,
pub particles: bool,
pub camera_attached: bool,
pub depth_fog_enabled: bool,
pub characters: bool,
pub depth_of_field_debug: bool,
pub depth_of_field_enabled: bool,
pub environment: bool,
pub banners: Option<bool>,
pub interface_neutral_timers: Option<bool>,
pub interface_quests: Option<bool>,
pub camera_move_speed: f64,
pub camera_look_speed: f64,
pub depth_fog_end: f64,
pub depth_fog_intensity: f64,
pub depth_fog_start: f64,
pub depth_of_field_circle: f64,
pub depth_of_field_far: f64,
pub depth_of_field_mid: f64,
pub depth_of_field_near: f64,
pub depth_of_field_width: f64,
pub far_clip: f64,
pub field_of_view: f64,
pub height_fog_end: f64,
pub height_fog_intensity: f64,
pub height_fog_start: f64,
pub nav_grid_offset: f64,
pub near_clip: f64,
pub skybox_offset: f64,
pub skybox_radius: f64,
pub skybox_rotation: f64,
pub sun_direction: Vector3f,
pub camera_position: Vector3f,
pub camera_rotation: Vector3f,
pub selection_offset: Vector3f,
pub height_fog_color: ColorValue,
pub depth_fog_color: ColorValue,
pub skybox_path: String,
pub selection_name: String,
pub camera_mode: HudCameraMode,
}
impl Render {
pub fn follow(&mut self, selection_name: String) {
self.camera_mode = HudCameraMode::Fps;
self.selection_offset = Vector3f {
x: 0.0,
y: 1911.85,
z: -1200.0,
};
self.camera_attached = true;
self.selection_name = selection_name;
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct Sequence {
pub camera_position: Vec<KeyFrameVector3>,
pub camera_rotation: Vec<KeyFrameVector3>,
pub depth_fog_color: Vec<KeyFrameColor>,
pub depth_fog_enabled: Vec<KeyFrameBool>,
pub depth_fog_end: Vec<KeyFrameF64>,
pub depth_fog_intensity: Vec<KeyFrameF64>,
pub depth_fog_start: Vec<KeyFrameF64>,
pub depth_of_field_circle: Vec<KeyFrameF64>,
pub depth_of_field_enabled: Vec<KeyFrameBool>,
pub depth_of_field_far: Vec<KeyFrameF64>,
pub depth_of_field_mid: Vec<KeyFrameF64>,
pub depth_of_field_near: Vec<KeyFrameF64>,
pub depth_of_field_width: Vec<KeyFrameF64>,
pub far_clip: Vec<KeyFrameF64>,
pub field_of_view: Vec<KeyFrameF64>,
pub height_fog_color: Vec<KeyFrameColor>,
pub height_fog_enabled: Vec<KeyFrameBool>,
pub height_fog_end: Vec<KeyFrameF64>,
pub height_fog_intensity: Vec<KeyFrameF64>,
pub height_fog_start: Vec<KeyFrameF64>,
pub nav_grid_offset: Vec<KeyFrameF64>,
pub near_clip: Vec<KeyFrameF64>,
pub playback_speed: Vec<KeyFrameF64>,
pub selection_name: Vec<KeyFrameString>,
pub selection_offset: Vec<KeyFrameVector3>,
pub skybox_offset: Vec<KeyFrameF64>,
pub skybox_radius: Vec<KeyFrameF64>,
pub skybox_rotation: Vec<KeyFrameF64>,
pub sun_direction: Vec<KeyFrameVector3>,
}
impl Sequence {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_render(render: &Render, playback_speed: f64, time: Duration) -> Self {
let mut sequence = Self::default();
sequence.push_render(render, playback_speed, time);
sequence
}
#[rustfmt::skip]
pub fn push_render(&mut self, render: &Render, playback_speed: f64, time: Duration) {
self.camera_position.push(KeyFrameT::new_default_blending(render.camera_position, time));
self.camera_rotation.push(KeyFrameT::new_default_blending(render.camera_rotation, time));
self.depth_fog_color.push(KeyFrameT::new_default_blending(render.depth_fog_color, time));
self.depth_fog_enabled.push(KeyFrameT::new_default_blending(render.depth_fog_enabled, time));
self.depth_fog_end.push(KeyFrameT::new_default_blending(render.depth_fog_end, time));
self.depth_fog_intensity.push(KeyFrameT::new_default_blending(render.depth_fog_intensity, time));
self.depth_fog_start.push(KeyFrameT::new_default_blending(render.depth_fog_start, time));
self.depth_of_field_circle.push(KeyFrameT::new_default_blending(render.depth_of_field_circle, time));
self.depth_of_field_enabled.push(KeyFrameT::new_default_blending(render.depth_of_field_enabled, time));
self.depth_of_field_far.push(KeyFrameT::new_default_blending(render.depth_of_field_far, time));
self.depth_of_field_mid.push(KeyFrameT::new_default_blending(render.depth_of_field_mid, time));
self.depth_of_field_near.push(KeyFrameT::new_default_blending(render.depth_of_field_near, time));
self.depth_of_field_width.push(KeyFrameT::new_default_blending(render.depth_of_field_width, time));
self.far_clip.push(KeyFrameT::new_default_blending(render.far_clip, time));
self.field_of_view.push(KeyFrameT::new_default_blending(render.field_of_view, time));
self.height_fog_color.push(KeyFrameT::new_default_blending(render.height_fog_color, time));
self.height_fog_enabled.push(KeyFrameT::new_default_blending(render.height_fog_enabled, time));
self.height_fog_end.push(KeyFrameT::new_default_blending(render.height_fog_end, time));
self.height_fog_intensity.push(KeyFrameT::new_default_blending(render.height_fog_intensity, time));
self.height_fog_start.push(KeyFrameT::new_default_blending(render.height_fog_start, time));
self.nav_grid_offset.push(KeyFrameT::new_default_blending(render.nav_grid_offset, time));
self.near_clip.push(KeyFrameT::new_default_blending(render.near_clip, time));
self.playback_speed.push(KeyFrameT::new_default_blending(playback_speed, time));
if let Some(selection) = self.selection_name.last() {
if selection.value != render.selection_name {
self.selection_name.push(KeyFrameT::new_default_blending(render.selection_name.clone(), time));
}
}
self.selection_offset.push(KeyFrameT::new_default_blending(render.selection_offset, time));
self.skybox_offset.push(KeyFrameT::new_default_blending(render.skybox_offset, time));
self.skybox_radius.push(KeyFrameT::new_default_blending(render.skybox_radius, time));
self.skybox_rotation.push(KeyFrameT::new_default_blending(render.skybox_rotation, time));
self.sun_direction.push(KeyFrameT::new_default_blending(render.sun_direction, time));
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Vector3f {
pub x: f64,
pub y: f64,
pub z: f64,
}
mod pid {
use serde::de::{Error, Visitor};
use serde::{Deserializer, Serializer};
use std::fmt::Formatter;
use sysinfo::Pid;
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn serialize<S: Serializer>(pid: &Pid, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u32(pid.as_u32())
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Pid, D::Error> {
struct PidVisitor;
impl Visitor<'_> for PidVisitor {
type Value = Pid;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("A number corresponding to the PID of league")
}
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Pid::from_u32(v))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Pid::from(usize::try_from(v).map_err(E::custom)?))
}
}
deserializer.deserialize_u32(PidVisitor)
}
}