mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 18:14:11 +02:00
cart
This commit is contained in:
parent
88c61917f0
commit
8000c148d0
2 changed files with 72 additions and 5 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::state::{ParsedDemo, SearchableEvent};
|
use crate::state::{ParsedDemo, SearchableEvent};
|
||||||
use js_sys::Function;
|
use js_sys::Function;
|
||||||
|
use std::iter::once;
|
||||||
use tf_demo_parser::demo::header::Header;
|
use tf_demo_parser::demo::header::Header;
|
||||||
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::{GameStateAnalyser, World};
|
use tf_demo_parser::demo::parser::gamestateanalyser::{GameStateAnalyser, World};
|
||||||
|
|
@ -44,6 +45,7 @@ impl From<World> for WorldBoundaries {
|
||||||
pub struct FlatState {
|
pub struct FlatState {
|
||||||
pub player_count: usize,
|
pub player_count: usize,
|
||||||
pub building_count: usize,
|
pub building_count: usize,
|
||||||
|
pub has_cart: bool,
|
||||||
pub projectile_count: usize,
|
pub projectile_count: usize,
|
||||||
pub boundaries: WorldBoundaries,
|
pub boundaries: WorldBoundaries,
|
||||||
pub interval_per_tick: f32,
|
pub interval_per_tick: f32,
|
||||||
|
|
@ -69,17 +71,20 @@ impl FlatState {
|
||||||
max_building_count,
|
max_building_count,
|
||||||
max_projectile_count,
|
max_projectile_count,
|
||||||
tick,
|
tick,
|
||||||
|
cart,
|
||||||
..
|
..
|
||||||
} = parsed;
|
} = parsed;
|
||||||
|
|
||||||
let player_count = players.len();
|
let player_count = players.len();
|
||||||
let building_count = max_building_count;
|
let building_count = max_building_count;
|
||||||
let projectile_count = max_projectile_count;
|
let projectile_count = max_projectile_count;
|
||||||
|
let has_cart = !cart.is_empty();
|
||||||
|
|
||||||
let flat: Vec<_> = players
|
let flat: Vec<_> = players
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(buildings)
|
.chain(buildings)
|
||||||
.chain(projectiles)
|
.chain(projectiles)
|
||||||
|
.chain(once(cart))
|
||||||
.flat_map(Vec::into_iter)
|
.flat_map(Vec::into_iter)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
@ -87,6 +92,7 @@ impl FlatState {
|
||||||
player_count,
|
player_count,
|
||||||
building_count,
|
building_count,
|
||||||
projectile_count,
|
projectile_count,
|
||||||
|
has_cart,
|
||||||
tick_count: tick as u32,
|
tick_count: tick as u32,
|
||||||
boundaries: world.into(),
|
boundaries: world.into(),
|
||||||
interval_per_tick: header.duration / (header.ticks as f32),
|
interval_per_tick: header.duration / (header.ticks as f32),
|
||||||
|
|
|
||||||
71
src/state.rs
71
src/state.rs
|
|
@ -1,5 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tf_demo_parser::demo::data::game_state::{PlayerCondition, Projectile, ProjectileType};
|
use tf_demo_parser::demo::data::game_state::{
|
||||||
|
Cart, Objective, PlayerCondition, Projectile, ProjectileType,
|
||||||
|
};
|
||||||
use tf_demo_parser::demo::data::DemoTick;
|
use tf_demo_parser::demo::data::DemoTick;
|
||||||
use tf_demo_parser::demo::gamevent::GameEvent;
|
use tf_demo_parser::demo::gamevent::GameEvent;
|
||||||
use tf_demo_parser::demo::header::Header;
|
use tf_demo_parser::demo::header::Header;
|
||||||
|
|
@ -34,6 +36,7 @@ pub struct ParsedDemo {
|
||||||
pub players: Vec<Vec<u8>>,
|
pub players: Vec<Vec<u8>>,
|
||||||
pub buildings: Vec<Vec<u8>>,
|
pub buildings: Vec<Vec<u8>>,
|
||||||
pub projectiles: Vec<Vec<u8>>,
|
pub projectiles: Vec<Vec<u8>>,
|
||||||
|
pub cart: Vec<u8>,
|
||||||
pub kills: Vec<Kill>,
|
pub kills: Vec<Kill>,
|
||||||
pub events: Vec<SearchableEvent>,
|
pub events: Vec<SearchableEvent>,
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
|
|
@ -50,6 +53,7 @@ impl ParsedDemo {
|
||||||
players: Vec::new(),
|
players: Vec::new(),
|
||||||
buildings: Vec::new(),
|
buildings: Vec::new(),
|
||||||
projectiles: Vec::new(),
|
projectiles: Vec::new(),
|
||||||
|
cart: Vec::new(),
|
||||||
kills: Vec::new(),
|
kills: Vec::new(),
|
||||||
player_info: Vec::new(),
|
player_info: Vec::new(),
|
||||||
max_building_count: 0,
|
max_building_count: 0,
|
||||||
|
|
@ -135,6 +139,10 @@ impl ParsedDemo {
|
||||||
parsed_projectiles.extend_from_slice(&state.pack(world));
|
parsed_projectiles.extend_from_slice(&state.pack(world));
|
||||||
}
|
}
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
|
if let Some(cart) = game_state.objectives.values().find_map(Objective::as_cart) {
|
||||||
|
let state = CartState::new(cart);
|
||||||
|
self.cart.extend_from_slice(&state.pack(world));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.last_tick = game_state.tick;
|
self.last_tick = game_state.tick;
|
||||||
}
|
}
|
||||||
|
|
@ -486,6 +494,7 @@ pub struct ProjectileState {
|
||||||
team: Team,
|
team: Team,
|
||||||
ty: ProjectileType,
|
ty: ProjectileType,
|
||||||
angle: Angle,
|
angle: Angle,
|
||||||
|
critical: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectileState {
|
impl ProjectileState {
|
||||||
|
|
@ -501,6 +510,7 @@ impl ProjectileState {
|
||||||
angle: Angle::from(projectile.rotation.y),
|
angle: Angle::from(projectile.rotation.y),
|
||||||
team: projectile.team,
|
team: projectile.team,
|
||||||
ty: projectile.ty,
|
ty: projectile.ty,
|
||||||
|
critical: projectile.critical,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,11 +519,14 @@ impl ProjectileState {
|
||||||
let y = pack_f32(self.position.y, world.boundary_min.y, world.boundary_max.y).to_le_bytes();
|
let y = pack_f32(self.position.y, world.boundary_min.y, world.boundary_max.y).to_le_bytes();
|
||||||
// 1 bit team
|
// 1 bit team
|
||||||
// 3 bits for type
|
// 3 bits for type
|
||||||
// 4 bits for angle, 16 angles should be enough for projectiles
|
|
||||||
let team = if self.team == Team::Blue { 0 } else { 1 };
|
let team = if self.team == Team::Blue { 0 } else { 1 };
|
||||||
let team_type = ((self.ty as u8) << 5) + ((team as u8) << 4);
|
let team_type = ((self.ty as u8) << 5) + ((team as u8) << 4);
|
||||||
|
|
||||||
[x[0], x[1], y[0], y[1], team_type, self.angle.0]
|
// 1 bit for critical
|
||||||
|
// 7 bits for angle
|
||||||
|
let angle_critical = ((self.critical as u8) << 7) + (self.angle.0 >> 1);
|
||||||
|
|
||||||
|
[x[0], x[1], y[0], y[1], team_type, angle_critical]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -536,13 +549,15 @@ impl ProjectileState {
|
||||||
Team::Red
|
Team::Red
|
||||||
};
|
};
|
||||||
let ty = ProjectileType::from((team_type >> 5) & 7);
|
let ty = ProjectileType::from((team_type >> 5) & 7);
|
||||||
let angle = Angle(bytes[5]);
|
let critical = bytes[5] >> 7 == 1;
|
||||||
|
let angle = Angle(bytes[5] << 1);
|
||||||
|
|
||||||
ProjectileState {
|
ProjectileState {
|
||||||
position: VectorXY { x, y },
|
position: VectorXY { x, y },
|
||||||
angle,
|
angle,
|
||||||
team,
|
team,
|
||||||
ty,
|
ty,
|
||||||
|
critical,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -559,6 +574,7 @@ fn test_projectile_packing() {
|
||||||
angle: Angle::from(123.0),
|
angle: Angle::from(123.0),
|
||||||
team: Team::Blue,
|
team: Team::Blue,
|
||||||
ty: ProjectileType::Flare,
|
ty: ProjectileType::Flare,
|
||||||
|
critical: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes = input.pack(&world);
|
let bytes = input.pack(&world);
|
||||||
|
|
@ -566,7 +582,8 @@ fn test_projectile_packing() {
|
||||||
let unpacked = ProjectileState::unpack(bytes, &world);
|
let unpacked = ProjectileState::unpack(bytes, &world);
|
||||||
assert_eq!(input.ty, unpacked.ty);
|
assert_eq!(input.ty, unpacked.ty);
|
||||||
assert_eq!(input.team, unpacked.team);
|
assert_eq!(input.team, unpacked.team);
|
||||||
assert_eq!(input.angle, unpacked.angle);
|
assert_eq!(input.critical, unpacked.critical);
|
||||||
|
assert!(u8::abs_diff(input.angle.0, unpacked.angle.0) <= 1);
|
||||||
|
|
||||||
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
|
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
|
||||||
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);
|
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);
|
||||||
|
|
@ -635,3 +652,47 @@ impl SearchableEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
|
pub struct CartState {
|
||||||
|
position: VectorXY,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CartState {
|
||||||
|
const PACKET_SIZE: usize = 4;
|
||||||
|
|
||||||
|
pub fn new(cart: &Cart) -> Self {
|
||||||
|
let position = cart.position;
|
||||||
|
CartState {
|
||||||
|
position: VectorXY {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pack(&self, world: &World) -> [u8; Self::PACKET_SIZE] {
|
||||||
|
let x = pack_f32(self.position.x, world.boundary_min.x, world.boundary_max.x).to_le_bytes();
|
||||||
|
let y = pack_f32(self.position.y, world.boundary_min.y, world.boundary_max.y).to_le_bytes();
|
||||||
|
|
||||||
|
[x[0], x[1], y[0], y[1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn unpack(bytes: [u8; Self::PACKET_SIZE], world: &World) -> Self {
|
||||||
|
let x = unpack_f32(
|
||||||
|
u16::from_le_bytes([bytes[0], bytes[1]]),
|
||||||
|
world.boundary_min.x,
|
||||||
|
world.boundary_max.x,
|
||||||
|
);
|
||||||
|
let y = unpack_f32(
|
||||||
|
u16::from_le_bytes([bytes[2], bytes[3]]),
|
||||||
|
world.boundary_min.y,
|
||||||
|
world.boundary_max.y,
|
||||||
|
);
|
||||||
|
|
||||||
|
CartState {
|
||||||
|
position: VectorXY { x, y },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue