use crate::plugin::CallbackChannel;
use crate::ToAkTransform;
use bevy::math::Affine3A;
use bevy::prelude::*;
#[cfg(wwrelease)]
use rrise::sound_engine::register_game_obj;
#[cfg(not(wwrelease))]
use rrise::sound_engine::register_named_game_obj;
use rrise::sound_engine::{add_default_listener, set_position, stop_all, PostEvent};
use rrise::{
AkCallbackInfo, AkCallbackType, AkGameObjectID, AkID, AkPlayingID, AkResult,
AK_INVALID_PLAYING_ID,
};
use std::sync::{Arc, RwLock};
use tracing;
#[derive(Component)]
pub struct RrRegistered;
#[derive(Debug, Component)]
pub struct RrEmitter {
pub event_id: AkID<'static>,
pub flags: AkCallbackType,
pub auto_post: bool,
pub despawn_on_silent: bool,
pub(crate) playing_ids: Arc<RwLock<Vec<AkPlayingID>>>,
pub(crate) entity: Option<Entity>,
}
#[derive(Bundle, Default)]
pub struct RrEmitterBundle {
pub rr: RrEmitter,
pub global_tfm: GlobalTransform,
}
#[derive(Bundle, Default)]
pub struct RrDynamicEmitterBundle {
#[bundle]
emitter: RrEmitterBundle,
tfm: Transform,
}
#[derive(Debug, Component)]
pub struct RrListener {
is_default: bool,
pub(crate) entity: Option<Entity>,
}
impl RrListener {
pub fn new(is_default: bool) -> Self {
Self {
is_default,
..default()
}
}
pub fn is_default(&self) -> bool {
self.is_default
}
}
impl Default for RrListener {
fn default() -> Self {
Self {
is_default: true,
entity: None,
}
}
}
#[derive(Bundle, Default)]
pub struct RrListenerBundle {
#[bundle]
pub tfm: TransformBundle,
pub listener: RrListener,
}
impl RrListenerBundle {
pub fn with_is_default(mut self, is_default: bool) -> Self {
self.listener.is_default = is_default;
self
}
}
impl Default for RrEmitter {
fn default() -> Self {
Self {
event_id: AkID::Name(""),
flags: AkCallbackType::default(),
auto_post: false,
despawn_on_silent: false,
playing_ids: Arc::new(RwLock::new(vec![])),
entity: None,
}
}
}
impl RrEmitterBundle {
pub fn new(position: Vec3) -> Self {
Self {
global_tfm: GlobalTransform::from_translation(position),
..default()
}
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.global_tfm = GlobalTransform::from(Affine3A::from_rotation_translation(
rotation,
self.global_tfm.translation(),
));
self
}
pub fn with_event<T: Into<AkID<'static>>>(mut self, event: T, despawn_on_silent: bool) -> Self {
self.rr.event_id = event.into();
self.rr.auto_post = true;
self.rr.despawn_on_silent = despawn_on_silent;
self
}
pub fn with_flags(mut self, flags: AkCallbackType) -> Self {
self.rr.flags = flags;
self
}
}
impl RrDynamicEmitterBundle {
pub fn new(position: Vec3) -> Self {
Self {
tfm: Transform::from_translation(position),
..default()
}
}
pub fn from_transform(at: Transform) -> Self {
Self {
tfm: at,
..default()
}
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.tfm = self.tfm.with_rotation(rotation);
self
}
pub fn with_event<T: Into<AkID<'static>>>(mut self, event: T, despawn_on_silent: bool) -> Self {
self.emitter.rr.event_id = event.into();
self.emitter.rr.auto_post = true;
self.emitter.rr.despawn_on_silent = despawn_on_silent;
self
}
pub fn with_flags(mut self, flags: AkCallbackType) -> Self {
self.emitter.rr.flags = flags;
self
}
}
impl RrListenerBundle {
pub fn new(position: Vec3) -> Self {
Self {
tfm: TransformBundle {
local: Transform::from_translation(position),
..default()
},
..default()
}
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.tfm.local = self.tfm.local.with_rotation(rotation);
self
}
}
#[doc(hidden)]
macro_rules! post_event_internal {
($event_id:ident on $entity:ident with $flags:expr; store in $safe_playing_ids:ident; react with $cb_info:ident then { $($then:stmt)* }) => {
PostEvent::new($entity.index() as AkGameObjectID, $event_id)
.flags($flags | AkCallbackType::AK_EndOfEvent)
.post_with_callback(move |$cb_info| {
{
$($then)*
}
if let AkCallbackInfo::Event {
playing_id,
callback_type: AkCallbackType::AK_EndOfEvent,
..
} = $cb_info
{
let mut lock = $safe_playing_ids.write().unwrap();
(*lock).retain(|&p_id| p_id != playing_id);
};
})
};
($event_id:ident on $entity:ident with $flags:expr; store in $safe_playing_ids:ident) => {
post_event_internal![$event_id on $entity with $flags; store in $safe_playing_ids; react with cb_info then {}]
};
}
impl RrEmitter {
pub fn is_playing(&self) -> bool {
!self.playing_ids.read().unwrap().is_empty()
}
pub fn is_registered(&self) -> bool {
self.entity.is_some()
}
pub fn stop(&self) {
if let Some(entity) = self.entity {
stop_all(Some(entity.index() as u64));
}
}
pub fn post_associated_event(&mut self, cb_channel: Option<CallbackChannel>) -> AkPlayingID {
self.post_event(self.event_id, self.flags, cb_channel)
}
pub fn post_event<'b, T: Into<AkID<'b>>>(
&mut self,
event: T,
flags: AkCallbackType,
cb_channel: Option<CallbackChannel>,
) -> AkPlayingID {
if let Some(entity) = self.entity {
let has_flags = flags.0 > AkCallbackType(0).0;
let event = event.into();
let safe_playing_ids = self.playing_ids.clone();
let post_result = match (has_flags, cb_channel) {
(false, _) => {
post_event_internal![
event on entity with flags;
store in safe_playing_ids]
}
(true, None) => {
warn!(
"Event {} on {:?} wants callbacks {} but didn't pass a CallbackChannel; you won't receive bevy events for it",
event,
self.entity,
flags,
);
post_event_internal![
event on entity with AkCallbackType(0);
store in safe_playing_ids]
}
(true, Some(cb_channel)) => {
post_event_internal![
event on entity with flags;
store in safe_playing_ids;
react with cb_info then {
if cb_channel.sender.try_send(cb_info.clone()).is_err() {
warn!("Could not send {:?}", cb_info);
}
}]
}
};
match post_result {
Ok(playing_id) => {
self.playing_ids.write().unwrap().push(playing_id);
playing_id
}
Err(akr) => {
error!("Couldn't post '{}' on {:?} - {}", event, self.entity, akr);
AK_INVALID_PLAYING_ID
}
}
} else {
error!("RrComponent is not yet registered: {:?}", self);
AK_INVALID_PLAYING_ID
}
}
}
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) fn init_new_rr_objects(
mut commands: Commands,
mut listeners: Query<
(Entity, Option<&Name>, &mut RrListener, &GlobalTransform),
Added<RrListener>,
>,
mut emitters: Query<
(Entity, Option<&Name>, &mut RrEmitter, &GlobalTransform),
Added<RrEmitter>,
>,
cb_channel: Res<CallbackChannel>,
) -> Result<(), AkResult> {
for (e, name, mut rr_l, &tfm) in listeners.iter_mut() {
rr_l.entity = Some(e);
let id = e.index() as AkGameObjectID;
#[cfg(not(wwrelease))]
{
if let Err(akr) = register_named_game_obj(
id,
name.map(|n| n.as_str())
.unwrap_or(format!("RrListener_{}", e.index()).as_str()),
) {
error!("Couldn't register listener {} - {}", id, akr);
continue;
}
}
#[cfg(wwrelease)]
if let Err(akr) = register_game_obj(id) {
error!("Couldn't register listener {:?} - {}", e, akr);
continue;
}
if rr_l.is_default {
if let Err(akr) = add_default_listener(id) {
error!("Couldn't add default listener {:?} - {}", e, akr);
continue;
}
}
if let Err(akr) = set_position(id, tfm.to_ak_transform()) {
error!("Couldn't set listener {:?} position - {}", e, akr);
continue;
}
commands.entity(e).insert(RrRegistered);
debug!("Listener {} now registered", id);
}
for (e, name, mut rr_e, &tfm) in emitters.iter_mut() {
rr_e.entity = Some(e);
let id = e.index() as AkGameObjectID;
#[cfg(not(wwrelease))]
{
if let Err(akr) = register_named_game_obj(
id,
name.map(|n| n.as_str())
.unwrap_or(format!("RrEmitter_{}", e.index()).as_str()),
) {
error!("Couldn't register emitter {} - {}", id, akr);
continue;
}
}
#[cfg(wwrelease)]
if let Err(akr) = register_game_obj(id) {
error!("Couldn't register emitter {:?} - {}", e, akr);
continue;
}
if let Err(akr) = set_position(id, tfm.to_ak_transform()) {
error!("Couldn't set emitter {:?} position - {}", e, akr);
continue;
}
if rr_e.auto_post {
rr_e.post_associated_event(Some(cb_channel.clone()));
}
commands.entity(e).insert(RrRegistered);
debug!("Emitter {} now registered", id);
}
Ok(())
}
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) fn stop_destroyed_emitters(
destroyed_emitters: RemovedComponents<RrEmitter>,
) -> Result<(), AkResult> {
for e in destroyed_emitters.iter() {
stop_all(Some(e.index() as AkGameObjectID));
debug!("Stopped emitter {} because it got despawned", e.index());
}
Ok(())
}
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) fn despawn_silent_emitters(
mut commands: Commands,
emitters: Query<&RrEmitter, With<RrRegistered>>,
) -> Result<(), AkResult> {
for rr in emitters.iter() {
if rr.despawn_on_silent && rr.playing_ids.read().unwrap().is_empty() {
commands.entity(rr.entity.unwrap()).despawn();
debug!(
"Despawned emitter {} because it became silent",
rr.entity.unwrap().index()
);
}
}
Ok(())
}
#[allow(clippy::type_complexity)]
pub(crate) fn update_rr_position(
mut emitters: Query<
(&mut RrEmitter, &GlobalTransform),
(With<RrRegistered>, Changed<GlobalTransform>),
>,
mut listeners: Query<
(&mut RrListener, &GlobalTransform),
(With<RrRegistered>, Changed<GlobalTransform>),
>,
) -> Result<(), AkResult> {
for (rr, &tfm) in emitters.iter_mut() {
set_position(
rr.entity.unwrap().index() as AkGameObjectID,
tfm.to_ak_transform(),
)?;
}
for (rr, &tfm) in listeners.iter_mut() {
set_position(
rr.entity.unwrap().index() as AkGameObjectID,
tfm.to_ak_transform(),
)?;
}
Ok(())
}