use crate::entities::Entity;
use crate::{Game, Server};
use sdk_raw::tf2::conditions as raw;
use sdk_raw::tf2::script_binding::BindingError;
use std::ptr::NonNull;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[doc(alias("ETFCond"))]
pub struct Condition(i32);
impl Condition {
#[doc(alias("TF_COND_AIMING"))]
pub const AIMING: Self = Self(sys::ETFCond_TF_COND_AIMING);
#[doc(alias("TF_COND_BLEEDING"))]
pub const BLEEDING: Self = Self(sys::ETFCond_TF_COND_BLEEDING);
#[doc(alias("TF_COND_BURNING"))]
pub const BURNING: Self = Self(sys::ETFCond_TF_COND_BURNING);
#[doc(alias("TF_COND_CRITBOOSTED"))]
pub const CRITBOOSTED: Self = Self(sys::ETFCond_TF_COND_CRITBOOSTED);
#[doc(alias("TF_COND_DISGUISED"))]
pub const DISGUISED: Self = Self(sys::ETFCond_TF_COND_DISGUISED);
#[doc(alias("TF_COND_INVULNERABLE"))]
pub const INVULNERABLE: Self = Self(sys::ETFCond_TF_COND_INVULNERABLE);
#[doc(alias("TF_COND_MARKEDFORDEATH"))]
pub const MARKED_FOR_DEATH: Self = Self(sys::ETFCond_TF_COND_MARKEDFORDEATH);
#[doc(alias("TF_COND_SPEED_BOOST"))]
pub const SPEED_BOOST: Self = Self(sys::ETFCond_TF_COND_SPEED_BOOST);
#[doc(alias("TF_COND_STEALTHED"))]
pub const STEALTHED: Self = Self(sys::ETFCond_TF_COND_STEALTHED);
#[doc(alias("TF_COND_ZOOMED"))]
pub const ZOOMED: Self = Self(sys::ETFCond_TF_COND_ZOOMED);
pub const fn from_raw(raw: sys::ETFCond) -> Option<Self> {
if raw >= 0 && raw < sys::ETFCond_TF_COND_LAST {
Some(Self(raw))
} else {
None
}
}
pub const fn to_raw(self) -> sys::ETFCond {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ConditionDuration(f32);
impl ConditionDuration {
#[doc(alias("PERMANENT_CONDITION"))]
pub const PERMANENT: Self = Self(raw::PERMANENT_CONDITION);
pub fn seconds(seconds: f32) -> Option<Self> {
(seconds.is_finite() && seconds >= 0.0).then_some(Self(seconds))
}
pub const fn as_raw(self) -> f32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum ConditionError {
#[error("conditions require a TF2 player")]
NotTfPlayer,
#[error("the game does not expose the expected native condition method")]
UnsupportedMethod,
#[error("the native condition method rejected its arguments")]
Rejected,
}
impl From<BindingError> for ConditionError {
fn from(error: BindingError) -> Self {
match error {
BindingError::Unavailable | BindingError::SignatureMismatch => Self::UnsupportedMethod,
BindingError::Rejected => Self::Rejected,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PlayerConditions<'s> {
player: Entity<'s>,
}
impl<'s> PlayerConditions<'s> {
pub fn new(server: Server<'s>, player: Entity<'s>) -> Result<Self, ConditionError> {
if server.game() != Game::TeamFortress2 || player.class_name() != c"player" {
return Err(ConditionError::NotTfPlayer);
}
Ok(Self { player })
}
#[doc(alias("AddCond", "AddCondEx"))]
pub fn add(
self,
condition: Condition,
duration: ConditionDuration,
) -> Result<bool, ConditionError> {
unsafe {
raw::add_cond_ex(
self.raw_player(),
condition.0,
duration.0,
std::ptr::null_mut(),
)
}?;
self.in_cond(condition)
}
#[doc(alias("InCond"))]
pub fn in_cond(self, condition: Condition) -> Result<bool, ConditionError> {
Ok(unsafe { raw::in_cond(self.raw_player(), condition.0) }?)
}
pub const fn player(self) -> Entity<'s> {
self.player
}
fn raw_player(self) -> NonNull<sys::CBaseEntity> {
unsafe { NonNull::new_unchecked(self.player.as_ptr()) }
}
#[doc(alias("RemoveCond", "RemoveCondEx"))]
pub fn remove(
self,
condition: Condition,
ignore_duration: bool,
) -> Result<bool, ConditionError> {
unsafe { raw::remove_cond_ex(self.raw_player(), condition.0, ignore_duration) }?;
Ok(!self.in_cond(condition)?)
}
#[doc(alias("RemoveAllCond"))]
pub fn remove_all(self) -> Result<(), ConditionError> {
unsafe { raw::remove_all_cond(self.raw_player()) }?;
Ok(())
}
}