use core::time::Duration;
use bevy::{ecs::error::warn, prelude::*};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use crate::prelude::*;
#[derive(Component, Debug, Clone, Copy)]
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Clone, Component, Debug))]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
all(feature = "reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
)]
pub struct ActionMock {
pub state: TriggerState,
pub value: ActionValue,
pub span: MockSpan,
pub enabled: bool,
}
impl ActionMock {
#[must_use]
pub fn once(state: TriggerState, value: impl Into<ActionValue>) -> Self {
Self::new(state, value, MockSpan::once())
}
#[must_use]
pub fn new(
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> Self {
Self {
state,
value: value.into(),
span: span.into(),
enabled: true,
}
}
}
impl Default for ActionMock {
fn default() -> Self {
Self {
state: TriggerState::None,
value: ActionValue::Bool(false),
span: MockSpan::Manual,
enabled: false,
}
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Clone, Debug))]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
all(feature = "reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
)]
pub enum MockSpan {
Updates(u32),
Duration(Duration),
Manual,
}
impl MockSpan {
#[inline]
#[must_use]
pub fn once() -> Self {
Self::Updates(1)
}
}
impl From<Duration> for MockSpan {
fn from(value: Duration) -> Self {
Self::Duration(value)
}
}
pub trait MockEntityWorldMutExt {
fn mock<C: Component, A: InputAction>(
self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> Result<()>;
fn mock_once<C: Component, A: InputAction>(
self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> Result<()>;
}
impl MockEntityWorldMutExt for EntityWorldMut<'_> {
fn mock<C: Component, A: InputAction>(
self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> Result<()> {
mock::<C, A>(state, value, span).apply(self)
}
fn mock_once<C: Component, A: InputAction>(
self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> Result<()> {
mock_once::<C, A>(state, value).apply(self)
}
}
pub trait MockEntityCommandsExt {
fn mock<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> &mut Self;
fn try_mock<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> &mut Self;
fn mock_once<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> &mut Self;
fn try_mock_once<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> &mut Self;
}
impl MockEntityCommandsExt for EntityCommands<'_> {
fn mock<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> &mut Self {
self.queue_handled(mock::<C, A>(state, value, span), warn)
}
fn try_mock<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> &mut Self {
self.queue_silenced(mock::<C, A>(state, value, span))
}
fn mock_once<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> &mut Self {
self.queue_handled(mock_once::<C, A>(state, value), warn)
}
fn try_mock_once<C: Component, A: InputAction>(
&mut self,
state: TriggerState,
value: impl Into<ActionValue>,
) -> &mut Self {
self.queue_silenced(mock_once::<C, A>(state, value))
}
}
pub fn mock<C: Component, A: InputAction>(
state: TriggerState,
value: impl Into<ActionValue>,
span: impl Into<MockSpan>,
) -> impl EntityCommand<Result<()>> {
let value = value.into();
let span = span.into();
move |entity: EntityWorldMut| -> Result<()> {
let context = entity.id();
let actions = entity.get::<Actions<C>>().ok_or_else(|| {
format!(
"entity {} has no `{}`",
context,
ShortName::of::<Actions<C>>(),
)
})?;
let action = actions
.iter()
.find(|&a| entity.world().get::<Action<A>>(a).is_some())
.ok_or_else(|| {
format!(
"entity {} has no `{}` in its `{}`",
context,
ShortName::of::<Action<A>>(),
ShortName::of::<Actions<C>>(),
)
})?;
let world = entity.into_world_mut();
world
.entity_mut(action)
.insert(ActionMock::new(state, value, span));
Ok(())
}
}
pub fn mock_once<C: Component, A: InputAction>(
state: TriggerState,
value: impl Into<ActionValue>,
) -> impl EntityCommand<Result<()>> {
mock::<C, A>(state, value, MockSpan::once())
}