Skip to content

Commit

Permalink
Tiny refactor + begin resman example
Browse files Browse the repository at this point in the history
  • Loading branch information
victorb committed Aug 9, 2024
1 parent cfbec2d commit 33d6889
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 136 deletions.
1 change: 1 addition & 0 deletions crates/bevy_dogoap/examples/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ fn print_current_local_state(
Option<&GoToSmelterAction>,
Option<&GoToMerchantAction>,
)>,
// action_query: Query<&dyn ActionComponent>,
mut q_child: Query<&mut Text, With<NeedsText>>,
) {
for (entity, hunger, energy, has_ore, has_metal, gold_amount, children) in query.iter() {
Expand Down
198 changes: 126 additions & 72 deletions crates/bevy_dogoap/examples/resman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ use dogoap::prelude::*;

fn main() {
let mut app = App::new();
register_components!(app, vec![Thirst, CarryingItem]);
// Customer components + actions
register_components!(app, vec![Thirst, CarryingItem, PlacedOrder, OrderReady]);
register_actions!(
app,
vec![DrinkLemonade, PickupLemonade, WaitForOrder, PlaceOrder]
);
// Worker components + actions
register_components!(app, vec![Energy]);
register_actions!(app, vec![Rest]);

app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
Expand All @@ -40,21 +48,41 @@ fn main() {
.run();
}

// LocalFields
// LocalFields for customer

#[derive(Component, Clone, DatumComponent)]
struct Thirst(f64);

#[derive(Component, Clone, EnumComponent)]
struct CarryingItem(Item);

#[derive(Component, Clone, DatumComponent)]
struct PlacedOrder(bool);

#[derive(Component, Clone, DatumComponent)]
struct CarryingItem(usize); // `Items::Lemonade as usize` for example
struct OrderReady(bool);

// Actions for customer

// Actions
#[derive(Component, Clone, Default, ActionComponent)]
struct DrinkLemonade;

#[derive(Component, Clone, Default, ActionComponent)]
struct PickupLemonade;

#[derive(Component, Clone, Default, ActionComponent)]
struct DrinkLemonade();
struct WaitForOrder;

#[derive(Component, Clone, Default, ActionComponent)]
struct PickupLemonade();
struct PlaceOrder;

// DatumComponents for worker

#[derive(Component, Clone, DatumComponent)]
struct Energy(f64);

#[derive(Component, Clone, Default, ActionComponent)]
struct Rest;

// Markers

Expand All @@ -67,7 +95,7 @@ struct Customer;
#[derive(Component)]
struct Worker;

#[derive(Clone, Default, Reflect)]
#[derive(Clone, Default, Copy, Reflect)]
enum Item {
#[default]
Nothing,
Expand All @@ -87,59 +115,56 @@ struct StateDebugText;
fn setup(mut commands: Commands) {
// Spawn customers
for _i in 0..1 {
let goal = Goal::new().with_req(&Thirst::key(), Compare::LessThanEquals(Datum::F64(1.0)));

let goals = vec![goal.clone()];
let goal = Goal::from_reqs(&[Thirst::is_less(1.0)]);

// Requires us to carry a lemonade, results in us having 10 less thirst + carrying Nothing
let drink_lemonade_action = Action::new(&DrinkLemonade::key())
.with_precondition(
&CarryingItem::key(),
Compare::Equals(Datum::Enum(Item::Lemonade as usize)),
)
.with_effect(
Effect::new(&DrinkLemonade::key())
.with_mutator(Mutator::Set(
CarryingItem::key(),
Datum::Enum(Item::Nothing as usize),
))
.with_mutator(Mutator::Decrement(Thirst::key(), Datum::F64(10.0))),
);
let drink_lemonade_action = DrinkLemonade::new()
.add_precondition(CarryingItem::is(Item::Lemonade))
.add_mutator(CarryingItem::set(Item::Nothing))
.add_mutator(Thirst::decrease(10.0));

// Requires us to not be carrying nothing, and leads to us having a lemonade
let pickup_lemonade_action = Action::new(&PickupLemonade::key())
.with_precondition(
&CarryingItem::key(),
Compare::Equals(Datum::Enum(Item::Nothing as usize)),
)
.with_effect(
Effect::new(&PickupLemonade::key()).with_mutator(Mutator::Set(
CarryingItem::key(),
Datum::Enum(Item::Lemonade as usize),
)),
);

let actions_map = create_action_map!(
(DrinkLemonade, drink_lemonade_action),
(PickupLemonade, pickup_lemonade_action)
);

let initial_state = (Thirst(0.0), CarryingItem(Item::Nothing as usize));
let state = create_state!(Thirst(0.0), CarryingItem(Item::Nothing as usize));

let mut planner = Planner::new(state, goals, actions_map);
let pickup_lemonade_action = PickupLemonade::new()
.add_precondition(CarryingItem::is(Item::Nothing))
.add_mutator(CarryingItem::set(Item::Lemonade));

let wait_for_order_action = WaitForOrder::new()
.add_precondition(PlacedOrder::is(true))
.add_precondition(OrderReady::is(false))
.add_mutator(OrderReady::set(true));

let place_order_action = PlaceOrder::new()
.add_precondition(PlacedOrder::is(false))
.add_mutator(PlacedOrder::set(true));

// Drink Lemonade
// Pickup Lemonade
// Wait for Order
// Place Order
// Go To Order Desk

let (mut planner, components) = create_planner!({
actions: [
(DrinkLemonade, drink_lemonade_action),
(PickupLemonade, pickup_lemonade_action)
],
state: [Thirst(0.0), CarryingItem(Item::Nothing)],
goals: [goal],
});

planner.remove_goal_on_no_plan_found = false; // Don't remove the goal
planner.always_plan = true; // Re-calculate our plan whenever we can
planner.current_goal = Some(goal.clone());

let t = Transform::from_scale(Vec3::splat(2.0));

commands
.spawn((
Agent,
Name::new("Customer"),
Customer,
planner,
initial_state,
components,
TransformBundle::from(Transform::from_xyz(-200.0, -100.0, 1.0)),
))
.with_children(|subcommands| {
Expand All @@ -164,11 +189,25 @@ fn setup(mut commands: Commands) {

// Spawn worker
for _i in 0..1 {
let goal = Goal::from_reqs(&[Energy::is_more(1.0)]);

let rest_action = Rest::new()
.add_precondition(Energy::is_less(10.0))
.add_mutator(Energy::increase(50.0));

let (planner, components) = create_planner!({
actions: [(Rest, rest_action)],
state: [Energy(50.0)],
goals: [goal],
});

commands
.spawn((
Agent,
Name::new("Worker"),
Worker,
planner,
components,
TransformBundle::from(Transform::from_xyz(0.0, 0.0, 1.0)),
))
.with_children(|subcommands| {
Expand Down Expand Up @@ -214,7 +253,7 @@ fn handle_pickup_lemonade(
match progresses.get_mut(&entity) {
Some(progress) => {
if progress.tick(time.delta()).just_finished() {
state.0 = Item::Lemonade as usize;
state.0 = Item::Lemonade;
commands.entity(entity).remove::<PickupLemonade>();
progresses.remove(&entity);
} else {
Expand All @@ -239,7 +278,7 @@ fn handle_drink_lemonade(
match progresses.get_mut(&entity) {
Some(progress) => {
if progress.tick(time.delta()).just_finished() {
state.0 = Item::Nothing as usize;
state.0 = Item::Nothing;
thirst.0 = (thirst.0 - 5.0).max(0.0);

commands.entity(entity).remove::<DrinkLemonade>();
Expand All @@ -266,9 +305,10 @@ fn update_thirst(time: Res<Time>, mut query: Query<&mut Thirst>) {
}

fn draw_state_debug(
q_customers: Query<(Entity, &Name, &Thirst, &Children), With<Customer>>,
q_customer_actions: Query<(Option<&DrinkLemonade>, Option<&PickupLemonade>)>,
q_workers: Query<(Entity, &Name, &Children), With<Worker>>,
q_planners: Query<(Entity, &Name, &Children), With<Planner>>,
// q_workers: Query<(Entity, &Name, &Children), With<Worker>>,
q_actions: Query<(Entity, &dyn ActionComponent)>,
q_datums: Query<(Entity, &dyn DatumComponent)>,
// q_worker_actions: Query<(Option<>)>
// q_actions: Query<(
// Option<&IsPlanning>,
Expand All @@ -278,42 +318,56 @@ fn draw_state_debug(
// )>,
mut q_child: Query<&mut Text, With<StateDebugText>>,
) {
for (entity, name, thirst, children) in q_customers.iter() {
let thirst = thirst.0;

for (entity, name, children) in q_planners.iter() {
let mut current_action = "Idle";

let (drink_lemonade_action, pickup_lemonade_action) =
q_customer_actions.get(entity).unwrap();

if drink_lemonade_action.is_some() {
current_action = "Drinking Lemonade";
// Get current action, should always be one so grab the first one we find
for (_entity, actions) in q_actions.get(entity).iter() {
for action in actions.iter() {
current_action = action.action_type_name();
break;
}
}

if pickup_lemonade_action.is_some() {
current_action = "Picking up Lemonade";
// Concat all the found DatumComponents for this entity
let mut state: String = "".to_string();
for (_entity, data) in q_datums.get(entity).iter() {
for datum in data.iter() {
state = format!(
"{}\n{}: {}",
state,
datum.field_key().to_string(),
match datum.field_value() {
Datum::Bool(v) => v.to_string(),
Datum::F64(v) => format!("{:.2}", v).to_string(),
Datum::I64(v) => format!("{}", v).to_string(),
Datum::Enum(v) => format!("{}", v).to_string(),
}
);
}
}

// Render it out
for &child in children.iter() {
let mut text = q_child.get_mut(child).unwrap();
text.sections[0].value =
format!("{name}\n{current_action}\nThirst: {thirst:.2}\nEntity: {entity}");
format!("{name}\n{current_action}\nEntity: {entity}\n---\n{state}");
}
}
for (entity, name, children) in q_workers.iter() {
let current_action = "Idle";
// for (entity, name, children) in q_workers.iter() {
// let current_action = "Idle";

// let (is_planning, eat, go_to_mushroom, replicate) = q_actions.get(entity).unwrap();
// // let (is_planning, eat, go_to_mushroom, replicate) = q_actions.get(entity).unwrap();

// if is_planning.is_some() {
// current_action = "Planning...";
// }
// // if is_planning.is_some() {
// // current_action = "Planning...";
// // }

for &child in children.iter() {
let mut text = q_child.get_mut(child).unwrap();
text.sections[0].value = format!("{name}\n{current_action}\nEntity: {entity}");
}
}
// for &child in children.iter() {
// let mut text = q_child.get_mut(child).unwrap();
// text.sections[0].value = format!("{name}\n{current_action}\nEntity: {entity}");
// }
// }
}

fn draw_ui(
Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_dogoap/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ macro_rules! register_components {
};
}

#[macro_export]
macro_rules! register_actions {
($app:ident, vec![$($comp:ty),*]) => {
$(
$app.register_component_as::<dyn ActionComponent, $comp>();
)*
};
}

#[macro_export]
macro_rules! create_goal {
($(($type:ident, $comp:path, $field:expr)),*) => {{
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_dogoap/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use dogoap::prelude::{

pub use crate::{
create_action_map, create_goal, create_planner, create_state, planner::IsPlanning,
planner::Planner, register_components,
planner::Planner, register_actions, register_components,
};

pub use crate::plugin::DogoapPlugin;
Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_dogoap/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@ pub trait DatumComponent: Send + Sync {
/// Action::new("my_action")
/// );
/// ```
#[bevy_trait_query::queryable]
#[reflect_trait]
pub trait ActionComponent: Send + Sync {
/// Gets the action key but in snake_case ("AtLocation" becomes "at_location")
fn key() -> String;
fn key() -> String
where
Self: Sized;
/// Creates a new [`Action`] with our snake_case key
fn new() -> Action;
fn new() -> Action
where
Self: Sized;
/// Returns the type name
fn action_type_name(&self) -> &'static str;
}
Expand Down
Loading

0 comments on commit 33d6889

Please sign in to comment.