use rusteal_core::{OwnedStruct, UObjectRef, UeClass, RustealResult};
use crate::core_ue::FTransform;
use crate::engine::{Actor, ActorExt, World};
fn warn_no_root_component(handle: rusteal_ffi::UObjectHandle) {
use crate::engine::Pawn;
let actor_ref: UObjectRef<Actor> = unsafe { UObjectRef::from_raw(handle) };
if let Ok(actual_class) = actor_ref.get_class() {
if actual_class == Actor::static_class()
|| actual_class == Pawn::static_class()
{
return;
}
}
if let Ok(checked) = actor_ref.checked() {
let root = checked.k2_get_root_component();
if !root.is_valid() {
rusteal_core::ulog!(rusteal_core::LOG_WARNING,
"[Rusteal] spawn_actor: spawned actor has no RootComponent — \
spawn transform will be ignored and set_actor_location will fail. \
Consider inheriting from a class with a default RootComponent."
);
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default)]
pub enum SpawnCollisionMethod {
#[default]
Undefined = 0,
AlwaysSpawn = 1,
AdjustIfPossibleButAlwaysSpawn = 2,
AdjustIfPossibleButDontSpawnIfColliding = 3,
DontSpawnIfColliding = 4,
}
pub trait WorldSpawnExt {
fn spawn_actor<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<UObjectRef<T>>;
fn spawn_actor_with_owner<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
owner: &UObjectRef<Actor>,
) -> RustealResult<UObjectRef<T>>;
fn spawn_actor_deferred<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<UObjectRef<T>>;
fn spawn_actor_deferred_full<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
owner: rusteal_ffi::UObjectHandle,
instigator: rusteal_ffi::UObjectHandle,
collision_method: SpawnCollisionMethod,
) -> RustealResult<UObjectRef<T>>;
fn finish_spawning(
&self,
actor: &UObjectRef<Actor>,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<()>;
fn get_all_actors_of_class<T: UeClass>(&self) -> RustealResult<Vec<UObjectRef<T>>>;
}
impl WorldSpawnExt for UObjectRef<World> {
fn spawn_actor<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<UObjectRef<T>> {
let world = self.checked()?.raw();
let class = T::static_class();
let null_owner = rusteal_ffi::UObjectHandle::null();
let handle = rusteal_core::world::spawn_actor_raw(
world,
class,
&transform.to_bytes(),
null_owner,
)?;
warn_no_root_component(handle);
Ok(unsafe { UObjectRef::from_raw(handle) })
}
fn spawn_actor_with_owner<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
owner: &UObjectRef<Actor>,
) -> RustealResult<UObjectRef<T>> {
let world = self.checked()?.raw();
let class = T::static_class();
let owner_handle = owner.checked()?.raw();
let handle = rusteal_core::world::spawn_actor_raw(
world,
class,
&transform.to_bytes(),
owner_handle,
)?;
warn_no_root_component(handle);
Ok(unsafe { UObjectRef::from_raw(handle) })
}
fn spawn_actor_deferred<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<UObjectRef<T>> {
let world = self.checked()?.raw();
let class = T::static_class();
let null = rusteal_ffi::UObjectHandle::null();
let handle = rusteal_core::world::spawn_actor_deferred_raw(
world, class, &transform.to_bytes(), null, null, 0,
)?;
warn_no_root_component(handle);
Ok(unsafe { UObjectRef::from_raw(handle) })
}
fn spawn_actor_deferred_full<T: UeClass>(
&self,
transform: &OwnedStruct<FTransform>,
owner: rusteal_ffi::UObjectHandle,
instigator: rusteal_ffi::UObjectHandle,
collision_method: SpawnCollisionMethod,
) -> RustealResult<UObjectRef<T>> {
let world = self.checked()?.raw();
let class = T::static_class();
let handle = rusteal_core::world::spawn_actor_deferred_raw(
world, class, &transform.to_bytes(), owner, instigator, collision_method as u8,
)?;
warn_no_root_component(handle);
Ok(unsafe { UObjectRef::from_raw(handle) })
}
fn finish_spawning(
&self,
actor: &UObjectRef<Actor>,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<()> {
let actor_handle = actor.checked()?.raw();
rusteal_core::world::finish_spawning_raw(actor_handle, &transform.to_bytes())
}
fn get_all_actors_of_class<T: UeClass>(&self) -> RustealResult<Vec<UObjectRef<T>>> {
let world = self.checked()?.raw();
let class = T::static_class();
let handles = rusteal_core::world::get_all_actors_of_class_raw(world, class)?;
Ok(handles
.into_iter()
.map(|h| unsafe { UObjectRef::from_raw(h) })
.collect())
}
}
pub fn find_object<T: UeClass>(path: &str) -> RustealResult<UObjectRef<T>> {
let class = T::static_class();
let handle = rusteal_core::world::find_object_raw(class, path)?;
Ok(unsafe { UObjectRef::from_raw(handle) })
}
pub fn load_object<T: UeClass>(path: &str) -> RustealResult<UObjectRef<T>> {
let class = T::static_class();
let handle = rusteal_core::world::load_object_raw(class, path)?;
Ok(unsafe { UObjectRef::from_raw(handle) })
}
pub fn new_object<T: UeClass>(outer: &UObjectRef<impl UeClass>) -> RustealResult<UObjectRef<T>> {
let outer_handle = outer.checked()?.raw();
let class = T::static_class();
let handle = rusteal_core::world::new_object_raw(outer_handle, class)?;
Ok(unsafe { UObjectRef::from_raw(handle) })
}
pub fn new_object_transient<T: UeClass>() -> RustealResult<UObjectRef<T>> {
let class = T::static_class();
let null_outer = rusteal_ffi::UObjectHandle::null();
let handle = rusteal_core::world::new_object_raw(null_outer, class)?;
Ok(unsafe { UObjectRef::from_raw(handle) })
}
pub fn spawn_actor_dynamic(
world: &UObjectRef<World>,
class: rusteal_ffi::UClassHandle,
transform: &OwnedStruct<FTransform>,
) -> RustealResult<rusteal_ffi::UObjectHandle> {
let world_handle = world.checked()?.raw();
let null_owner = rusteal_ffi::UObjectHandle::null();
rusteal_core::world::spawn_actor_raw(world_handle, class, &transform.to_bytes(), null_owner)
}
pub fn new_object_dynamic(
outer: rusteal_ffi::UObjectHandle,
class: rusteal_ffi::UClassHandle,
) -> RustealResult<rusteal_ffi::UObjectHandle> {
rusteal_core::world::new_object_raw(outer, class)
}