#[cfg(test)]
#[path = "../tests/entities/hierarchy.rs"]
mod tests;
use crate::entities::fields::{BaseField, FieldError};
use crate::entities::{Entity, EntityHandle};
use crate::inputs::{InputError, InputValue};
use crate::interfaces::ServerTools;
use std::ffi::CStr;
use std::num::NonZeroU8;
const MAX_MOVE_CHILDREN: usize = sdk_raw::entities::NUM_ENT_ENTRIES;
const MAX_PARENT_DEPTH: usize = sdk_raw::entities::NUM_ENT_ENTRIES;
static MOVE_CHILD: BaseField<EntityHandle> =
BaseField::new(c"m_hMoveChild", sys::_fieldtypes_FIELD_EHANDLE);
static MOVE_PEER: BaseField<EntityHandle> =
BaseField::new(c"m_hMovePeer", sys::_fieldtypes_FIELD_EHANDLE);
static PARENT_ATTACHMENT: BaseField<u8> =
BaseField::new(c"m_iParentAttachment", sys::_fieldtypes_FIELD_CHARACTER);
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ParentError {
#[error("the parent is the child, or moves with it")]
Cycle,
#[error(transparent)]
Field(#[from] FieldError),
#[error(transparent)]
Input(#[from] InputError),
#[error("the parent has no attachment of that name")]
UnknownAttachment,
}
impl<'s> Entity<'s> {
#[doc(alias("FirstMoveChild", "m_hMoveChild"))]
pub fn move_child(self) -> Result<Option<EntityHandle>, FieldError> {
let child = MOVE_CHILD.read(self)?;
Ok(child.is_valid().then_some(child))
}
#[doc(alias("NextMovePeer", "m_hMovePeer"))]
pub fn move_peer(self) -> Result<Option<EntityHandle>, FieldError> {
let peer = MOVE_PEER.read(self)?;
Ok(peer.is_valid().then_some(peer))
}
#[doc(alias("GetParentAttachment", "m_iParentAttachment"))]
pub fn parent_attachment(self) -> Result<Option<NonZeroU8>, FieldError> {
PARENT_ATTACHMENT.read(self).map(NonZeroU8::new)
}
}
impl<'s> ServerTools<'s> {
#[doc(alias("ClearParent", "AcceptEntityInput"))]
pub fn clear_parent(self, child: Entity<'_>) -> Result<(), InputError> {
self.accept_input(child, c"ClearParent", InputValue::Void, child, child)
}
#[doc(alias("FirstMoveChild", "NextMovePeer"))]
pub fn move_children(self, entity: Entity<'_>) -> Result<Vec<Entity<'s>>, FieldError> {
let mut children = Vec::new();
let mut next = entity.move_child()?;
while let Some(handle) = next
&& children.len() < MAX_MOVE_CHILDREN
{
let Some(child) = self.entity_by_handle(handle) else {
break;
};
next = child.move_peer()?;
children.push(child);
}
Ok(children)
}
#[doc(alias("SetParent", "AcceptEntityInput"))]
pub fn set_parent(self, child: Entity<'_>, parent: Entity<'_>) -> Result<(), ParentError> {
let mut ancestor = Some(parent);
for _ in 0..MAX_PARENT_DEPTH {
let Some(entity) = ancestor else {
return self
.accept_input(
child,
c"SetParent",
InputValue::String(c"!activator"),
parent,
child,
)
.map_err(ParentError::from);
};
if entity == child {
return Err(ParentError::Cycle);
}
ancestor = entity
.move_parent()?
.and_then(|handle| self.entity_by_handle(handle));
}
Err(ParentError::Cycle)
}
#[doc(alias("SetParentAttachment", "SetParentAttachmentMaintainOffset"))]
pub fn set_parent_attachment(
self,
child: Entity<'_>,
parent: Entity<'_>,
attachment: &CStr,
keep_offset: bool,
) -> Result<(), ParentError> {
self.set_parent(child, parent)?;
let input = if keep_offset {
c"SetParentAttachmentMaintainOffset"
} else {
c"SetParentAttachment"
};
self.accept_input(child, input, InputValue::String(attachment), parent, child)?;
match child.parent_attachment()? {
Some(_) => Ok(()),
None => Err(ParentError::UnknownAttachment),
}
}
}