mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
move gamestate structs
This commit is contained in:
parent
fb471a5a44
commit
a9c031345f
3 changed files with 411 additions and 402 deletions
405
src/demo/data/game_state.rs
Normal file
405
src/demo/data/game_state.rs
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
use crate::demo::data::DemoTick;
|
||||||
|
use crate::demo::gameevent_gen::PlayerDeathEvent;
|
||||||
|
use crate::demo::message::packetentities::EntityId;
|
||||||
|
use crate::demo::packet::datatable::{ClassId, ServerClass};
|
||||||
|
use crate::demo::parser::analyser::{Class, Team, UserId, UserInfo};
|
||||||
|
use crate::demo::vector::Vector;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub enum PlayerState {
|
||||||
|
#[default]
|
||||||
|
Alive = 0,
|
||||||
|
Dying = 1,
|
||||||
|
Death = 2,
|
||||||
|
Respawnable = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerState {
|
||||||
|
pub fn new(number: i64) -> Self {
|
||||||
|
match number {
|
||||||
|
1 => PlayerState::Dying,
|
||||||
|
2 => PlayerState::Death,
|
||||||
|
3 => PlayerState::Respawnable,
|
||||||
|
_ => PlayerState::Alive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Box {
|
||||||
|
pub min: Vector,
|
||||||
|
pub max: Vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Box {
|
||||||
|
pub fn new(min: Vector, max: Vector) -> Box {
|
||||||
|
Box { min, max }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, point: Vector) -> bool {
|
||||||
|
point.x >= self.min.x
|
||||||
|
&& point.x <= self.max.x
|
||||||
|
&& point.y >= self.min.y
|
||||||
|
&& point.y <= self.max.y
|
||||||
|
&& point.z >= self.min.z
|
||||||
|
&& point.z <= self.max.z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct Player {
|
||||||
|
pub entity: EntityId,
|
||||||
|
pub position: Vector,
|
||||||
|
pub health: u16,
|
||||||
|
pub max_health: u16,
|
||||||
|
pub class: Class,
|
||||||
|
pub team: Team,
|
||||||
|
pub view_angle: f32,
|
||||||
|
pub pitch_angle: f32,
|
||||||
|
pub state: PlayerState,
|
||||||
|
pub info: Option<UserInfo>,
|
||||||
|
pub charge: u8,
|
||||||
|
pub simtime: u16,
|
||||||
|
pub ping: u16,
|
||||||
|
pub in_pvs: bool,
|
||||||
|
pub bounds: Box,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PLAYER_BOX_DEFAULT: Box = Box {
|
||||||
|
min: Vector {
|
||||||
|
x: -24.0,
|
||||||
|
y: -24.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
max: Vector {
|
||||||
|
x: 24.0,
|
||||||
|
y: 24.0,
|
||||||
|
z: 82.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Player {
|
||||||
|
pub fn new(entity: EntityId) -> Player {
|
||||||
|
Player {
|
||||||
|
entity,
|
||||||
|
bounds: PLAYER_BOX_DEFAULT,
|
||||||
|
..Player::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collides(&self, projectile: &Projectile, time_per_tick: f32) -> bool {
|
||||||
|
let current_position = projectile.position;
|
||||||
|
let next_position = projectile.position + (projectile.speed * time_per_tick);
|
||||||
|
match projectile.bounds {
|
||||||
|
Some(_) => todo!(),
|
||||||
|
None => {
|
||||||
|
self.bounds.contains(current_position - self.position)
|
||||||
|
|| self.bounds.contains(next_position - self.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct Sentry {
|
||||||
|
pub entity: EntityId,
|
||||||
|
pub builder: UserId,
|
||||||
|
pub position: Vector,
|
||||||
|
pub level: u8,
|
||||||
|
pub max_health: u16,
|
||||||
|
pub health: u16,
|
||||||
|
pub building: bool,
|
||||||
|
pub sapped: bool,
|
||||||
|
pub team: Team,
|
||||||
|
pub angle: f32,
|
||||||
|
pub player_controlled: bool,
|
||||||
|
pub auto_aim_target: UserId,
|
||||||
|
pub shells: u16,
|
||||||
|
pub rockets: u16,
|
||||||
|
pub is_mini: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct Dispenser {
|
||||||
|
pub entity: EntityId,
|
||||||
|
pub builder: UserId,
|
||||||
|
pub position: Vector,
|
||||||
|
pub level: u8,
|
||||||
|
pub max_health: u16,
|
||||||
|
pub health: u16,
|
||||||
|
pub building: bool,
|
||||||
|
pub sapped: bool,
|
||||||
|
pub team: Team,
|
||||||
|
pub angle: f32,
|
||||||
|
pub healing: Vec<UserId>,
|
||||||
|
pub metal: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct Teleporter {
|
||||||
|
pub entity: EntityId,
|
||||||
|
pub builder: UserId,
|
||||||
|
pub position: Vector,
|
||||||
|
pub level: u8,
|
||||||
|
pub max_health: u16,
|
||||||
|
pub health: u16,
|
||||||
|
pub building: bool,
|
||||||
|
pub sapped: bool,
|
||||||
|
pub team: Team,
|
||||||
|
pub angle: f32,
|
||||||
|
pub is_entrance: bool,
|
||||||
|
pub other_end: EntityId,
|
||||||
|
pub recharge_time: f32,
|
||||||
|
pub recharge_duration: f32,
|
||||||
|
pub times_used: u16,
|
||||||
|
pub yaw_to_exit: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum Building {
|
||||||
|
Sentry(Sentry),
|
||||||
|
Dispenser(Dispenser),
|
||||||
|
Teleporter(Teleporter),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Building {
|
||||||
|
pub fn new(entity_id: EntityId, class: BuildingClass) -> Building {
|
||||||
|
match class {
|
||||||
|
BuildingClass::Sentry => Building::Sentry(Sentry {
|
||||||
|
entity: entity_id,
|
||||||
|
..Sentry::default()
|
||||||
|
}),
|
||||||
|
BuildingClass::Dispenser => Building::Dispenser(Dispenser {
|
||||||
|
entity: entity_id,
|
||||||
|
..Dispenser::default()
|
||||||
|
}),
|
||||||
|
BuildingClass::Teleporter => Building::Teleporter(Teleporter {
|
||||||
|
entity: entity_id,
|
||||||
|
..Teleporter::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entity_id(&self) -> EntityId {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { entity, .. })
|
||||||
|
| Building::Dispenser(Dispenser { entity, .. })
|
||||||
|
| Building::Teleporter(Teleporter { entity, .. }) => *entity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn level(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { level, .. })
|
||||||
|
| Building::Dispenser(Dispenser { level, .. })
|
||||||
|
| Building::Teleporter(Teleporter { level, .. }) => *level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> Vector {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { position, .. })
|
||||||
|
| Building::Dispenser(Dispenser { position, .. })
|
||||||
|
| Building::Teleporter(Teleporter { position, .. }) => *position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder(&self) -> UserId {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { builder, .. })
|
||||||
|
| Building::Dispenser(Dispenser { builder, .. })
|
||||||
|
| Building::Teleporter(Teleporter { builder, .. }) => *builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn angle(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { angle, .. })
|
||||||
|
| Building::Dispenser(Dispenser { angle, .. })
|
||||||
|
| Building::Teleporter(Teleporter { angle, .. }) => *angle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_health(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { max_health, .. })
|
||||||
|
| Building::Dispenser(Dispenser { max_health, .. })
|
||||||
|
| Building::Teleporter(Teleporter { max_health, .. }) => *max_health,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn health(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { health, .. })
|
||||||
|
| Building::Dispenser(Dispenser { health, .. })
|
||||||
|
| Building::Teleporter(Teleporter { health, .. }) => *health,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sapped(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { sapped, .. })
|
||||||
|
| Building::Dispenser(Dispenser { sapped, .. })
|
||||||
|
| Building::Teleporter(Teleporter { sapped, .. }) => *sapped,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn team(&self) -> Team {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(Sentry { team, .. })
|
||||||
|
| Building::Dispenser(Dispenser { team, .. })
|
||||||
|
| Building::Teleporter(Teleporter { team, .. }) => *team,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn class(&self) -> BuildingClass {
|
||||||
|
match self {
|
||||||
|
Building::Sentry(_) => BuildingClass::Sentry,
|
||||||
|
Building::Dispenser(_) => BuildingClass::Sentry,
|
||||||
|
Building::Teleporter(_) => BuildingClass::Teleporter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BuildingClass {
|
||||||
|
Sentry,
|
||||||
|
Dispenser,
|
||||||
|
Teleporter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Projectile {
|
||||||
|
pub id: EntityId,
|
||||||
|
pub team: Team,
|
||||||
|
pub class: ClassId,
|
||||||
|
pub position: Vector,
|
||||||
|
pub speed: Vector,
|
||||||
|
pub bounds: Option<Box>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Projectile {
|
||||||
|
pub fn new(id: EntityId, class: ClassId) -> Self {
|
||||||
|
Projectile {
|
||||||
|
id,
|
||||||
|
team: Team::default(),
|
||||||
|
class,
|
||||||
|
position: Vector::default(),
|
||||||
|
speed: Vector::default(),
|
||||||
|
bounds: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Collision {
|
||||||
|
pub tick: DemoTick,
|
||||||
|
pub target: EntityId,
|
||||||
|
pub projectile: Projectile,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
pub struct World {
|
||||||
|
pub boundary_min: Vector,
|
||||||
|
pub boundary_max: Vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
pub struct Kill {
|
||||||
|
pub attacker_id: u16,
|
||||||
|
pub assister_id: u16,
|
||||||
|
pub victim_id: u16,
|
||||||
|
pub weapon: String,
|
||||||
|
pub tick: DemoTick,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Kill {
|
||||||
|
pub fn new(tick: DemoTick, death: &PlayerDeathEvent) -> Self {
|
||||||
|
Kill {
|
||||||
|
attacker_id: death.attacker,
|
||||||
|
assister_id: death.assister,
|
||||||
|
victim_id: death.user_id,
|
||||||
|
weapon: death.weapon.to_string(),
|
||||||
|
tick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct GameState {
|
||||||
|
pub players: Vec<Player>,
|
||||||
|
pub buildings: BTreeMap<EntityId, Building>,
|
||||||
|
pub projectiles: BTreeMap<EntityId, Projectile>,
|
||||||
|
pub collisions: Vec<Collision>,
|
||||||
|
pub world: Option<World>,
|
||||||
|
pub kills: Vec<Kill>,
|
||||||
|
pub tick: DemoTick,
|
||||||
|
pub server_classes: Vec<ServerClass>,
|
||||||
|
pub interval_per_tick: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameState {
|
||||||
|
pub fn get_player(&self, id: EntityId) -> Option<&Player> {
|
||||||
|
self.players.iter().find(|player| player.entity == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_create_player(&mut self, entity_id: EntityId) -> &mut Player {
|
||||||
|
let index = match self
|
||||||
|
.players
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_index, player)| player.entity == entity_id)
|
||||||
|
.map(|(index, _)| index)
|
||||||
|
{
|
||||||
|
Some(index) => index,
|
||||||
|
None => {
|
||||||
|
let index = self.players.len();
|
||||||
|
self.players.push(Player::new(entity_id));
|
||||||
|
index
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::indexing_slicing)]
|
||||||
|
&mut self.players[index]
|
||||||
|
}
|
||||||
|
pub fn get_or_create_building(
|
||||||
|
&mut self,
|
||||||
|
entity_id: EntityId,
|
||||||
|
class: BuildingClass,
|
||||||
|
) -> &mut Building {
|
||||||
|
self.buildings
|
||||||
|
.entry(entity_id)
|
||||||
|
.or_insert_with(|| Building::new(entity_id, class))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_create_projectile(&mut self, id: EntityId, class: ClassId) -> &mut Projectile {
|
||||||
|
self.projectiles
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| Projectile::new(id, class))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_collision(&self, projectile: &Projectile) -> Option<&Player> {
|
||||||
|
self.players
|
||||||
|
.iter()
|
||||||
|
.filter(|player| player.state == PlayerState::Alive)
|
||||||
|
.filter(|player| player.team != projectile.team)
|
||||||
|
.find(|player| player.collides(projectile, self.interval_per_tick))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn projectile_destroy(&mut self, id: EntityId) {
|
||||||
|
if let Some(projectile) = self.projectiles.remove(&id) {
|
||||||
|
if let Some(target) = self.check_collision(&projectile) {
|
||||||
|
self.collisions.push(Collision {
|
||||||
|
tick: self.tick,
|
||||||
|
target: target.entity,
|
||||||
|
projectile,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_building(&mut self, entity_id: EntityId) {
|
||||||
|
self.buildings.remove(&entity_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod game_state;
|
||||||
pub mod userinfo;
|
pub mod userinfo;
|
||||||
|
|
||||||
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteStream, Endianness};
|
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteStream, Endianness};
|
||||||
|
|
|
||||||
|
|
@ -1,423 +1,26 @@
|
||||||
|
pub use crate::demo::data::game_state::{
|
||||||
|
Building, BuildingClass, Dispenser, GameState, Kill, PlayerState, Sentry, Teleporter, World,
|
||||||
|
};
|
||||||
use crate::demo::data::DemoTick;
|
use crate::demo::data::DemoTick;
|
||||||
use crate::demo::gameevent_gen::{ObjectDestroyedEvent, PlayerDeathEvent};
|
use crate::demo::gameevent_gen::ObjectDestroyedEvent;
|
||||||
use crate::demo::gamevent::GameEvent;
|
use crate::demo::gamevent::GameEvent;
|
||||||
use crate::demo::message::gameevent::GameEventMessage;
|
use crate::demo::message::gameevent::GameEventMessage;
|
||||||
use crate::demo::message::packetentities::{EntityId, PacketEntity, UpdateType};
|
use crate::demo::message::packetentities::{EntityId, PacketEntity, UpdateType};
|
||||||
use crate::demo::message::Message;
|
use crate::demo::message::Message;
|
||||||
use crate::demo::packet::datatable::{ClassId, ParseSendTable, ServerClass, ServerClassName};
|
use crate::demo::packet::datatable::{ParseSendTable, ServerClass, ServerClassName};
|
||||||
use crate::demo::packet::message::MessagePacketMeta;
|
use crate::demo::packet::message::MessagePacketMeta;
|
||||||
use crate::demo::packet::stringtable::StringTableEntry;
|
use crate::demo::packet::stringtable::StringTableEntry;
|
||||||
use crate::demo::parser::analyser::UserInfo;
|
|
||||||
pub use crate::demo::parser::analyser::{Class, Team, UserId};
|
pub use crate::demo::parser::analyser::{Class, Team, UserId};
|
||||||
use crate::demo::parser::handler::BorrowMessageHandler;
|
use crate::demo::parser::handler::BorrowMessageHandler;
|
||||||
use crate::demo::parser::MessageHandler;
|
use crate::demo::parser::MessageHandler;
|
||||||
use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
|
use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
|
||||||
use crate::demo::vector::{Vector, VectorXY};
|
use crate::demo::vector::{Vector, VectorXY};
|
||||||
use crate::{MessageType, ParserState, ReadResult, Stream};
|
use crate::{MessageType, ParserState, ReadResult, Stream};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub struct CachedEntities {}
|
pub struct CachedEntities {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub enum PlayerState {
|
|
||||||
#[default]
|
|
||||||
Alive = 0,
|
|
||||||
Dying = 1,
|
|
||||||
Death = 2,
|
|
||||||
Respawnable = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerState {
|
|
||||||
pub fn new(number: i64) -> Self {
|
|
||||||
match number {
|
|
||||||
1 => PlayerState::Dying,
|
|
||||||
2 => PlayerState::Death,
|
|
||||||
3 => PlayerState::Respawnable,
|
|
||||||
_ => PlayerState::Alive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Box {
|
|
||||||
pub min: Vector,
|
|
||||||
pub max: Vector,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Box {
|
|
||||||
pub fn new(min: Vector, max: Vector) -> Box {
|
|
||||||
Box { min, max }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, point: Vector) -> bool {
|
|
||||||
point.x >= self.min.x
|
|
||||||
&& point.x <= self.max.x
|
|
||||||
&& point.y >= self.min.y
|
|
||||||
&& point.y <= self.max.y
|
|
||||||
&& point.z >= self.min.z
|
|
||||||
&& point.z <= self.max.z
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct Player {
|
|
||||||
entity: EntityId,
|
|
||||||
pub position: Vector,
|
|
||||||
pub health: u16,
|
|
||||||
pub max_health: u16,
|
|
||||||
pub class: Class,
|
|
||||||
pub team: Team,
|
|
||||||
pub view_angle: f32,
|
|
||||||
pub pitch_angle: f32,
|
|
||||||
pub state: PlayerState,
|
|
||||||
pub info: Option<UserInfo>,
|
|
||||||
pub charge: u8,
|
|
||||||
pub simtime: u16,
|
|
||||||
pub ping: u16,
|
|
||||||
pub in_pvs: bool,
|
|
||||||
pub bounds: Box,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const PLAYER_BOX_DEFAULT: Box = Box {
|
|
||||||
min: Vector {
|
|
||||||
x: -24.0,
|
|
||||||
y: -24.0,
|
|
||||||
z: 0.0,
|
|
||||||
},
|
|
||||||
max: Vector {
|
|
||||||
x: 24.0,
|
|
||||||
y: 24.0,
|
|
||||||
z: 82.0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Player {
|
|
||||||
pub fn new(entity: EntityId) -> Player {
|
|
||||||
Player {
|
|
||||||
entity,
|
|
||||||
bounds: PLAYER_BOX_DEFAULT,
|
|
||||||
..Player::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collides(&self, projectile: &Projectile, time_per_tick: f32) -> bool {
|
|
||||||
let current_position = projectile.position;
|
|
||||||
let next_position = projectile.position + (projectile.speed * time_per_tick);
|
|
||||||
match projectile.bounds {
|
|
||||||
Some(_) => todo!(),
|
|
||||||
None => {
|
|
||||||
self.bounds.contains(current_position - self.position)
|
|
||||||
|| self.bounds.contains(next_position - self.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct Sentry {
|
|
||||||
pub entity: EntityId,
|
|
||||||
pub builder: UserId,
|
|
||||||
pub position: Vector,
|
|
||||||
pub level: u8,
|
|
||||||
pub max_health: u16,
|
|
||||||
pub health: u16,
|
|
||||||
pub building: bool,
|
|
||||||
pub sapped: bool,
|
|
||||||
pub team: Team,
|
|
||||||
pub angle: f32,
|
|
||||||
pub player_controlled: bool,
|
|
||||||
pub auto_aim_target: UserId,
|
|
||||||
pub shells: u16,
|
|
||||||
pub rockets: u16,
|
|
||||||
pub is_mini: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct Dispenser {
|
|
||||||
pub entity: EntityId,
|
|
||||||
pub builder: UserId,
|
|
||||||
pub position: Vector,
|
|
||||||
pub level: u8,
|
|
||||||
pub max_health: u16,
|
|
||||||
pub health: u16,
|
|
||||||
pub building: bool,
|
|
||||||
pub sapped: bool,
|
|
||||||
pub team: Team,
|
|
||||||
pub angle: f32,
|
|
||||||
pub healing: Vec<UserId>,
|
|
||||||
pub metal: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct Teleporter {
|
|
||||||
pub entity: EntityId,
|
|
||||||
pub builder: UserId,
|
|
||||||
pub position: Vector,
|
|
||||||
pub level: u8,
|
|
||||||
pub max_health: u16,
|
|
||||||
pub health: u16,
|
|
||||||
pub building: bool,
|
|
||||||
pub sapped: bool,
|
|
||||||
pub team: Team,
|
|
||||||
pub angle: f32,
|
|
||||||
pub is_entrance: bool,
|
|
||||||
pub other_end: EntityId,
|
|
||||||
pub recharge_time: f32,
|
|
||||||
pub recharge_duration: f32,
|
|
||||||
pub times_used: u16,
|
|
||||||
pub yaw_to_exit: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum Building {
|
|
||||||
Sentry(Sentry),
|
|
||||||
Dispenser(Dispenser),
|
|
||||||
Teleporter(Teleporter),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Building {
|
|
||||||
pub fn new(entity_id: EntityId, class: BuildingClass) -> Building {
|
|
||||||
match class {
|
|
||||||
BuildingClass::Sentry => Building::Sentry(Sentry {
|
|
||||||
entity: entity_id,
|
|
||||||
..Sentry::default()
|
|
||||||
}),
|
|
||||||
BuildingClass::Dispenser => Building::Dispenser(Dispenser {
|
|
||||||
entity: entity_id,
|
|
||||||
..Dispenser::default()
|
|
||||||
}),
|
|
||||||
BuildingClass::Teleporter => Building::Teleporter(Teleporter {
|
|
||||||
entity: entity_id,
|
|
||||||
..Teleporter::default()
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entity_id(&self) -> EntityId {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { entity, .. })
|
|
||||||
| Building::Dispenser(Dispenser { entity, .. })
|
|
||||||
| Building::Teleporter(Teleporter { entity, .. }) => *entity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn level(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { level, .. })
|
|
||||||
| Building::Dispenser(Dispenser { level, .. })
|
|
||||||
| Building::Teleporter(Teleporter { level, .. }) => *level,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(&self) -> Vector {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { position, .. })
|
|
||||||
| Building::Dispenser(Dispenser { position, .. })
|
|
||||||
| Building::Teleporter(Teleporter { position, .. }) => *position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn builder(&self) -> UserId {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { builder, .. })
|
|
||||||
| Building::Dispenser(Dispenser { builder, .. })
|
|
||||||
| Building::Teleporter(Teleporter { builder, .. }) => *builder,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn angle(&self) -> f32 {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { angle, .. })
|
|
||||||
| Building::Dispenser(Dispenser { angle, .. })
|
|
||||||
| Building::Teleporter(Teleporter { angle, .. }) => *angle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_health(&self) -> u16 {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { max_health, .. })
|
|
||||||
| Building::Dispenser(Dispenser { max_health, .. })
|
|
||||||
| Building::Teleporter(Teleporter { max_health, .. }) => *max_health,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn health(&self) -> u16 {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { health, .. })
|
|
||||||
| Building::Dispenser(Dispenser { health, .. })
|
|
||||||
| Building::Teleporter(Teleporter { health, .. }) => *health,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sapped(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { sapped, .. })
|
|
||||||
| Building::Dispenser(Dispenser { sapped, .. })
|
|
||||||
| Building::Teleporter(Teleporter { sapped, .. }) => *sapped,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn team(&self) -> Team {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(Sentry { team, .. })
|
|
||||||
| Building::Dispenser(Dispenser { team, .. })
|
|
||||||
| Building::Teleporter(Teleporter { team, .. }) => *team,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn class(&self) -> BuildingClass {
|
|
||||||
match self {
|
|
||||||
Building::Sentry(_) => BuildingClass::Sentry,
|
|
||||||
Building::Dispenser(_) => BuildingClass::Sentry,
|
|
||||||
Building::Teleporter(_) => BuildingClass::Teleporter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum BuildingClass {
|
|
||||||
Sentry,
|
|
||||||
Dispenser,
|
|
||||||
Teleporter,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Projectile {
|
|
||||||
pub id: EntityId,
|
|
||||||
pub team: Team,
|
|
||||||
pub class: ClassId,
|
|
||||||
pub position: Vector,
|
|
||||||
pub speed: Vector,
|
|
||||||
pub bounds: Option<Box>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Projectile {
|
|
||||||
pub fn new(id: EntityId, class: ClassId) -> Self {
|
|
||||||
Projectile {
|
|
||||||
id,
|
|
||||||
team: Team::default(),
|
|
||||||
class,
|
|
||||||
position: Vector::default(),
|
|
||||||
speed: Vector::default(),
|
|
||||||
bounds: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Collision {
|
|
||||||
pub tick: DemoTick,
|
|
||||||
pub target: EntityId,
|
|
||||||
pub projectile: Projectile,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
||||||
pub struct World {
|
|
||||||
pub boundary_min: Vector,
|
|
||||||
pub boundary_max: Vector,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
||||||
pub struct Kill {
|
|
||||||
pub attacker_id: u16,
|
|
||||||
pub assister_id: u16,
|
|
||||||
pub victim_id: u16,
|
|
||||||
pub weapon: String,
|
|
||||||
pub tick: DemoTick,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Kill {
|
|
||||||
fn new(tick: DemoTick, death: &PlayerDeathEvent) -> Self {
|
|
||||||
Kill {
|
|
||||||
attacker_id: death.attacker,
|
|
||||||
assister_id: death.assister,
|
|
||||||
victim_id: death.user_id,
|
|
||||||
weapon: death.weapon.to_string(),
|
|
||||||
tick,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct GameState {
|
|
||||||
pub players: Vec<Player>,
|
|
||||||
pub buildings: BTreeMap<EntityId, Building>,
|
|
||||||
pub projectiles: BTreeMap<EntityId, Projectile>,
|
|
||||||
pub collisions: Vec<Collision>,
|
|
||||||
pub world: Option<World>,
|
|
||||||
pub kills: Vec<Kill>,
|
|
||||||
pub tick: DemoTick,
|
|
||||||
pub server_classes: Vec<ServerClass>,
|
|
||||||
pub interval_per_tick: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GameState {
|
|
||||||
pub fn get_player(&self, id: EntityId) -> Option<&Player> {
|
|
||||||
self.players.iter().find(|player| player.entity == id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_or_create_player(&mut self, entity_id: EntityId) -> &mut Player {
|
|
||||||
let index = match self
|
|
||||||
.players
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_index, player)| player.entity == entity_id)
|
|
||||||
.map(|(index, _)| index)
|
|
||||||
{
|
|
||||||
Some(index) => index,
|
|
||||||
None => {
|
|
||||||
let index = self.players.len();
|
|
||||||
self.players.push(Player::new(entity_id));
|
|
||||||
index
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
&mut self.players[index]
|
|
||||||
}
|
|
||||||
pub fn get_or_create_building(
|
|
||||||
&mut self,
|
|
||||||
entity_id: EntityId,
|
|
||||||
class: BuildingClass,
|
|
||||||
) -> &mut Building {
|
|
||||||
self.buildings
|
|
||||||
.entry(entity_id)
|
|
||||||
.or_insert_with(|| Building::new(entity_id, class))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_or_create_projectile(&mut self, id: EntityId, class: ClassId) -> &mut Projectile {
|
|
||||||
self.projectiles
|
|
||||||
.entry(id)
|
|
||||||
.or_insert_with(|| Projectile::new(id, class))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_collision(&self, projectile: &Projectile) -> Option<&Player> {
|
|
||||||
self.players
|
|
||||||
.iter()
|
|
||||||
.filter(|player| player.state == PlayerState::Alive)
|
|
||||||
.filter(|player| player.team != projectile.team)
|
|
||||||
.find(|player| player.collides(projectile, self.interval_per_tick))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn projectile_destroy(&mut self, id: EntityId) {
|
|
||||||
if let Some(projectile) = self.projectiles.remove(&id) {
|
|
||||||
if let Some(target) = self.check_collision(&projectile) {
|
|
||||||
self.collisions.push(Collision {
|
|
||||||
tick: self.tick,
|
|
||||||
target: target.entity,
|
|
||||||
projectile,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_building(&mut self, entity_id: EntityId) {
|
|
||||||
self.buildings.remove(&entity_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct GameStateAnalyser {
|
pub struct GameStateAnalyser {
|
||||||
pub state: GameState,
|
pub state: GameState,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue