1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 10:14:06 +02:00

heal target

This commit is contained in:
Robin Appelman 2025-06-28 20:57:52 +02:00
commit 5a7d4e12aa
4 changed files with 73 additions and 327 deletions

View file

@ -11,7 +11,7 @@ use crate::demo::vector::Vector;
use num_enum::TryFromPrimitive;
use parse_display::Display;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use std::ops::Rem;
#[derive(Default, Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash, Display)]
@ -106,6 +106,7 @@ pub enum PlayerClassData {
Medic {
charge: u8,
medigun: MedigunType,
target: Option<EntityId>,
},
Spy {
disguise_team: Team,
@ -120,6 +121,7 @@ impl PlayerClassData {
Class::Medic => PlayerClassData::Medic {
charge: 0,
medigun: MedigunType::Uber,
target: None,
},
Class::Spy => PlayerClassData::Spy {
disguise_team: Team::Other,
@ -360,9 +362,18 @@ impl Building {
pub fn construction_progress(&self) -> f32 {
match self {
Building::Sentry(Sentry { construction_progress, .. })
| Building::Dispenser(Dispenser { construction_progress, .. })
| Building::Teleporter(Teleporter { construction_progress, .. }) => *construction_progress,
Building::Sentry(Sentry {
construction_progress,
..
})
| Building::Dispenser(Dispenser {
construction_progress,
..
})
| Building::Teleporter(Teleporter {
construction_progress,
..
}) => *construction_progress,
}
}
}
@ -553,7 +564,6 @@ pub struct GameState {
pub tick: DemoTick,
pub server_classes: Vec<ServerClass>,
pub interval_per_tick: f32,
pub outer_map: HashMap<Handle, EntityId>,
pub events: Vec<(DemoTick, GameEvent)>,
pub objectives: BTreeMap<EntityId, Objective>,
}

View file

@ -3,7 +3,8 @@ pub use crate::demo::data::game_state::{
Building, BuildingClass, Dispenser, GameState, Kill, PlayerState, Sentry, Teleporter, World,
};
use crate::demo::data::game_state::{
Cart, Handle, MedigunType, Objective, PipeType, PlayerClassData, Projectile, ProjectileType,
Cart, Handle, MedigunType, Objective, PipeType, Player, PlayerClassData, Projectile,
ProjectileType,
};
use crate::demo::data::DemoTick;
use crate::demo::gameevent_gen::ObjectDestroyedEvent;
@ -20,6 +21,7 @@ use crate::demo::parser::MessageHandler;
use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
use crate::demo::vector::{Vector, VectorXY};
use crate::{MessageType, ParserState, ReadResult, Stream};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::str::FromStr;
@ -30,6 +32,8 @@ pub struct GameStateAnalyser {
pub state: GameState,
tick: DemoTick,
class_names: Vec<ServerClassName>, // indexed by ClassId
outer_map: HashMap<Handle, EntityId>,
outer_map_rev: HashMap<EntityId, Handle>,
}
impl MessageHandler for GameStateAnalyser {
@ -139,15 +143,18 @@ impl GameStateAnalyser {
pub fn handle_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
const OUTER: SendPropIdentifier =
SendPropIdentifier::new("DT_AttributeContainer", "m_hOuter");
const OUTER2: SendPropIdentifier =
SendPropIdentifier::new("DT_AttributeManager", "m_hOuter");
let Some(class_name) = self.class_names.get(usize::from(entity.server_class)) else {
return;
};
for prop in &entity.props {
if prop.identifier == OUTER {
if prop.identifier == OUTER || prop.identifier == OUTER2 {
let outer = Handle::try_from(&prop.value).unwrap_or_default();
self.state.outer_map.insert(outer, entity.entity_index);
self.outer_map.insert(outer, entity.entity_index);
self.outer_map_rev.insert(entity.entity_index, outer);
}
}
@ -365,6 +372,8 @@ impl GameStateAnalyser {
pub fn handle_medigun_entity(&mut self, entity: &PacketEntity, _parser_state: &ParserState) {
const OUTER: SendPropIdentifier =
SendPropIdentifier::new("DT_AttributeContainer", "m_hOuter");
const TARGET: SendPropIdentifier =
SendPropIdentifier::new("DT_WeaponMedigun", "m_hHealingTarget");
if entity.update_type == UpdateType::Enter {
let mut ty = MedigunType::Uber;
@ -391,6 +400,23 @@ impl GameStateAnalyser {
}
}
}
if let Some(target_handle) = entity.get_own_prop_value_by_identifier::<Handle>(TARGET) {
let target_id = self
.get_player_by_handle(target_handle)
.map(|target| target.entity);
let medic = self
.outer_map_rev
.get(&entity.entity_index)
.copied()
.and_then(|self_handle| self.get_player_by_weapon_handle(self_handle));
if let Some(medic) = medic {
if let PlayerClassData::Medic { target, .. } = &mut medic.class_data {
*target = target_id;
}
}
}
}
pub fn handle_world_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
@ -558,7 +584,7 @@ impl GameStateAnalyser {
fn handle_building(
&mut self,
entity: &PacketEntity,
parser_state: &ParserState,
_parser_state: &ParserState,
class: BuildingClass,
) {
let building = self
@ -621,13 +647,21 @@ impl GameStateAnalyser {
construction_progress,
..
}) => {
// picked up
if entity.update_type == UpdateType::Leave {
*health = 0;
}
for prop in &entity.props {
match prop.identifier {
LOCAL_ORIGIN => {
*position = Vector::try_from(&prop.value).unwrap_or_default()
}
TEAM => *team = Team::new(i64::try_from(&prop.value).unwrap_or_default()),
ANGLE => *angle = f32::try_from(&prop.value).unwrap_or_default(),
ANGLE => {
*angle = Vector::try_from(&prop.value)
.map(|v| v.y)
.unwrap_or_default()
}
SAPPED => *sapped = i64::try_from(&prop.value).unwrap_or_default() > 0,
BUILDING => *building = i64::try_from(&prop.value).unwrap_or_default() > 0,
LEVEL => *level = i64::try_from(&prop.value).unwrap_or_default() as u8,
@ -795,4 +829,19 @@ impl GameStateAnalyser {
Ok(())
}
fn get_player_by_weapon_handle(&mut self, handle: Handle) -> Option<&mut Player> {
self.state
.players
.iter_mut()
.find(|player| player.weapons.contains(&handle))
}
fn get_player_by_handle(&mut self, handle: Handle) -> Option<&mut Player> {
let entity_id = self.outer_map.get(&handle)?;
self.state
.players
.iter_mut()
.find(|player| player.entity == *entity_id)
}
}