1//! TF2's players: their class, team and state, their view, what they see of
2//! the HUD and of the game's menus, their custom models, and the game's own
3//! ways to change these.
4//!
5//! [`TfPlayer`] wraps a player within one engine callback. It reads the
6//! player's networked variables, and changes the player through the members
7//! `CTFPlayer` exposes to VScript, whose native bindings are found by name and
8//! checked against the SDK's signatures before each call, or through the
9//! player's vtable, at the slots [`sdk_raw::tf2::player`] checks.
10//!
11//! Other parts of a player have modules of their own:
12//! [`weapons`](crate::tf2::weapons) and [`ammo`](crate::tf2::ammo) for what
13//! they carry, [`buildings`](crate::tf2::buildings) for an Engineer's
14//! buildings, [`conditions`](crate::tf2::conditions) for their conditions,
15//! [`observer`](crate::tf2::observer) for spectating, and
16//! [`respawn`](crate::tf2::respawn).
17//!
18//! # Unverified
19//!
20//! No running server has tested this module yet. Its vtable slots agree with
21//! SourceMod's TF2 gamedata, and its bindings are checked by name and
22//! signature as they are called.
2324#[cfg(test)]
25#[path = "../tests/tf2/player.rs"]
26mod tests;
2728use crate::datatables::{NetProp, NetPropError};
29use crate::entities::{Entity, EntityHandle};
30use crate::math::{QAngle, Vector};
31use crate::tf2::PlayerClass;
32use crate::tf2::observer::VIEW_OFFSET;
33use crate::tf2::ragdolls::TfRagdoll;
34use crate::tf2::script_binding::{selfas binding, BindingError};
35use crate::tf2::teams::Team;
36use crate::user_messages::messages::VguiMenu;
37use crate::user_messages::{self, Recipients, UserMessageError};
38use crate::{Game, InterfaceError, Server};
39use sdk_raw::tf2::playeras raw;
40use sdk_raw::tf2::script_binding::{BOOL, INT, boolean, int, qangle, vector};
41use sdk_raw::{vcall, vcall_by_value};
42use std::ffi::{CStr, c_int};
4344/// The bounds of a crouching player, relative to their origin at their feet
45/// (`VEC_DUCK_HULL_MIN` and `VEC_DUCK_HULL_MAX` in TF2's view vectors,
46/// `game/shared/tf/tf_gamerules.cpp`): as wide and deep as standing, and 62
47/// units tall.
48#[doc(alias("VEC_DUCK_HULL_MIN", "VEC_DUCK_HULL_MAX"))]
49pub const PLAYER_DUCK_HULL: (Vector, Vector) = (
50Vector::new(-24.0, -24.0, 0.0),
51Vector::new(24.0, 24.0, 62.0),
52);
5354/// The bounds of a standing player of any class, relative to their origin
55/// at their feet (`VEC_HULL_MIN` and `VEC_HULL_MAX` in TF2's view vectors,
56/// `game/shared/tf/tf_gamerules.cpp`): 48 units wide and deep, and 82 tall.
57#[doc(alias("VEC_HULL_MIN", "VEC_HULL_MAX"))]
58pub const PLAYER_HULL: (Vector, Vector) = (
59Vector::new(-24.0, -24.0, 0.0),
60Vector::new(24.0, 24.0, 82.0),
61);
6263/// The script class that declares the members [`TfPlayer`] calls.
64const SCRIPT_CLASS: &CStr = c"CTFPlayer";
6566/// The script class that declares the members every player has, such as
67/// `SnapEyeAngles`.
68const BASE_SCRIPT_CLASS: &CStr = c"CBasePlayer";
6970/// Whether a player's view is forced into third person, as taunts force it
71/// (`m_nForceTauntCam`).
72#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ForcedTauntCam {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ForcedTauntCam::Off => "Off",
ForcedTauntCam::WhileAlive => "WhileAlive",
ForcedTauntCam::Always => "Always",
})
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ForcedTauntCam { }
#[automatically_derived]
impl ::core::clone::Clone for ForcedTauntCam {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ForcedTauntCam { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ForcedTauntCam { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ForcedTauntCam {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ForcedTauntCam { }Eq, #[automatically_derived]
impl ::core::hash::Hash for ForcedTauntCam {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
state)
}
}Hash, #[automatically_derived]
impl ::core::default::Default for ForcedTauntCam {
#[inline]
fn default() -> Self { Self::Off }
}Default)]
73pub enum ForcedTauntCam {
74/// The player chooses their own view.
75#[default]
76Off,
7778/// Third person while the player is alive.
79WhileAlive,
8081/// Third person, even while the player is dead.
82Always,
83}
8485impl ForcedTauntCam {
86/// The value of `m_nForceTauntCam`.
87pub const fn to_raw(self) -> c_int {
88match self {
89Self::Off => 0,
90Self::WhileAlive => 1,
91Self::Always => 2,
92 }
93 }
94}
9596#[doc = r" The parts of the HUD a player's `m_iHideHUD` hides (`HIDEHUD_*`)."]
#[doc = r""]
#[doc =
r" Clients receive only the low [`HIDEHUD_BITCOUNT`](raw::HIDEHUD_BITCOUNT)"]
#[doc =
r" bits, which the named flags cover. Others read back as set, but no"]
#[doc = r" client sees them."]
pub struct HideHud(<HideHud as ::bitflags::__private::PublicFlags>::Internal);
#[automatically_derived]
impl ::core::fmt::Debug for HideHud {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "HideHud",
&&self.0)
}
}
#[automatically_derived]
impl ::core::default::Default for HideHud {
#[inline]
fn default() -> Self { HideHud(::core::default::Default::default()) }
}
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for HideHud { }
#[automatically_derived]
impl ::core::clone::Clone for HideHud {
#[inline]
fn clone(&self) -> Self {
let _:
::core::clone::AssertParamIsClone<<HideHud as
::bitflags::__private::PublicFlags>::Internal>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for HideHud { }
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for HideHud { }
#[automatically_derived]
impl ::core::cmp::PartialEq for HideHud {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
#[automatically_derived]
impl ::core::cmp::Eq for HideHud {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _:
::core::cmp::AssertParamIsEq<<HideHud as
::bitflags::__private::PublicFlags>::Internal>;
}
}
#[automatically_derived]
impl ::core::hash::Hash for HideHud {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl HideHud {
#[doc = r" The whole HUD."]
#[doc(alias("HIDEHUD_ALL"))]
pub const ALL: Self = Self::from_bits_retain(raw::HIDEHUD_ALL as u32);
#[doc = r" The bonus progress display of other Source games."]
#[doc(alias("HIDEHUD_BONUS_PROGRESS"))]
pub const BONUS_PROGRESS: Self =
Self::from_bits_retain(raw::HIDEHUD_BONUS_PROGRESS as u32);
#[doc = r" The Engineer's building status."]
#[doc(alias("HIDEHUD_BUILDING_STATUS"))]
pub const BUILDING_STATUS: Self =
Self::from_bits_retain(raw::HIDEHUD_BUILDING_STATUS as u32);
#[doc = r" Chat, and the other communication elements, such as the voice"]
#[doc = r" icons."]
#[doc(alias("HIDEHUD_CHAT"))]
pub const CHAT: Self = Self::from_bits_retain(raw::HIDEHUD_CHAT as u32);
#[doc = r" The item effect meters, such as the Spy's cloak."]
#[doc(alias("HIDEHUD_CLOAK_AND_FEIGN"))]
pub const CLOAK_AND_FEIGN: Self =
Self::from_bits_retain(raw::HIDEHUD_CLOAK_AND_FEIGN as u32);
#[doc = r" The crosshair."]
#[doc(alias("HIDEHUD_CROSSHAIR"))]
pub const CROSSHAIR: Self =
Self::from_bits_retain(raw::HIDEHUD_CROSSHAIR as u32);
#[doc = r" The flashlight meter of other Source games."]
#[doc(alias("HIDEHUD_FLASHLIGHT"))]
pub const FLASHLIGHT: Self =
Self::from_bits_retain(raw::HIDEHUD_FLASHLIGHT as u32);
#[doc = r" The health display."]
#[doc(alias("HIDEHUD_HEALTH"))]
pub const HEALTH: Self =
Self::from_bits_retain(raw::HIDEHUD_HEALTH as u32);
#[doc = r" What a player in a vehicle should not see."]
#[doc(alias("HIDEHUD_INVEHICLE"))]
pub const IN_VEHICLE: Self =
Self::from_bits_retain(raw::HIDEHUD_INVEHICLE as u32);
#[doc =
r" The match status, such as the round timer and the team status at"]
#[doc = r" the top of the screen."]
#[doc(alias("HIDEHUD_MATCH_STATUS"))]
pub const MATCH_STATUS: Self =
Self::from_bits_retain(raw::HIDEHUD_MATCH_STATUS as u32);
#[doc = r" The Engineer's metal display."]
#[doc(alias("HIDEHUD_METAL"))]
pub const METAL: Self = Self::from_bits_retain(raw::HIDEHUD_METAL as u32);
#[doc =
r" Miscellaneous status elements, such as the pickup history and the"]
#[doc = r" kill feed."]
#[doc(alias("HIDEHUD_MISCSTATUS"))]
pub const MISC_STATUS: Self =
Self::from_bits_retain(raw::HIDEHUD_MISCSTATUS as u32);
#[doc = r" What a player without Half-Life 2's HEV suit should not see."]
#[doc(alias("HIDEHUD_NEEDSUIT"))]
pub const NEED_SUIT: Self =
Self::from_bits_retain(raw::HIDEHUD_NEEDSUIT as u32);
#[doc = r" The Demoman's sticky bomb count and shield charge meter."]
#[doc(alias("HIDEHUD_PIPES_AND_CHARGE"))]
pub const PIPES_AND_CHARGE: Self =
Self::from_bits_retain(raw::HIDEHUD_PIPES_AND_CHARGE as u32);
#[doc = r" What a dead player should not see."]
#[doc(alias("HIDEHUD_PLAYERDEAD"))]
pub const PLAYER_DEAD: Self =
Self::from_bits_retain(raw::HIDEHUD_PLAYERDEAD as u32);
#[doc = r" The target ID: the name and health of the player under the"]
#[doc = r" crosshair."]
#[doc(alias("HIDEHUD_TARGET_ID"))]
pub const TARGET_ID: Self =
Self::from_bits_retain(raw::HIDEHUD_TARGET_ID as u32);
#[doc = r" The vehicle crosshair of other Source games."]
#[doc(alias("HIDEHUD_VEHICLE_CROSSHAIR"))]
pub const VEHICLE_CROSSHAIR: Self =
Self::from_bits_retain(raw::HIDEHUD_VEHICLE_CROSSHAIR as u32);
#[doc = r" The ammo count and the weapon selection."]
#[doc(alias("HIDEHUD_WEAPONSELECTION"))]
pub const WEAPON_SELECTION: Self =
Self::from_bits_retain(raw::HIDEHUD_WEAPONSELECTION as u32);
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl ::bitflags::Flags for HideHud {
const FLAGS: &'static [::bitflags::Flag<HideHud>] =
{
mod __bitflags_flag_names {
#[allow(unused_imports)]
use super::*;
pub(super) const ALL: &'static str = "ALL";
pub(super) const BONUS_PROGRESS: &'static str =
"BONUS_PROGRESS";
pub(super) const BUILDING_STATUS: &'static str =
"BUILDING_STATUS";
pub(super) const CHAT: &'static str = "CHAT";
pub(super) const CLOAK_AND_FEIGN: &'static str =
"CLOAK_AND_FEIGN";
pub(super) const CROSSHAIR: &'static str = "CROSSHAIR";
pub(super) const FLASHLIGHT: &'static str = "FLASHLIGHT";
pub(super) const HEALTH: &'static str = "HEALTH";
pub(super) const IN_VEHICLE: &'static str = "IN_VEHICLE";
pub(super) const MATCH_STATUS: &'static str = "MATCH_STATUS";
pub(super) const METAL: &'static str = "METAL";
pub(super) const MISC_STATUS: &'static str = "MISC_STATUS";
pub(super) const NEED_SUIT: &'static str = "NEED_SUIT";
pub(super) const PIPES_AND_CHARGE: &'static str =
"PIPES_AND_CHARGE";
pub(super) const PLAYER_DEAD: &'static str = "PLAYER_DEAD";
pub(super) const TARGET_ID: &'static str = "TARGET_ID";
pub(super) const VEHICLE_CROSSHAIR: &'static str =
"VEHICLE_CROSSHAIR";
pub(super) const WEAPON_SELECTION: &'static str =
"WEAPON_SELECTION";
}
&[{
::bitflags::Flag::new(__bitflags_flag_names::ALL,
HideHud::ALL)
},
{
::bitflags::Flag::new(__bitflags_flag_names::BONUS_PROGRESS,
HideHud::BONUS_PROGRESS)
},
{
::bitflags::Flag::new(__bitflags_flag_names::BUILDING_STATUS,
HideHud::BUILDING_STATUS)
},
{
::bitflags::Flag::new(__bitflags_flag_names::CHAT,
HideHud::CHAT)
},
{
::bitflags::Flag::new(__bitflags_flag_names::CLOAK_AND_FEIGN,
HideHud::CLOAK_AND_FEIGN)
},
{
::bitflags::Flag::new(__bitflags_flag_names::CROSSHAIR,
HideHud::CROSSHAIR)
},
{
::bitflags::Flag::new(__bitflags_flag_names::FLASHLIGHT,
HideHud::FLASHLIGHT)
},
{
::bitflags::Flag::new(__bitflags_flag_names::HEALTH,
HideHud::HEALTH)
},
{
::bitflags::Flag::new(__bitflags_flag_names::IN_VEHICLE,
HideHud::IN_VEHICLE)
},
{
::bitflags::Flag::new(__bitflags_flag_names::MATCH_STATUS,
HideHud::MATCH_STATUS)
},
{
::bitflags::Flag::new(__bitflags_flag_names::METAL,
HideHud::METAL)
},
{
::bitflags::Flag::new(__bitflags_flag_names::MISC_STATUS,
HideHud::MISC_STATUS)
},
{
::bitflags::Flag::new(__bitflags_flag_names::NEED_SUIT,
HideHud::NEED_SUIT)
},
{
::bitflags::Flag::new(__bitflags_flag_names::PIPES_AND_CHARGE,
HideHud::PIPES_AND_CHARGE)
},
{
::bitflags::Flag::new(__bitflags_flag_names::PLAYER_DEAD,
HideHud::PLAYER_DEAD)
},
{
::bitflags::Flag::new(__bitflags_flag_names::TARGET_ID,
HideHud::TARGET_ID)
},
{
::bitflags::Flag::new(__bitflags_flag_names::VEHICLE_CROSSHAIR,
HideHud::VEHICLE_CROSSHAIR)
},
{
::bitflags::Flag::new(__bitflags_flag_names::WEAPON_SELECTION,
HideHud::WEAPON_SELECTION)
}]
};
type Bits = u32;
fn bits(&self) -> u32 { HideHud::bits(self) }
fn from_bits_retain(bits: u32) -> HideHud {
HideHud::from_bits_retain(bits)
}
fn all_named() -> HideHud {
const ALL_NAMED: u32 =
{
let mut truncated = <u32 as ::bitflags::Bits>::EMPTY;
let mut i = 0;
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
{
{
let flag = &<HideHud as ::bitflags::Flags>::FLAGS[i];
if flag.is_named() {
truncated = truncated | flag.value().bits();
}
i += 1;
}
};
let _ = i;
truncated
};
HideHud::from_bits_retain(ALL_NAMED)
}
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
const _: () =
{
#[repr(transparent)]
pub struct InternalBitFlags(u32);
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InternalBitFlags { }
#[automatically_derived]
impl ::core::clone::Clone for InternalBitFlags {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for InternalBitFlags { }
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for InternalBitFlags { }
#[automatically_derived]
impl ::core::cmp::PartialEq for InternalBitFlags {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
#[automatically_derived]
impl ::core::cmp::Eq for InternalBitFlags {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for InternalBitFlags {
#[inline]
fn partial_cmp(&self, other: &Self)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self,
other))
}
}
#[automatically_derived]
impl ::core::cmp::Ord for InternalBitFlags {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}
#[automatically_derived]
impl ::core::hash::Hash for InternalBitFlags {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}
impl ::bitflags::__private::PublicFlags for HideHud {
type Primitive = u32;
type Internal = InternalBitFlags;
}
impl ::bitflags::__private::core::default::Default for
InternalBitFlags {
#[inline]
fn default() -> Self { InternalBitFlags::empty() }
}
impl ::bitflags::__private::core::fmt::Debug for InternalBitFlags {
fn fmt(&self,
f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
-> ::bitflags::__private::core::fmt::Result {
if self.is_empty() {
f.write_fmt(format_args!("{0:#x}",
<u32 as ::bitflags::Bits>::EMPTY))
} else {
::bitflags::__private::core::fmt::Display::fmt(self, f)
}
}
}
impl ::bitflags::__private::core::fmt::Display for InternalBitFlags {
fn fmt(&self,
f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
-> ::bitflags::__private::core::fmt::Result {
::bitflags::parser::to_writer(&HideHud(*self), f)
}
}
impl ::bitflags::__private::core::str::FromStr for InternalBitFlags {
type Err = ::bitflags::parser::ParseError;
fn from_str(s: &str)
->
::bitflags::__private::core::result::Result<Self,
Self::Err> {
::bitflags::parser::from_str::<HideHud>(s).map(|flags|
flags.0)
}
}
impl ::bitflags::__private::core::convert::AsRef<u32> for
InternalBitFlags {
fn as_ref(&self) -> &u32 { &self.0 }
}
impl ::bitflags::__private::core::convert::From<u32> for
InternalBitFlags {
fn from(bits: u32) -> Self { Self::from_bits_retain(bits) }
}
impl InternalBitFlags {
/// Get a flags value with all bits unset.
#[inline]
pub const fn empty() -> Self {
Self(<u32 as ::bitflags::Bits>::EMPTY)
}
/// Get a flags value with all known bits set.
#[inline]
pub const fn all() -> Self {
const ALL: InternalBitFlags =
{
let mut truncated = <u32 as ::bitflags::Bits>::EMPTY;
let mut _i = 0;
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
{
{
truncated |=
<HideHud as ::bitflags::Flags>::FLAGS[_i].value().bits();
_i += 1;
}
};
InternalBitFlags(truncated)
};
ALL
}
/// Get the underlying bits value.
///
/// The returned value is exactly the bits set in this flags value.
#[inline]
pub const fn bits(&self) -> u32 { self.0 }
/// Convert from a bits value.
///
/// This method will return `None` if any unknown bits are set.
#[inline]
pub const fn from_bits(bits: u32)
-> ::bitflags::__private::core::option::Option<Self> {
let truncated = Self::from_bits_truncate(bits).0;
if truncated == bits {
::bitflags::__private::core::option::Option::Some(Self(bits))
} else { ::bitflags::__private::core::option::Option::None }
}
/// Convert from a bits value, unsetting any unknown bits.
#[inline]
pub const fn from_bits_truncate(bits: u32) -> Self {
Self(bits & Self::all().0)
}
/// Convert from a bits value exactly.
#[inline]
pub const fn from_bits_retain(bits: u32) -> Self { Self(bits) }
/// Get a flags value with the bits of a flag with the given name set.
///
/// This method will return `None` if `name` is empty or doesn't
/// correspond to any named flag.
#[inline]
pub fn from_name(name: &str)
-> ::bitflags::__private::core::option::Option<Self> {
mod __bitflags_flag_names {
#[allow(unused_imports)]
use super::*;
pub(super) const ALL: &'static str = "ALL";
pub(super) const BONUS_PROGRESS: &'static str =
"BONUS_PROGRESS";
pub(super) const BUILDING_STATUS: &'static str =
"BUILDING_STATUS";
pub(super) const CHAT: &'static str = "CHAT";
pub(super) const CLOAK_AND_FEIGN: &'static str =
"CLOAK_AND_FEIGN";
pub(super) const CROSSHAIR: &'static str = "CROSSHAIR";
pub(super) const FLASHLIGHT: &'static str = "FLASHLIGHT";
pub(super) const HEALTH: &'static str = "HEALTH";
pub(super) const IN_VEHICLE: &'static str = "IN_VEHICLE";
pub(super) const MATCH_STATUS: &'static str =
"MATCH_STATUS";
pub(super) const METAL: &'static str = "METAL";
pub(super) const MISC_STATUS: &'static str = "MISC_STATUS";
pub(super) const NEED_SUIT: &'static str = "NEED_SUIT";
pub(super) const PIPES_AND_CHARGE: &'static str =
"PIPES_AND_CHARGE";
pub(super) const PLAYER_DEAD: &'static str = "PLAYER_DEAD";
pub(super) const TARGET_ID: &'static str = "TARGET_ID";
pub(super) const VEHICLE_CROSSHAIR: &'static str =
"VEHICLE_CROSSHAIR";
pub(super) const WEAPON_SELECTION: &'static str =
"WEAPON_SELECTION";
}
{
{
if name == __bitflags_flag_names::ALL {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::ALL.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::BONUS_PROGRESS {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::BONUS_PROGRESS.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::BUILDING_STATUS {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::BUILDING_STATUS.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::CHAT {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::CHAT.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::CLOAK_AND_FEIGN {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::CLOAK_AND_FEIGN.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::CROSSHAIR {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::CROSSHAIR.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::FLASHLIGHT {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::FLASHLIGHT.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::HEALTH {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::HEALTH.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::IN_VEHICLE {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::IN_VEHICLE.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::MATCH_STATUS {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::MATCH_STATUS.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::METAL {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::METAL.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::MISC_STATUS {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::MISC_STATUS.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::NEED_SUIT {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::NEED_SUIT.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::PIPES_AND_CHARGE {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::PIPES_AND_CHARGE.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::PLAYER_DEAD {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::PLAYER_DEAD.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::TARGET_ID {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::TARGET_ID.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::VEHICLE_CROSSHAIR {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::VEHICLE_CROSSHAIR.bits()));
}
};
};
{
{
if name == __bitflags_flag_names::WEAPON_SELECTION {
return ::bitflags::__private::core::option::Option::Some(Self(HideHud::WEAPON_SELECTION.bits()));
}
};
};
let _ = name;
::bitflags::__private::core::option::Option::None
}
/// Whether all bits in `self` are unset.
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == <u32 as ::bitflags::Bits>::EMPTY
}
/// Whether all known bits in this flags value are set.
#[inline]
pub const fn is_all(&self) -> bool {
Self::all().0 | self.0 == self.0
}
/// Whether any set bits in `other` are also set in `self`.
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0 & other.0 != <u32 as ::bitflags::Bits>::EMPTY
}
/// Whether all set bits in `other` are also set in `self`.
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0 & other.0 == other.0
}
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
pub fn insert(&mut self, other: Self) {
*self = Self(self.0).union(other);
}
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `remove` won't truncate `other`, but the `!` operator will.
#[inline]
pub fn remove(&mut self, other: Self) {
*self = Self(self.0).difference(other);
}
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
pub fn toggle(&mut self, other: Self) {
*self = Self(self.0).symmetric_difference(other);
}
/// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
if value { self.insert(other); } else { self.remove(other); }
}
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0 ^ other.0)
}
/// The bitwise negation (`!`) of the bits in `self`, truncating the result.
#[inline]
#[must_use]
pub const fn complement(self) -> Self {
Self::from_bits_truncate(!self.0)
}
}
impl ::bitflags::__private::core::fmt::Binary for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::Octal for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::LowerHex for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::UpperHex for InternalBitFlags {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::ops::BitOr for InternalBitFlags {
type Output = Self;
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
fn bitor(self, other: InternalBitFlags) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for
InternalBitFlags {
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
type Output = Self;
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
InternalBitFlags {
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
type Output = Self;
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
InternalBitFlags {
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
fn bitand_assign(&mut self, other: Self) {
*self =
Self::from_bits_retain(self.bits()).intersection(other);
}
}
impl ::bitflags::__private::core::ops::Sub for InternalBitFlags {
type Output = Self;
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
{
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
type Output = Self;
/// The bitwise negation (`!`) of the bits in `self`, truncating the result.
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
InternalBitFlags {
/// The bitwise or (`|`) of the bits in each flags value.
fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(&mut self, iterator: T) {
for item in iterator { self.insert(item) }
}
}
impl ::bitflags::__private::core::iter::FromIterator<InternalBitFlags>
for InternalBitFlags {
/// The bitwise or (`|`) of the bits in each flags value.
fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(iterator: T) -> Self {
use ::bitflags::__private::core::iter::Extend;
let mut result = Self::empty();
result.extend(iterator);
result
}
}
impl InternalBitFlags {
/// Yield a set of contained flags values.
///
/// Each yielded flags value will correspond to a defined named flag. Any unknown bits
/// will be yielded together as a final flags value.
#[inline]
pub const fn iter(&self) -> ::bitflags::iter::Iter<HideHud> {
::bitflags::iter::Iter::__private_const_new(<HideHud as
::bitflags::Flags>::FLAGS,
HideHud::from_bits_retain(self.bits()),
HideHud::from_bits_retain(self.bits()))
}
/// Yield a set of contained named flags values.
///
/// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
/// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
#[inline]
pub const fn iter_names(&self)
-> ::bitflags::iter::IterNames<HideHud> {
::bitflags::iter::IterNames::__private_const_new(<HideHud as
::bitflags::Flags>::FLAGS,
HideHud::from_bits_retain(self.bits()),
HideHud::from_bits_retain(self.bits()))
}
}
impl ::bitflags::__private::core::iter::IntoIterator for
InternalBitFlags {
type Item = HideHud;
type IntoIter = ::bitflags::iter::Iter<HideHud>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
impl InternalBitFlags {
/// Returns a mutable reference to the raw value of the flags currently stored.
#[inline]
pub fn bits_mut(&mut self) -> &mut u32 { &mut self.0 }
}
impl HideHud {
/// Get a flags value with all bits unset.
#[inline]
pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
/// Get a flags value with all known bits set.
#[inline]
pub const fn all() -> Self { Self(InternalBitFlags::all()) }
/// Get the underlying bits value.
///
/// The returned value is exactly the bits set in this flags value.
#[inline]
pub const fn bits(&self) -> u32 { self.0.bits() }
/// Convert from a bits value.
///
/// This method will return `None` if any unknown bits are set.
#[inline]
pub const fn from_bits(bits: u32)
-> ::bitflags::__private::core::option::Option<Self> {
match InternalBitFlags::from_bits(bits) {
::bitflags::__private::core::option::Option::Some(bits) =>
::bitflags::__private::core::option::Option::Some(Self(bits)),
::bitflags::__private::core::option::Option::None =>
::bitflags::__private::core::option::Option::None,
}
}
/// Convert from a bits value, unsetting any unknown bits.
#[inline]
pub const fn from_bits_truncate(bits: u32) -> Self {
Self(InternalBitFlags::from_bits_truncate(bits))
}
/// Convert from a bits value exactly.
#[inline]
pub const fn from_bits_retain(bits: u32) -> Self {
Self(InternalBitFlags::from_bits_retain(bits))
}
/// Get a flags value with the bits of a flag with the given name set.
///
/// This method will return `None` if `name` is empty or doesn't
/// correspond to any named flag.
#[inline]
pub fn from_name(name: &str)
-> ::bitflags::__private::core::option::Option<Self> {
match InternalBitFlags::from_name(name) {
::bitflags::__private::core::option::Option::Some(bits) =>
::bitflags::__private::core::option::Option::Some(Self(bits)),
::bitflags::__private::core::option::Option::None =>
::bitflags::__private::core::option::Option::None,
}
}
/// Whether all bits in `self` are unset.
#[inline]
pub const fn is_empty(&self) -> bool { self.0.is_empty() }
/// Whether all known bits in this flags value are set.
#[inline]
pub const fn is_all(&self) -> bool { self.0.is_all() }
/// Whether any set bits in `other` are also set in `self`.
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0.intersects(other.0)
}
/// Whether all set bits in `other` are also set in `self`.
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0.contains(other.0)
}
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `remove` won't truncate `other`, but the `!` operator will.
#[inline]
pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
/// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
self.0.set(other.0, value)
}
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0.intersection(other.0))
}
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0.union(other.0))
}
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0.difference(other.0))
}
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0.symmetric_difference(other.0))
}
/// The bitwise negation (`!`) of the bits in `self`, truncating the result.
#[inline]
#[must_use]
pub const fn complement(self) -> Self {
Self(self.0.complement())
}
}
impl ::bitflags::__private::core::fmt::Binary for HideHud {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::Octal for HideHud {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::LowerHex for HideHud {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::fmt::UpperHex for HideHud {
fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
-> ::bitflags::__private::core::fmt::Result {
let inner = self.0;
::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
}
}
impl ::bitflags::__private::core::ops::BitOr for HideHud {
type Output = Self;
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
fn bitor(self, other: HideHud) -> Self { self.union(other) }
}
impl ::bitflags::__private::core::ops::BitOrAssign for HideHud {
/// The bitwise or (`|`) of the bits in `self` and `other`.
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for HideHud {
type Output = Self;
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for HideHud {
/// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for HideHud {
type Output = Self;
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for HideHud {
/// The bitwise and (`&`) of the bits in `self` and `other`.
#[inline]
fn bitand_assign(&mut self, other: Self) {
*self =
Self::from_bits_retain(self.bits()).intersection(other);
}
}
impl ::bitflags::__private::core::ops::Sub for HideHud {
type Output = Self;
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for HideHud {
/// The intersection of `self` with the complement of `other` (`&!`).
///
/// This method is not equivalent to `self & !other` when `other` has unknown bits set.
/// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for HideHud {
type Output = Self;
/// The bitwise negation (`!`) of the bits in `self`, truncating the result.
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<HideHud> for HideHud {
/// The bitwise or (`|`) of the bits in each flags value.
fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(&mut self, iterator: T) {
for item in iterator { self.insert(item) }
}
}
impl ::bitflags::__private::core::iter::FromIterator<HideHud> for
HideHud {
/// The bitwise or (`|`) of the bits in each flags value.
fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
= Self>>(iterator: T) -> Self {
use ::bitflags::__private::core::iter::Extend;
let mut result = Self::empty();
result.extend(iterator);
result
}
}
impl HideHud {
/// Yield a set of contained flags values.
///
/// Each yielded flags value will correspond to a defined named flag. Any unknown bits
/// will be yielded together as a final flags value.
#[inline]
pub const fn iter(&self) -> ::bitflags::iter::Iter<HideHud> {
::bitflags::iter::Iter::__private_const_new(<HideHud as
::bitflags::Flags>::FLAGS,
HideHud::from_bits_retain(self.bits()),
HideHud::from_bits_retain(self.bits()))
}
/// Yield a set of contained named flags values.
///
/// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
/// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
#[inline]
pub const fn iter_names(&self)
-> ::bitflags::iter::IterNames<HideHud> {
::bitflags::iter::IterNames::__private_const_new(<HideHud as
::bitflags::Flags>::FLAGS,
HideHud::from_bits_retain(self.bits()),
HideHud::from_bits_retain(self.bits()))
}
}
impl ::bitflags::__private::core::iter::IntoIterator for HideHud {
type Item = HideHud;
type IntoIter = ::bitflags::iter::Iter<HideHud>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
};bitflags::bitflags! {
97/// The parts of the HUD a player's `m_iHideHUD` hides (`HIDEHUD_*`).
98 ///
99 /// Clients receive only the low [`HIDEHUD_BITCOUNT`](raw::HIDEHUD_BITCOUNT)
100 /// bits, which the named flags cover. Others read back as set, but no
101 /// client sees them.
102#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
103pub struct HideHud: u32 {
104/// The whole HUD.
105#[doc(alias("HIDEHUD_ALL"))]
106const ALL = raw::HIDEHUD_ALL as u32;
107108/// The bonus progress display of other Source games.
109#[doc(alias("HIDEHUD_BONUS_PROGRESS"))]
110const BONUS_PROGRESS = raw::HIDEHUD_BONUS_PROGRESS as u32;
111112/// The Engineer's building status.
113#[doc(alias("HIDEHUD_BUILDING_STATUS"))]
114const BUILDING_STATUS = raw::HIDEHUD_BUILDING_STATUS as u32;
115116/// Chat, and the other communication elements, such as the voice
117 /// icons.
118#[doc(alias("HIDEHUD_CHAT"))]
119const CHAT = raw::HIDEHUD_CHAT as u32;
120121/// The item effect meters, such as the Spy's cloak.
122#[doc(alias("HIDEHUD_CLOAK_AND_FEIGN"))]
123const CLOAK_AND_FEIGN = raw::HIDEHUD_CLOAK_AND_FEIGN as u32;
124125/// The crosshair.
126#[doc(alias("HIDEHUD_CROSSHAIR"))]
127const CROSSHAIR = raw::HIDEHUD_CROSSHAIR as u32;
128129/// The flashlight meter of other Source games.
130#[doc(alias("HIDEHUD_FLASHLIGHT"))]
131const FLASHLIGHT = raw::HIDEHUD_FLASHLIGHT as u32;
132133/// The health display.
134#[doc(alias("HIDEHUD_HEALTH"))]
135const HEALTH = raw::HIDEHUD_HEALTH as u32;
136137/// What a player in a vehicle should not see.
138#[doc(alias("HIDEHUD_INVEHICLE"))]
139const IN_VEHICLE = raw::HIDEHUD_INVEHICLE as u32;
140141/// The match status, such as the round timer and the team status at
142 /// the top of the screen.
143#[doc(alias("HIDEHUD_MATCH_STATUS"))]
144const MATCH_STATUS = raw::HIDEHUD_MATCH_STATUS as u32;
145146/// The Engineer's metal display.
147#[doc(alias("HIDEHUD_METAL"))]
148const METAL = raw::HIDEHUD_METAL as u32;
149150/// Miscellaneous status elements, such as the pickup history and the
151 /// kill feed.
152#[doc(alias("HIDEHUD_MISCSTATUS"))]
153const MISC_STATUS = raw::HIDEHUD_MISCSTATUS as u32;
154155/// What a player without Half-Life 2's HEV suit should not see.
156#[doc(alias("HIDEHUD_NEEDSUIT"))]
157const NEED_SUIT = raw::HIDEHUD_NEEDSUIT as u32;
158159/// The Demoman's sticky bomb count and shield charge meter.
160#[doc(alias("HIDEHUD_PIPES_AND_CHARGE"))]
161const PIPES_AND_CHARGE = raw::HIDEHUD_PIPES_AND_CHARGE as u32;
162163/// What a dead player should not see.
164#[doc(alias("HIDEHUD_PLAYERDEAD"))]
165const PLAYER_DEAD = raw::HIDEHUD_PLAYERDEAD as u32;
166167/// The target ID: the name and health of the player under the
168 /// crosshair.
169#[doc(alias("HIDEHUD_TARGET_ID"))]
170const TARGET_ID = raw::HIDEHUD_TARGET_ID as u32;
171172/// The vehicle crosshair of other Source games.
173#[doc(alias("HIDEHUD_VEHICLE_CROSSHAIR"))]
174const VEHICLE_CROSSHAIR = raw::HIDEHUD_VEHICLE_CROSSHAIR as u32;
175176/// The ammo count and the weapon selection.
177#[doc(alias("HIDEHUD_WEAPONSELECTION"))]
178const WEAPON_SELECTION = raw::HIDEHUD_WEAPONSELECTION as u32;
179 }
180}181182/// Why a [`TfPlayer`] operation failed.
183#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PlayerError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Interface(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Interface", &__self_0),
Self::MarkedForDeletion =>
::core::fmt::Formatter::write_str(f, "MarkedForDeletion"),
Self::NetProp(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"NetProp", &__self_0),
Self::NonFinite =>
::core::fmt::Formatter::write_str(f, "NonFinite"),
Self::NotAlive =>
::core::fmt::Formatter::write_str(f, "NotAlive"),
Self::NotNetworked =>
::core::fmt::Formatter::write_str(f, "NotNetworked"),
Self::NotPlaying =>
::core::fmt::Formatter::write_str(f, "NotPlaying"),
Self::NotTfPlayer =>
::core::fmt::Formatter::write_str(f, "NotTfPlayer"),
Self::Rejected =>
::core::fmt::Formatter::write_str(f, "Rejected"),
Self::UnknownClass(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UnknownClass", &__self_0),
Self::UnknownState(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UnknownState", &__self_0),
Self::UnknownTeam(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UnknownTeam", &__self_0),
Self::UnsupportedMethod =>
::core::fmt::Formatter::write_str(f, "UnsupportedMethod"),
Self::UserMessage(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UserMessage", &__self_0),
}
}
}Debug, #[allow(unused_qualifications)]
#[automatically_derived]
impl ::thiserror::__private21::Error for PlayerError {
fn source(&self)
->
::core::option::Option<&(dyn ::thiserror::__private21::Error +
'static)> {
use ::thiserror::__private21::AsDynError as _;
#[allow(deprecated)]
match self {
PlayerError::Interface { 0: transparent } =>
::thiserror::__private21::Error::source(transparent.as_dyn_error()),
PlayerError::MarkedForDeletion { .. } =>
::core::option::Option::None,
PlayerError::NetProp { 0: transparent } =>
::thiserror::__private21::Error::source(transparent.as_dyn_error()),
PlayerError::NonFinite { .. } => ::core::option::Option::None,
PlayerError::NotAlive { .. } => ::core::option::Option::None,
PlayerError::NotNetworked { .. } => ::core::option::Option::None,
PlayerError::NotPlaying { .. } => ::core::option::Option::None,
PlayerError::NotTfPlayer { .. } => ::core::option::Option::None,
PlayerError::Rejected { .. } => ::core::option::Option::None,
PlayerError::UnknownClass { .. } => ::core::option::Option::None,
PlayerError::UnknownState { .. } => ::core::option::Option::None,
PlayerError::UnknownTeam { .. } => ::core::option::Option::None,
PlayerError::UnsupportedMethod { .. } =>
::core::option::Option::None,
PlayerError::UserMessage { 0: transparent } =>
::thiserror::__private21::Error::source(transparent.as_dyn_error()),
}
}
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::core::fmt::Display for PlayerError {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter)
-> ::core::fmt::Result {
use ::thiserror::__private21::AsDisplay as _;
#[allow(unused_variables, deprecated, clippy ::
used_underscore_binding)]
match self {
PlayerError::Interface(_0) =>
::core::fmt::Display::fmt(_0, __formatter),
PlayerError::MarkedForDeletion {} =>
__formatter.write_str("the player is marked for deletion"),
PlayerError::NetProp(_0) =>
::core::fmt::Display::fmt(_0, __formatter),
PlayerError::NonFinite {} =>
__formatter.write_str("a value given is NaN or infinite"),
PlayerError::NotAlive {} =>
__formatter.write_str("the player is not alive"),
PlayerError::NotNetworked {} =>
__formatter.write_str("the player is not networked"),
PlayerError::NotPlaying {} =>
__formatter.write_str("the player is not on a playing team"),
PlayerError::NotTfPlayer {} =>
__formatter.write_str("player operations require a TF2 player"),
PlayerError::Rejected {} =>
__formatter.write_str("the native method rejected its arguments"),
PlayerError::UnknownClass(_0) =>
match (_0.as_display(),) {
(__display0,) =>
__formatter.write_fmt(format_args!("the player\'s class {0} is not one of TF2\'s playable classes",
__display0)),
},
PlayerError::UnknownState(_0) =>
match (_0.as_display(),) {
(__display0,) =>
__formatter.write_fmt(format_args!("the player\'s state {0} is not one of TF2\'s",
__display0)),
},
PlayerError::UnknownTeam(_0) =>
match (_0.as_display(),) {
(__display0,) =>
__formatter.write_fmt(format_args!("the player\'s team {0} is not one of TF2\'s",
__display0)),
},
PlayerError::UnsupportedMethod {} =>
__formatter.write_str("the game does not expose the expected native method"),
PlayerError::UserMessage(_0) =>
::core::fmt::Display::fmt(_0, __formatter),
}
}
}
PlayerError
#[allow(clippy :: redundant_field_names)]
fn from(source: InterfaceError) -> Self {
PlayerError::Interface { 0: source }
}
PlayerError
#[allow(clippy :: redundant_field_names)]
fn from(source: NetPropError) -> Self { PlayerError::NetProp { 0: source } }
PlayerError
#[allow(clippy :: redundant_field_names)]
fn from(source: UserMessageError) -> Self {
PlayerError::UserMessage { 0: source }
}thiserror::Error)]
184pub enum PlayerError {
185/// A required engine interface is unavailable.
186#[error(transparent)]
187Interface(#[from] InterfaceError),
188189/// The player is marked for deletion.
190#[error("the player is marked for deletion")]
191MarkedForDeletion,
192193/// A networked variable could not be read or written.
194#[error(transparent)]
195NetProp(#[from] NetPropError),
196197/// A vector, angle or time given has a component that is NaN or infinite.
198#[error("a value given is NaN or infinite")]
199NonFinite,
200201/// The player is not alive.
202#[error("the player is not alive")]
203NotAlive,
204205/// The player has no edict, so no client knows it.
206#[error("the player is not networked")]
207NotNetworked,
208209/// The player is on neither RED nor BLU, which have the class menus.
210#[error("the player is not on a playing team")]
211NotPlaying,
212213/// The entity is not a TF2 player, or the server does not run TF2.
214#[error("player operations require a TF2 player")]
215NotTfPlayer,
216217/// The native binding of the member reported failure.
218#[error("the native method rejected its arguments")]
219Rejected,
220221/// The player's `m_iClass` or `m_iDesiredPlayerClass` holds neither
222 /// `TF_CLASS_UNDEFINED` nor a playable class, as scripts can make it.
223#[error("the player's class {0} is not one of TF2's playable classes")]
224UnknownClass(c_int),
225226/// The player's `m_nPlayerState` is not one of TF2's player states.
227#[error("the player's state {0} is not one of TF2's")]
228UnknownState(c_int),
229230/// The player's `m_iTeamNum` is not one of TF2's teams.
231#[error("the player's team {0} is not one of TF2's")]
232UnknownTeam(c_int),
233234/// The player's script class descriptors lack the member, or its
235 /// signature differs from the SDK's.
236#[error("the game does not expose the expected native method")]
237UnsupportedMethod,
238239/// The game's menu could not be sent to the player.
240#[error(transparent)]
241UserMessage(#[from] UserMessageError),
242}
243244impl From<BindingError> for PlayerError {
245fn from(error: BindingError) -> Self {
246match error {
247 BindingError::Unavailable | BindingError::SignatureMismatch => Self::UnsupportedMethod,
248 BindingError::Rejected => Self::Rejected,
249 }
250 }
251}
252253/// Where a TF2 player is in the game's handling of them (`m_nPlayerState`).
254#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PlayerState {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
PlayerState::Active => "Active",
PlayerState::Welcome => "Welcome",
PlayerState::Observer => "Observer",
PlayerState::Dying => "Dying",
})
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PlayerState { }
#[automatically_derived]
impl ::core::clone::Clone for PlayerState {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PlayerState { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for PlayerState { }
#[automatically_derived]
impl ::core::cmp::PartialEq for PlayerState {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for PlayerState { }Eq, #[automatically_derived]
impl ::core::hash::Hash for PlayerState {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
state)
}
}Hash)]
255pub enum PlayerState {
256/// Playing, alive or dead, until they start to observe
257 /// (`TF_STATE_ACTIVE`).
258#[doc(alias("TF_STATE_ACTIVE"))]
259Active,
260261/// Just joined the server, and has chosen no team yet
262 /// (`TF_STATE_WELCOME`).
263#[doc(alias("TF_STATE_WELCOME"))]
264Welcome,
265266/// Observing, as spectators, unassigned players, and dead players once
267 /// the death cam ends, do (`TF_STATE_OBSERVER`).
268#[doc(alias("TF_STATE_OBSERVER"))]
269Observer,
270271/// Dying: from death until the player starts to observe
272 /// (`TF_STATE_DYING`).
273#[doc(alias("TF_STATE_DYING"))]
274Dying,
275}
276277impl PlayerState {
278/// The state TF2 numbers `raw`, or `None` if it numbers none so.
279pub const fn from_raw(raw: c_int) -> Option<Self> {
280match raw {
281 raw::TF_STATE_ACTIVE => Some(Self::Active),
282 raw::TF_STATE_WELCOME => Some(Self::Welcome),
283 raw::TF_STATE_OBSERVER => Some(Self::Observer),
284 raw::TF_STATE_DYING => Some(Self::Dying),
285_ => None,
286 }
287 }
288289/// TF2's number for the state.
290pub const fn to_raw(self) -> c_int {
291match self {
292Self::Active => raw::TF_STATE_ACTIVE,
293Self::Welcome => raw::TF_STATE_WELCOME,
294Self::Observer => raw::TF_STATE_OBSERVER,
295Self::Dying => raw::TF_STATE_DYING,
296 }
297 }
298}
299300/// A TF2 player or bot, within one engine callback.
301///
302/// Keep the player's [`EntityHandle`] across callbacks, and wrap it again in
303/// each.
304#[derive(#[automatically_derived]
impl<'s> ::core::fmt::Debug for TfPlayer<'s> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "TfPlayer",
"server", &self.server, "player", &&self.player)
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl<'s> ::core::clone::TrivialClone for TfPlayer<'s> { }
#[automatically_derived]
impl<'s> ::core::clone::Clone for TfPlayer<'s> {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<Server<'s>>;
let _: ::core::clone::AssertParamIsClone<Entity<'s>>;
*self
}
}Clone, #[automatically_derived]
impl<'s> ::core::marker::Copy for TfPlayer<'s> { }Copy)]
305pub struct TfPlayer<'s> {
306 server: Server<'s>,
307 player: Entity<'s>,
308}
309310impl<'s> TfPlayer<'s> {
311/// Wraps a player, or returns [`PlayerError::NotTfPlayer`] unless the
312 /// server runs TF2 and `player`'s datamaps include `CTFPlayer`.
313pub fn new(server: Server<'s>, player: Entity<'s>) -> Result<Self, PlayerError> {
314if server.game() != Game::TeamFortress2 || !player.has_data_map_class(c"CTFPlayer") {
315return Err(PlayerError::NotTfPlayer);
316 }
317318Ok(Self { server, player })
319 }
320321/// Hides more parts of the player's HUD, keeping those hidden already
322 /// (`AddHudHideFlags`).
323#[doc(alias("AddHudHideFlags", "m_iHideHUD"))]
324pub fn add_hud_hide_flags(self, flags: HideHud) -> Result<(), PlayerError> {
325self.call_with_flags(c"AddHudHideFlags", flags)
326 }
327328/// Pushes the player, adding `impulse` to their velocity as an explosion's
329 /// knockback does (`ApplyAbsVelocityImpulse`), in units per second.
330 ///
331 /// TF2 scales the push first: it doubles it for a player shrunk by
332 /// `TF_COND_HALLOWEEN_TINY`, scales it by a scoped Sniper's
333 /// `mult_aiming_knockback_resistance`, and its horizontal part by 1.5 for
334 /// a player with a parachute open (or by 0 for one of Mann vs. Machine's
335 /// robots). Fails with [`PlayerError::NonFinite`] for an impulse that is
336 /// not finite, before the game is called.
337#[doc(alias("ApplyAbsVelocityImpulse"))]
338pub fn apply_impulse(self, impulse: Vector) -> Result<(), PlayerError> {
339if !impulse.is_finite() {
340return Err(PlayerError::NonFinite);
341 }
342343let impulse = sys::Vector::from(impulse);
344345// SAFETY: The checked member adds the finite impulse, which it only
346 // reads during the call, to the player's velocity.
347unsafe {
348self.call(
349c"ApplyAbsVelocityImpulse",
350&mut [vector(&impulse)],
351 binding::VOID,
352 )
353 }?;
354355Ok(())
356 }
357358/// Calls one of the native members `CTFPlayer` exposes to VScript, for a
359 /// player not marked for deletion.
360 ///
361 /// # Safety
362 ///
363 /// As for [`binding::call`]: the member must accept this player and these
364 /// arguments, which must stay valid for the call.
365unsafe fn call(
366self,
367 name: &CStr,
368 arguments: &mut [sys::ScriptVariant_t],
369 result_type: sys::ScriptDataType_t,
370 ) -> Result<sys::ScriptVariant_t, PlayerError> {
371self.check_live()?;
372373// SAFETY: The caller vouches for the member and its arguments.
374Ok(unsafe { binding::call(self.player, SCRIPT_CLASS, name, arguments, result_type) }?)
375 }
376377/// Calls a member that returns one float.
378 ///
379 /// # Safety
380 ///
381 /// As for [`Self::call`].
382unsafe fn call_for_float(self, name: &CStr) -> Result<f32, PlayerError> {
383// SAFETY: The caller vouches for the member.
384let result = unsafe { self.call(name, &mut [], binding::FLOAT) }?;
385386// SAFETY: The checked return type selects the float member.
387Ok(unsafe { result.__bindgen_anon_1.m_float })
388 }
389390/// Calls a member that takes one boolean and returns nothing.
391 ///
392 /// # Safety
393 ///
394 /// As for [`Self::call`].
395unsafe fn call_with_bool(self, name: &CStr, value: bool) -> Result<(), PlayerError> {
396// SAFETY: The caller vouches for the member.
397unsafe { self.call(name, &mut [boolean(value)], binding::VOID) }?;
398399Ok(())
400 }
401402/// Calls one of the members that write `m_iHideHUD` with `flags`.
403fn call_with_flags(self, name: &CStr, flags: HideHud) -> Result<(), PlayerError> {
404// SAFETY: The HUD flag members only compute and store the player's
405 // `m_iHideHUD`, of which clients read the bits they know.
406unsafe { self.call(name, &mut [int(flags.bits() as c_int)], binding::VOID) }?;
407408Ok(())
409 }
410411/// Calls a member that takes a model's path and returns nothing.
412fn call_with_model(self, name: &CStr, model: &CStr) -> Result<(), PlayerError> {
413// SAFETY: The custom model members precache the model, copying its
414 // path, which they read only during the call, into the string pool.
415 // Updating the player's model frees nothing.
416unsafe { self.call(name, &mut [binding::string(model)], binding::VOID) }?;
417418Ok(())
419 }
420421/// Fails with [`PlayerError::MarkedForDeletion`] for a player marked for
422 /// deletion.
423fn check_live(self) -> Result<(), PlayerError> {
424if self.player.is_marked_for_deletion() {
425Err(PlayerError::MarkedForDeletion)
426 } else {
427Ok(())
428 }
429 }
430431/// The player's class (`m_iClass`), or `None` until they first spawn as
432 /// one (`TF_CLASS_UNDEFINED`).
433 ///
434 /// Fails with [`PlayerError::UnknownClass`] for any other value, such as
435 /// the civilian a script can set.
436#[doc(alias("m_iClass", "GetPlayerClass"))]
437pub fn class(self) -> Result<Option<PlayerClass>, PlayerError> {
438self.read_class(c"m_iClass")
439 }
440441/// Clears the player's custom model, its offset and its rotation, so they
442 /// show their class's model again (`SetCustomModel` with an empty path).
443#[doc(alias("SetCustomModel"))]
444pub fn clear_custom_model(self) -> Result<(), PlayerError> {
445self.call_with_model(c"SetCustomModel", c"")
446 }
447448/// Clears the rotation of the player's custom model
449 /// (`ClearCustomModelRotation`).
450#[doc(alias("ClearCustomModelRotation"))]
451pub fn clear_custom_model_rotation(self) -> Result<(), PlayerError> {
452// SAFETY: The member resets the stored rotation, and invalidates the
453 // player's cached angles.
454unsafe { self.call(c"ClearCustomModelRotation", &mut [], binding::VOID) }?;
455456Ok(())
457 }
458459/// Kills the player as the `kill` and `explode` commands do, through the
460 /// game's `CTFPlayer::CommitSuicide`, and returns whether they died.
461 ///
462 /// With `explode`, the player bursts into gibs. TF2 refuses the suicide of
463 /// a player who is not alive, who has no class or is not in
464 /// [`PlayerState::Active`], who is a ghost or in a kart, while the match
465 /// summary shows, and, without `force`, of a player on the losing team
466 /// after a round is won.
467 ///
468 /// The death runs the game's, other plugins', and this plugin's own death
469 /// callbacks synchronously, before this returns, such as hooks of the
470 /// player's `Event_Killed` and listeners of `player_death`. They must keep
471 /// to the contract of [`Server::new`].
472#[doc(alias("CommitSuicide"))]
473pub fn commit_suicide(self, explode: bool, force: bool) -> Result<bool, PlayerError> {
474self.check_live()?;
475476if !self.player.is_alive() {
477return Ok(false);
478 }
479480let player = self.player.as_ptr().cast::<sys::CTFPlayer>();
481482// SAFETY: `new` found `CTFPlayer` in the player's datamaps, so it is
483 // one, whose entity base `sdk_raw::tf2` asserts is at offset zero, and
484 // whose generated TF2 vtable has this entry under the target's ABI, at
485 // the slot `sdk_raw::tf2::player` checks. The callback keeps the player
486 // allocated, and the callbacks the death runs are bound by
487 // `Server::new`'s contract, which lets them free entities only through
488 // deferred deletion.
489unsafe {
490{
let this = player;
let vtable =
::source_sdk_2013_raw::util::vtable::vtable_pointer::<sys::CTFPlayer__bindgen_vtable>(this);
((*vtable).CTFPlayer_CommitSuicide)(this.cast(), explode, force)
}vcall!(player as sys::CTFPlayer__bindgen_vtable => CTFPlayer_CommitSuicide(explode, force))491 };
492493Ok(!self.player.is_alive())
494 }
495496/// The class the player chose to play next (`m_iDesiredPlayerClass`),
497 /// which they spawn as, or `None` if they have chosen none.
498 ///
499 /// Fails with [`PlayerError::UnknownClass`] for a value that is neither
500 /// `TF_CLASS_UNDEFINED` nor a playable class.
501#[doc(alias("m_iDesiredPlayerClass", "GetDesiredPlayerClassIndex"))]
502pub fn desired_class(self) -> Result<Option<PlayerClass>, PlayerError> {
503self.read_class(c"m_iDesiredPlayerClass")
504 }
505506/// The angles the player looks along (`EyeAngles`): their view angles, in
507 /// the world's frame even while they move with a parent entity, whose frame
508 /// the game keeps their view angles in then.
509#[doc(alias("EyeAngles"))]
510pub fn eye_angles(self) -> Result<QAngle, PlayerError> {
511self.check_live()?;
512513let player = self.player.as_ptr().cast::<sys::CTFPlayer>();
514515// SAFETY: As for `commit_suicide`, at the slot `sdk_raw::tf2::player`
516 // checks. The method only reads the player and its parent, and returns
517 // a reference to angles the player holds, or to a static, copied before
518 // anything can change them.
519let angles = unsafe {
520{
let this = player;
let vtable =
::source_sdk_2013_raw::util::vtable::vtable_pointer::<sys::CTFPlayer__bindgen_vtable>(this);
((*vtable).CTFPlayer_EyeAngles)(this.cast())
}vcall!(player as sys::CTFPlayer__bindgen_vtable => CTFPlayer_EyeAngles()).read()
521 };
522523Ok(angles.into())
524 }
525526/// Where the player sees from (`EyePosition`): their origin raised by their
527 /// [view offset](Self::view_offset).
528#[doc(alias("EyePosition"))]
529pub fn eye_position(self) -> Result<Vector, PlayerError> {
530self.check_live()?;
531532let player = self.player.as_ptr().cast::<sys::CTFPlayer>();
533534// SAFETY: As for `commit_suicide`, at the slot `sdk_raw::tf2::player`
535 // checks, whose generated entry returns a `Vector` by value in one of
536 // the shapes `vcall_by_value!` calls. The method only reads the player.
537let position = unsafe {
538{
let result: sys::Vector =
{
let this = player;
let vtable =
::source_sdk_2013_raw::util::vtable::vtable_pointer::<sys::CTFPlayer__bindgen_vtable>(this);
((*vtable).CTFPlayer_EyePosition)(this.cast())
};
result
}vcall_by_value!(player as sys::CTFPlayer__bindgen_vtable => CTFPlayer_EyePosition() -> sys::Vector)539 };
540541Ok(position.into())
542 }
543544/// Moves the player to `team` as the game's own team changes do
545 /// (`ForceChangeTeam`), whatever the team limits and balance allow.
546 ///
547 /// TF2 removes the player's buildings and projectiles, and leaves a
548 /// living player alive, on the new team, unless `full_team_switch` is
549 /// false in highlander mode, which kills them and clears their class. A
550 /// player moved to [`Team::Spectator`] loses their weapons, and one moved
551 /// to [`Team::Spectator`] or [`Team::Unassigned`] starts to observe.
552 /// Without `full_team_switch`, the player's dominations end too, as they
553 /// do when a player changes teams themselves; the game passes it when it
554 /// swaps whole teams.
555 ///
556 /// TF2 changes nothing for a player already on the team, nor for one in a
557 /// duel or coaching another, and outside community game modes its game
558 /// rules can assign another team, as Mann vs. Machine does. The change
559 /// runs the game's, other plugins', and this plugin's own callbacks
560 /// synchronously, such as `metamod_source`'s `team_hooks` and listeners of
561 /// `player_team`, and they must keep to the contract of [`Server::new`].
562#[doc(alias("ForceChangeTeam", "ChangeClientTeam"))]
563pub fn force_change_team(self, team: Team, full_team_switch: bool) -> Result<(), PlayerError> {
564// SAFETY: The checked member accepts every global team, and frees the
565 // entities it removes, and those its callbacks free, only through
566 // deferred deletion (`Server::new`'s contract).
567unsafe {
568self.call(
569c"ForceChangeTeam",
570&mut [int(team.to_raw()), boolean(full_team_switch)],
571 binding::VOID,
572 )
573 }?;
574575Ok(())
576 }
577578/// Hides both teams' class menus from the player, if shown.
579pub fn hide_class_menu(self) -> Result<(), PlayerError> {
580self.send_panel(raw::PANEL_CLASS_RED, false)?;
581self.send_panel(raw::PANEL_CLASS_BLUE, false)
582 }
583584/// Hides the team menu from the player, if shown.
585pub fn hide_team_menu(self) -> Result<(), PlayerError> {
586self.send_panel(raw::PANEL_TEAM, false)
587 }
588589/// The parts of the HUD hidden from the player (`GetHudHideFlags`).
590#[doc(alias("GetHudHideFlags", "m_iHideHUD"))]
591pub fn hud_hide_flags(self) -> Result<HideHud, PlayerError> {
592// SAFETY: The member returns the player's `m_iHideHUD`.
593let result = unsafe { self.call(c"GetHudHideFlags", &mut [], INT) }?;
594595// SAFETY: The checked return type selects the integer member.
596let flags = unsafe { result.__bindgen_anon_1.m_int };
597598Ok(HideHud::from_bits_retain(flagsas u32))
599 }
600601/// Whether the player has called for a Medic in the last few seconds
602 /// (`IsCallingForMedic`).
603#[doc(alias("IsCallingForMedic"))]
604pub fn is_calling_for_medic(self) -> Result<bool, PlayerError> {
605// SAFETY: The member compares the time of the player's last call with
606 // the current time.
607let result = unsafe { self.call(c"IsCallingForMedic", &mut [], BOOL) }?;
608609// SAFETY: The checked return type selects the boolean member.
610Ok(unsafe { result.__bindgen_anon_1.m_bool })
611 }
612613/// Resolves one of the player's networked variables.
614fn net_prop(self, name: &CStr) -> Result<NetProp<'s>, PlayerError> {
615Ok(self
616.server
617 .server_game_dll()?
618.entity_net_prop(self.player, name)?)
619 }
620621/// The game time from which the player may change their class again
622 /// (`GetNextChangeClassTime`), as the game's current time counts it.
623#[doc(alias("GetNextChangeClassTime", "m_flNextChangeClassTime"))]
624pub fn next_change_class_time(self) -> Result<f32, PlayerError> {
625// SAFETY: The member returns a field of the player.
626unsafe { self.call_for_float(c"GetNextChangeClassTime") }
627 }
628629/// The game time from which the player may change their team again
630 /// (`GetNextChangeTeamTime`), as the game's current time counts it.
631#[doc(alias("GetNextChangeTeamTime", "m_flNextChangeTeamTime"))]
632pub fn next_change_team_time(self) -> Result<f32, PlayerError> {
633// SAFETY: The member returns a field of the player.
634unsafe { self.call_for_float(c"GetNextChangeTeamTime") }
635 }
636637/// The player.
638pub const fn player(self) -> Entity<'s> {
639self.player
640 }
641642/// The player's latest `tf_ragdoll` (`m_hRagdoll`), from which clients
643 /// make the ragdoll or gibs of their last death, or `None` if they have
644 /// none, or it is marked for deletion.
645#[doc(alias("m_hRagdoll"))]
646pub fn ragdoll(self) -> Result<Option<TfRagdoll<'s>>, PlayerError> {
647let handle = self.net_prop(c"m_hRagdoll")?.get_handle(self.player)?;
648649Ok(self
650.server
651 .server_tools()?
652.entity_by_handle(handle)
653 .filter(|ragdoll| !ragdoll.is_marked_for_deletion())
654 .and_then(|ragdoll| TfRagdoll::new(self.server, ragdoll).ok()))
655 }
656657/// Reads a class variable, which holds `TF_CLASS_UNDEFINED` or a playable
658 /// class.
659fn read_class(self, name: &CStr) -> Result<Option<PlayerClass>, PlayerError> {
660let raw = self.net_prop(name)?.get::<c_int>(self.player)?;
661662match raw {
6630 => Ok(None),
664665 raw => PlayerClass::from_raw(raw)
666 .map(Some)
667 .ok_or(PlayerError::UnknownClass(raw)),
668 }
669 }
670671/// Resupplies the living player as a resupply cabinet does
672 /// (`Regenerate`).
673 ///
674 /// TF2 sets the player up as their class afresh, which hands out their
675 /// loadout's weapons and cosmetics again, keeps any overheal, and, with
676 /// `refill_health_and_ammo`, refills their health and ammo and ends the
677 /// conditions a resupply cabinet cures, such as burning and bleeding.
678 /// Without it, their health and ammo stay as they are.
679 ///
680 /// Handing out items creates entities, which runs entity-creation and
681 /// spawn callbacks synchronously, before this returns: the game's, other
682 /// plugins', and this plugin's own. They must keep to the contract of
683 /// [`Server::new`]. Fails with [`PlayerError::NotAlive`] before the game is
684 /// called for a player who is not alive.
685#[doc(alias("Regenerate"))]
686pub fn regenerate(self, refill_health_and_ammo: bool) -> Result<(), PlayerError> {
687self.check_live()?;
688689if !self.player.is_alive() {
690return Err(PlayerError::NotAlive);
691 }
692693// SAFETY: The checked member resupplies this living player. It frees
694 // the items it replaces, and its callbacks free entities, only through
695 // deferred deletion (`Server::new`'s contract).
696unsafe { self.call_with_bool(c"Regenerate", refill_health_and_ammo) }
697 }
698699/// Respawns the player afresh, dead or alive, as their desired class, with
700 /// their health, ammo and items as a resupply leaves them
701 /// (`ForceRegenerateAndRespawn`).
702 ///
703 /// This goes through the player's `ForceRespawn`, as
704 /// [`force_respawn`](crate::tf2::respawn::force_respawn) does, and does
705 /// nothing in the same cases.
706#[doc(alias("ForceRegenerateAndRespawn"))]
707pub fn regenerate_and_respawn(self) -> Result<(), PlayerError> {
708// SAFETY: The checked member respawns the player as `force_respawn`
709 // does, whose spawn callbacks are bound by `Server::new`'s contract.
710unsafe { self.call(c"ForceRegenerateAndRespawn", &mut [], binding::VOID) }?;
711712Ok(())
713 }
714715/// Shows parts of the player's HUD again (`RemoveHudHideFlags`).
716#[doc(alias("RemoveHudHideFlags", "m_iHideHUD"))]
717pub fn remove_hud_hide_flags(self, flags: HideHud) -> Result<(), PlayerError> {
718self.call_with_flags(c"RemoveHudHideFlags", flags)
719 }
720721/// Removes the player's [ragdoll](Self::ragdoll), so clients make none of
722 /// their last death, or none from now on if they made it already, and
723 /// returns whether there was one to remove.
724 ///
725 /// The `tf_ragdoll` is removed as the game removes it, with deferred
726 /// deletion, and `m_hRagdoll` cleared, which the game checks before each
727 /// use. Remove it in the frame of the death to keep clients from making
728 /// its ragdoll at all.
729#[doc(alias("m_hRagdoll"))]
730pub fn remove_ragdoll(self) -> Result<bool, PlayerError> {
731self.check_live()?;
732733let prop = self.net_prop(c"m_hRagdoll")?;
734let engine = self.server.valve_engine()?;
735let tools = self.server.server_tools()?;
736let Some(ragdoll) = self.ragdoll()? else {
737return Ok(false);
738 };
739740if tools.remove(ragdoll.entity()).is_err() {
741return Ok(false);
742 }
743744// SAFETY: A player has no ragdoll until their first death, and the
745 // game checks it for one before each use.
746unsafe { prop.set_handle(engine, self.player, EntityHandle::INVALID) }?;
747748Ok(true)
749 }
750751/// Sends the player the game's message showing or hiding a panel.
752fn send_panel(self, name: &CStr, show: bool) -> Result<(), PlayerError> {
753self.check_live()?;
754755let edict = self.player.edict().ok_or(PlayerError::NotNetworked)?;
756let menu = VguiMenu {
757name,
758show,
759 keys: &[],
760 };
761762 user_messages::send(self.server, &Recipients::player(edict).reliable(), &menu)?;
763764Ok(())
765 }
766767/// Changes the player's class at once, as scripts do (`SetPlayerClass`),
768 /// without respawning them.
769 ///
770 /// TF2 sets the player up as the class: their speed, maximum health and
771 /// abilities follow it, and their weapons are told of the change. Their
772 /// model, weapons and health stay as they are until they next spawn or
773 /// resupply, as do their [desired class](Self::desired_class), which they
774 /// spawn as, and their custom model, which this clears.
775#[doc(alias("SetPlayerClass", "m_iClass"))]
776pub fn set_class(self, class: PlayerClass) -> Result<(), PlayerError> {
777// SAFETY: The checked member sets the player up as a playable class,
778 // as the game does itself, and frees nothing.
779unsafe { self.call(c"SetPlayerClass", &mut [int(class.to_raw())], binding::VOID) }?;
780781Ok(())
782 }
783784/// Marks the class menu as open on the player's client, or closed, as
785 /// the client reports it, by running the client's `menuopen` or
786 /// `menuclosed` command for them.
787 ///
788 /// While the game counts a dead player's class menu as open, they miss
789 /// respawn waves, and once it closes, they spawn at once if the game
790 /// would have spawned them meanwhile. A client reports its own menu
791 /// whenever it shows or hides it, overriding this.
792 ///
793 /// The command goes through the engine's client command handling, as a
794 /// client's own commands do, so command hooks and listeners, the game's
795 /// and other plugins' included, run before this returns, and it counts
796 /// toward `sv_quota_stringcmdspersecond`.
797#[doc(alias("menuopen", "menuclosed", "m_bIsClassMenuOpen"))]
798pub fn set_class_menu_open(self, open: bool) -> Result<(), PlayerError> {
799self.check_live()?;
800801let edict = self.player.edict().ok_or(PlayerError::NotNetworked)?;
802let command = if open { c"menuopen" } else { c"menuclosed" };
803804self.server.plugin_helpers()?.client_command(edict, command);
805806Ok(())
807 }
808809/// Shows the player as `model`, a model's path such as
810 /// `models/bots/scout/bot_scout.mdl`, with the model's own animations
811 /// (`SetCustomModel`).
812 ///
813 /// TF2 precaches the model if needed, which takes an entry of the level's
814 /// model table until the level ends, and fails the level if the table is
815 /// full. So precache models when the level starts, and do not take paths
816 /// from untrusted input. The model stays until it is cleared or the player
817 /// changes class, through respawns.
818#[doc(alias("SetCustomModel"))]
819pub fn set_custom_model(self, model: &CStr) -> Result<(), PlayerError> {
820self.call_with_model(c"SetCustomModel", model)
821 }
822823/// Offsets the player's custom model from where the player stands
824 /// (`SetCustomModelOffset`). Fails with [`PlayerError::NonFinite`] for an
825 /// offset that is not finite.
826#[doc(alias("SetCustomModelOffset"))]
827pub fn set_custom_model_offset(self, offset: Vector) -> Result<(), PlayerError> {
828if !offset.is_finite() {
829return Err(PlayerError::NonFinite);
830 }
831832let offset = sys::Vector::from(offset);
833834// SAFETY: The checked member stores the finite offset, which it only
835 // reads during the call, and invalidates the player's cached position.
836unsafe {
837self.call(
838c"SetCustomModelOffset",
839&mut [vector(&offset)],
840 binding::VOID,
841 )
842 }?;
843844Ok(())
845 }
846847/// Whether the player's custom model turns with the player, as it does by
848 /// default (`SetCustomModelRotates`).
849#[doc(alias("SetCustomModelRotates"))]
850pub fn set_custom_model_rotates(self, rotates: bool) -> Result<(), PlayerError> {
851// SAFETY: The checked member stores the flag, and invalidates the
852 // player's cached angles.
853unsafe { self.call_with_bool(c"SetCustomModelRotates", rotates) }
854 }
855856/// Rotates the player's custom model (`SetCustomModelRotation`). Fails
857 /// with [`PlayerError::NonFinite`] for angles that are not finite.
858#[doc(alias("SetCustomModelRotation"))]
859pub fn set_custom_model_rotation(self, rotation: QAngle) -> Result<(), PlayerError> {
860if ![rotation.pitch, rotation.yaw, rotation.roll]
861 .iter()
862 .all(|angle| angle.is_finite())
863 {
864return Err(PlayerError::NonFinite);
865 }
866867let rotation = sys::QAngle::from(rotation);
868869// SAFETY: The checked member stores the finite angles, which it only
870 // reads during the call, and invalidates the player's cached angles.
871unsafe {
872self.call(
873c"SetCustomModelRotation",
874&mut [qangle(&rotation)],
875 binding::VOID,
876 )
877 }?;
878879Ok(())
880 }
881882/// Turns the player to look along `angles` at once (`SnapEyeAngles`), as
883 /// teleporters do. The game holds the angles for the player's next
884 /// command: a human's client is turned to them, and a bot's command runs
885 /// with them in place of its own, so the bot's weapon fires along them.
886 /// A bot's own aiming turns it again from there on its next update.
887 ///
888 /// Fails with [`PlayerError::NonFinite`] for angles that are not finite.
889#[doc(alias("SnapEyeAngles"))]
890pub fn snap_eye_angles(self, angles: QAngle) -> Result<(), PlayerError> {
891if ![angles.pitch, angles.yaw, angles.roll]
892 .iter()
893 .all(|angle| angle.is_finite())
894 {
895return Err(PlayerError::NonFinite);
896 }
897898self.check_live()?;
899900let angles = sys::QAngle::from(angles);
901902// SAFETY: The checked member copies the finite angles, which it only
903 // reads during the call, into the player's view angles, and marks them
904 // fixed for the player's next command.
905unsafe {
906 binding::call(
907self.player,
908 BASE_SCRIPT_CLASS,
909c"SnapEyeAngles",
910&mut [qangle(&angles)],
911 binding::VOID,
912 )
913 }?;
914915Ok(())
916 }
917918/// Whether the player sees their custom model in third person, as they do
919 /// by default (`SetCustomModelVisibleToSelf`).
920#[doc(alias("SetCustomModelVisibleToSelf"))]
921pub fn set_custom_model_visible_to_self(self, visible: bool) -> Result<(), PlayerError> {
922// SAFETY: The checked member stores the flag.
923unsafe { self.call_with_bool(c"SetCustomModelVisibleToSelf", visible) }
924 }
925926/// As [`Self::set_custom_model`], but animates the model with the
927 /// player's class's animations, as Mann vs. Machine's robots are
928 /// (`SetCustomModelWithClassAnimations`).
929#[doc(alias("SetCustomModelWithClassAnimations"))]
930pub fn set_custom_model_with_class_animations(self, model: &CStr) -> Result<(), PlayerError> {
931self.call_with_model(c"SetCustomModelWithClassAnimations", model)
932 }
933934/// Sets the class the player spawns as next (`m_iDesiredPlayerClass`),
935 /// as choosing it from the class menu would, without its checks or
936 /// respawn, or clears it with `None`, which keeps the player from
937 /// spawning until they choose one.
938#[doc(alias("m_iDesiredPlayerClass", "SetDesiredPlayerClassIndex"))]
939pub fn set_desired_class(self, class: Option<PlayerClass>) -> Result<(), PlayerError> {
940self.check_live()?;
941942let engine = self.server.valve_engine()?;
943let raw = class.map_or(0, PlayerClass::to_raw);
944945// SAFETY: The game assigns `TF_CLASS_UNDEFINED` and the playable
946 // classes itself.
947unsafe {
948self.net_prop(c"m_iDesiredPlayerClass")?
949.set(engine, self.player, raw)
950 }?;
951952Ok(())
953 }
954955/// Forces the player's view into third person, as taunts do, or lets
956 /// them choose it again (`SetForcedTauntCam`). The game clears it as the
957 /// player spawns.
958#[doc(alias("SetForcedTauntCam", "m_nForceTauntCam"))]
959pub fn set_forced_taunt_cam(self, cam: ForcedTauntCam) -> Result<(), PlayerError> {
960// SAFETY: The checked member stores one of the values the game uses.
961unsafe {
962self.call(
963c"SetForcedTauntCam",
964&mut [int(cam.to_raw())],
965 binding::VOID,
966 )
967 }?;
968969Ok(())
970 }
971972/// Hides exactly `flags`'s parts of the player's HUD, showing the others
973 /// (`SetHudHideFlags`).
974#[doc(alias("SetHudHideFlags", "m_iHideHUD"))]
975pub fn set_hud_hide_flags(self, flags: HideHud) -> Result<(), PlayerError> {
976self.call_with_flags(c"SetHudHideFlags", flags)
977 }
978979/// Sets the game time from which the player may change their class again
980 /// (`SetNextChangeClassTime`). Fails with [`PlayerError::NonFinite`] for a
981 /// time that is not finite.
982#[doc(alias("SetNextChangeClassTime", "m_flNextChangeClassTime"))]
983pub fn set_next_change_class_time(self, time: f32) -> Result<(), PlayerError> {
984self.set_time(c"SetNextChangeClassTime", time)
985 }
986987/// Sets the game time from which the player may change their team again
988 /// (`SetNextChangeTeamTime`). Fails with [`PlayerError::NonFinite`] for a
989 /// time that is not finite.
990#[doc(alias("SetNextChangeTeamTime", "m_flNextChangeTeamTime"))]
991pub fn set_next_change_team_time(self, time: f32) -> Result<(), PlayerError> {
992self.set_time(c"SetNextChangeTeamTime", time)
993 }
994995/// Calls a member that stores a finite game time.
996fn set_time(self, name: &CStr, time: f32) -> Result<(), PlayerError> {
997if !time.is_finite() {
998return Err(PlayerError::NonFinite);
999 }
10001001// SAFETY: The time members store a field of the player.
1002unsafe { self.call(name, &mut [binding::float(time)], binding::VOID) }?;
10031004Ok(())
1005 }
10061007/// Shows the player their team's class menu, as the game does as they join
1008 /// a team. Fails with [`PlayerError::NotPlaying`] for a player on neither
1009 /// RED nor BLU.
1010pub fn show_class_menu(self) -> Result<(), PlayerError> {
1011let panel = match self.team()? {
1012 Team::Red => raw::PANEL_CLASS_RED,
1013 Team::Blue => raw::PANEL_CLASS_BLUE,
1014 Team::Unassigned | Team::Spectator => return Err(PlayerError::NotPlaying),
1015 };
10161017self.send_panel(panel, true)
1018 }
10191020/// Shows the player the team menu, as the game does as they join the
1021 /// server.
1022pub fn show_team_menu(self) -> Result<(), PlayerError> {
1023self.send_panel(raw::PANEL_TEAM, true)
1024 }
10251026/// A bit the game flips each time the player spawns (`m_iSpawnCounter`),
1027 /// from which clients tell that they respawned. A change between two
1028 /// reads means they spawned an odd number of times in between.
1029 ///
1030 /// Despite its name, the variable is a `bool`, which TF2 sends as an 8-bit
1031 /// integer.
1032#[doc(alias("m_iSpawnCounter"))]
1033pub fn spawn_parity(self) -> Result<bool, PlayerError> {
1034Ok(self
1035.net_prop(c"m_iSpawnCounter")?
1036.get::<bool>(self.player)?)
1037 }
10381039/// Where the player is in the game's handling of them
1040 /// (`m_nPlayerState`).
1041#[doc(alias("m_nPlayerState"))]
1042pub fn state(self) -> Result<PlayerState, PlayerError> {
1043let raw = self
1044.net_prop(c"m_nPlayerState")?
1045.get::<c_int>(self.player)?;
10461047PlayerState::from_raw(raw).ok_or(PlayerError::UnknownState(raw))
1048 }
10491050/// The player's team (`m_iTeamNum`).
1051#[doc(alias("m_iTeamNum", "GetTeamNumber"))]
1052pub fn team(self) -> Result<Team, PlayerError> {
1053let raw = self.net_prop(c"m_iTeamNum")?.get::<c_int>(self.player)?;
10541055Team::from_raw(raw).ok_or(PlayerError::UnknownTeam(raw))
1056 }
10571058/// The player's view offset (`m_vecViewOffset`): how far above their
1059 /// origin they see from, which depends on their class and whether they
1060 /// duck.
1061#[doc(alias("m_vecViewOffset", "GetViewOffset"))]
1062pub fn view_offset(self) -> Result<Vector, PlayerError> {
1063let [x, y, z] = VIEW_OFFSET.map(|name| {
1064self.net_prop(name)
1065 .and_then(|prop| Ok(prop.get::<f32>(self.player)?))
1066 });
10671068Ok(Vector::new(x?, y?, z?))
1069 }
1070}