@@ -4,7 +4,7 @@ use crate::demo::gamevent::GameEvent;
44use crate :: demo:: message:: gameevent:: GameEventMessage ;
55use crate :: demo:: message:: packetentities:: { EntityId , PacketEntity , UpdateType } ;
66use crate :: demo:: message:: Message ;
7- use crate :: demo:: packet:: datatable:: { ParseSendTable , ServerClass , ServerClassName } ;
7+ use crate :: demo:: packet:: datatable:: { ClassId , ParseSendTable , ServerClass , ServerClassName } ;
88use crate :: demo:: packet:: message:: MessagePacketMeta ;
99use crate :: demo:: packet:: stringtable:: StringTableEntry ;
1010use crate :: demo:: parser:: analyser:: UserInfo ;
@@ -41,6 +41,27 @@ impl PlayerState {
4141 }
4242}
4343
44+ #[ derive( Debug , Clone , Default , PartialEq , Serialize , Deserialize ) ]
45+ pub struct Box {
46+ pub min : Vector ,
47+ pub max : Vector ,
48+ }
49+
50+ impl Box {
51+ pub fn new ( min : Vector , max : Vector ) -> Box {
52+ Box { min, max }
53+ }
54+
55+ pub fn contains ( & self , point : Vector ) -> bool {
56+ point. x >= self . min . x
57+ && point. x <= self . max . x
58+ && point. y >= self . min . y
59+ && point. y <= self . max . y
60+ && point. z >= self . min . z
61+ && point. z <= self . max . z
62+ }
63+ }
64+
4465#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , Default ) ]
4566pub struct Player {
4667 entity : EntityId ,
@@ -57,6 +78,42 @@ pub struct Player {
5778 pub simtime : u16 ,
5879 pub ping : u16 ,
5980 pub in_pvs : bool ,
81+ pub bounds : Box ,
82+ }
83+
84+ pub const PLAYER_BOX_DEFAULT : Box = Box {
85+ min : Vector {
86+ x : -24.0 ,
87+ y : -24.0 ,
88+ z : 0.0 ,
89+ } ,
90+ max : Vector {
91+ x : 24.0 ,
92+ y : 24.0 ,
93+ z : 82.0 ,
94+ } ,
95+ } ;
96+
97+ impl Player {
98+ pub fn new ( entity : EntityId ) -> Player {
99+ Player {
100+ entity,
101+ bounds : PLAYER_BOX_DEFAULT ,
102+ ..Player :: default ( )
103+ }
104+ }
105+
106+ pub fn collides ( & self , projectile : & Projectile , time_per_tick : f32 ) -> bool {
107+ let current_position = projectile. position ;
108+ let next_position = projectile. position + ( projectile. speed * time_per_tick) ;
109+ match projectile. bounds {
110+ Some ( _) => todo ! ( ) ,
111+ None => {
112+ self . bounds . contains ( current_position - self . position )
113+ || self . bounds . contains ( next_position - self . position )
114+ }
115+ }
116+ }
60117}
61118
62119#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , Default ) ]
@@ -226,6 +283,36 @@ pub enum BuildingClass {
226283 Teleporter ,
227284}
228285
286+ #[ derive( Debug , Clone , PartialEq , Serialize , Deserialize ) ]
287+ pub struct Projectile {
288+ pub id : EntityId ,
289+ pub team : Team ,
290+ pub class : ClassId ,
291+ pub position : Vector ,
292+ pub speed : Vector ,
293+ pub bounds : Option < Box > ,
294+ }
295+
296+ impl Projectile {
297+ pub fn new ( id : EntityId , class : ClassId ) -> Self {
298+ Projectile {
299+ id,
300+ team : Team :: default ( ) ,
301+ class,
302+ position : Vector :: default ( ) ,
303+ speed : Vector :: default ( ) ,
304+ bounds : None ,
305+ }
306+ }
307+ }
308+
309+ #[ derive( Debug , PartialEq , Serialize , Deserialize ) ]
310+ pub struct Collision {
311+ pub tick : DemoTick ,
312+ pub target : EntityId ,
313+ pub projectile : Projectile ,
314+ }
315+
229316#[ derive( Default , Debug , Serialize , Deserialize , PartialEq , Clone ) ]
230317pub struct World {
231318 pub boundary_min : Vector ,
@@ -257,12 +344,20 @@ impl Kill {
257344pub struct GameState {
258345 pub players : Vec < Player > ,
259346 pub buildings : BTreeMap < EntityId , Building > ,
347+ pub projectiles : BTreeMap < EntityId , Projectile > ,
348+ pub collisions : Vec < Collision > ,
260349 pub world : Option < World > ,
261350 pub kills : Vec < Kill > ,
262351 pub tick : DemoTick ,
352+ pub server_classes : Vec < ServerClass > ,
353+ pub interval_per_tick : f32 ,
263354}
264355
265356impl GameState {
357+ pub fn get_player ( & self , id : EntityId ) -> Option < & Player > {
358+ self . players . iter ( ) . find ( |player| player. entity == id)
359+ }
360+
266361 pub fn get_or_create_player ( & mut self , entity_id : EntityId ) -> & mut Player {
267362 let index = match self
268363 . players
@@ -274,10 +369,7 @@ impl GameState {
274369 Some ( index) => index,
275370 None => {
276371 let index = self . players . len ( ) ;
277- self . players . push ( Player {
278- entity : entity_id,
279- ..Player :: default ( )
280- } ) ;
372+ self . players . push ( Player :: new ( entity_id) ) ;
281373 index
282374 }
283375 } ;
@@ -295,6 +387,32 @@ impl GameState {
295387 . or_insert_with ( || Building :: new ( entity_id, class) )
296388 }
297389
390+ pub fn get_or_create_projectile ( & mut self , id : EntityId , class : ClassId ) -> & mut Projectile {
391+ self . projectiles
392+ . entry ( id)
393+ . or_insert_with ( || Projectile :: new ( id, class) )
394+ }
395+
396+ pub fn check_collision ( & self , projectile : & Projectile ) -> Option < & Player > {
397+ self . players
398+ . iter ( )
399+ . filter ( |player| player. state == PlayerState :: Alive )
400+ . filter ( |player| player. team != projectile. team )
401+ . find ( |player| player. collides ( projectile, self . interval_per_tick ) )
402+ }
403+
404+ pub fn projectile_destroy ( & mut self , id : EntityId ) {
405+ if let Some ( projectile) = self . projectiles . remove ( & id) {
406+ if let Some ( target) = self . check_collision ( & projectile) {
407+ self . collisions . push ( Collision {
408+ tick : self . tick ,
409+ target : target. entity ,
410+ projectile,
411+ } )
412+ }
413+ }
414+ }
415+
298416 pub fn remove_building ( & mut self , entity_id : EntityId ) {
299417 self . buildings . remove ( & entity_id) ;
300418 }
@@ -313,7 +431,7 @@ impl MessageHandler for GameStateAnalyser {
313431 fn does_handle ( message_type : MessageType ) -> bool {
314432 matches ! (
315433 message_type,
316- MessageType :: PacketEntities | MessageType :: GameEvent
434+ MessageType :: PacketEntities | MessageType :: GameEvent | MessageType :: ServerInfo
317435 )
318436 }
319437
@@ -324,6 +442,9 @@ impl MessageHandler for GameStateAnalyser {
324442 self . handle_entity ( entity, parser_state) ;
325443 }
326444 }
445+ Message :: ServerInfo ( message) => {
446+ self . state . interval_per_tick = message. interval_per_tick
447+ }
327448 Message :: GameEvent ( GameEventMessage { event, .. } ) => match event {
328449 GameEvent :: PlayerDeath ( death) => {
329450 self . state . kills . push ( Kill :: new ( self . tick , death. as_ref ( ) ) )
@@ -382,7 +503,8 @@ impl MessageHandler for GameStateAnalyser {
382503 self . tick = tick;
383504 }
384505
385- fn into_output ( self , _state : & ParserState ) -> Self :: Output {
506+ fn into_output ( mut self , state : & ParserState ) -> Self :: Output {
507+ self . state . server_classes = state. server_classes . clone ( ) ;
386508 self . state
387509 }
388510}
@@ -411,6 +533,9 @@ impl GameStateAnalyser {
411533 "CObjectSentrygun" => self . handle_sentry_entity ( entity, parser_state) ,
412534 "CObjectDispenser" => self . handle_dispenser_entity ( entity, parser_state) ,
413535 "CObjectTeleporter" => self . handle_teleporter_entity ( entity, parser_state) ,
536+ _ if class_name. starts_with ( "CTFProjectile_" ) => {
537+ self . handle_projectile_entity ( entity, parser_state)
538+ }
414539 _ => { }
415540 }
416541 }
@@ -482,6 +607,8 @@ impl GameStateAnalyser {
482607
483608 const SIMTIME_PROP : SendPropIdentifier =
484609 SendPropIdentifier :: new ( "DT_BaseEntity" , "m_flSimulationTime" ) ;
610+ const PROP_BB_MAX : SendPropIdentifier =
611+ SendPropIdentifier :: new ( "DT_CollisionProperty" , "m_vecMaxsPreScaled" ) ;
485612
486613 player. in_pvs = entity. in_pvs ;
487614
@@ -513,6 +640,10 @@ impl GameStateAnalyser {
513640 SIMTIME_PROP => {
514641 player. simtime = i64:: try_from ( & prop. value ) . unwrap_or_default ( ) as u16
515642 }
643+ PROP_BB_MAX => {
644+ let max = Vector :: try_from ( & prop. value ) . unwrap_or_default ( ) ;
645+ player. bounds . max = max;
646+ }
516647 _ => { }
517648 }
518649 }
@@ -766,6 +897,46 @@ impl GameStateAnalyser {
766897 }
767898 }
768899
900+ pub fn handle_projectile_entity ( & mut self , entity : & PacketEntity , parser_state : & ParserState ) {
901+ const ROCKET_ORIGIN : SendPropIdentifier =
902+ SendPropIdentifier :: new ( "DT_TFBaseRocket" , "m_vecOrigin" ) ; // rockets, arrows, more?
903+ const GRENADE_ORIGIN : SendPropIdentifier =
904+ SendPropIdentifier :: new ( "DT_TFWeaponBaseGrenadeProj" , "m_vecOrigin" ) ;
905+ // todo: flares?
906+ const TEAM : SendPropIdentifier = SendPropIdentifier :: new ( "DT_BaseEntity" , "m_iTeamNum" ) ;
907+ const INITIAL_SPEED : SendPropIdentifier =
908+ SendPropIdentifier :: new ( "DT_TFBaseRocket" , "m_vInitialVelocity" ) ;
909+
910+ if entity. in_pvs {
911+ let projectile = self
912+ . state
913+ . get_or_create_projectile ( entity. entity_index , entity. server_class ) ;
914+
915+ // todo: bounds for grenades
916+ // todo: track owner
917+
918+ for prop in entity. props ( parser_state) {
919+ match prop. identifier {
920+ ROCKET_ORIGIN | GRENADE_ORIGIN => {
921+ let pos = Vector :: try_from ( & prop. value ) . unwrap_or_default ( ) ;
922+ projectile. position = pos
923+ }
924+ TEAM => {
925+ let team = Team :: new ( i64:: try_from ( & prop. value ) . unwrap_or_default ( ) ) ;
926+ projectile. team = team;
927+ }
928+ INITIAL_SPEED => {
929+ let speed = Vector :: try_from ( & prop. value ) . unwrap_or_default ( ) ;
930+ projectile. speed = speed;
931+ }
932+ _ => { }
933+ }
934+ }
935+ } else {
936+ self . state . projectile_destroy ( entity. entity_index ) ;
937+ }
938+ }
939+
769940 fn parse_user_info (
770941 & mut self ,
771942 index : usize ,
0 commit comments