#[cfg(test)]
#[path = "../tests/entities/render.rs"]
mod tests;
use crate::entities::Entity;
use crate::entities::fields::{BaseField, FieldError, PooledString};
use crate::interfaces::{ModelInfo, ValveEngine};
use crate::math::Color32;
use sdk_raw::edicts::FL_EDICT_DIRTY_PVS_INFORMATION;
use sdk_raw::entities::{
EF_BONEMERGE, EF_BONEMERGE_FASTCULL, EF_BRIGHTLIGHT, EF_DIMLIGHT, EF_ITEM_BLINK, EF_NODRAW,
EF_NOINTERP, EF_NORECEIVESHADOW, EF_NOSHADOW, EF_PARENT_ANIMATES,
};
use sdk_raw::util::cstr::borrow_cstr;
use std::ffi::{CStr, CString, c_int};
static EFFECTS: BaseField<c_int> = BaseField::new(c"m_fEffects", sys::_fieldtypes_FIELD_INTEGER);
static MODEL_INDEX: BaseField<i16> = BaseField::new(c"m_nModelIndex", sys::_fieldtypes_FIELD_SHORT);
static MODEL_NAME: BaseField<PooledString> =
BaseField::new(c"m_ModelName", sys::_fieldtypes_FIELD_MODELNAME);
static RENDER_COLOR: BaseField<Color32> =
BaseField::new(c"m_clrRender", sys::_fieldtypes_FIELD_COLOR32);
static RENDER_MODE: BaseField<u8> =
BaseField::new(c"m_nRenderMode", sys::_fieldtypes_FIELD_CHARACTER);
static TRANSMIT_STATE_OWNERS: BaseField<u8> = BaseField::new(
c"m_nTransmitStateOwnedCounter",
sys::_fieldtypes_FIELD_CHARACTER,
);
bitflags::bitflags! {
#[doc(alias("m_fEffects"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Effects: c_int {
#[doc(alias("EF_BONEMERGE"))]
const BONE_MERGE = EF_BONEMERGE;
#[doc(alias("EF_BONEMERGE_FASTCULL"))]
const BONE_MERGE_FAST_CULL = EF_BONEMERGE_FASTCULL;
#[doc(alias("EF_BRIGHTLIGHT"))]
const BRIGHT_LIGHT = EF_BRIGHTLIGHT;
#[doc(alias("EF_DIMLIGHT"))]
const DIM_LIGHT = EF_DIMLIGHT;
#[doc(alias("EF_ITEM_BLINK"))]
const ITEM_BLINK = EF_ITEM_BLINK;
#[doc(alias("EF_NODRAW"))]
const NO_DRAW = EF_NODRAW;
#[doc(alias("EF_NOINTERP"))]
const NO_INTERP = EF_NOINTERP;
#[doc(alias("EF_NORECEIVESHADOW"))]
const NO_RECEIVE_SHADOW = EF_NORECEIVESHADOW;
#[doc(alias("EF_NOSHADOW"))]
const NO_SHADOW = EF_NOSHADOW;
#[doc(alias("EF_PARENT_ANIMATES"))]
const PARENT_ANIMATES = EF_PARENT_ANIMATES;
const _ = !0;
}
}
#[doc(alias("RenderMode_t", "m_nRenderMode"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RenderMode {
#[doc(alias("kRenderNormal"))]
Normal,
#[doc(alias("kRenderTransColor"))]
TransColor,
#[doc(alias("kRenderTransTexture"))]
TransTexture,
#[doc(alias("kRenderGlow"))]
Glow,
#[doc(alias("kRenderTransAlpha"))]
TransAlpha,
#[doc(alias("kRenderTransAdd"))]
TransAdd,
#[doc(alias("kRenderEnvironmental"))]
Environmental,
#[doc(alias("kRenderTransAddFrameBlend"))]
TransAddFrameBlend,
#[doc(alias("kRenderTransAlphaAdd"))]
TransAlphaAdd,
#[doc(alias("kRenderWorldGlow"))]
WorldGlow,
#[doc(alias("kRenderNone"))]
None,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum SetModelError {
#[error("the model {model:?} is not precached")]
NotPrecached {
model: CString,
},
}
impl RenderMode {
pub const fn from_raw(value: u8) -> Option<Self> {
Some(match value as sys::RenderMode_t {
sys::RenderMode_t_kRenderNormal => Self::Normal,
sys::RenderMode_t_kRenderTransColor => Self::TransColor,
sys::RenderMode_t_kRenderTransTexture => Self::TransTexture,
sys::RenderMode_t_kRenderGlow => Self::Glow,
sys::RenderMode_t_kRenderTransAlpha => Self::TransAlpha,
sys::RenderMode_t_kRenderTransAdd => Self::TransAdd,
sys::RenderMode_t_kRenderEnvironmental => Self::Environmental,
sys::RenderMode_t_kRenderTransAddFrameBlend => Self::TransAddFrameBlend,
sys::RenderMode_t_kRenderTransAlphaAdd => Self::TransAlphaAdd,
sys::RenderMode_t_kRenderWorldGlow => Self::WorldGlow,
sys::RenderMode_t_kRenderNone => Self::None,
_ => return None,
})
}
pub const fn to_raw(self) -> u8 {
(match self {
Self::Normal => sys::RenderMode_t_kRenderNormal,
Self::TransColor => sys::RenderMode_t_kRenderTransColor,
Self::TransTexture => sys::RenderMode_t_kRenderTransTexture,
Self::Glow => sys::RenderMode_t_kRenderGlow,
Self::TransAlpha => sys::RenderMode_t_kRenderTransAlpha,
Self::TransAdd => sys::RenderMode_t_kRenderTransAdd,
Self::Environmental => sys::RenderMode_t_kRenderEnvironmental,
Self::TransAddFrameBlend => sys::RenderMode_t_kRenderTransAddFrameBlend,
Self::TransAlphaAdd => sys::RenderMode_t_kRenderTransAlphaAdd,
Self::WorldGlow => sys::RenderMode_t_kRenderWorldGlow,
Self::None => sys::RenderMode_t_kRenderNone,
}) as u8
}
}
impl<'s> Entity<'s> {
#[doc(alias("GetBody", "m_nBody"))]
pub fn body(self) -> Result<c_int, FieldError> {
self.data_field(c"m_nBody")
}
#[doc(alias("GetEffects", "IsEffectActive", "m_fEffects"))]
pub fn effects(self) -> Result<Effects, FieldError> {
EFFECTS.read(self).map(Effects::from_bits_retain)
}
#[doc(alias("m_flFadeScale"))]
pub fn fade_scale(self) -> Result<f32, FieldError> {
self.data_field(c"m_flFadeScale")
}
#[doc(alias("GetModelIndex", "m_nModelIndex"))]
pub fn model_index(self) -> Result<c_int, FieldError> {
MODEL_INDEX.read(self).map(c_int::from)
}
#[doc(alias("GetModelName", "m_ModelName"))]
pub fn model_name(self) -> Result<Option<&'s CStr>, FieldError> {
let name = MODEL_NAME.read(self)?;
Ok(unsafe { borrow_cstr(name.0) }.filter(|name| !name.is_empty()))
}
#[doc(alias("GetRenderColor", "GetRenderAlpha", "m_clrRender"))]
pub fn render_color(self) -> Result<Color32, FieldError> {
RENDER_COLOR.read(self)
}
#[doc(alias("GetRenderMode", "m_nRenderMode"))]
pub fn render_mode(self) -> Result<Option<RenderMode>, FieldError> {
RENDER_MODE.read(self).map(RenderMode::from_raw)
}
#[doc(alias("SetBodygroup", "m_nBody"))]
pub fn set_body(self, engine: ValveEngine<'_>, body: c_int) -> Result<(), FieldError> {
self.set_data_field(engine, c"m_nBody", body)
}
#[doc(alias("SetEffects", "AddEffects", "RemoveEffects", "m_fEffects"))]
pub fn set_effects(self, engine: ValveEngine<'_>, effects: Effects) -> Result<(), FieldError> {
let old = self.effects()?;
if old == effects {
return Ok(());
}
EFFECTS.write(engine, self, effects.bits())?;
if old.contains(Effects::NO_DRAW)
&& !effects.contains(Effects::NO_DRAW)
&& let Some(edict) = self.edict()
{
unsafe {
let flags = &raw mut (*edict.as_ptr())._base.m_fStateFlags;
flags.write(flags.read() | FL_EDICT_DIRTY_PVS_INFORMATION);
}
}
self.update_transmit_state()
}
#[doc(alias("m_flFadeScale"))]
pub fn set_fade_scale(self, engine: ValveEngine<'_>, scale: f32) -> Result<(), FieldError> {
self.set_data_field(engine, c"m_flFadeScale", scale)
}
#[doc(alias("SetModel", "SetEntityModel"))]
pub fn set_model(self, models: ModelInfo<'_>, model: &CStr) -> Result<(), SetModelError> {
if models.model_index(model).is_none() {
return Err(SetModelError::NotPrecached {
model: model.to_owned(),
});
}
unsafe { sdk_raw::entities::spawn::set_model(self.as_ptr(), model.as_ptr()) };
Ok(())
}
#[doc(alias("SetRenderColor", "SetRenderColorA", "m_clrRender"))]
pub fn set_render_color(
self,
engine: ValveEngine<'_>,
color: Color32,
) -> Result<(), FieldError> {
RENDER_COLOR.write(engine, self, color)
}
#[doc(alias("SetRenderMode", "m_nRenderMode"))]
pub fn set_render_mode(
self,
engine: ValveEngine<'_>,
mode: RenderMode,
) -> Result<(), FieldError> {
RENDER_MODE.write(engine, self, mode.to_raw())
}
#[doc(alias("m_nSkin"))]
pub fn set_skin(self, engine: ValveEngine<'_>, skin: c_int) -> Result<(), FieldError> {
self.set_data_field(engine, c"m_nSkin", skin)
}
#[doc(alias("GetSkin", "m_nSkin"))]
pub fn skin(self) -> Result<c_int, FieldError> {
self.data_field(c"m_nSkin")
}
#[doc(alias("DispatchUpdateTransmitState", "UpdateTransmitState"))]
pub fn update_transmit_state(self) -> Result<(), FieldError> {
if TRANSMIT_STATE_OWNERS.read(self)? != 0 {
return Ok(());
}
unsafe { sdk_raw::transmit::update_transmit_state(self.as_ptr()) };
Ok(())
}
}