#[cfg(test)]
#[path = "../tests/entities/think.rs"]
mod tests;
use crate::entities::Entity;
use sdk_raw::entities::think as raw;
use std::ffi::{CStr, c_int};
use std::sync::OnceLock;
static THINK_FUNCTIONS_OFFSET: OnceLock<usize> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
pub enum ThinkError {
#[error(
"the game's CBaseEntity datamap does not declare m_aThinkFunctions where the SDK expects it"
)]
UnsupportedLayout,
}
impl<'s> Entity<'s> {
#[doc(alias("SetNextThink", "TICK_NEVER_THINK"))]
pub fn cancel_think_context(self, context: &CStr) -> Result<bool, ThinkError> {
let offset = self.think_functions_offset()?;
let Some(index) = (unsafe { raw::think_context_index(self.as_ptr(), offset, context) })
else {
return Ok(false);
};
let is_scheduled = unsafe { raw::think_context_tick(self.as_ptr(), offset, index) }
.is_some_and(|tick| tick > 0);
if is_scheduled {
unsafe {
raw::set_think_context_tick(self.as_ptr(), offset, index, raw::TICK_NEVER_THINK)
};
}
Ok(is_scheduled)
}
#[doc(alias("GetNextThinkTick"))]
pub fn next_think_tick(self, context: &CStr) -> Result<Option<c_int>, ThinkError> {
let offset = self.think_functions_offset()?;
let tick = unsafe {
raw::think_context_index(self.as_ptr(), offset, context)
.and_then(|index| raw::think_context_tick(self.as_ptr(), offset, index))
};
Ok(tick.filter(|&tick| tick > 0))
}
fn think_functions_offset(self) -> Result<usize, ThinkError> {
if let Some(&offset) = THINK_FUNCTIONS_OFFSET.get() {
return Ok(offset);
}
let offset =
raw::find_think_functions(self.data_maps()).ok_or(ThinkError::UnsupportedLayout)?;
Ok(*THINK_FUNCTIONS_OFFSET.get_or_init(|| offset))
}
}