mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 10:04:12 +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 js_sys::Function;
|
||||
use std::iter::once;
|
||||
use tf_demo_parser::demo::header::Header;
|
||||
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
||||
use tf_demo_parser::demo::parser::gamestateanalyser::{GameStateAnalyser, World};
|
||||
|
|
@ -44,6 +45,7 @@ impl From<World> for WorldBoundaries {
|
|||
pub struct FlatState {
|
||||
pub player_count: usize,
|
||||
pub building_count: usize,
|
||||
pub has_cart: bool,
|
||||
pub projectile_count: usize,
|
||||
pub boundaries: WorldBoundaries,
|
||||
pub interval_per_tick: f32,
|
||||
|
|
@ -69,17 +71,20 @@ impl FlatState {
|
|||
max_building_count,
|
||||
max_projectile_count,
|
||||
tick,
|
||||
cart,
|
||||
..
|
||||
} = parsed;
|
||||
|
||||
let player_count = players.len();
|
||||
let building_count = max_building_count;
|
||||
let projectile_count = max_projectile_count;
|
||||
let has_cart = !cart.is_empty();
|
||||
|
||||
let flat: Vec<_> = players
|
||||
.into_iter()
|
||||
.chain(buildings)
|
||||
.chain(projectiles)
|
||||
.chain(once(cart))
|
||||
.flat_map(Vec::into_iter)
|
||||
.collect();
|
||||
|
||||
|
|
@ -87,6 +92,7 @@ impl FlatState {
|
|||
player_count,
|
||||
building_count,
|
||||
projectile_count,
|
||||
has_cart,
|
||||
tick_count: tick as u32,
|
||||
boundaries: world.into(),
|
||||
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 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::gamevent::GameEvent;
|
||||
use tf_demo_parser::demo::header::Header;
|
||||
|
|
@ -34,6 +36,7 @@ pub struct ParsedDemo {
|
|||
pub players: Vec<Vec<u8>>,
|
||||
pub buildings: Vec<Vec<u8>>,
|
||||
pub projectiles: Vec<Vec<u8>>,
|
||||
pub cart: Vec<u8>,
|
||||
pub kills: Vec<Kill>,
|
||||
pub events: Vec<SearchableEvent>,
|
||||
pub header: Header,
|
||||
|
|
@ -50,6 +53,7 @@ impl ParsedDemo {
|
|||
players: Vec::new(),
|
||||
buildings: Vec::new(),
|
||||
projectiles: Vec::new(),
|
||||
cart: Vec::new(),
|
||||
kills: Vec::new(),
|
||||
player_info: Vec::new(),
|
||||
max_building_count: 0,
|
||||
|
|
@ -135,6 +139,10 @@ impl ParsedDemo {
|
|||
parsed_projectiles.extend_from_slice(&state.pack(world));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -486,6 +494,7 @@ pub struct ProjectileState {
|
|||
team: Team,
|
||||
ty: ProjectileType,
|
||||
angle: Angle,
|
||||
critical: bool,
|
||||
}
|
||||
|
||||
impl ProjectileState {
|
||||
|
|
@ -501,6 +510,7 @@ impl ProjectileState {
|
|||
angle: Angle::from(projectile.rotation.y),
|
||||
team: projectile.team,
|
||||
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();
|
||||
// 1 bit team
|
||||
// 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_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)]
|
||||
|
|
@ -536,13 +549,15 @@ impl ProjectileState {
|
|||
Team::Red
|
||||
};
|
||||
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 {
|
||||
position: VectorXY { x, y },
|
||||
angle,
|
||||
team,
|
||||
ty,
|
||||
critical,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -559,6 +574,7 @@ fn test_projectile_packing() {
|
|||
angle: Angle::from(123.0),
|
||||
team: Team::Blue,
|
||||
ty: ProjectileType::Flare,
|
||||
critical: true,
|
||||
};
|
||||
|
||||
let bytes = input.pack(&world);
|
||||
|
|
@ -566,7 +582,8 @@ fn test_projectile_packing() {
|
|||
let unpacked = ProjectileState::unpack(bytes, &world);
|
||||
assert_eq!(input.ty, unpacked.ty);
|
||||
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.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