use crate::NotThreadSafe;
use crate::edicts::Edict;
use crate::math::{QAngle, Vector};
use crate::players::UserId;
use sdk_raw::interfaces::player_info_manager as raw;
use sdk_raw::util::cstr::copy_cstr;
use sdk_raw::vcall;
use std::ffi::{CString, c_int};
use std::marker::PhantomData;
use std::ptr::NonNull;
interface! {
#[doc(alias("IPlayerInfoManager", "CPlayerInfoManager"))]
pub struct PlayerInfoManager(sys::IPlayerInfoManager) = GameServer sdk_raw::interfaces::player_info_manager::VERSION;
}
#[doc(alias("CGlobalVars"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalVars<'s> {
raw: NonNull<sys::CGlobalVars>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> PlayerInfoManager<'s> {
#[doc(alias("GetGlobalVars", "gpGlobals"))]
pub fn global_vars(self) -> Option<GlobalVars<'s>> {
let globals =
NonNull::new(unsafe { vcall!(self.as_ptr() => IPlayerInfoManager_GetGlobalVars()) })?;
Some(GlobalVars {
raw: globals,
_scope: PhantomData,
_not_thread_safe: PhantomData,
})
}
#[doc(alias("GetPlayerInfo"))]
pub fn player_info(self, edict: Edict<'_>) -> Option<PlayerInfo<'s>> {
let max_clients = self.global_vars().map_or(0, GlobalVars::max_clients);
if edict.is_free() || !(1..=max_clients).contains(&edict.index()) {
return None;
}
let info = NonNull::new(unsafe {
vcall!(self.as_ptr() => IPlayerInfoManager_GetPlayerInfo(edict.as_ptr()))
})?;
Some(PlayerInfo {
raw: info,
_scope: PhantomData,
_not_thread_safe: PhantomData,
})
}
}
macro_rules! global_var {
($(#[$meta:meta])* $name:ident: $Type:ty = $($field:ident).+) => {
$(#[$meta])*
pub fn $name(self) -> $Type {
unsafe { (&raw const (*self.raw.as_ptr()).$($field).+).read() }
}
};
}
#[doc(alias("IPlayerInfo", "CPlayerInfo"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PlayerInfo<'s> {
raw: NonNull<sys::IPlayerInfo>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> PlayerInfo<'s> {
#[doc(alias("GetAbsAngles"))]
pub fn abs_angles(self) -> QAngle {
unsafe { raw::abs_angles(self.as_ptr()) }.into()
}
#[doc(alias("GetAbsOrigin"))]
pub fn abs_origin(self) -> Vector {
unsafe { raw::abs_origin(self.as_ptr()) }.into()
}
#[doc(alias("GetArmorValue", "ArmorValue", "m_ArmorValue"))]
pub fn armor(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetArmorValue()) }
}
pub const fn as_ptr(self) -> *mut sys::IPlayerInfo {
self.raw.as_ptr()
}
#[doc(alias("GetDeathCount", "DeathCount", "m_iDeaths"))]
pub fn death_count(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetDeathCount()) }
}
#[doc(alias("GetFragCount", "FragCount", "m_iFrags"))]
pub fn frag_count(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetFragCount()) }
}
#[doc(alias("GetHealth"))]
pub fn health(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetHealth()) }
}
#[doc(alias("IsConnected"))]
pub fn is_connected(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsConnected()) }
}
#[doc(alias("IsDead"))]
pub fn is_dead(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsDead()) }
}
#[doc(alias("IsFakeClient"))]
pub fn is_fake_client(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsFakeClient()) }
}
#[doc(alias("IsHLTV"))]
pub fn is_hltv(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsHLTV()) }
}
#[doc(alias("IsInAVehicle"))]
pub fn is_in_vehicle(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsInAVehicle()) }
}
#[doc(alias("IsObserver"))]
pub fn is_observer(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsObserver()) }
}
#[doc(alias("IsPlayer"))]
pub fn is_player(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsPlayer()) }
}
#[doc(alias("IsReplay"))]
pub fn is_replay(self) -> bool {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_IsReplay()) }
}
#[doc(alias("GetMaxHealth"))]
pub fn max_health(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetMaxHealth()) }
}
#[doc(alias("GetModelName"))]
pub fn model_name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_ptr() => IPlayerInfo_GetModelName())) }
}
#[doc(alias("GetName"))]
pub fn name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_ptr() => IPlayerInfo_GetName())) }
}
#[doc(alias("GetNetworkIDString"))]
pub fn network_id(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_ptr() => IPlayerInfo_GetNetworkIDString())) }
}
#[doc(alias("GetPlayerMaxs"))]
pub fn player_maxs(self) -> Vector {
unsafe { raw::player_maxs(self.as_ptr()) }.into()
}
#[doc(alias("GetPlayerMins"))]
pub fn player_mins(self) -> Vector {
unsafe { raw::player_mins(self.as_ptr()) }.into()
}
#[doc(alias("GetTeamIndex"))]
pub fn team(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetTeamIndex()) }
}
#[doc(alias("GetUserID"))]
pub fn user_id(self) -> Option<UserId> {
UserId::from_raw(unsafe { vcall!(self.as_ptr() => IPlayerInfo_GetUserID()) }).ok()
}
#[doc(alias("GetWeaponName"))]
pub fn weapon_name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_ptr() => IPlayerInfo_GetWeaponName())) }
}
}
impl<'s> GlobalVars<'s> {
pub const fn as_ptr(self) -> *mut sys::CGlobalVars {
self.raw.as_ptr()
}
global_var! {
#[doc(alias("curtime"))]
current_time: f32 = _base.curtime
}
global_var! {
#[doc(alias("frametime"))]
frame_time: f32 = _base.frametime
}
global_var! {
#[doc(alias("maxClients"))]
max_clients: c_int = _base.maxClients
}
global_var! {
#[doc(alias("tickcount"))]
tick_count: c_int = _base.tickcount
}
global_var! {
interval_per_tick: f32 = _base.interval_per_tick
}
#[doc(alias("mapname"))]
pub fn map_name(self) -> Option<CString> {
unsafe { copy_cstr((&raw const (*self.raw.as_ptr()).mapname.pszValue).read()) }
}
#[doc(alias("TIME_TO_TICKS"))]
pub fn time_to_ticks(self, time: f32) -> c_int {
sdk_raw::entities::think::time_to_ticks(time, self.interval_per_tick())
}
}