Skip to content

Commit 33d6889

Browse files
committed
Tiny refactor + begin resman example
1 parent cfbec2d commit 33d6889

File tree

7 files changed

+202
-136
lines changed

7 files changed

+202
-136
lines changed

crates/bevy_dogoap/examples/miner.rs

+1
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ fn print_current_local_state(
818818
Option<&GoToSmelterAction>,
819819
Option<&GoToMerchantAction>,
820820
)>,
821+
// action_query: Query<&dyn ActionComponent>,
821822
mut q_child: Query<&mut Text, With<NeedsText>>,
822823
) {
823824
for (entity, hunger, energy, has_ore, has_metal, gold_amount, children) in query.iter() {

crates/bevy_dogoap/examples/resman.rs

+126-72
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ use dogoap::prelude::*;
2323

2424
fn main() {
2525
let mut app = App::new();
26-
register_components!(app, vec![Thirst, CarryingItem]);
26+
// Customer components + actions
27+
register_components!(app, vec![Thirst, CarryingItem, PlacedOrder, OrderReady]);
28+
register_actions!(
29+
app,
30+
vec![DrinkLemonade, PickupLemonade, WaitForOrder, PlaceOrder]
31+
);
32+
// Worker components + actions
33+
register_components!(app, vec![Energy]);
34+
register_actions!(app, vec![Rest]);
2735

2836
app.add_plugins(DefaultPlugins.set(WindowPlugin {
2937
primary_window: Some(Window {
@@ -40,21 +48,41 @@ fn main() {
4048
.run();
4149
}
4250

43-
// LocalFields
51+
// LocalFields for customer
4452

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

56+
#[derive(Component, Clone, EnumComponent)]
57+
struct CarryingItem(Item);
58+
59+
#[derive(Component, Clone, DatumComponent)]
60+
struct PlacedOrder(bool);
61+
4862
#[derive(Component, Clone, DatumComponent)]
49-
struct CarryingItem(usize); // `Items::Lemonade as usize` for example
63+
struct OrderReady(bool);
64+
65+
// Actions for customer
5066

51-
// Actions
67+
#[derive(Component, Clone, Default, ActionComponent)]
68+
struct DrinkLemonade;
69+
70+
#[derive(Component, Clone, Default, ActionComponent)]
71+
struct PickupLemonade;
5272

5373
#[derive(Component, Clone, Default, ActionComponent)]
54-
struct DrinkLemonade();
74+
struct WaitForOrder;
5575

5676
#[derive(Component, Clone, Default, ActionComponent)]
57-
struct PickupLemonade();
77+
struct PlaceOrder;
78+
79+
// DatumComponents for worker
80+
81+
#[derive(Component, Clone, DatumComponent)]
82+
struct Energy(f64);
83+
84+
#[derive(Component, Clone, Default, ActionComponent)]
85+
struct Rest;
5886

5987
// Markers
6088

@@ -67,7 +95,7 @@ struct Customer;
6795
#[derive(Component)]
6896
struct Worker;
6997

70-
#[derive(Clone, Default, Reflect)]
98+
#[derive(Clone, Default, Copy, Reflect)]
7199
enum Item {
72100
#[default]
73101
Nothing,
@@ -87,59 +115,56 @@ struct StateDebugText;
87115
fn setup(mut commands: Commands) {
88116
// Spawn customers
89117
for _i in 0..1 {
90-
let goal = Goal::new().with_req(&Thirst::key(), Compare::LessThanEquals(Datum::F64(1.0)));
91-
92-
let goals = vec![goal.clone()];
118+
let goal = Goal::from_reqs(&[Thirst::is_less(1.0)]);
93119

94120
// Requires us to carry a lemonade, results in us having 10 less thirst + carrying Nothing
95-
let drink_lemonade_action = Action::new(&DrinkLemonade::key())
96-
.with_precondition(
97-
&CarryingItem::key(),
98-
Compare::Equals(Datum::Enum(Item::Lemonade as usize)),
99-
)
100-
.with_effect(
101-
Effect::new(&DrinkLemonade::key())
102-
.with_mutator(Mutator::Set(
103-
CarryingItem::key(),
104-
Datum::Enum(Item::Nothing as usize),
105-
))
106-
.with_mutator(Mutator::Decrement(Thirst::key(), Datum::F64(10.0))),
107-
);
121+
let drink_lemonade_action = DrinkLemonade::new()
122+
.add_precondition(CarryingItem::is(Item::Lemonade))
123+
.add_mutator(CarryingItem::set(Item::Nothing))
124+
.add_mutator(Thirst::decrease(10.0));
108125

109126
// Requires us to not be carrying nothing, and leads to us having a lemonade
110-
let pickup_lemonade_action = Action::new(&PickupLemonade::key())
111-
.with_precondition(
112-
&CarryingItem::key(),
113-
Compare::Equals(Datum::Enum(Item::Nothing as usize)),
114-
)
115-
.with_effect(
116-
Effect::new(&PickupLemonade::key()).with_mutator(Mutator::Set(
117-
CarryingItem::key(),
118-
Datum::Enum(Item::Lemonade as usize),
119-
)),
120-
);
121-
122-
let actions_map = create_action_map!(
123-
(DrinkLemonade, drink_lemonade_action),
124-
(PickupLemonade, pickup_lemonade_action)
125-
);
126-
127-
let initial_state = (Thirst(0.0), CarryingItem(Item::Nothing as usize));
128-
let state = create_state!(Thirst(0.0), CarryingItem(Item::Nothing as usize));
129-
130-
let mut planner = Planner::new(state, goals, actions_map);
127+
let pickup_lemonade_action = PickupLemonade::new()
128+
.add_precondition(CarryingItem::is(Item::Nothing))
129+
.add_mutator(CarryingItem::set(Item::Lemonade));
130+
131+
let wait_for_order_action = WaitForOrder::new()
132+
.add_precondition(PlacedOrder::is(true))
133+
.add_precondition(OrderReady::is(false))
134+
.add_mutator(OrderReady::set(true));
135+
136+
let place_order_action = PlaceOrder::new()
137+
.add_precondition(PlacedOrder::is(false))
138+
.add_mutator(PlacedOrder::set(true));
139+
140+
// Drink Lemonade
141+
// Pickup Lemonade
142+
// Wait for Order
143+
// Place Order
144+
// Go To Order Desk
145+
146+
let (mut planner, components) = create_planner!({
147+
actions: [
148+
(DrinkLemonade, drink_lemonade_action),
149+
(PickupLemonade, pickup_lemonade_action)
150+
],
151+
state: [Thirst(0.0), CarryingItem(Item::Nothing)],
152+
goals: [goal],
153+
});
131154

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

159+
let t = Transform::from_scale(Vec3::splat(2.0));
160+
136161
commands
137162
.spawn((
138163
Agent,
139164
Name::new("Customer"),
140165
Customer,
141166
planner,
142-
initial_state,
167+
components,
143168
TransformBundle::from(Transform::from_xyz(-200.0, -100.0, 1.0)),
144169
))
145170
.with_children(|subcommands| {
@@ -164,11 +189,25 @@ fn setup(mut commands: Commands) {
164189

165190
// Spawn worker
166191
for _i in 0..1 {
192+
let goal = Goal::from_reqs(&[Energy::is_more(1.0)]);
193+
194+
let rest_action = Rest::new()
195+
.add_precondition(Energy::is_less(10.0))
196+
.add_mutator(Energy::increase(50.0));
197+
198+
let (planner, components) = create_planner!({
199+
actions: [(Rest, rest_action)],
200+
state: [Energy(50.0)],
201+
goals: [goal],
202+
});
203+
167204
commands
168205
.spawn((
169206
Agent,
170207
Name::new("Worker"),
171208
Worker,
209+
planner,
210+
components,
172211
TransformBundle::from(Transform::from_xyz(0.0, 0.0, 1.0)),
173212
))
174213
.with_children(|subcommands| {
@@ -214,7 +253,7 @@ fn handle_pickup_lemonade(
214253
match progresses.get_mut(&entity) {
215254
Some(progress) => {
216255
if progress.tick(time.delta()).just_finished() {
217-
state.0 = Item::Lemonade as usize;
256+
state.0 = Item::Lemonade;
218257
commands.entity(entity).remove::<PickupLemonade>();
219258
progresses.remove(&entity);
220259
} else {
@@ -239,7 +278,7 @@ fn handle_drink_lemonade(
239278
match progresses.get_mut(&entity) {
240279
Some(progress) => {
241280
if progress.tick(time.delta()).just_finished() {
242-
state.0 = Item::Nothing as usize;
281+
state.0 = Item::Nothing;
243282
thirst.0 = (thirst.0 - 5.0).max(0.0);
244283

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

268307
fn draw_state_debug(
269-
q_customers: Query<(Entity, &Name, &Thirst, &Children), With<Customer>>,
270-
q_customer_actions: Query<(Option<&DrinkLemonade>, Option<&PickupLemonade>)>,
271-
q_workers: Query<(Entity, &Name, &Children), With<Worker>>,
308+
q_planners: Query<(Entity, &Name, &Children), With<Planner>>,
309+
// q_workers: Query<(Entity, &Name, &Children), With<Worker>>,
310+
q_actions: Query<(Entity, &dyn ActionComponent)>,
311+
q_datums: Query<(Entity, &dyn DatumComponent)>,
272312
// q_worker_actions: Query<(Option<>)>
273313
// q_actions: Query<(
274314
// Option<&IsPlanning>,
@@ -278,42 +318,56 @@ fn draw_state_debug(
278318
// )>,
279319
mut q_child: Query<&mut Text, With<StateDebugText>>,
280320
) {
281-
for (entity, name, thirst, children) in q_customers.iter() {
282-
let thirst = thirst.0;
283-
321+
for (entity, name, children) in q_planners.iter() {
284322
let mut current_action = "Idle";
285323

286-
let (drink_lemonade_action, pickup_lemonade_action) =
287-
q_customer_actions.get(entity).unwrap();
288-
289-
if drink_lemonade_action.is_some() {
290-
current_action = "Drinking Lemonade";
324+
// Get current action, should always be one so grab the first one we find
325+
for (_entity, actions) in q_actions.get(entity).iter() {
326+
for action in actions.iter() {
327+
current_action = action.action_type_name();
328+
break;
329+
}
291330
}
292331

293-
if pickup_lemonade_action.is_some() {
294-
current_action = "Picking up Lemonade";
332+
// Concat all the found DatumComponents for this entity
333+
let mut state: String = "".to_string();
334+
for (_entity, data) in q_datums.get(entity).iter() {
335+
for datum in data.iter() {
336+
state = format!(
337+
"{}\n{}: {}",
338+
state,
339+
datum.field_key().to_string(),
340+
match datum.field_value() {
341+
Datum::Bool(v) => v.to_string(),
342+
Datum::F64(v) => format!("{:.2}", v).to_string(),
343+
Datum::I64(v) => format!("{}", v).to_string(),
344+
Datum::Enum(v) => format!("{}", v).to_string(),
345+
}
346+
);
347+
}
295348
}
296349

350+
// Render it out
297351
for &child in children.iter() {
298352
let mut text = q_child.get_mut(child).unwrap();
299353
text.sections[0].value =
300-
format!("{name}\n{current_action}\nThirst: {thirst:.2}\nEntity: {entity}");
354+
format!("{name}\n{current_action}\nEntity: {entity}\n---\n{state}");
301355
}
302356
}
303-
for (entity, name, children) in q_workers.iter() {
304-
let current_action = "Idle";
357+
// for (entity, name, children) in q_workers.iter() {
358+
// let current_action = "Idle";
305359

306-
// let (is_planning, eat, go_to_mushroom, replicate) = q_actions.get(entity).unwrap();
360+
// // let (is_planning, eat, go_to_mushroom, replicate) = q_actions.get(entity).unwrap();
307361

308-
// if is_planning.is_some() {
309-
// current_action = "Planning...";
310-
// }
362+
// // if is_planning.is_some() {
363+
// // current_action = "Planning...";
364+
// // }
311365

312-
for &child in children.iter() {
313-
let mut text = q_child.get_mut(child).unwrap();
314-
text.sections[0].value = format!("{name}\n{current_action}\nEntity: {entity}");
315-
}
316-
}
366+
// for &child in children.iter() {
367+
// let mut text = q_child.get_mut(child).unwrap();
368+
// text.sections[0].value = format!("{name}\n{current_action}\nEntity: {entity}");
369+
// }
370+
// }
317371
}
318372

319373
fn draw_ui(

crates/bevy_dogoap/src/macros.rs

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ macro_rules! register_components {
5959
};
6060
}
6161

62+
#[macro_export]
63+
macro_rules! register_actions {
64+
($app:ident, vec![$($comp:ty),*]) => {
65+
$(
66+
$app.register_component_as::<dyn ActionComponent, $comp>();
67+
)*
68+
};
69+
}
70+
6271
#[macro_export]
6372
macro_rules! create_goal {
6473
($(($type:ident, $comp:path, $field:expr)),*) => {{

crates/bevy_dogoap/src/prelude.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub use dogoap::prelude::{
88

99
pub use crate::{
1010
create_action_map, create_goal, create_planner, create_state, planner::IsPlanning,
11-
planner::Planner, register_components,
11+
planner::Planner, register_actions, register_components,
1212
};
1313

1414
pub use crate::plugin::DogoapPlugin;

crates/bevy_dogoap/src/traits.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ pub trait DatumComponent: Send + Sync {
5858
/// Action::new("my_action")
5959
/// );
6060
/// ```
61+
#[bevy_trait_query::queryable]
62+
#[reflect_trait]
6163
pub trait ActionComponent: Send + Sync {
6264
/// Gets the action key but in snake_case ("AtLocation" becomes "at_location")
63-
fn key() -> String;
65+
fn key() -> String
66+
where
67+
Self: Sized;
6468
/// Creates a new [`Action`] with our snake_case key
65-
fn new() -> Action;
69+
fn new() -> Action
70+
where
71+
Self: Sized;
6672
/// Returns the type name
6773
fn action_type_name(&self) -> &'static str;
6874
}

0 commit comments

Comments
 (0)