use bevy::{
ecs::{component::ComponentId, entity::EntityHashMap},
prelude::*,
};
mod first_to_score;
mod highest;
#[cfg(feature = "rand")]
mod random;
pub use first_to_score::*;
pub use highest::*;
#[cfg(feature = "rand")]
pub use random::*;
use crate::{
ecs::TriggerGetEntity,
event::{OnPick, RunPicking},
};
pub struct PickingPlugin;
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app.observe(Self::run_picking);
}
}
impl PickingPlugin {
pub fn run_picking(trigger: Trigger<RunPicking>, mut commands: Commands, pickers: Query<Entity, With<Picker>>) {
fn trigger_picking(target: Entity, mut commands: Commands) {
commands.trigger_targets(OnPick, target);
}
if let Some(target) = trigger.get_entity() {
trigger_picking(target, commands.reborrow());
} else {
for target in pickers.iter() {
trigger_picking(target, commands.reborrow());
}
}
}
}
#[derive(Component, Reflect)]
#[derive(Clone, PartialEq, Debug)]
#[reflect(Component)]
pub struct Picker {
pub default: ComponentId,
pub choices: EntityHashMap<ComponentId>,
pub picked: ComponentId,
}
impl Picker {
pub fn new(default: ComponentId) -> Self {
Self {
default,
choices: EntityHashMap::default(),
picked: default,
}
}
pub fn with(mut self, score_entity: Entity, action: ComponentId) -> Self {
self.choices.insert(score_entity, action);
self
}
pub fn pick(&mut self, score_entity: Option<Entity>) -> ComponentId {
let action = score_entity
.and_then(|entity| self.choices.get(&entity).copied())
.unwrap_or(self.default);
self.picked = action;
action
}
pub fn is_default(&self, action: ComponentId) -> bool {
action == self.default
}
pub fn picked_default(&self) -> bool {
self.picked == self.default
}
}
#[cfg(test)]
mod tests {
use bevy::prelude::*;
use crate::{
event::{RunPicking, RunScoring},
picking::{FirstToScore, Highest, Picker},
scoring::{FixedScore, Score},
};
#[derive(Component)]
struct MyAction;
#[derive(Component)]
struct IdleAction;
#[test]
fn pick_first_to_score() {
let mut app = App::new();
app.add_plugins(crate::ObservedUtilityPlugins::RealTime);
let world = app.world_mut();
let my_action = world.init_component::<MyAction>();
let idle_action = world.init_component::<IdleAction>();
let mut commands = world.commands();
let scorer = commands.spawn((FixedScore::new(0.7), Score::default())).id();
let actor = commands
.spawn((Picker::new(idle_action).with(scorer, my_action), FirstToScore::new(0.5)))
.add_child(scorer)
.id();
commands.trigger_targets(RunScoring, scorer);
commands.trigger_targets(RunPicking, actor);
world.flush();
assert_eq!(my_action, world.get::<Picker>(actor).unwrap().picked);
}
#[test]
fn pick_highest() {
let mut app = App::new();
app.add_plugins(crate::ObservedUtilityPlugins::RealTime);
let world = app.world_mut();
let my_action = world.init_component::<MyAction>();
let idle_action = world.init_component::<IdleAction>();
let mut commands = world.commands();
let scorer = commands.spawn((FixedScore::new(0.7), Score::default())).id();
let actor = commands
.spawn((Picker::new(idle_action).with(scorer, my_action), Highest))
.add_child(scorer)
.id();
commands.trigger_targets(RunScoring, scorer);
commands.trigger_targets(RunPicking, actor);
world.flush();
assert_eq!(my_action, world.get::<Picker>(actor).unwrap().picked);
}
}