diff --git a/crates/beet_sim/examples/sim_test.rs b/crates/beet_sim/examples/sim_test.rs index 6dcb1596..de31e105 100644 --- a/crates/beet_sim/examples/sim_test.rs +++ b/crates/beet_sim/examples/sim_test.rs @@ -3,7 +3,6 @@ use beet_sim::prelude::*; use beet_spatial::prelude::*; use beetmash::prelude::*; use bevy::prelude::*; -use std::f32::consts::TAU; const STRESS: &str = "Stress"; @@ -51,20 +50,6 @@ fn camera(mut commands: Commands) { )); } -fn orbital_transform(index: usize, total: usize) -> Transform { - let angle = TAU / total as f32 * index as f32; - let pos = Vec3::new(f32::cos(angle), f32::sin(angle), 0.); - Transform::from_translation(pos * 0.7).with_scale(CHILD_SCALE) -} - - -const CHILD_SCALE: Vec3 = Vec3 { - x: 0.5, - y: 0.5, - z: 0.5, -}; - - fn agent(mut commands: Commands, stat_map: Res) { commands .spawn(( @@ -81,7 +66,7 @@ fn agent(mut commands: Commands, stat_map: Res) { let stress = parent .spawn(( Name::new(STRESS), - orbital_transform(0, total_children), + orbital_child(0, total_children), stat_map.get_id_by_name(STRESS).unwrap(), stat_map.get_default_by_name(STRESS).unwrap(), )) @@ -89,14 +74,14 @@ fn agent(mut commands: Commands, stat_map: Res) { let self_control = parent .spawn(( Name::new(SELF_CONTROL), - orbital_transform(1, total_children), + orbital_child(1, total_children), stat_map.get_id_by_name(SELF_CONTROL).unwrap(), stat_map.get_default_by_name(SELF_CONTROL).unwrap(), )) .id(); parent.spawn(( Name::new("Walk"), - orbital_transform(2, total_children), + orbital_child(2, total_children), TargetEntity(agent), Walk::default(), )); @@ -105,7 +90,7 @@ fn agent(mut commands: Commands, stat_map: Res) { .spawn(( Name::new("Behavior"), Emoji::new("1F5FA"), - orbital_transform(3, total_children), + orbital_child(3, total_children), RunOnSpawn, RunOnChange::::default() .with_source(vec![stress, self_control]), @@ -149,7 +134,7 @@ fn kids_crying(mut commands: Commands, stat_map: Res) { )) .with_child(( Name::new(STRESS), - orbital_transform(0, 2), + orbital_child(0, 2), stat_map.get_id_by_name(STRESS).unwrap(), StatValue::new(0.1), CollectableStat::default(), @@ -165,14 +150,14 @@ fn alcohol(mut commands: Commands, stat_map: Res) { )) .with_child(( Name::new(STRESS), - orbital_transform(0, 2), + orbital_child(0, 2), stat_map.get_id_by_name(STRESS).unwrap(), StatValue::new(-0.1), CollectableStat::default(), )) .with_child(( Name::new(SELF_CONTROL), - orbital_transform(1, 2), + orbital_child(1, 2), stat_map.get_id_by_name(SELF_CONTROL).unwrap(), StatValue::new(-0.1), CollectableStat::default(), @@ -185,18 +170,18 @@ fn short_stroll(mut commands: Commands, stat_map: Res) { Name::new("Short Stroll"), Emoji::new("1F6B6"), CollectableStat::default(), - Transform::from_xyz(3., -1., 0.), + Transform::from_xyz(3., -1.5, 0.), )) .with_child(( Name::new(STRESS), - orbital_transform(0, 2), + orbital_child(0, 2), stat_map.get_id_by_name(STRESS).unwrap(), StatValue::new(-0.1), ZoneStat::default(), )) .with_child(( Name::new(SELF_CONTROL), - orbital_transform(1, 2), + orbital_child(1, 2), stat_map.get_id_by_name(SELF_CONTROL).unwrap(), StatValue::new(0.1), ZoneStat::default(), diff --git a/crates/beet_sim/src/plugins/beet_sim_plugin.rs b/crates/beet_sim/src/plugins/beet_sim_plugin.rs index ed9f5e95..25f780f3 100644 --- a/crates/beet_sim/src/plugins/beet_sim_plugin.rs +++ b/crates/beet_sim/src/plugins/beet_sim_plugin.rs @@ -15,6 +15,7 @@ impl Plugin for BeetSimPlugin { RunOnChange, FindStatSteerTarget, )>::default(), - )); + )) + .add_systems(Update, render_valency); } } diff --git a/crates/beet_sim/src/render/mod.rs b/crates/beet_sim/src/render/mod.rs index beea7614..84008023 100644 --- a/crates/beet_sim/src/render/mod.rs +++ b/crates/beet_sim/src/render/mod.rs @@ -1,3 +1,9 @@ pub mod emoji; #[allow(unused_imports)] pub use self::emoji::*; +pub mod orbital_child; +#[allow(unused_imports)] +pub use self::orbital_child::*; +pub mod valency; +#[allow(unused_imports)] +pub use self::valency::*; diff --git a/crates/beet_sim/src/render/orbital_child.rs b/crates/beet_sim/src/render/orbital_child.rs new file mode 100644 index 00000000..0071f68b --- /dev/null +++ b/crates/beet_sim/src/render/orbital_child.rs @@ -0,0 +1,16 @@ +use bevy::prelude::*; +use std::f32::consts::TAU; + +/// Arrange children in a circle around the parent +pub fn orbital_child(index: usize, total: usize) -> Transform { + let angle = TAU / total as f32 * index as f32; + let pos = Vec3::new(f32::cos(angle), f32::sin(angle), 0.); + Transform::from_translation(pos * 0.7).with_scale(CHILD_SCALE) +} + + +const CHILD_SCALE: Vec3 = Vec3 { + x: 0.5, + y: 0.5, + z: 0.5, +}; diff --git a/crates/beet_sim/src/render/valency.rs b/crates/beet_sim/src/render/valency.rs new file mode 100644 index 00000000..38864ea3 --- /dev/null +++ b/crates/beet_sim/src/render/valency.rs @@ -0,0 +1,45 @@ +use crate::prelude::*; +use bevy::prelude::*; + + +#[derive(Default, Component, Reflect)] +#[reflect(Default, Component)] +pub struct ValencyRender; + +pub fn render_valency( + mut commands: Commands, + stat_map: Res, + query: Populated< + (Entity, &StatId, &StatValue), + (Changed, With), + >, + children: Query<&Children>, + existing: Query<&ValencyRender>, +) { + for (entity, stat_id, value) in query.iter() { + // 1. remove existing valency renders + for child in children.iter_descendants(entity) { + if existing.get(child).is_ok() { + commands.entity(child).despawn_recursive(); + } + } + let emoji = match value.is_sign_positive() { + true => "2795", // ➕ + false => "2796", // ➖ + }; + let range = *stat_map.get(stat_id).unwrap().total_range(); + let frac = value.0.abs() / range; + + let num_emojis = match frac { + f if f < 0.33 => 1, + f if f < 0.66 => 2, + _ => 3, + }; + + commands.entity(entity).with_children(|parent| { + for i in 0..num_emojis { + parent.spawn((Emoji::new(emoji), orbital_child(i, num_emojis))); + } + }); + } +} diff --git a/crates/beet_sim/src/stats/collectable_stat.rs b/crates/beet_sim/src/stats/collectable_stat.rs index 476985c0..70b0e3fa 100644 --- a/crates/beet_sim/src/stats/collectable_stat.rs +++ b/crates/beet_sim/src/stats/collectable_stat.rs @@ -1,8 +1,11 @@ use bevy::prelude::*; +//// Superset of [`CollectableStat`], [`ZoneStat`] etc +#[derive(Default, Component, Reflect)] +#[reflect(Default, Component)] +pub struct StatProvider; #[derive(Default, Component, Reflect)] #[reflect(Default, Component)] -pub struct CollectableStat{ - -} +#[require(StatProvider)] +pub struct CollectableStat {} diff --git a/crates/beet_sim/src/stats/find_stat_steer_target.rs b/crates/beet_sim/src/stats/find_stat_steer_target.rs index 20f8d3a3..f8425aaf 100644 --- a/crates/beet_sim/src/stats/find_stat_steer_target.rs +++ b/crates/beet_sim/src/stats/find_stat_steer_target.rs @@ -22,11 +22,8 @@ impl FindStatSteerTarget {} fn find_steer_target( trigger: Trigger, mut commands: Commands, - agents: Query<&Transform>, - targets: Query< - (Entity, &Transform, &StatId, &StatValue), - Or<(With, With)>, - >, + transforms: Query<&Transform>, + targets: Query<(&StatId, &StatValue, &Parent), With>, query: Populated<( &TargetEntity, &FindStatSteerTarget, @@ -38,19 +35,21 @@ fn find_steer_target( .get(trigger.entity()) .expect(expect_action::ACTION_QUERY_MISSING); - let agent_transform = agents + let agent_transform = transforms .get(**agent_entity) .expect(expect_action::TARGET_MISSING); let mut best_score = f32::MAX; let mut closest_target = None; - for (pickup_entity, pickup_transform, pickup_id, pickup_value) in - targets.iter() - { + for (pickup_id, pickup_value, pickup_parent) in targets.iter() { if pickup_id != goal_id { continue; } + let pickup_transform = transforms + .get(**pickup_parent) + .expect(expect_action::TARGET_MISSING); + let new_dist = Vec3::distance( agent_transform.translation, pickup_transform.translation, @@ -68,7 +67,7 @@ fn find_steer_target( if new_score < best_score { best_score = new_score; - closest_target = Some(pickup_entity); + closest_target = Some(**pickup_parent); } } diff --git a/crates/beet_sim/src/stats/stat_descriptor.rs b/crates/beet_sim/src/stats/stat_descriptor.rs index 94450a74..89d103c7 100644 --- a/crates/beet_sim/src/stats/stat_descriptor.rs +++ b/crates/beet_sim/src/stats/stat_descriptor.rs @@ -14,3 +14,10 @@ pub struct StatDescriptor { /// Unless overridden this is the default value for this stat pub default_value: StatValue, } + + +impl StatDescriptor { + pub fn total_range(&self) -> StatValue { + StatValue(*self.global_range.end - *self.global_range.start) + } +} diff --git a/crates/beet_sim/src/stats/zone_stat.rs b/crates/beet_sim/src/stats/zone_stat.rs index 0df9e8df..3115757e 100644 --- a/crates/beet_sim/src/stats/zone_stat.rs +++ b/crates/beet_sim/src/stats/zone_stat.rs @@ -1,8 +1,9 @@ use bevy::prelude::*; - +use crate::prelude::*; #[derive(Default, Component, Reflect)] #[reflect(Default, Component)] +#[require(StatProvider)] pub struct ZoneStat{ }